8237842: Separate definitions for default cache line and padding sizes

Reviewed-by: stefank, kvn, stuefe, tschatzl
This commit is contained in:
Aleksey Shipilev 2024-01-04 08:39:50 +00:00
parent 83564ea5f3
commit dd517c6404
25 changed files with 84 additions and 72 deletions

@ -42,8 +42,12 @@ const bool CCallingConventionRequiresIntsAsLongs = false;
// and Operational Models for ARMv8"
#define CPU_MULTI_COPY_ATOMIC
// The expected size in bytes of a cache line.
#define DEFAULT_CACHE_LINE_SIZE 64
// The default padding size for data structures to avoid false sharing.
#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE
// According to the ARMv8 ARM, "Concurrent modification and execution
// of instructions can lead to the resulting instruction performing
// any behavior that can be achieved by executing any sequence of

@ -49,8 +49,12 @@ const bool HaveVFP = true;
// arm32 is not specified as multi-copy-atomic
// So we must not #define CPU_MULTI_COPY_ATOMIC
// The expected size in bytes of a cache line.
#define DEFAULT_CACHE_LINE_SIZE 64
// The default padding size for data structures to avoid false sharing.
#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE
#define STUBROUTINES_MD_HPP "stubRoutines_arm.hpp"
#define INTERP_MASM_MD_HPP "interp_masm_arm.hpp"
#define TEMPLATETABLE_MD_HPP "templateTable_arm.hpp"

@ -48,9 +48,12 @@ const bool CCallingConventionRequiresIntsAsLongs = true;
// PPC64 is not specified as multi-copy-atomic
// So we must not #define CPU_MULTI_COPY_ATOMIC
// The expected size in bytes of a cache line, used to pad data structures.
// The expected size in bytes of a cache line.
#define DEFAULT_CACHE_LINE_SIZE 128
// The default padding size for data structures to avoid false sharing.
#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE
#define SUPPORT_RESERVED_STACK_AREA
// If UseSIGTRAP is active, we only use the poll bit and no polling page.

@ -50,6 +50,10 @@ const bool CCallingConventionRequiresIntsAsLongs = false;
#define USE_POINTERS_TO_REGISTER_IMPL_ARRAY
// The expected size in bytes of a cache line.
#define DEFAULT_CACHE_LINE_SIZE 64
// The default padding size for data structures to avoid false sharing.
#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE
#endif // CPU_RISCV_GLOBALDEFINITIONS_RISCV_HPP

@ -48,6 +48,9 @@ const bool CCallingConventionRequiresIntsAsLongs = true;
// The expected size in bytes of a cache line, used to pad data structures.
#define DEFAULT_CACHE_LINE_SIZE 256
// The default padding size for data structures to avoid false sharing.
#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE
#define SUPPORT_RESERVED_STACK_AREA
#endif // CPU_S390_GLOBALDEFINITIONS_S390_HPP

@ -38,29 +38,18 @@ const bool CCallingConventionRequiresIntsAsLongs = false;
#define CPU_MULTI_COPY_ATOMIC
// The expected size in bytes of a cache line, used to pad data structures.
#if COMPILER1_AND_COMPILER2
#ifdef _LP64
// tiered, 64-bit, large machine
#define DEFAULT_CACHE_LINE_SIZE 128
#define OM_CACHE_LINE_SIZE 64
#else
// tiered, 32-bit, medium machine
#define DEFAULT_CACHE_LINE_SIZE 64
#endif
#elif defined(COMPILER1)
// pure C1, 32-bit, small machine
// i486 was the last Intel chip with 16-byte cache line size
#define DEFAULT_CACHE_LINE_SIZE 32
#elif defined(COMPILER2)
#ifdef _LP64
// pure C2, 64-bit, large machine
#define DEFAULT_CACHE_LINE_SIZE 128
#define OM_CACHE_LINE_SIZE 64
#else
// pure C2, 32-bit, medium machine
#define DEFAULT_CACHE_LINE_SIZE 64
#endif
// The expected size in bytes of a cache line.
#define DEFAULT_CACHE_LINE_SIZE 64
// The default padding size for data structures to avoid false sharing.
#ifdef _LP64
// The common wisdom is that adjacent cache line prefetchers on some hardware
// may pull two cache lines on access, so we have to pessimistically assume twice
// the cache line size for padding. TODO: Check if this is still true for modern
// hardware. If not, DEFAULT_CACHE_LINE_SIZE might as well suffice.
#define DEFAULT_PADDING_SIZE (DEFAULT_CACHE_LINE_SIZE*2)
#else
#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE
#endif
#if defined(COMPILER2)

@ -30,8 +30,12 @@
#define SUPPORTS_NATIVE_CX8
#endif
// The expected size in bytes of a cache line.
#define DEFAULT_CACHE_LINE_SIZE 64
// The default padding size for data structures to avoid false sharing.
#define DEFAULT_PADDING_SIZE DEFAULT_CACHE_LINE_SIZE
#define SUPPORT_MONITOR_COUNT
#include <ffi.h>

@ -167,9 +167,9 @@ private:
size_t _num_buckets;
bool _should_grow;
TaskQueueEntryChunk* volatile* _buckets;
char _pad0[DEFAULT_CACHE_LINE_SIZE];
char _pad0[DEFAULT_PADDING_SIZE];
volatile size_t _size;
char _pad4[DEFAULT_CACHE_LINE_SIZE - sizeof(size_t)];
char _pad4[DEFAULT_PADDING_SIZE - sizeof(size_t)];
size_t bucket_size(size_t bucket) {
return (bucket == 0) ?
@ -232,12 +232,12 @@ private:
ChunkAllocator _chunk_allocator;
char _pad0[DEFAULT_CACHE_LINE_SIZE];
char _pad0[DEFAULT_PADDING_SIZE];
TaskQueueEntryChunk* volatile _free_list; // Linked list of free chunks that can be allocated by users.
char _pad1[DEFAULT_CACHE_LINE_SIZE - sizeof(TaskQueueEntryChunk*)];
char _pad1[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*)];
TaskQueueEntryChunk* volatile _chunk_list; // List of chunks currently containing data.
volatile size_t _chunks_in_chunk_list;
char _pad2[DEFAULT_CACHE_LINE_SIZE - sizeof(TaskQueueEntryChunk*) - sizeof(size_t)];
char _pad2[DEFAULT_PADDING_SIZE - sizeof(TaskQueueEntryChunk*) - sizeof(size_t)];
// Atomically add the given chunk to the list.
void add_chunk_to_list(TaskQueueEntryChunk* volatile* list, TaskQueueEntryChunk* elem);

@ -135,7 +135,7 @@ class G1DirtyCardQueueSet: public PtrQueueSet {
// and install the next list, and meanwhile there can be a thread dealing
// with the previous list.
PausedList* volatile _plist;
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(PausedList*));
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(PausedList*));
NONCOPYABLE(PausedBuffers);
@ -157,19 +157,19 @@ class G1DirtyCardQueueSet: public PtrQueueSet {
HeadTail take_all();
};
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0);
// Upper bound on the number of cards in the completed and paused buffers.
volatile size_t _num_cards;
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(size_t));
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(size_t));
// If the queue contains more cards than configured here, the
// mutator must start doing some of the concurrent refinement work.
volatile size_t _mutator_refinement_threshold;
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(size_t));
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, sizeof(size_t));
// Buffers ready for refinement.
// NonblockingQueue has inner padding of one cache line.
NonblockingQueue<BufferNode, &BufferNode::next_ptr> _completed;
// Add a trailer padding after NonblockingQueue.
DEFINE_PAD_MINUS_SIZE(3, DEFAULT_CACHE_LINE_SIZE, sizeof(BufferNode*));
DEFINE_PAD_MINUS_SIZE(3, DEFAULT_PADDING_SIZE, sizeof(BufferNode*));
// Buffers for which refinement is temporarily paused.
// PausedBuffers has inner padding, including trailer.
PausedBuffers _paused;

@ -125,7 +125,7 @@ class G1MonotonicArena::Segment {
char* _bottom; // Actual data.
// Do not add class member variables beyond this point
static size_t header_size() { return align_up(sizeof(Segment), DEFAULT_CACHE_LINE_SIZE); }
static size_t header_size() { return align_up(sizeof(Segment), DEFAULT_PADDING_SIZE); }
static size_t payload_size(uint slot_size, uint num_slots) {
// The cast (size_t) is required to guard against overflow wrap around.

@ -95,7 +95,7 @@ G1ParScanThreadState::G1ParScanThreadState(G1CollectedHeap* g1h,
// entries, since entry 0 keeps track of surviving bytes for non-young regions.
// We also add a few elements at the beginning and at the end in
// an attempt to eliminate cache contention
const size_t padding_elem_num = (DEFAULT_CACHE_LINE_SIZE / sizeof(size_t));
const size_t padding_elem_num = (DEFAULT_PADDING_SIZE / sizeof(size_t));
size_t array_length = padding_elem_num + _surviving_words_length + padding_elem_num;
_surviving_young_words_base = NEW_C_HEAP_ARRAY(size_t, array_length, mtGC);

@ -66,11 +66,11 @@ public:
// collected (and processed) buffers reverts back to collecting, allowing
// the set to be reused for another round of redirtying.
class G1RedirtyCardsQueueSet : public PtrQueueSet {
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, 0);
BufferNode::Stack _list;
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(size_t));
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, sizeof(size_t));
volatile size_t _entry_count;
DEFINE_PAD_MINUS_SIZE(3, DEFAULT_CACHE_LINE_SIZE, sizeof(BufferNode*));
DEFINE_PAD_MINUS_SIZE(3, DEFAULT_PADDING_SIZE, sizeof(BufferNode*));
BufferNode* _tail;
DEBUG_ONLY(mutable bool _collecting;)

@ -109,10 +109,10 @@ class FreeListAllocator {
typedef LockFreeStack<FreeNode, &next_ptr> Stack;
FreeListConfig* _config;
char _name[DEFAULT_CACHE_LINE_SIZE - sizeof(FreeListConfig*)]; // Use name as padding.
char _name[DEFAULT_PADDING_SIZE - sizeof(FreeListConfig*)]; // Use name as padding.
#define DECLARE_PADDED_MEMBER(Id, Type, Name) \
Type Name; DEFINE_PAD_MINUS_SIZE(Id, DEFAULT_CACHE_LINE_SIZE, sizeof(Type))
Type Name; DEFINE_PAD_MINUS_SIZE(Id, DEFAULT_PADDING_SIZE, sizeof(Type))
DECLARE_PADDED_MEMBER(1, volatile size_t, _free_count);
DECLARE_PADDED_MEMBER(2, Stack, _free_list);
DECLARE_PADDED_MEMBER(3, volatile bool, _transfer_lock);

@ -85,7 +85,7 @@ public:
class SATBMarkQueueSet: public PtrQueueSet {
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, 0);
PaddedEnd<BufferNode::Stack> _list;
volatile size_t _count_and_process_flag;
// These are rarely (if ever) changed, so same cache line as count.
@ -93,7 +93,7 @@ class SATBMarkQueueSet: public PtrQueueSet {
size_t _buffer_enqueue_threshold;
// SATB is only active during marking. Enqueuing is only done when active.
bool _all_active;
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, 4 * sizeof(size_t));
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, 4 * sizeof(size_t));
BufferNode* get_completed_buffer();
void abandon_completed_buffers();

@ -72,9 +72,9 @@ class TaskTerminator : public CHeapObj<mtGC> {
uint _n_threads;
TaskQueueSetSuper* _queue_set;
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0);
volatile uint _offered_termination;
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(volatile uint));
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(volatile uint));
Monitor _blocker;
Thread* _spin_master;

@ -233,11 +233,11 @@ protected:
}
private:
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0);
// Index of the first free element after the last one pushed (mod N).
volatile uint _bottom;
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(uint));
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(uint));
// top() is the index of the oldest pushed element (mod N), and tag()
// is the associated epoch, to distinguish different modifications of
@ -245,7 +245,7 @@ private:
// (_bottom - top()) mod N == N-1; the latter indicates underflow
// during concurrent pop_local/pop_global.
volatile Age _age;
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(Age));
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, sizeof(Age));
NONCOPYABLE(TaskQueueSuper);
@ -396,7 +396,7 @@ private:
// Element array.
E* _elems;
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(E*));
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(E*));
// Queue owner local variables. Not to be accessed by other threads.
static const uint InvalidQueueId = uint(-1);
@ -404,7 +404,7 @@ private:
int _seed; // Current random seed used for selecting a random queue during stealing.
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_CACHE_LINE_SIZE, sizeof(uint) + sizeof(int));
DEFINE_PAD_MINUS_SIZE(2, DEFAULT_PADDING_SIZE, sizeof(uint) + sizeof(int));
public:
int next_random_queue_id();

@ -105,9 +105,9 @@ class JfrVersionSystem : public JfrCHeapObj {
NodePtr synchronize_with(Type version, NodePtr last) const;
DEBUG_ONLY(void assert_state(const Node* node) const;)
struct PaddedTip {
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0);
volatile Type _value;
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(volatile Type));
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(volatile Type));
};
PaddedTip _tip;
NodePtr _head;

@ -40,14 +40,14 @@
// effective only when applied to derived-most (leaf) classes.
// When no args are passed to the base ctor.
template <class T, size_t alignment = DEFAULT_CACHE_LINE_SIZE>
template <class T, size_t alignment = DEFAULT_PADDING_SIZE>
class Padded : public T {
private:
char _pad_buf_[PADDING_SIZE(T, alignment)];
};
// When either 0 or 1 args may be passed to the base ctor.
template <class T, typename Arg1T, size_t alignment = DEFAULT_CACHE_LINE_SIZE>
template <class T, typename Arg1T, size_t alignment = DEFAULT_PADDING_SIZE>
class Padded01 : public T {
public:
Padded01(): T() { }
@ -75,7 +75,7 @@ class PaddedEndImpl<T, /*pad_size*/ 0> : public T {
// minimal amount of padding needed to make the size of the objects be aligned.
// This will help reducing false sharing,
// if the start address is a multiple of alignment.
template <class T, size_t alignment = DEFAULT_CACHE_LINE_SIZE>
template <class T, size_t alignment = DEFAULT_PADDING_SIZE>
class PaddedEnd : public PaddedEndImpl<T, PADDED_END_SIZE(T, alignment)> {
// C++ doesn't allow zero-length arrays. The padding is put in a
// super class that is specialized for the pad_size == 0 case.
@ -89,7 +89,7 @@ class PaddedEnd : public PaddedEndImpl<T, PADDED_END_SIZE(T, alignment)> {
// Helper class to create an array of PaddedEnd<T> objects. All elements will
// start at a multiple of alignment and the size will be aligned to alignment.
template <class T, MEMFLAGS flags, size_t alignment = DEFAULT_CACHE_LINE_SIZE>
template <class T, MEMFLAGS flags, size_t alignment = DEFAULT_PADDING_SIZE>
class PaddedArray {
public:
// Creates an aligned padded array.
@ -100,7 +100,7 @@ class PaddedArray {
// Helper class to create an array of references to arrays of primitive types
// Both the array of references and the data arrays are aligned to the given
// alignment. The allocated memory is zero-filled.
template <class T, MEMFLAGS flags, size_t alignment = DEFAULT_CACHE_LINE_SIZE>
template <class T, MEMFLAGS flags, size_t alignment = DEFAULT_PADDING_SIZE>
class Padded2DArray {
public:
// Creates an aligned padded 2D array.
@ -112,7 +112,7 @@ class Padded2DArray {
// Helper class to create an array of T objects. The array as a whole will
// start at a multiple of alignment and its size will be aligned to alignment.
template <class T, MEMFLAGS flags, size_t alignment = DEFAULT_CACHE_LINE_SIZE>
template <class T, MEMFLAGS flags, size_t alignment = DEFAULT_PADDING_SIZE>
class PaddedPrimitiveArray {
public:
static T* create_unfreeable(size_t length);

@ -221,7 +221,7 @@ class Monitor : public Mutex {
class PaddedMutex : public Mutex {
enum {
CACHE_LINE_PADDING = (int)DEFAULT_CACHE_LINE_SIZE - (int)sizeof(Mutex),
CACHE_LINE_PADDING = (int)DEFAULT_PADDING_SIZE - (int)sizeof(Mutex),
PADDING_LEN = CACHE_LINE_PADDING > 0 ? CACHE_LINE_PADDING : 1
};
char _padding[PADDING_LEN];
@ -232,7 +232,7 @@ public:
class PaddedMonitor : public Monitor {
enum {
CACHE_LINE_PADDING = (int)DEFAULT_CACHE_LINE_SIZE - (int)sizeof(Monitor),
CACHE_LINE_PADDING = (int)DEFAULT_PADDING_SIZE - (int)sizeof(Monitor),
PADDING_LEN = CACHE_LINE_PADDING > 0 ? CACHE_LINE_PADDING : 1
};
char _padding[PADDING_LEN];

@ -121,11 +121,7 @@ class ObjectWaiter : public StackObj {
// intptr_t. There's no reason to use a 64-bit type for this field
// in a 64-bit JVM.
#ifndef OM_CACHE_LINE_SIZE
// Use DEFAULT_CACHE_LINE_SIZE if not already specified for
// the current build platform.
#define OM_CACHE_LINE_SIZE DEFAULT_CACHE_LINE_SIZE
#endif
class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
friend class ObjectSynchronizer;

@ -46,9 +46,9 @@ class GlobalCounter : public AllStatic {
// Since do not know what we will end up next to in BSS, we make sure the
// counter is on a separate cacheline.
struct PaddedCounter {
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0);
volatile uintx _counter;
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(volatile uintx));
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(volatile uintx));
};
// The global counter

@ -602,11 +602,16 @@ const bool support_IRIW_for_not_multiple_copy_atomic_cpu = false;
const bool support_IRIW_for_not_multiple_copy_atomic_cpu = PPC64_ONLY(true) NOT_PPC64(false);
#endif
// The expected size in bytes of a cache line, used to pad data structures.
// The expected size in bytes of a cache line.
#ifndef DEFAULT_CACHE_LINE_SIZE
#error "Platform should define DEFAULT_CACHE_LINE_SIZE"
#endif
// The default padding size for data structures to avoid false sharing.
#ifndef DEFAULT_PADDING_SIZE
#error "Platform should define DEFAULT_PADDING_SIZE"
#endif
//----------------------------------------------------------------------------------------------------
// Utility macros for compilers

@ -62,7 +62,7 @@ template<typename T, T* volatile* (*next_ptr)(T&)>
class NonblockingQueue {
T* volatile _head;
// Padding of one cache line to avoid false sharing.
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, sizeof(T*));
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, sizeof(T*));
T* volatile _tail;
NONCOPYABLE(NonblockingQueue);

@ -38,7 +38,7 @@ private:
// This would insulate from stalls when adjacent cells have returning
// workers and contend over the cache line for current latency-critical
// cell.
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0);
Semaphore _sem;
@ -81,14 +81,14 @@ private:
Cell _cells[CELLS_COUNT];
// Trailing padding to protect the last cell.
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(0, DEFAULT_PADDING_SIZE, 0);
volatile int _barrier_tag;
// Trailing padding to insulate the rest of the barrier from adjacent
// data structures. The leading padding is not needed, as cell padding
// handles this for us.
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_CACHE_LINE_SIZE, 0);
DEFINE_PAD_MINUS_SIZE(1, DEFAULT_PADDING_SIZE, 0);
NONCOPYABLE(GenericWaitBarrier);

@ -239,7 +239,7 @@ static void run_test(BufferNode::Allocator* allocator, CompletedList* cbl) {
}
TEST_VM(BufferNodeAllocatorTest, stress_free_list_allocator) {
const size_t buffer_capacity = DEFAULT_CACHE_LINE_SIZE / sizeof(void*);
const size_t buffer_capacity = DEFAULT_PADDING_SIZE / sizeof(void*);
BufferNode::Allocator allocator("Test Allocator", buffer_capacity);
CompletedList completed;
run_test(&allocator, &completed);