rmem.d (dmd-2.094.1) | : | rmem.d (dmd-2.094.2) | ||
---|---|---|---|---|
skipping to change at line 413 | skipping to change at line 413 | |||
pure nothrow unittest | pure nothrow unittest | |||
{ | { | |||
auto s1 = [0, 1, 2]; | auto s1 = [0, 1, 2]; | |||
auto s2 = s1.arraydup; | auto s2 = s1.arraydup; | |||
s2[0] = 4; | s2[0] = 4; | |||
assert(s1 == [0, 1, 2]); | assert(s1 == [0, 1, 2]); | |||
assert(s2 == [4, 1, 2]); | assert(s2 == [4, 1, 2]); | |||
string sEmpty; | string sEmpty; | |||
assert(sEmpty.arraydup is null); | assert(sEmpty.arraydup is null); | |||
} | } | |||
// Define this to have Pool emit traces of objects allocated and disposed | ||||
//debug = Pool; | ||||
// Define this in addition to Pool to emit per-call traces (otherwise summaries | ||||
are printed at the end). | ||||
//debug = PoolVerbose; | ||||
/** | ||||
Defines a pool for class objects. Objects can be fetched from the pool with make | ||||
() and returned to the pool with | ||||
dispose(). Using a reference that has been dispose()d has undefined behavior. ma | ||||
ke() may return memory that has been | ||||
previously dispose()d. | ||||
Currently the pool has effect only if the GC is NOT used (i.e. either `version(G | ||||
C)` or `mem.isGCEnabled` is false). | ||||
Otherwise `make` just forwards to `new` and `dispose` does nothing. | ||||
Internally the implementation uses a singly-linked freelist with a global root. | ||||
The "next" pointer is stored in the | ||||
first word of each disposed object. | ||||
*/ | ||||
struct Pool(T) | ||||
if (is(T == class)) | ||||
{ | ||||
/// The freelist's root | ||||
private static T root; | ||||
private static void trace(string fun, string f, uint l)() | ||||
{ | ||||
debug(Pool) | ||||
{ | ||||
debug(PoolVerbose) | ||||
{ | ||||
fprintf(stderr, "%.*s(%u): bytes: %lu Pool!(%.*s)."~fun~"()\n", | ||||
cast(int) f.length, f.ptr, l, T.classinfo.initializer.length | ||||
, | ||||
cast(int) T.stringof.length, T.stringof.ptr); | ||||
} | ||||
else | ||||
{ | ||||
static ulong calls; | ||||
if (calls == 0) | ||||
{ | ||||
// Plant summary printer | ||||
static extern(C) void summarize() | ||||
{ | ||||
fprintf(stderr, "%.*s(%u): bytes: %lu calls: %lu Pool!(% | ||||
.*s)."~fun~"()\n", | ||||
cast(int) f.length, f.ptr, l, ((T.classinfo.initiali | ||||
zer.length + 15) & ~15) * calls, | ||||
calls, cast(int) T.stringof.length, T.stringof.ptr); | ||||
} | ||||
atexit(&summarize); | ||||
} | ||||
++calls; | ||||
} | ||||
} | ||||
} | ||||
/** | ||||
Returns a reference to a new object in the same state as if created with new | ||||
T(args). | ||||
*/ | ||||
static T make(string f = __FILE__, uint l = __LINE__, A...)(auto ref A args) | ||||
{ | ||||
if (!root) | ||||
{ | ||||
trace!("makeNew", f, l)(); | ||||
return new T(args); | ||||
} | ||||
else | ||||
{ | ||||
trace!("makeReuse", f, l)(); | ||||
auto result = root; | ||||
root = *(cast(T*) root); | ||||
memcpy(cast(void*) result, T.classinfo.initializer.ptr, T.classinfo. | ||||
initializer.length); | ||||
result.__ctor(args); | ||||
return result; | ||||
} | ||||
} | ||||
/** | ||||
Signals to the pool that this object is no longer used, so it can recycle it | ||||
s memory. | ||||
*/ | ||||
static void dispose(string f = __FILE__, uint l = __LINE__, A...)(T goner) | ||||
{ | ||||
version(GC) | ||||
{ | ||||
if (mem.isGCEnabled) return; | ||||
} | ||||
trace!("dispose", f, l)(); | ||||
debug | ||||
{ | ||||
// Stomp the memory so as to maximize the chance of quick failure if | ||||
used after dispose(). | ||||
auto p = cast(ulong*) goner; | ||||
p[0 .. T.classinfo.initializer.length / ulong.sizeof] = 0xdeadbeef; | ||||
} | ||||
*(cast(T*) goner) = root; | ||||
root = goner; | ||||
} | ||||
} | ||||
End of changes. 1 change blocks. | ||||
0 lines changed or deleted | 0 lines changed or added |