Constructs a block allocator given a hunk of memory, or a desired capacity in bytes.
If ParentAllocator is not NullAllocator and defines deallocate, the destructor is defined to deallocate the block held.
The alignment offered is user-configurable statically through parameter theAlignment, defaulted to platformAlignment.
If blockSize == chooseAtRuntime, SharedBitmappedBlock offers a read/write property blockSize. It must be set before any use of the allocator. Otherwise (i.e. theBlockSize is a legit constant), blockSize is an alias for theBlockSize. Whether constant or variable, must also be a multiple of alignment. This constraint is asserted statically and dynamically.
Allocates a block with specified alignment a. The alignment must be a power of 2. If a <= alignment, function forwards to allocate. Otherwise, it attempts to overallocate and then adjust the result for proper alignment. In the worst case the slack memory is around two blocks.
Reallocates a block previously allocated with alignedAllocate. Contractions do not occur in place.
Allocates s bytes of memory and returns it, or null if memory could not be allocated.
If the SharedBitmappedBlock object is empty (has no active allocation), allocates all memory within and returns a slice to it. Otherwise, returns null (i.e. no attempt is made to allocate the largest available block).
Allocates s bytes of memory and returns it, or null if memory could not be allocated. allocateFresh behaves just like allocate, the only difference being that this always returns unused(fresh) memory. Although there may still be available space in the SharedBitmappedBlock, allocateFresh could still return null, because all the available blocks have been previously deallocated.
Deallocates the given buffer b, by atomically setting the corresponding bit to 0. b must be valid, and cannot contain multiple adjacent blocks.
Forcibly deallocates all memory allocated by this allocator, making it available for further allocations. Does not return memory to ParentAllocator.
Returns Ternary.yes if no memory is currently allocated with this allocator, otherwise Ternary.no. This method never returns Ternary.unknown.
Expands in place a buffer previously allocated by SharedBitmappedBlock. Expansion fails if the new length exceeds the block size.
Returns the actual bytes allocated when n bytes are requested, i.e. n.roundUpToMultipleOf(blockSize).
Returns Ternary.yes if b belongs to the SharedBitmappedBlock object, Ternary.no otherwise. Never returns Ternary.unkown. (This method is somewhat tolerant in that accepts an interior slice.)
Reallocates a previously-allocated block. Contractions occur in place.
The parent allocator. Depending on whether ParentAllocator holds state or not, this is a member variable or an alias for ParentAllocator.instance.
the length of a block, which must be a multiple of theAlignment
alignment of each block
allocator from which the BitmappedBlock will draw memory. If set to NullAllocator, the storage must be passed via the constructor
Yes.multiblock to support allocations spanning across multiple blocks and No.multiblock to support single block allocations. Although limited by single block allocations, No.multiblock will generally provide higher performance.
1 import std.experimental.allocator.mallocator : Mallocator; 2 import std.experimental.allocator.common : platformAlignment; 3 import std.typecons : Flag, Yes, No; 4 5 // Create 'numThreads' threads, each allocating in parallel a chunk of memory 6 static void testAlloc(Allocator)(ref Allocator a, size_t allocSize) 7 { 8 import core.thread : ThreadGroup; 9 import std.algorithm.sorting : sort; 10 import core.internal.spinlock : SpinLock; 11 12 SpinLock lock = SpinLock(SpinLock.Contention.brief); 13 enum numThreads = 10; 14 void[][numThreads] buf; 15 size_t count = 0; 16 17 // Each threads allocates 'allocSize' 18 void fun() 19 { 20 void[] b = a.allocate(allocSize); 21 assert(b.length == allocSize); 22 23 lock.lock(); 24 scope(exit) lock.unlock(); 25 26 buf[count] = b; 27 count++; 28 } 29 30 auto tg = new ThreadGroup; 31 foreach (i; 0 .. numThreads) 32 { 33 tg.create(&fun); 34 } 35 tg.joinAll(); 36 37 // Sorting the allocations made by each thread, we expect the buffers to be 38 // adjacent inside the SharedBitmappedBlock 39 sort!((a, b) => a.ptr < b.ptr)(buf[0 .. numThreads]); 40 foreach (i; 0 .. numThreads - 1) 41 { 42 assert(buf[i].ptr + a.goodAllocSize(buf[i].length) <= buf[i + 1].ptr); 43 } 44 45 // Deallocate everything 46 foreach (i; 0 .. numThreads) 47 { 48 assert(a.deallocate(buf[i])); 49 } 50 } 51 52 enum blockSize = 64; 53 auto alloc1 = SharedBitmappedBlock!(blockSize, platformAlignment, Mallocator, Yes.multiblock)(1024 * 1024); 54 auto alloc2 = SharedBitmappedBlock!(blockSize, platformAlignment, Mallocator, No.multiblock)(1024 * 1024); 55 testAlloc(alloc1, 2 * blockSize); 56 testAlloc(alloc2, blockSize);
The threadsafe version of the BitmappedBlock. The semantics of the SharedBitmappedBlock are identical to the regular BitmappedBlock.