SharedBitmappedBlock

The threadsafe version of the BitmappedBlock. The semantics of the SharedBitmappedBlock are identical to the regular BitmappedBlock.

Constructors

this
this(ubyte[] data)
this(ubyte[] data, uint blockSize)
this(size_t capacity)
this(ParentAllocator parent, size_t capacity)
this(size_t capacity, uint blockSize)
this(ParentAllocator parent, size_t capacity, uint blockSize)

Constructs a block allocator given a hunk of memory, or a desired capacity in bytes.

  • If ParentAllocator is `NullAllocator`, only the constructor taking data is defined and the user is responsible for freeing data if desired.
  • Otherwise, both constructors are defined. The data-based constructor assumes memory has been allocated with the parent allocator. The capacity-based constructor uses ParentAllocator to allocate an appropriate contiguous hunk of memory. Regardless of the constructor used, the destructor releases the memory by using ParentAllocator.deallocate.

Destructor

~this
~this()

If ParentAllocator is not NullAllocator and defines deallocate, the destructor is defined to deallocate the block held.

Members

Aliases

alignment
alias alignment = theAlignment

The alignment offered is user-configurable statically through parameter theAlignment, defaulted to platformAlignment.

blockSize
alias blockSize = theBlockSize

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.

Functions

alignedAllocate
void[] alignedAllocate(size_t n, uint a)

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.

alignedReallocate
bool alignedReallocate(void[] b, size_t newSize, uint a)

Reallocates a block previously allocated with alignedAllocate. Contractions do not occur in place.

allocate
void[] allocate(size_t s)

Allocates s bytes of memory and returns it, or null if memory could not be allocated.

allocateAll
void[] allocateAll()

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).

allocateFresh
void[] allocateFresh(size_t s)

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.

deallocate
bool deallocate(void[] b)

Deallocates the given buffer b, by atomically setting the corresponding bit to 0. b must be valid, and cannot contain multiple adjacent blocks.

deallocateAll
bool deallocateAll()

Forcibly deallocates all memory allocated by this allocator, making it available for further allocations. Does not return memory to ParentAllocator.

empty
Ternary empty()

Returns Ternary.yes if no memory is currently allocated with this allocator, otherwise Ternary.no. This method never returns Ternary.unknown.

expand
bool expand(void[] b, size_t delta)

Expands in place a buffer previously allocated by SharedBitmappedBlock. Expansion fails if the new length exceeds the block size.

goodAllocSize
size_t goodAllocSize(size_t n)

Returns the actual bytes allocated when n bytes are requested, i.e. n.roundUpToMultipleOf(blockSize).

owns
Ternary owns(void[] b)

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.)

reallocate
bool reallocate(void[] b, size_t newSize)

Reallocates a previously-allocated block. Contractions occur in place.

Variables

parent
ParentAllocator parent;

The parent allocator. Depending on whether ParentAllocator holds state or not, this is a member variable or an alias for ParentAllocator.instance.

Parameters

theBlockSize

the length of a block, which must be a multiple of theAlignment

theAlignment

alignment of each block

ParentAllocator

allocator from which the BitmappedBlock will draw memory. If set to NullAllocator, the storage must be passed via the constructor

f

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.

Examples

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);

Meta