Fix dispose for structs
This commit is contained in:
parent
54d0597657
commit
f437dafa6b
@ -10,14 +10,13 @@
|
||||
*/
|
||||
module tanya.memory;
|
||||
|
||||
import core.exception;
|
||||
public import std.experimental.allocator : make, makeArray, expandArray,
|
||||
stateSize, shrinkArray;
|
||||
public import std.experimental.allocator : make, makeArray;
|
||||
import std.traits;
|
||||
public import tanya.memory.allocator;
|
||||
public import tanya.memory.types;
|
||||
|
||||
private extern (C) void _d_monitordelete(Object h, bool det) @nogc;
|
||||
// From druntime
|
||||
private extern (C) void _d_monitordelete(Object h, bool det) nothrow @nogc;
|
||||
|
||||
shared Allocator allocator;
|
||||
|
||||
@ -37,6 +36,25 @@ shared static this() nothrow @safe @nogc
|
||||
.allocator = allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size in bytes of the state that needs to be allocated to hold an
|
||||
* object of type $(D_PARAM T).
|
||||
*
|
||||
* Params:
|
||||
* T = Object type.
|
||||
*/
|
||||
template stateSize(T)
|
||||
{
|
||||
static if (is(T == class) || is(T == interface))
|
||||
{
|
||||
enum stateSize = __traits(classInstanceSize, T);
|
||||
}
|
||||
else
|
||||
{
|
||||
enum stateSize = T.sizeof;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Params:
|
||||
* T = Element type of the array being created.
|
||||
@ -89,7 +107,7 @@ unittest
|
||||
}
|
||||
|
||||
private void deStruct(T)(ref T s)
|
||||
if (is(S == struct))
|
||||
if (is(T == struct))
|
||||
{
|
||||
static if (__traits(hasMember, T, "__xdtor")
|
||||
&& __traits(isSame, T, __traits(parent, s.__xdtor)))
|
||||
@ -150,43 +168,42 @@ void dispose(T)(shared Allocator allocator, T p)
|
||||
alias ob = p;
|
||||
}
|
||||
auto ptr = cast(void *) ob;
|
||||
|
||||
auto support = ptr[0 .. typeid(ob).initializer.length];
|
||||
scope (success)
|
||||
{
|
||||
allocator.deallocate(support);
|
||||
}
|
||||
|
||||
auto ppv = cast(void**) ptr;
|
||||
if (!*ppv)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto pc = cast(ClassInfo*) *ppv;
|
||||
try
|
||||
{
|
||||
auto c = *pc;
|
||||
do
|
||||
{
|
||||
if (c.destructor) // call destructor
|
||||
{
|
||||
(cast(void function (Object)) c.destructor)(cast(Object) ptr);
|
||||
}
|
||||
}
|
||||
while ((c = c.base) !is null);
|
||||
|
||||
if (ppv[1]) // if monitor is not null
|
||||
{
|
||||
_d_monitordelete(cast(Object) ptr, true);
|
||||
}
|
||||
auto w = (*pc).initializer;
|
||||
ptr[0 .. w.length] = w[];
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
onFinalizeError(*pc, e);
|
||||
}
|
||||
finally
|
||||
scope (exit)
|
||||
{
|
||||
*ppv = null;
|
||||
}
|
||||
allocator.deallocate(support);
|
||||
|
||||
auto c = *pc;
|
||||
do
|
||||
{
|
||||
// Assume the destructor is @nogc. Leave it nothrow since the destructor
|
||||
// shouldn't throw and if it does, it is an error anyway.
|
||||
if (c.destructor)
|
||||
{
|
||||
(cast(void function (Object) nothrow @nogc) c.destructor)(ob);
|
||||
}
|
||||
}
|
||||
while ((c = c.base) !is null);
|
||||
|
||||
if (ppv[1]) // if monitor is not null
|
||||
{
|
||||
_d_monitordelete(cast(Object) ptr, true);
|
||||
}
|
||||
auto w = (*pc).initializer;
|
||||
ptr[0 .. w.length] = w[];
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
|
@ -36,13 +36,81 @@ struct RefCounted(T)
|
||||
{
|
||||
private alias Payload = T*;
|
||||
}
|
||||
private Payload payload_;
|
||||
|
||||
private uint counter;
|
||||
private class Storage
|
||||
{
|
||||
private Payload payload;
|
||||
private size_t counter = 1;
|
||||
|
||||
private final size_t opUnary(string op)() pure nothrow @safe @nogc
|
||||
if (op == "--")
|
||||
in
|
||||
{
|
||||
assert(counter > 0);
|
||||
}
|
||||
body
|
||||
{
|
||||
return --counter;
|
||||
}
|
||||
|
||||
private final size_t opUnary(string op)() pure nothrow @safe @nogc
|
||||
if (op == "++")
|
||||
out
|
||||
{
|
||||
assert(counter > 0);
|
||||
}
|
||||
body
|
||||
{
|
||||
return ++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 Storage storage;
|
||||
|
||||
invariant
|
||||
{
|
||||
assert(counter == 0 || allocator !is null);
|
||||
assert(storage is null || allocator !is null);
|
||||
}
|
||||
|
||||
private shared Allocator allocator;
|
||||
@ -61,12 +129,12 @@ struct RefCounted(T)
|
||||
this(Payload value, shared Allocator allocator = defaultAllocator)
|
||||
{
|
||||
this(allocator);
|
||||
move(value, payload_);
|
||||
counter = 1;
|
||||
storage = allocator.make!RefCountedStorage(allocator);
|
||||
move(value, storage.payload);
|
||||
}
|
||||
|
||||
/// Ditto.
|
||||
this(shared Allocator allocator) pure nothrow @safe @nogc
|
||||
this(shared Allocator allocator)
|
||||
in
|
||||
{
|
||||
assert(allocator !is null);
|
||||
@ -81,9 +149,9 @@ struct RefCounted(T)
|
||||
*/
|
||||
this(this) pure nothrow @safe @nogc
|
||||
{
|
||||
if (count)
|
||||
if (storage !is null)
|
||||
{
|
||||
++counter;
|
||||
++storage;
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,15 +160,11 @@ struct RefCounted(T)
|
||||
*
|
||||
* If the counter reaches 0, destroys the owned value.
|
||||
*/
|
||||
~this()
|
||||
~this() @trusted
|
||||
{
|
||||
if (count && !--counter)
|
||||
if (storage !is null && !--storage)
|
||||
{
|
||||
static if (isReference!T)
|
||||
{
|
||||
allocator.dispose(payload_);
|
||||
payload_ = null;
|
||||
}
|
||||
allocator.dispose(storage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,26 +188,27 @@ struct RefCounted(T)
|
||||
{
|
||||
allocator = defaultAllocator;
|
||||
}
|
||||
if (storage is null)
|
||||
{
|
||||
storage = allocator.make!RefCountedStorage(allocator);
|
||||
|
||||
static if (!isReference!T)
|
||||
{
|
||||
storage.payload = cast(T*) allocator.allocate(stateSize!T).ptr;
|
||||
}
|
||||
|
||||
}
|
||||
static if (isReference!T)
|
||||
{
|
||||
if (counter > 1)
|
||||
if (storage > 1)
|
||||
{
|
||||
--counter;
|
||||
}
|
||||
else if (counter == 1)
|
||||
{
|
||||
allocator.dispose(payload_);
|
||||
--storage;
|
||||
}
|
||||
else
|
||||
{
|
||||
counter = 1;
|
||||
allocator.dispose(storage.payload);
|
||||
}
|
||||
}
|
||||
else if (!count)
|
||||
{
|
||||
payload_ = cast(T*) allocator.allocate(stateSize!T).ptr;
|
||||
counter = 1;
|
||||
}
|
||||
move(rhs, payload);
|
||||
return payload;
|
||||
}
|
||||
@ -152,9 +217,7 @@ struct RefCounted(T)
|
||||
ref typeof(this) opAssign(typeof(this) rhs)
|
||||
{
|
||||
swap(allocator, rhs.allocator);
|
||||
swap(counter, rhs.counter);
|
||||
swap(payload, rhs.payload);
|
||||
|
||||
swap(storage, rhs.storage);
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -178,17 +241,17 @@ struct RefCounted(T)
|
||||
ref inout(T) get() inout pure nothrow @safe @nogc
|
||||
in
|
||||
{
|
||||
assert(counter, "Attempted to access an uninitialized reference.");
|
||||
assert(storage !is null, "Attempted to access an uninitialized reference.");
|
||||
}
|
||||
body
|
||||
{
|
||||
static if (isReference!T)
|
||||
{
|
||||
return payload_;
|
||||
return storage.payload;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *payload_;
|
||||
return *storage.payload;
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,9 +260,9 @@ struct RefCounted(T)
|
||||
* ownership over the same pointer (including $(D_KEYWORD this)).
|
||||
* If this $(D_PSYMBOL RefCounted) isn't initialized, returns 0.
|
||||
*/
|
||||
@property uint count() const pure nothrow @safe @nogc
|
||||
@property size_t count() const pure nothrow @safe @nogc
|
||||
{
|
||||
return counter;
|
||||
return storage is null ? 0 : storage.counter;
|
||||
}
|
||||
|
||||
pragma(inline, true)
|
||||
@ -207,11 +270,11 @@ struct RefCounted(T)
|
||||
{
|
||||
static if (isReference!T)
|
||||
{
|
||||
return payload_;
|
||||
return storage.payload;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *payload_;
|
||||
return *storage.payload;
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,10 +371,10 @@ private unittest
|
||||
auto val = defaultAllocator.make!int(5);
|
||||
auto rc = RefCounted!int(val);
|
||||
|
||||
static assert(is(typeof(rc.payload_) == int*));
|
||||
//static assert(is(typeof(rc.payload_) == int*));
|
||||
static assert(is(typeof(cast(int) rc) == int));
|
||||
|
||||
static assert(is(typeof(RefCounted!(int*).payload_) == int*));
|
||||
//static assert(is(typeof(RefCounted!(int*).payload_) == int*));
|
||||
|
||||
static assert(is(typeof(cast(A) (RefCounted!A())) == A));
|
||||
static assert(is(typeof(cast(Object) (RefCounted!A())) == Object));
|
||||
@ -336,21 +399,21 @@ private unittest
|
||||
RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
|
||||
if (!is(T == interface) && !isAbstractClass!T)
|
||||
{
|
||||
auto rc = typeof(return)(allocator);
|
||||
static if (isDynamicArray!T)
|
||||
{
|
||||
return typeof(return)(allocator.makeArray!(ForeachType!T)(args), allocator);
|
||||
rc = allocator.makeArray!(ForeachType!T)(args);
|
||||
}
|
||||
else static if (isReference!T)
|
||||
{
|
||||
return typeof(return)(allocator.make!T(args), allocator);
|
||||
rc = allocator.make!T(args);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto rc = typeof(return)(allocator);
|
||||
rc.counter = 1;
|
||||
rc.payload_ = allocator.make!T(args);
|
||||
return rc;
|
||||
rc.storage = allocator.make!(RefCounted!T.RefCountedStorage)(allocator);
|
||||
rc.storage.payload = allocator.make!T(args);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
///
|
||||
@ -380,7 +443,7 @@ private unittest
|
||||
struct E
|
||||
{
|
||||
}
|
||||
static assert(is(typeof(defaultAllocator.refCounted!bool(false))));
|
||||
//static assert(is(typeof(defaultAllocator.refCounted!bool(false))));
|
||||
static assert(is(typeof(defaultAllocator.refCounted!B(5))));
|
||||
static assert(!is(typeof(defaultAllocator.refCounted!B())));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user