Make allocators pure.
* Methods allocating/deallocating memory are pure.
* Allocator.instance is pure (once initialized, it always returns
  the same instance).
* defaultAllocator getter property is pure (should be set at the
  beginning, and always return the same instance after that).
This commit is contained in:
Eugen Wissner 2017-07-13 16:01:21 +02:00
parent 839c740cb1
commit 657f4a60d5
4 changed files with 93 additions and 52 deletions

View File

@ -33,7 +33,7 @@ interface Allocator
*
* Returns: Pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared nothrow @nogc;
void[] allocate(const size_t size) shared pure nothrow @nogc;
/**
* Deallocates a memory block.
@ -43,7 +43,7 @@ interface Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc;
bool deallocate(void[] p) shared pure nothrow @nogc;
/**
* Increases or decreases the size of a memory block.
@ -54,7 +54,7 @@ interface Allocator
*
* Returns: Pointer to the allocated memory.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc;
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc;
/**
* Reallocates a memory block in place if possible or returns
@ -69,5 +69,11 @@ interface Allocator
* Returns: $(D_KEYWORD true) if successful, $(D_KEYWORD false) otherwise.
*/
bool reallocateInPlace(ref void[] p, const size_t size)
shared nothrow @nogc;
shared pure nothrow @nogc;
}
package template GetPureInstance(T : Allocator)
{
alias GetPureInstance = shared(T) function()
pure nothrow @nogc;
}

View File

@ -21,6 +21,13 @@ import tanya.memory.allocator;
*/
final class Mallocator : Allocator
{
private alias MallocType = extern (C) void* function(size_t)
pure nothrow @system @nogc;
private alias FreeType = extern (C) void function(void*)
pure nothrow @system @nogc;
private alias ReallocType = extern (C) void* function(void*, size_t)
pure nothrow @system @nogc;
/**
* Allocates $(D_PARAM size) bytes of memory.
*
@ -29,13 +36,13 @@ final class Mallocator : Allocator
*
* Returns: The pointer to the new allocated memory.
*/
void[] allocate(const size_t size) shared nothrow @nogc
void[] allocate(const size_t size) shared pure nothrow @nogc
{
if (size == 0)
{
return null;
}
auto p = malloc(size + psize);
auto p = (cast(MallocType) &malloc)(size + psize);
return p is null ? null : p[psize .. psize + size];
}
@ -59,11 +66,11 @@ final class Mallocator : Allocator
*
* Returns: Whether the deallocation was successful.
*/
bool deallocate(void[] p) shared nothrow @nogc
bool deallocate(void[] p) shared pure nothrow @nogc
{
if (p !is null)
{
free(p.ptr - psize);
(cast(FreeType) &free)(p.ptr - psize);
}
return true;
}
@ -87,7 +94,8 @@ final class Mallocator : Allocator
*
* Returns: $(D_KEYWORD false).
*/
bool reallocateInPlace(ref void[] p, const size_t size) shared nothrow @nogc
bool reallocateInPlace(ref void[] p, const size_t size)
shared pure nothrow @nogc
{
return false;
}
@ -108,7 +116,7 @@ final class Mallocator : Allocator
*
* Returns: Whether the reallocation was successful.
*/
bool reallocate(ref void[] p, const size_t size) shared nothrow @nogc
bool reallocate(ref void[] p, const size_t size) shared pure nothrow @nogc
{
if (size == 0)
{
@ -125,7 +133,7 @@ final class Mallocator : Allocator
}
else
{
auto r = realloc(p.ptr - psize, size + psize);
auto r = (cast(ReallocType) &realloc)(p.ptr - psize, size + psize);
if (r !is null)
{
@ -177,12 +185,7 @@ final class Mallocator : Allocator
assert(Mallocator.instance.alignment == (void*).alignof);
}
/**
* Static allocator instance and initializer.
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property ref shared(Mallocator) instance() @nogc nothrow
static private shared(Mallocator) instantiate() nothrow @nogc
{
if (instance_ is null)
{
@ -198,6 +201,16 @@ final class Mallocator : Allocator
return instance_;
}
/**
* Static allocator instance and initializer.
*
* Returns: The global $(D_PSYMBOL Allocator) instance.
*/
static @property shared(Mallocator) instance() pure nothrow @nogc
{
return (cast(GetPureInstance!Mallocator) &instantiate)();
}
///
@nogc nothrow unittest
{

View File

@ -167,7 +167,7 @@ final class MmapPool : Allocator
- MmapPool.alignment_
- BlockEntry.sizeof * 2
- RegionEntry.sizeof
- pageSize;
- MmapPool.instance.pageSize;
assert(MmapPool.instance.allocate(tooMuchMemory) is null);
assert(MmapPool.instance.allocate(size_t.max) is null);
@ -471,46 +471,52 @@ final class MmapPool : Allocator
MmapPool.instance.deallocate(p);
}
/**
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property ref shared(MmapPool) instance() nothrow @nogc
static private shared(MmapPool) instantiate() nothrow @nogc
{
if (instance_ is null)
{
// Get system dependend page size.
version (Posix)
{
pageSize_ = sysconf(_SC_PAGE_SIZE);
if (pageSize_ < 65536)
size_t pageSize = sysconf(_SC_PAGE_SIZE);
if (pageSize < 65536)
{
pageSize_ = pageSize_ * 65536 / pageSize_;
pageSize = pageSize * 65536 / pageSize;
}
}
else version (Windows)
{
SYSTEM_INFO si;
GetSystemInfo(&si);
pageSize_ = si.dwPageSize;
size_t pageSize = si.dwPageSize;
}
const instanceSize = addAlignment(__traits(classInstanceSize,
MmapPool));
Region head; // Will become soon our region list head
void* data = initializeRegion(instanceSize, head);
void* data = initializeRegion(instanceSize, head, pageSize);
if (data !is null)
{
memcpy(data, typeid(MmapPool).initializer.ptr, instanceSize);
instance_ = cast(shared MmapPool) data;
instance_.head = head;
instance_.pageSize = pageSize;
}
}
return instance_;
}
/**
* Static allocator instance and initializer.
*
* Returns: Global $(D_PSYMBOL MmapPool) instance.
*/
static @property shared(MmapPool) instance() pure nothrow @nogc
{
return (cast(GetPureInstance!MmapPool) &instantiate)();
}
///
nothrow unittest
{
@ -526,10 +532,12 @@ final class MmapPool : Allocator
*
* Returns: A pointer to the data.
*/
private static void* initializeRegion(const size_t size, ref Region head)
private static void* initializeRegion(const size_t size,
ref Region head,
const size_t pageSize)
pure nothrow @nogc
{
const regionSize = calculateRegionSize(size);
const regionSize = calculateRegionSize(size, pageSize);
if (regionSize < size)
{
return null;
@ -578,7 +586,7 @@ final class MmapPool : Allocator
private void* initializeRegion(const size_t size) shared pure nothrow @nogc
{
return initializeRegion(size, head);
return initializeRegion(size, this.head, this.pageSize);
}
/*
@ -595,10 +603,12 @@ final class MmapPool : Allocator
/*
* Params:
* x = Required space.
* pageSize = Page size.
*
* Returns: Minimum region size (a multiple of $(D_PSYMBOL pageSize)).
*/
private static size_t calculateRegionSize(const size_t x)
private static size_t calculateRegionSize(ref const size_t x,
ref const size_t pageSize)
pure nothrow @safe @nogc
{
return (x + RegionEntry.sizeof + BlockEntry.sizeof * 2)
@ -618,18 +628,10 @@ final class MmapPool : Allocator
assert(MmapPool.instance.alignment == MmapPool.alignment_);
}
private static @property size_t pageSize() pure nothrow @trusted @nogc
{
const pageSize = function size_t() nothrow @safe @nogc {
return pageSize_;
};
return (cast(size_t function() pure nothrow @safe @nogc) pageSize)();
}
private enum uint alignment_ = 8;
private shared static MmapPool instance_;
private shared static size_t pageSize_;
private shared size_t pageSize;
private shared struct RegionEntry
{

View File

@ -36,7 +36,7 @@ mixin template DefaultAllocator()
*
* Precondition: $(D_INLINECODE allocator_ !is null)
*/
this(shared Allocator allocator)
this(shared Allocator allocator) pure nothrow @safe @nogc
in
{
assert(allocator !is null);
@ -54,7 +54,7 @@ mixin template DefaultAllocator()
*
* Postcondition: $(D_INLINECODE allocator !is null)
*/
protected @property shared(Allocator) allocator() nothrow @safe @nogc
protected @property shared(Allocator) allocator() pure nothrow @safe @nogc
out (allocator)
{
assert(allocator !is null);
@ -69,7 +69,7 @@ mixin template DefaultAllocator()
}
/// Ditto.
@property shared(Allocator) allocator() const nothrow @trusted @nogc
@property shared(Allocator) allocator() const pure nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
@ -85,25 +85,44 @@ mixin template DefaultAllocator()
}
// From druntime
private extern (C) void _d_monitordelete(Object h, bool det) nothrow @nogc;
extern (C)
private void _d_monitordelete(Object h, bool det) pure nothrow @nogc;
shared Allocator allocator;
shared static this() nothrow @trusted @nogc
shared static this() nothrow @nogc
{
allocator = MmapPool.instance;
}
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
private shared(Allocator) getAllocatorInstance() nothrow @nogc
{
return allocator;
}
/**
* Returns: Default allocator.
*
* Postcondition: $(D_INLINECODE allocator !is null).
*/
@property shared(Allocator) defaultAllocator() pure nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
return allocator;
return (cast(GetPureInstance!Allocator) &getAllocatorInstance)();
}
/**
* Sets the default allocator.
*
* Params:
* allocator = $(D_PSYMBOL Allocator) instance.
*
* Precondition: $(D_INLINECODE allocator !is null).
*/
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
in
{
@ -271,7 +290,8 @@ package(tanya) void[] finalize(T)(ref T p)
// shouldn't throw and if it does, it is an error anyway.
if (c.destructor)
{
(cast(void function (Object) nothrow @safe @nogc) c.destructor)(ob);
alias DtorType = void function(Object) pure nothrow @safe @nogc;
(cast(DtorType) c.destructor)(ob);
}
}
while ((c = c.base) !is null);
@ -322,7 +342,7 @@ private unittest
}
// Works with interfaces.
private unittest
private pure unittest
{
interface I
{