From 657f4a60d51766b7474d588d16918b7cba8dee00 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Thu, 13 Jul 2017 16:01:21 +0200 Subject: [PATCH] Fix #246 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). --- source/tanya/memory/allocator.d | 14 ++++++--- source/tanya/memory/mallocator.d | 39 +++++++++++++++-------- source/tanya/memory/mmappool.d | 54 +++++++++++++++++--------------- source/tanya/memory/package.d | 38 ++++++++++++++++------ 4 files changed, 93 insertions(+), 52 deletions(-) diff --git a/source/tanya/memory/allocator.d b/source/tanya/memory/allocator.d index 369a189..d47d10a 100644 --- a/source/tanya/memory/allocator.d +++ b/source/tanya/memory/allocator.d @@ -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; } diff --git a/source/tanya/memory/mallocator.d b/source/tanya/memory/mallocator.d index 887ade9..ebb8ffc 100644 --- a/source/tanya/memory/mallocator.d +++ b/source/tanya/memory/mallocator.d @@ -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 { diff --git a/source/tanya/memory/mmappool.d b/source/tanya/memory/mmappool.d index 9f59afc..f802247 100644 --- a/source/tanya/memory/mmappool.d +++ b/source/tanya/memory/mmappool.d @@ -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); } /* @@ -594,11 +602,13 @@ final class MmapPool : Allocator /* * Params: - * x = Required space. + * 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 { diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index 5293f24..eef28e4 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -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 {