tanya/source/tanya/memory/package.d

337 lines
7.4 KiB
D
Raw Normal View History

2016-08-24 18:15:21 +02:00
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Copyright: Eugene Wissner 2016-2017.
2016-08-24 18:15:21 +02:00
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
* Mozilla Public License, v. 2.0).
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
*/
2016-10-05 13:12:50 +02:00
module tanya.memory;
2016-08-24 18:15:21 +02:00
2016-12-22 22:05:48 +01:00
import core.exception;
import std.algorithm.iteration;
2017-03-19 06:54:59 +01:00
public import std.experimental.allocator : make;
2016-12-07 11:01:51 +01:00
import std.traits;
2016-12-04 14:05:53 +01:00
public import tanya.memory.allocator;
import tanya.memory.mmappool;
/**
* The mixin generates common methods for classes and structs using
* allocators. It provides a protected member, constructor and a read-only property,
* that checks if an allocator was already set and sets it to the default
* one, if not (useful for structs which don't have a default constructor).
*/
mixin template DefaultAllocator()
{
2017-03-19 06:10:27 +01:00
/// Allocator.
protected shared Allocator allocator_;
2017-03-19 06:10:27 +01:00
/**
* Params:
* allocator = The allocator should be used.
*
* Precondition: $(D_INLINECODE allocator_ !is null)
*/
this(shared Allocator allocator)
in
{
assert(allocator !is null);
}
body
{
this.allocator_ = allocator;
}
2017-03-19 06:10:27 +01:00
/**
* This property checks if the allocator was set in the constructor
* and sets it to the default one, if not.
*
* Returns: Used allocator.
*
* Postcondition: $(D_INLINECODE allocator !is null)
*/
protected @property shared(Allocator) allocator() nothrow @safe @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
if (allocator_ is null)
{
allocator_ = defaultAllocator;
}
return allocator_;
}
2017-03-19 06:10:27 +01:00
/// Ditto.
@property shared(Allocator) allocator() const nothrow @trusted @nogc
out (allocator)
{
assert(allocator !is null);
}
body
{
if (allocator_ is null)
{
return defaultAllocator;
}
return cast(shared Allocator) allocator_;
}
}
2016-12-13 10:58:11 +01:00
// From druntime
private extern (C) void _d_monitordelete(Object h, bool det) nothrow @nogc;
2016-12-07 11:01:51 +01:00
shared Allocator allocator;
shared static this() nothrow @trusted @nogc
{
allocator = MmapPool.instance;
}
@property ref shared(Allocator) defaultAllocator() nothrow @safe @nogc
out (allocator)
{
2017-03-19 06:10:27 +01:00
assert(allocator !is null);
}
body
{
2017-03-19 06:10:27 +01:00
return allocator;
}
@property void defaultAllocator(shared(Allocator) allocator) nothrow @safe @nogc
in
{
2017-03-19 06:10:27 +01:00
assert(allocator !is null);
}
body
{
2017-03-19 06:10:27 +01:00
.allocator = allocator;
}
private nothrow @nogc unittest
{
import tanya.memory.mallocator;
auto oldAllocator = defaultAllocator;
defaultAllocator = Mallocator.instance;
assert(defaultAllocator is Mallocator.instance);
defaultAllocator = oldAllocator;
}
2016-12-13 10:58:11 +01:00
/**
* Returns the size in bytes of the state that needs to be allocated to hold an
* object of type $(D_PARAM T).
*
* Params:
2017-03-19 06:10:27 +01:00
* T = Object type.
2016-12-13 10:58:11 +01:00
*/
template stateSize(T)
{
2017-03-19 06:10:27 +01:00
static if (is(T == class) || is(T == interface))
{
enum stateSize = __traits(classInstanceSize, T);
}
else
{
enum stateSize = T.sizeof;
}
2016-12-13 10:58:11 +01:00
}
/**
2016-12-22 22:05:48 +01:00
* Params:
2017-03-19 06:10:27 +01:00
* size = Raw size.
* alignment = Alignment.
2016-12-22 22:05:48 +01:00
*
* Returns: Aligned size.
*/
size_t alignedSize(const size_t size, const size_t alignment = 8)
pure nothrow @safe @nogc
2016-12-22 22:05:48 +01:00
{
2017-03-19 06:10:27 +01:00
return (size - 1) / alignment * alignment + alignment;
2016-12-22 22:05:48 +01:00
}
/**
* Internal function used to create, resize or destroy a dynamic array. It
2017-03-19 06:10:27 +01:00
* may throw $(D_PSYMBOL OutOfMemoryError). The new
* allocated part of the array isn't initialized. This function can be trusted
* only in the data structures that can ensure that the array is
* allocated/rellocated/deallocated with the same allocator.
2016-12-22 22:05:48 +01:00
*
* Params:
2017-03-19 06:10:27 +01:00
* T = Element type of the array being created.
* allocator = The allocator used for getting memory.
* array = A reference to the array being changed.
* length = New array length.
*
2017-03-19 06:10:27 +01:00
* Returns: $(D_PARAM array).
*/
package(tanya) T[] resize(T)(shared Allocator allocator,
auto ref T[] array,
const size_t length) @trusted
{
if (length == 0)
{
if (allocator.deallocate(array))
{
return null;
}
else
{
onOutOfMemoryErrorNoGC();
}
}
void[] buf = array;
2017-03-19 06:10:27 +01:00
if (!allocator.reallocate(buf, length * T.sizeof))
{
onOutOfMemoryErrorNoGC();
2017-03-19 06:10:27 +01:00
}
// Casting from void[] is unsafe, but we know we cast to the original type.
array = cast(T[]) buf;
2016-12-22 22:05:48 +01:00
2017-03-19 06:10:27 +01:00
return array;
}
2017-03-19 06:10:27 +01:00
private unittest
{
2017-03-19 06:10:27 +01:00
int[] p;
2017-03-19 06:10:27 +01:00
p = defaultAllocator.resize(p, 20);
assert(p.length == 20);
2017-03-19 06:10:27 +01:00
p = defaultAllocator.resize(p, 30);
assert(p.length == 30);
2017-03-19 06:10:27 +01:00
p = defaultAllocator.resize(p, 10);
assert(p.length == 10);
2017-03-19 06:10:27 +01:00
p = defaultAllocator.resize(p, 0);
assert(p is null);
}
2016-12-07 11:01:51 +01:00
/*
* Destroys the object.
* Returns the memory should be freed.
2016-12-07 11:01:51 +01:00
*/
package(tanya) void[] finalize(T)(ref T* p)
2016-12-07 11:01:51 +01:00
{
2017-03-19 06:10:27 +01:00
static if (hasElaborateDestructor!T)
{
destroy(*p);
}
return (cast(void*) p)[0 .. T.sizeof];
2016-12-07 11:01:51 +01:00
}
package(tanya) void[] finalize(T)(ref T p)
2017-03-19 06:10:27 +01:00
if (is(T == class) || is(T == interface))
2016-12-07 11:01:51 +01:00
{
2017-03-19 06:10:27 +01:00
if (p is null)
{
return null;
2017-03-19 06:10:27 +01:00
}
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__);
2017-03-19 06:10:27 +01:00
}
auto ob = cast(Object) p;
}
else
{
alias ob = p;
}
auto ptr = cast(void*) ob;
2017-03-19 06:10:27 +01:00
auto support = ptr[0 .. typeid(ob).initializer.length];
2016-12-07 11:01:51 +01:00
2017-03-19 06:10:27 +01:00
auto ppv = cast(void**) ptr;
if (!*ppv)
{
return null;
2017-03-19 06:10:27 +01:00
}
auto pc = cast(ClassInfo*) *ppv;
scope (exit)
{
*ppv = null;
}
2016-12-07 11:01:51 +01:00
2017-03-19 06:10:27 +01:00
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 @safe @nogc) c.destructor)(ob);
}
}
while ((c = c.base) !is null);
2016-12-13 10:58:11 +01:00
2017-03-19 06:10:27 +01:00
if (ppv[1]) // if monitor is not null
{
_d_monitordelete(cast(Object) ptr, true);
}
return support;
2016-12-07 11:01:51 +01:00
}
package(tanya) void[] finalize(T)(ref T[] p)
2016-12-07 11:01:51 +01:00
{
2017-03-19 06:10:27 +01:00
static if (hasElaborateDestructor!(typeof(p[0])))
{
p.each!((ref e) => destroy(e));
2017-03-19 06:10:27 +01:00
}
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)); }();
2017-03-19 06:10:27 +01:00
p = null;
}
private unittest
{
2017-03-19 06:10:27 +01:00
struct S
{
~this()
{
}
}
auto p = cast(S[]) defaultAllocator.allocate(S.sizeof);
2017-03-19 06:10:27 +01:00
defaultAllocator.dispose(p);
2016-12-07 11:01:51 +01:00
}
// Works with interfaces.
private unittest
{
interface I
{
}
class C : I
{
}
auto c = defaultAllocator.make!C();
I i = c;
defaultAllocator.dispose(i);
defaultAllocator.dispose(i);
}