From 7aa9ac9f4ade415c29c7fc6d17c08433af8d3337 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 16 Apr 2017 20:13:20 +0200 Subject: [PATCH 1/2] Add internal finalize method for finalizing an object without deallocating --- source/tanya/memory/package.d | 67 ++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index 5e05943..352476f 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -11,6 +11,7 @@ module tanya.memory; import core.exception; +import std.algorithm.iteration; public import std.experimental.allocator : make; import std.traits; public import tanya.memory.allocator; @@ -87,8 +88,8 @@ shared Allocator allocator; shared static this() nothrow @trusted @nogc { - import tanya.memory.mallocator; - allocator = Mallocator.instance; + import tanya.memory.mmappool; + allocator = MmapPool.instance; } @property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc @@ -202,41 +203,33 @@ private unittest assert(p is null); } -/** - * Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T). - * It is assumed the respective entities had been allocated with the same - * allocator. - * - * Params: - * T = Type of $(D_PARAM p). - * allocator = Allocator the $(D_PARAM p) was allocated with. - * p = Object or array to be destroyed. +/* + * Destroys the object. + * Returns the memory should be freed. */ -void dispose(T)(shared Allocator allocator, auto ref T* p) +package(tanya) void[] finalize(T)(ref T* p) { static if (hasElaborateDestructor!T) { destroy(*p); } - () @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }(); - p = null; + return (cast(void*) p)[0 .. T.sizeof]; } -/// Ditto. -void dispose(T)(shared Allocator allocator, auto ref T p) +package(tanya) void[] finalize(T)(ref T p) if (is(T == class) || is(T == interface)) { if (p is null) { - return; + return null; } static if (is(T == interface)) { version(Windows) { import core.sys.windows.unknwn : IUnknown; - static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " - ~ __PRETTY_FUNCTION__); + static assert(!is(T : IUnknown), "COM interfaces can't be destroyed in " + ~ __PRETTY_FUNCTION__); } auto ob = cast(Object) p; } @@ -244,19 +237,13 @@ void dispose(T)(shared Allocator allocator, auto ref T p) { alias ob = p; } - auto ptr = cast(void *) ob; - + auto ptr = cast(void*) ob; auto support = ptr[0 .. typeid(ob).initializer.length]; - scope (success) - { - () @trusted { allocator.deallocate(support); }(); - p = null; - } auto ppv = cast(void**) ptr; if (!*ppv) { - return; + return null; } auto pc = cast(ClassInfo*) *ppv; scope (exit) @@ -280,21 +267,35 @@ void dispose(T)(shared Allocator allocator, auto ref T p) { _d_monitordelete(cast(Object) ptr, true); } + return support; } -/// Ditto. -void dispose(T)(shared Allocator allocator, auto ref T[] p) +package(tanya) void[] finalize(T)(ref T[] p) { static if (hasElaborateDestructor!(typeof(p[0]))) { - import std.algorithm.iteration; - p.each!(e => destroy(e)); + p.each!((ref e) => destroy(e)); } - () @trusted { allocator.deallocate(p); }(); + return p; +} + +/** + * Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T). + * It is assumed the respective entities had been allocated with the same + * allocator. + * + * Params: + * T = Type of $(D_PARAM p). + * allocator = Allocator the $(D_PARAM p) was allocated with. + * p = Object or array to be destroyed. + */ +void dispose(T)(shared Allocator allocator, auto ref T p) +{ + () @trusted { allocator.deallocate(finalize(p)); }(); p = null; } -unittest +private unittest { struct S { From 628153e2e88063a9c20b39ec64c05ef0007fb0b9 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Sun, 16 Apr 2017 20:14:04 +0200 Subject: [PATCH 2/2] Make RefCounted work with dynamic arrays --- source/tanya/memory/types.d | 233 +++++++++++++++++++++--------------- 1 file changed, 136 insertions(+), 97 deletions(-) diff --git a/source/tanya/memory/types.d b/source/tanya/memory/types.d index 1207d73..af0f1ac 100644 --- a/source/tanya/memory/types.d +++ b/source/tanya/memory/types.d @@ -14,9 +14,63 @@ import core.exception; import std.algorithm.comparison; import std.algorithm.mutation; import std.conv; +import std.range; import std.traits; import tanya.memory; +package(tanya) final class RefCountedStore(T) +{ + T payload; + size_t counter = 1; + + size_t opUnary(string op)() + if (op == "--" || op == "++") + in + { + assert(counter > 0); + } + body + { + mixin("return " ~ op ~ "counter;"); + } + + int opCmp(size_t counter) + { + if (this.counter > counter) + { + return 1; + } + else if (this.counter < counter) + { + return -1; + } + else + { + return 0; + } + } + + int opEquals(size_t counter) + { + return this.counter == counter; + } +} + +private void separateDeleter(T)(RefCountedStore!T storage, + shared Allocator allocator) +{ + allocator.dispose(storage.payload); + allocator.dispose(storage); +} + +private void unifiedDeleter(T)(RefCountedStore!T storage, + shared Allocator allocator) +{ + auto ptr1 = finalize(storage); + auto ptr2 = finalize(storage.payload); + allocator.deallocate(ptr1.ptr[0 .. ptr1.length + ptr2.length]); +} + /** * Reference-counted object containing a $(D_PARAM T) value as payload. * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and @@ -27,7 +81,7 @@ import tanya.memory; */ struct RefCounted(T) { - static if (is(T == class) || is(T == interface)) + static if (is(T == class) || is(T == interface) || isArray!T) { private alias Payload = T; } @@ -35,70 +89,16 @@ struct RefCounted(T) { private alias Payload = T*; } - - private class Storage - { - private Payload payload; - private size_t counter = 1; - - private final size_t opUnary(string op)() pure nothrow @safe @nogc - if (op == "--" || op == "++") - in - { - assert(counter > 0); - } - body - { - mixin("return " ~ op ~ "counter;"); - } - - private final int opCmp(size_t counter) const pure nothrow @safe @nogc - { - if (this.counter > counter) - { - return 1; - } - else if (this.counter < counter) - { - return -1; - } - else - { - return 0; - } - } - - private final int opEquals(size_t counter) const pure nothrow @safe @nogc - { - return this.counter == counter; - } - } - - private final class RefCountedStorage : Storage - { - private shared Allocator allocator; - - this(shared Allocator allocator) pure nothrow @safe @nogc - in - { - assert(allocator !is null); - } - body - { - this.allocator = allocator; - } - - ~this() nothrow @nogc - { - allocator.dispose(payload); - } - } + private alias Storage = RefCountedStore!Payload; private Storage storage; + private void function(Storage storage, + shared Allocator allocator) @nogc deleter; invariant { assert(storage is null || allocator_ !is null); + assert(storage is null || deleter !is null); } /** @@ -112,11 +112,13 @@ struct RefCounted(T) * * Precondition: $(D_INLINECODE allocator !is null) */ - this(Payload value, shared Allocator allocator = defaultAllocator) + this()(auto ref Payload value, + shared Allocator allocator = defaultAllocator) { this(allocator); - storage = allocator.make!RefCountedStorage(allocator); - move(value, storage.payload); + this.storage = allocator.make!Storage(); + this.deleter = &separateDeleter!Payload; + move(value, this.storage.payload); } /// Ditto. @@ -148,9 +150,9 @@ struct RefCounted(T) */ ~this() { - if (storage !is null && !(storage.counter && --storage)) + if (this.storage !is null && !(this.storage.counter && --this.storage)) { - allocator_.dispose(storage); + deleter(storage, allocator); } } @@ -170,54 +172,48 @@ struct RefCounted(T) * Params: * rhs = $(D_KEYWORD this). */ - ref typeof(this) opAssign(Payload rhs) + ref typeof(this) opAssign()(auto ref Payload rhs) { - if (storage is null) + if (this.storage is null) { - storage = allocator.make!RefCountedStorage(allocator); + this.storage = allocator.make!Storage(); + this.deleter = &separateDeleter!Payload; } - else if (storage > 1) + else if (this.storage > 1) { - --storage; - storage = allocator.make!RefCountedStorage(allocator); - } - else if (cast(RefCountedStorage) storage is null) - { - // Created with refCounted. Always destroyed togethter with the pointer. - assert(storage.counter != 0); - allocator.dispose(storage); - storage = allocator.make!RefCountedStorage(allocator); + --this.storage; + this.storage = allocator.make!Storage(); + this.deleter = &separateDeleter!Payload; } else { - allocator.dispose(storage.payload); + // Created with refCounted. Always destroyed togethter with the pointer. + assert(this.storage.counter != 0); + finalize(this.storage.payload); + this.storage.payload = Payload.init; } - move(rhs, storage.payload); + move(rhs, this.storage.payload); return this; } /// Ditto. ref typeof(this) opAssign(typeof(null)) { - if (storage is null) + if (this.storage is null) { return this; } - else if (storage > 1) + else if (this.storage > 1) { - --storage; - storage = null; - } - else if (cast(RefCountedStorage) storage is null) - { - // Created with refCounted. Always destroyed togethter with the pointer. - assert(storage.counter != 0); - allocator.dispose(storage); - return this; + --this.storage; + this.storage = null; } else { - allocator.dispose(storage.payload); + // Created with refCounted. Always destroyed togethter with the pointer. + assert(this.storage.counter != 0); + finalize(this.storage.payload); + this.storage.payload = Payload.init; } return this; } @@ -225,8 +221,9 @@ struct RefCounted(T) /// Ditto. ref typeof(this) opAssign(typeof(this) rhs) { - swap(allocator_, rhs.allocator_); - swap(storage, rhs.storage); + swap(this.allocator_, rhs.allocator_); + swap(this.storage, rhs.storage); + swap(this.deleter, rhs.deleter); return this; } @@ -380,15 +377,22 @@ private unittest * args = Constructor arguments of $(D_PARAM T). * * Returns: Newly created $(D_PSYMBOL RefCounted!T). + * + * Precondition: $(D_INLINECODE allocator !is null) */ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) if (!is(T == interface) && !isAbstractClass!T - && !isArray!T && !isAssociativeArray!T) + && !isAssociativeArray!T && !isArray!T) +in +{ + assert(allocator !is null); +} +body { auto rc = typeof(return)(allocator); - immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); - immutable size = alignedSize(stateSize!T + storageSize); + const storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); + const size = alignedSize(stateSize!T + storageSize); auto mem = (() @trusted => allocator.allocate(size))(); if (mem is null) @@ -399,7 +403,7 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) { () @trusted { allocator.deallocate(mem); }(); } - rc.storage = emplace!(RefCounted!T.Storage)(mem[0 .. storageSize]); + rc.storage = emplace!((RefCounted!T.Storage))(mem[0 .. storageSize]); static if (is(T == class)) { @@ -410,9 +414,38 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args) auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))(); rc.storage.payload = emplace!T(ptr, args); } + rc.deleter = &unifiedDeleter!(RefCounted!T.Payload); return rc; } +/** + * Constructs a new array with $(D_PARAM size) elements and wraps it in a + * $(D_PSYMBOL RefCounted) using. + * + * Params: + * T = Array type. + * size = Array size. + * allocator = Allocator. + * + * Returns: Newly created $(D_PSYMBOL RefCounted!T). + * + * Precondition: $(D_INLINECODE allocator !is null + * && size <= size_t.max / ElementType!T.sizeof) + */ + RefCounted!T refCounted(T)(shared Allocator allocator, const size_t size) + if (isArray!T) +in +{ + assert(allocator !is null); + assert(size <= size_t.max / ElementType!T.sizeof); +} +body +{ + auto payload = cast(T) allocator.allocate(ElementType!T.sizeof * size); + initializeAll(payload); + return RefCounted!T(payload, allocator); +} + /// unittest { @@ -456,3 +489,9 @@ private @nogc unittest assert(rc.count); } } + +private @nogc unittest +{ + auto rc = defaultAllocator.refCounted!(int[])(5); + assert(rc.length == 5); +}