MmapPool: add expand and empty methods.

This commit is contained in:
Eugen Wissner 2017-01-06 23:12:19 +01:00
parent 254b881da6
commit f3d48234c0
2 changed files with 161 additions and 34 deletions

View File

@ -10,13 +10,15 @@
*/
module tanya.memory.allocator;
import std.typecons;
/**
* Abstract class implementing a basic allocator.
*/
interface Allocator
{
/**
* Returns: Alignment.
* Returns: Alignment offered.
*/
@property uint alignment() const shared pure nothrow @safe @nogc;
@ -50,6 +52,26 @@ interface Allocator
* Returns: Pointer to the allocated memory.
*/
bool reallocate(ref void[] p, in size_t size) shared nothrow @nogc;
/**
* Expands a memory block in place.
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool expand(ref void[] p, in size_t size) shared nothrow @nogc;
/**
* Returns $(D Ternary.yes) if no memory is currently allocated from this
* allocator, $(D Ternary.no) if some allocations are currently active, or
* $(D Ternary.unknown) if not supported.
*
* Returns: Whether any memory is currently allocated.
*/
Ternary empty() shared nothrow @nogc;
}
/**

View File

@ -10,9 +10,9 @@
*/
module tanya.memory.mmappool;
import tanya.memory.allocator;
import core.atomic;
import core.stdc.string;
import std.typecons;
import tanya.memory.allocator;
version (Posix)
{
@ -72,7 +72,7 @@ final class MmapPool : Allocator
data = initializeRegion(dataSize);
}
return data is null ? null : data[0..size];
return data is null ? null : data[0 .. size];
}
///
@ -99,7 +99,7 @@ final class MmapPool : Allocator
Block block1;
RegionLoop: for (auto r = head; r !is null; r = r.next)
{
block1 = cast(Block) (cast(void*) r + regionEntrySize);
block1 = cast(Block) (cast(void*) r + RegionEntry.sizeof);
do
{
if (block1.free && block1.size >= size)
@ -113,13 +113,13 @@ final class MmapPool : Allocator
{
return null;
}
else if (block1.size >= size + alignment + blockEntrySize)
else if (block1.size >= size + alignment_ + BlockEntry.sizeof)
{ // Split the block if needed
Block block2 = cast(Block) (cast(void*) block1 + blockEntrySize + size);
Block block2 = cast(Block) (cast(void*) block1 + BlockEntry.sizeof + size);
block2.prev = block1;
block2.next = block1.next;
block2.free = true;
block2.size = block1.size - blockEntrySize - size;
block2.size = block1.size - BlockEntry.sizeof - size;
block2.region = block1.region;
if (block1.next !is null)
@ -132,7 +132,18 @@ final class MmapPool : Allocator
block1.free = false;
block1.region.blocks = block1.region.blocks + 1;
return cast(void*) block1 + blockEntrySize;
return cast(void*) block1 + BlockEntry.sizeof;
}
// Merge block with the next one.
private void mergeNext(Block block) shared const pure nothrow @safe @nogc
{
block.size = block.size + BlockEntry.sizeof + block.next.size;
if (block.next.next !is null)
{
block.next.next.prev = block;
}
block.next = block.next.next;
}
/**
@ -150,7 +161,7 @@ final class MmapPool : Allocator
return true;
}
Block block = cast(Block) (p.ptr - blockEntrySize);
Block block = cast(Block) (p.ptr - BlockEntry.sizeof);
if (block.region.blocks <= 1)
{
if (block.region.prev !is null)
@ -177,16 +188,11 @@ final class MmapPool : Allocator
// Merge blocks if neigbours are free.
if (block.next !is null && block.next.free)
{
block.size = block.size + blockEntrySize + block.next.size;
if (block.next.next !is null)
{
block.next.next.prev = block;
}
block.next = block.next.next;
mergeNext(block);
}
if (block.prev !is null && block.prev.free)
{
block.prev.size = block.prev.size + blockEntrySize + block.size;
block.prev.size = block.prev.size + BlockEntry.sizeof + block.size;
if (block.next !is null)
{
block.next.prev = block.prev;
@ -209,6 +215,96 @@ final class MmapPool : Allocator
assert(MmapPool.instance.deallocate(p));
}
/**
* Expands a memory block in place.
*
* Params:
* p = A pointer to the memory block.
* size = Size of the reallocated block.
*
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool expand(ref void[] p, in size_t size) shared nothrow @nogc
{
if (size <= p.length)
{
return true;
}
if (p is null)
{
return false;
}
Block block1 = cast(Block) (p.ptr - BlockEntry.sizeof);
if (block1.size >= size)
{
// Enough space in the current block. Can happen because of the alignment.
p = p.ptr[0 .. size];
return true;
}
immutable dataSize = addAlignment(size);
immutable delta = dataSize - p.length;
if (block1.next is null || block1.next.size + BlockEntry.sizeof < delta)
{
// It is the last block in the region or the next block is too small.
return false;
}
if (block1.next.size >= delta + alignment_)
{
// We should move the start position of the next block. The order may be
// important because the old block and the new one can overlap.
auto block2 = cast(Block) (p.ptr + dataSize);
block2.free = true;
block2.size = block1.next.size - delta;
block2.region = block1.next.region;
block2.next = block1.next.next;
block2.prev = block1;
block1.size = block1.size + delta;
if (block1.next.next !is null)
{
block1.next.next.prev = block2;
}
block1.next = block2;
}
else
{
// The next block has enough space, but is too small for further
// allocations. Merge it with the current block.
mergeNext(block1);
}
p = p.ptr[0 .. size];
return true;
}
///
nothrow unittest
{
void[] p;
assert(!MmapPool.instance.expand(p, 5));
assert(p is null);
p = MmapPool.instance.allocate(1);
auto orig = p.ptr;
assert(MmapPool.instance.expand(p, 2));
assert(p.length == 2);
assert(p.ptr == orig);
assert(MmapPool.instance.expand(p, 4));
assert(p.length == 4);
assert(p.ptr == orig);
assert(MmapPool.instance.expand(p, 2));
assert(p.length == 4);
assert(p.ptr == orig);
MmapPool.instance.deallocate(p);
}
/**
* Increases or decreases the size of a memory block.
*
@ -296,7 +392,7 @@ final class MmapPool : Allocator
pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
atomicOp!"*="(pageSize, 65536 / pageSize);
pageSize = pageSize * 65536 / pageSize;
}
}
else version (Windows)
@ -326,7 +422,7 @@ final class MmapPool : Allocator
assert(instance is instance);
}
/**
/*
* Initializes a region for one element.
*
* Params:
@ -379,13 +475,13 @@ final class MmapPool : Allocator
head = region;
// Initialize the data block
void* memoryPointer = p + regionEntrySize;
void* memoryPointer = p + RegionEntry.sizeof;
Block block1 = cast(Block) memoryPointer;
block1.size = size;
block1.free = false;
// It is what we want to return
void* data = memoryPointer + blockEntrySize;
void* data = memoryPointer + BlockEntry.sizeof;
// Free block after data
memoryPointer = data + size;
@ -393,26 +489,37 @@ final class MmapPool : Allocator
block1.prev = block2.next = null;
block1.next = block2;
block2.prev = block1;
block2.size = regionSize - size - regionEntrySize - blockEntrySize * 2;
block2.size = regionSize - size - RegionEntry.sizeof - BlockEntry.sizeof * 2;
block2.free = true;
block1.region = block2.region = region;
return data;
}
/// Ditto.
private void* initializeRegion(size_t size) shared nothrow @nogc
{
return initializeRegion(size, head);
}
/**
* Returns $(D Ternary.yes) if no memory is currently allocated from this
* allocator, $(D Ternary.no) if some allocations are currently active, or
* $(D Ternary.unknown) if not supported.
*
* Returns: Whether any memory is currently allocated.
*/
Ternary empty() shared pure nothrow @safe @nogc
{
// MmapPool always owns some memory because it allocates itself.
return Ternary.no;
}
/*
* Params:
* x = Space to be aligned.
*
* Returns: Aligned size of $(D_PARAM x).
*/
pragma(inline)
private static immutable(size_t) addAlignment(size_t x)
pure nothrow @safe @nogc
out (result)
@ -424,13 +531,12 @@ final class MmapPool : Allocator
return (x - 1) / alignment_ * alignment_ + alignment_;
}
/**
/*
* Params:
* x = Required space.
*
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/
pragma(inline)
private static immutable(size_t) calculateRegionSize(size_t x)
nothrow @safe @nogc
out (result)
@ -439,18 +545,20 @@ final class MmapPool : Allocator
}
body
{
x += regionEntrySize + blockEntrySize * 2;
x += RegionEntry.sizeof + BlockEntry.sizeof * 2;
return x / pageSize * pageSize + pageSize;
}
/**
* Returns: Alignment offered.
*/
@property uint alignment() shared const pure nothrow @safe @nogc
{
return alignment_;
}
private enum alignment_ = 8;
private static shared MmapPool instance_;
private shared static MmapPool instance_;
private shared static size_t pageSize;
private shared struct RegionEntry
@ -461,18 +569,15 @@ final class MmapPool : Allocator
size_t size;
}
private alias Region = shared RegionEntry*;
private enum regionEntrySize = 32;
private shared Region head;
private shared struct BlockEntry
{
Block prev;
Block next;
bool free;
size_t size;
Region region;
size_t size;
bool free;
}
private alias Block = shared BlockEntry*;
private enum blockEntrySize = 40;
}