[*] Remove critical section APIs while keeping the original thread-safe implementation using rt futexes
[*] amend port: f30d5203
This commit is contained in:
parent
f30d520343
commit
8f982a15d5
74
o1heap.cpp
74
o1heap.cpp
@ -115,8 +115,7 @@ struct O1HeapInstance
|
|||||||
Fragment *bins[NUM_BINS_MAX]; ///< Smallest fragments are in the bin at index 0.
|
Fragment *bins[NUM_BINS_MAX]; ///< Smallest fragments are in the bin at index 0.
|
||||||
size_t nonempty_bin_mask; ///< Bit 1 represents a non-empty bin; bin at index 0 is for the smallest fragments.
|
size_t nonempty_bin_mask; ///< Bit 1 represents a non-empty bin; bin at index 0 is for the smallest fragments.
|
||||||
|
|
||||||
O1HeapHook critical_section_enter;
|
AuFutexMutex mutex;
|
||||||
O1HeapHook critical_section_leave;
|
|
||||||
|
|
||||||
O1HeapDiagnostics diagnostics;
|
O1HeapDiagnostics diagnostics;
|
||||||
};
|
};
|
||||||
@ -141,22 +140,23 @@ O1HEAP_PRIVATE uint8_t log2Floor(const size_t x)
|
|||||||
O1HEAP_ASSERT(x > 0);
|
O1HEAP_ASSERT(x > 0);
|
||||||
AuUInt8 index {};
|
AuUInt8 index {};
|
||||||
AuBitScanReverse(index, x);
|
AuBitScanReverse(index, x);
|
||||||
return (uint8_t)(((sizeof(x) * CHAR_BIT) - 1U) - index);
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
O1HEAP_PRIVATE uint_fast8_t log2Ceil(const size_t x)
|
O1HEAP_PRIVATE uint_fast8_t log2Ceil(const size_t x)
|
||||||
{
|
{
|
||||||
AuUInt8 index {};
|
AuUInt8 index {};
|
||||||
AuBitScanReverse(index, x - 1);
|
AuBitScanReverse(index, x);
|
||||||
return (x <= 1U) ? 0U : (uint_fast8_t)((sizeof(x) * CHAR_BIT) - (index));
|
if (size_t(1) << size_t(index) == x)
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
return (x <= 1U) ? 0U : index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
O1HEAP_PRIVATE size_t roundUpToPowerOf2(const size_t x)
|
O1HEAP_PRIVATE size_t roundUpToPowerOf2(const size_t x)
|
||||||
{
|
{
|
||||||
O1HEAP_ASSERT(x >= 2U);
|
return AuRoundUpPow2(x);
|
||||||
AuUInt8 index {};
|
|
||||||
AuBitScanReverse(index, x - 1);
|
|
||||||
return ((size_t)1U) << ((sizeof(x) * CHAR_BIT) - (index));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raise 2 into the specified power.
|
/// Raise 2 into the specified power.
|
||||||
@ -168,15 +168,6 @@ O1HEAP_PRIVATE size_t pow2(const uint8_t power)
|
|||||||
return ((size_t)1U) << power;
|
return ((size_t)1U) << power;
|
||||||
}
|
}
|
||||||
|
|
||||||
O1HEAP_PRIVATE void invoke(const O1HeapInstance *const handle, const O1HeapHook &hook);
|
|
||||||
O1HEAP_PRIVATE void invoke(const O1HeapInstance *const handle, const O1HeapHook &hook)
|
|
||||||
{
|
|
||||||
if (hook != NULL)
|
|
||||||
{
|
|
||||||
hook(handle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Links two fragments so that their next/prev pointers point to each other; left goes before right.
|
/// Links two fragments so that their next/prev pointers point to each other; left goes before right.
|
||||||
O1HEAP_PRIVATE void interlink(Fragment *const left, Fragment *const right);
|
O1HEAP_PRIVATE void interlink(Fragment *const left, Fragment *const right);
|
||||||
O1HEAP_PRIVATE void interlink(Fragment *const left, Fragment *const right)
|
O1HEAP_PRIVATE void interlink(Fragment *const left, Fragment *const right)
|
||||||
@ -247,9 +238,7 @@ O1HEAP_PRIVATE void unbin(O1HeapInstance *const handle, const Fragment *const fr
|
|||||||
// ---------------------------------------- PUBLIC API IMPLEMENTATION ----------------------------------------
|
// ---------------------------------------- PUBLIC API IMPLEMENTATION ----------------------------------------
|
||||||
|
|
||||||
O1HeapInstance *o1heapInit(void *const base,
|
O1HeapInstance *o1heapInit(void *const base,
|
||||||
const size_t size,
|
const size_t size)
|
||||||
O1HeapHook critical_section_enter,
|
|
||||||
O1HeapHook critical_section_leave)
|
|
||||||
{
|
{
|
||||||
O1HeapInstance *out = NULL;
|
O1HeapInstance *out = NULL;
|
||||||
if ((base != NULL) && ((((size_t)base) % O1HEAP_ALIGNMENT) == 0U) &&
|
if ((base != NULL) && ((((size_t)base) % O1HEAP_ALIGNMENT) == 0U) &&
|
||||||
@ -262,8 +251,6 @@ O1HeapInstance *o1heapInit(void *const base,
|
|||||||
|
|
||||||
out = (O1HeapInstance *)base;
|
out = (O1HeapInstance *)base;
|
||||||
out->nonempty_bin_mask = 0U;
|
out->nonempty_bin_mask = 0U;
|
||||||
out->critical_section_enter = critical_section_enter;
|
|
||||||
out->critical_section_leave = critical_section_leave;
|
|
||||||
for (size_t i = 0; i < NUM_BINS_MAX; i++)
|
for (size_t i = 0; i < NUM_BINS_MAX; i++)
|
||||||
{
|
{
|
||||||
out->bins[i] = NULL;
|
out->bins[i] = NULL;
|
||||||
@ -329,7 +316,7 @@ void *o1heapAllocate(O1HeapInstance *const handle, const size_t amount)
|
|||||||
O1HEAP_ASSERT(optimal_bin_index < NUM_BINS_MAX);
|
O1HEAP_ASSERT(optimal_bin_index < NUM_BINS_MAX);
|
||||||
const size_t candidate_bin_mask = ~(pow2(optimal_bin_index) - 1U);
|
const size_t candidate_bin_mask = ~(pow2(optimal_bin_index) - 1U);
|
||||||
|
|
||||||
invoke(handle, handle->critical_section_enter);
|
AU_LOCK_GUARD(handle->mutex);
|
||||||
|
|
||||||
// Find the smallest non-empty bin we can use.
|
// Find the smallest non-empty bin we can use.
|
||||||
const size_t suitable_bins = handle->nonempty_bin_mask & candidate_bin_mask;
|
const size_t suitable_bins = handle->nonempty_bin_mask & candidate_bin_mask;
|
||||||
@ -381,30 +368,30 @@ void *o1heapAllocate(O1HeapInstance *const handle, const size_t amount)
|
|||||||
out = ((uint8_t *)frag) + O1HEAP_ALIGNMENT;
|
out = ((uint8_t *)frag) + O1HEAP_ALIGNMENT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
invoke(handle, handle->critical_section_enter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the diagnostics.
|
// Update the diagnostics.
|
||||||
if (O1HEAP_LIKELY(handle->diagnostics.peak_request_size < amount))
|
size_t uOld;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
handle->diagnostics.peak_request_size = amount;
|
uOld = AuAtomicLoad(&handle->diagnostics.peak_request_size);
|
||||||
|
if (uOld < amount)
|
||||||
|
{
|
||||||
|
if (AuAtomicCompareExchange(&handle->diagnostics.peak_request_size, amount, uOld) == uOld)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (O1HEAP_LIKELY((out == NULL) && (amount > 0U)))
|
while (uOld < amount);
|
||||||
|
|
||||||
|
if ((out == NULL) && (amount > 0U))
|
||||||
{
|
{
|
||||||
handle->diagnostics.oom_count++;
|
AuAtomicAdd(&handle->diagnostics.oom_count, uint64_t(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke(handle, handle->critical_section_leave);
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
void o1HeapReleaseCpp(O1HeapInstance *handle)
|
|
||||||
{
|
|
||||||
handle->~O1HeapInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
void o1heapFree(O1HeapInstance *const handle, void *const pointer)
|
void o1heapFree(O1HeapInstance *const handle, void *const pointer)
|
||||||
{
|
{
|
||||||
O1HEAP_ASSERT(handle != NULL);
|
O1HEAP_ASSERT(handle != NULL);
|
||||||
@ -425,7 +412,7 @@ void o1heapFree(O1HeapInstance *const handle, void *const pointer)
|
|||||||
O1HEAP_ASSERT(frag->header.size <= handle->diagnostics.capacity);
|
O1HEAP_ASSERT(frag->header.size <= handle->diagnostics.capacity);
|
||||||
O1HEAP_ASSERT((frag->header.size % FRAGMENT_SIZE_MIN) == 0U);
|
O1HEAP_ASSERT((frag->header.size % FRAGMENT_SIZE_MIN) == 0U);
|
||||||
|
|
||||||
invoke(handle, handle->critical_section_enter);
|
AU_LOCK_GUARD(handle->mutex);
|
||||||
|
|
||||||
// Even if we're going to drop the fragment later, mark it free anyway to prevent double-free.
|
// Even if we're going to drop the fragment later, mark it free anyway to prevent double-free.
|
||||||
frag->header.used = false;
|
frag->header.used = false;
|
||||||
@ -472,8 +459,6 @@ void o1heapFree(O1HeapInstance *const handle, void *const pointer)
|
|||||||
{
|
{
|
||||||
rebin(handle, frag);
|
rebin(handle, frag);
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke(handle, handle->critical_section_leave);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,7 +467,7 @@ bool o1heapDoInvariantsHold(const O1HeapInstance *const handle)
|
|||||||
O1HEAP_ASSERT(handle != NULL);
|
O1HEAP_ASSERT(handle != NULL);
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
|
|
||||||
invoke(handle, handle->critical_section_enter);
|
AU_LOCK_GUARD(handle->mutex);
|
||||||
|
|
||||||
// Check the bin mask consistency.
|
// Check the bin mask consistency.
|
||||||
for (size_t i = 0; i < NUM_BINS_MAX; i++) // Dear compiler, feel free to unroll this loop.
|
for (size_t i = 0; i < NUM_BINS_MAX; i++) // Dear compiler, feel free to unroll this loop.
|
||||||
@ -495,8 +480,6 @@ bool o1heapDoInvariantsHold(const O1HeapInstance *const handle)
|
|||||||
// Create a local copy of the diagnostics struct to check later and release the critical section early.
|
// Create a local copy of the diagnostics struct to check later and release the critical section early.
|
||||||
const O1HeapDiagnostics diag = handle->diagnostics;
|
const O1HeapDiagnostics diag = handle->diagnostics;
|
||||||
|
|
||||||
invoke(handle, handle->critical_section_leave);
|
|
||||||
|
|
||||||
// Capacity check.
|
// Capacity check.
|
||||||
valid = valid && (diag.capacity <= FRAGMENT_SIZE_MAX) && (diag.capacity >= FRAGMENT_SIZE_MIN) &&
|
valid = valid && (diag.capacity <= FRAGMENT_SIZE_MAX) && (diag.capacity >= FRAGMENT_SIZE_MIN) &&
|
||||||
((diag.capacity % FRAGMENT_SIZE_MIN) == 0U);
|
((diag.capacity % FRAGMENT_SIZE_MIN) == 0U);
|
||||||
@ -524,8 +507,7 @@ bool o1heapDoInvariantsHold(const O1HeapInstance *const handle)
|
|||||||
O1HeapDiagnostics o1heapGetDiagnostics(const O1HeapInstance *const handle)
|
O1HeapDiagnostics o1heapGetDiagnostics(const O1HeapInstance *const handle)
|
||||||
{
|
{
|
||||||
O1HEAP_ASSERT(handle != NULL);
|
O1HEAP_ASSERT(handle != NULL);
|
||||||
invoke(handle, handle->critical_section_enter);
|
AU_LOCK_GUARD(handle->mutex);
|
||||||
const O1HeapDiagnostics out = handle->diagnostics;
|
const O1HeapDiagnostics out = handle->diagnostics;
|
||||||
invoke(handle, handle->critical_section_leave);
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
10
o1heap.hpp
10
o1heap.hpp
@ -34,7 +34,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
/// The semantic version number of this distribution.
|
/// The semantic version number of this distribution.
|
||||||
#define O1HEAP_VERSION_MAJOR 1
|
#define O1HEAP_VERSION_MAJOR 1
|
||||||
@ -51,9 +50,6 @@
|
|||||||
/// The definition is private, so the user code can only operate on pointers. This is done to enforce encapsulation.
|
/// The definition is private, so the user code can only operate on pointers. This is done to enforce encapsulation.
|
||||||
typedef struct O1HeapInstance O1HeapInstance;
|
typedef struct O1HeapInstance O1HeapInstance;
|
||||||
|
|
||||||
/// A hook function invoked by the allocator. NULL hooks are silently not invoked (not an error).
|
|
||||||
using O1HeapHook = std::function<void(const O1HeapInstance *const handle)>;
|
|
||||||
|
|
||||||
/// Runtime diagnostic information. This information can be used to facilitate runtime self-testing,
|
/// Runtime diagnostic information. This information can be used to facilitate runtime self-testing,
|
||||||
/// as required by certain safety-critical development guidelines.
|
/// as required by certain safety-critical development guidelines.
|
||||||
/// If assertion checks are not disabled, the library will perform automatic runtime self-diagnostics that trigger
|
/// If assertion checks are not disabled, the library will perform automatic runtime self-diagnostics that trigger
|
||||||
@ -109,9 +105,7 @@ typedef struct
|
|||||||
///
|
///
|
||||||
/// The time complexity is unspecified.
|
/// The time complexity is unspecified.
|
||||||
O1HeapInstance *o1heapInit(void *const base,
|
O1HeapInstance *o1heapInit(void *const base,
|
||||||
const size_t size,
|
const size_t size);
|
||||||
O1HeapHook critical_section_enter,
|
|
||||||
O1HeapHook critical_section_leave);
|
|
||||||
|
|
||||||
/// The semantics follows malloc() with additional guarantees the full list of which is provided below.
|
/// The semantics follows malloc() with additional guarantees the full list of which is provided below.
|
||||||
///
|
///
|
||||||
@ -138,8 +132,6 @@ void *o1heapAllocate(O1HeapInstance *const handle, const size_t amount);
|
|||||||
/// The function may invoke critical_section_enter and critical_section_leave at most once each (NULL hooks ignored).
|
/// The function may invoke critical_section_enter and critical_section_leave at most once each (NULL hooks ignored).
|
||||||
void o1heapFree(O1HeapInstance *const handle, void *const pointer);
|
void o1heapFree(O1HeapInstance *const handle, void *const pointer);
|
||||||
|
|
||||||
void o1HeapReleaseCpp(O1HeapInstance *handle);
|
|
||||||
|
|
||||||
/// Performs a basic sanity check on the heap.
|
/// Performs a basic sanity check on the heap.
|
||||||
/// This function can be used as a weak but fast method of heap corruption detection.
|
/// This function can be used as a weak but fast method of heap corruption detection.
|
||||||
/// It invokes critical_section_enter once (unless NULL) and then critical_section_leave once (unless NULL).
|
/// It invokes critical_section_enter once (unless NULL) and then critical_section_leave once (unless NULL).
|
||||||
|
Loading…
Reference in New Issue
Block a user