Merge branch 'master' into utf8string

This commit is contained in:
Eugen Wissner 2017-04-16 20:15:11 +02:00
commit 4b1cd2cbfd
2 changed files with 170 additions and 130 deletions

View File

@ -11,6 +11,7 @@
module tanya.memory; module tanya.memory;
import core.exception; import core.exception;
import std.algorithm.iteration;
public import std.experimental.allocator : make; public import std.experimental.allocator : make;
import std.traits; import std.traits;
public import tanya.memory.allocator; public import tanya.memory.allocator;
@ -87,8 +88,8 @@ shared Allocator allocator;
shared static this() nothrow @trusted @nogc shared static this() nothrow @trusted @nogc
{ {
import tanya.memory.mallocator; import tanya.memory.mmappool;
allocator = Mallocator.instance; allocator = MmapPool.instance;
} }
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc @property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
@ -202,41 +203,33 @@ private unittest
assert(p is null); assert(p is null);
} }
/** /*
* Destroys and deallocates $(D_PARAM p) of type $(D_PARAM T). * Destroys the object.
* It is assumed the respective entities had been allocated with the same * Returns the memory should be freed.
* 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) package(tanya) void[] finalize(T)(ref T* p)
{ {
static if (hasElaborateDestructor!T) static if (hasElaborateDestructor!T)
{ {
destroy(*p); destroy(*p);
} }
() @trusted { allocator.deallocate((cast(void*) p)[0 .. T.sizeof]); }(); return (cast(void*) p)[0 .. T.sizeof];
p = null;
} }
/// Ditto. package(tanya) void[] finalize(T)(ref T p)
void dispose(T)(shared Allocator allocator, auto ref T p)
if (is(T == class) || is(T == interface)) if (is(T == class) || is(T == interface))
{ {
if (p is null) if (p is null)
{ {
return; return null;
} }
static if (is(T == interface)) static if (is(T == interface))
{ {
version(Windows) version(Windows)
{ {
import core.sys.windows.unknwn : IUnknown; import core.sys.windows.unknwn : IUnknown;
static assert(!is(T: IUnknown), "COM interfaces can't be destroyed in " static assert(!is(T : IUnknown), "COM interfaces can't be destroyed in "
~ __PRETTY_FUNCTION__); ~ __PRETTY_FUNCTION__);
} }
auto ob = cast(Object) p; auto ob = cast(Object) p;
} }
@ -244,19 +237,13 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
{ {
alias ob = p; alias ob = p;
} }
auto ptr = cast(void *) ob; auto ptr = cast(void*) ob;
auto support = ptr[0 .. typeid(ob).initializer.length]; auto support = ptr[0 .. typeid(ob).initializer.length];
scope (success)
{
() @trusted { allocator.deallocate(support); }();
p = null;
}
auto ppv = cast(void**) ptr; auto ppv = cast(void**) ptr;
if (!*ppv) if (!*ppv)
{ {
return; return null;
} }
auto pc = cast(ClassInfo*) *ppv; auto pc = cast(ClassInfo*) *ppv;
scope (exit) scope (exit)
@ -280,21 +267,35 @@ void dispose(T)(shared Allocator allocator, auto ref T p)
{ {
_d_monitordelete(cast(Object) ptr, true); _d_monitordelete(cast(Object) ptr, true);
} }
return support;
} }
/// Ditto. package(tanya) void[] finalize(T)(ref T[] p)
void dispose(T)(shared Allocator allocator, auto ref T[] p)
{ {
static if (hasElaborateDestructor!(typeof(p[0]))) static if (hasElaborateDestructor!(typeof(p[0])))
{ {
import std.algorithm.iteration; p.each!((ref e) => destroy(e));
p.each!(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; p = null;
} }
unittest private unittest
{ {
struct S struct S
{ {

View File

@ -14,9 +14,63 @@ import core.exception;
import std.algorithm.comparison; import std.algorithm.comparison;
import std.algorithm.mutation; import std.algorithm.mutation;
import std.conv; import std.conv;
import std.range;
import std.traits; import std.traits;
import tanya.memory; 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. * Reference-counted object containing a $(D_PARAM T) value as payload.
* $(D_PSYMBOL RefCounted) keeps track of all references of an object, and * $(D_PSYMBOL RefCounted) keeps track of all references of an object, and
@ -27,7 +81,7 @@ import tanya.memory;
*/ */
struct RefCounted(T) 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; private alias Payload = T;
} }
@ -35,70 +89,16 @@ struct RefCounted(T)
{ {
private alias Payload = T*; private alias Payload = T*;
} }
private alias Storage = RefCountedStore!Payload;
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 Storage storage; private Storage storage;
private void function(Storage storage,
shared Allocator allocator) @nogc deleter;
invariant invariant
{ {
assert(storage is null || allocator_ !is null); 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) * Precondition: $(D_INLINECODE allocator !is null)
*/ */
this(Payload value, shared Allocator allocator = defaultAllocator) this()(auto ref Payload value,
shared Allocator allocator = defaultAllocator)
{ {
this(allocator); this(allocator);
storage = allocator.make!RefCountedStorage(allocator); this.storage = allocator.make!Storage();
move(value, storage.payload); this.deleter = &separateDeleter!Payload;
move(value, this.storage.payload);
} }
/// Ditto. /// Ditto.
@ -148,9 +150,9 @@ struct RefCounted(T)
*/ */
~this() ~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: * Params:
* rhs = $(D_KEYWORD this). * 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; --this.storage;
storage = allocator.make!RefCountedStorage(allocator); this.storage = allocator.make!Storage();
} this.deleter = &separateDeleter!Payload;
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);
} }
else 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; return this;
} }
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(null)) ref typeof(this) opAssign(typeof(null))
{ {
if (storage is null) if (this.storage is null)
{ {
return this; return this;
} }
else if (storage > 1) else if (this.storage > 1)
{ {
--storage; --this.storage;
storage = null; this.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;
} }
else 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; return this;
} }
@ -225,8 +221,9 @@ struct RefCounted(T)
/// Ditto. /// Ditto.
ref typeof(this) opAssign(typeof(this) rhs) ref typeof(this) opAssign(typeof(this) rhs)
{ {
swap(allocator_, rhs.allocator_); swap(this.allocator_, rhs.allocator_);
swap(storage, rhs.storage); swap(this.storage, rhs.storage);
swap(this.deleter, rhs.deleter);
return this; return this;
} }
@ -380,15 +377,22 @@ private unittest
* args = Constructor arguments of $(D_PARAM T). * args = Constructor arguments of $(D_PARAM T).
* *
* Returns: Newly created $(D_PSYMBOL RefCounted!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) RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
if (!is(T == interface) && !isAbstractClass!T 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); auto rc = typeof(return)(allocator);
immutable storageSize = alignedSize(stateSize!(RefCounted!T.Storage)); const storageSize = alignedSize(stateSize!(RefCounted!T.Storage));
immutable size = alignedSize(stateSize!T + storageSize); const size = alignedSize(stateSize!T + storageSize);
auto mem = (() @trusted => allocator.allocate(size))(); auto mem = (() @trusted => allocator.allocate(size))();
if (mem is null) if (mem is null)
@ -399,7 +403,7 @@ RefCounted!T refCounted(T, A...)(shared Allocator allocator, auto ref A args)
{ {
() @trusted { allocator.deallocate(mem); }(); () @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)) 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))(); auto ptr = (() @trusted => (cast(T*) mem[storageSize .. $].ptr))();
rc.storage.payload = emplace!T(ptr, args); rc.storage.payload = emplace!T(ptr, args);
} }
rc.deleter = &unifiedDeleter!(RefCounted!T.Payload);
return rc; 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 unittest
{ {
@ -456,3 +489,9 @@ private @nogc unittest
assert(rc.count); assert(rc.count);
} }
} }
private @nogc unittest
{
auto rc = defaultAllocator.refCounted!(int[])(5);
assert(rc.length == 5);
}