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;
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
{

View File

@ -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);
}