Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
The GC will now call destructors on heap allocated structs. It is imp…
Browse files Browse the repository at this point in the history
…ossible to feasibly implement the invokation of arrays of finalizable structs, due to the inability to know which elements in the array, which may, or may not have been, initialized to begin with. There is also no way to know what the actual length of the array is, you can know how much memory was allocated for it, but not whether there was ever a reference to that point in the array. The destructors for structs in classes are still called as they were previously, which is immediately after the body of the outer class's finalizer.
  • Loading branch information
Orvid committed Dec 12, 2014
1 parent 109a604 commit 977c7f7
Show file tree
Hide file tree
Showing 8 changed files with 251 additions and 20 deletions.
1 change: 1 addition & 0 deletions mak/SRCS
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ SRCS=\
src\gc\bits.d \
src\gc\config.d \
src\gc\stats.d \
src\gc\structinfo.d \
src\gc\proxy.d \
\
src\rt\aApply.d \
Expand Down
83 changes: 83 additions & 0 deletions src/core/exception.d
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,71 @@ unittest
}
}

/**
* Thrown on struct finalize error.
*/
class StructFinalizeError : Error
{
StructInfo info;

@safe pure nothrow this( StructInfo si, Throwable next, string file = __FILE__, size_t line = __LINE__ )
{
this(si, file, line, next);
}

@safe pure nothrow this( StructInfo si, string file = __FILE__, size_t line = __LINE__, Throwable next = null )
{
super( "Finalization error", file, line, next );
info = si;
}

@safe override const string toString()
{
return "An exception was thrown while finalizing an instance of struct " ~ info.name;
}
}

unittest
{
StructInfo info = new StructInfo;
info.name = "testInfo";

{
auto fe = new StructFinalizeError(info);
assert(fe.file == __FILE__);
assert(fe.line == __LINE__ - 2);
assert(fe.next is null);
assert(fe.msg == "Finalization error");
assert(fe.info == info);
}

{
auto fe = new StructFinalizeError(info, new Exception("It's an Exception!"));
assert(fe.file == __FILE__);
assert(fe.line == __LINE__ - 2);
assert(fe.next !is null);
assert(fe.msg == "Finalization error");
assert(fe.info == info);
}

{
auto fe = new StructFinalizeError(info, "hello", 42);
assert(fe.file == "hello");
assert(fe.line == 42);
assert(fe.next is null);
assert(fe.msg == "Finalization error");
assert(fe.info == info);
}

{
auto fe = new StructFinalizeError(info, "hello", 42, new Exception("It's an Exception!"));
assert(fe.file == "hello");
assert(fe.line == 42);
assert(fe.next !is null);
assert(fe.msg == "Finalization error");
assert(fe.info == info);
}
}

/**
* Thrown on hidden function error.
Expand Down Expand Up @@ -488,6 +553,24 @@ extern (C) void onFinalizeError( ClassInfo info, Throwable e, string file = __FI
}


/**
* A callback for struct finalize errors in D. A StructFinalizeError will be thrown.
*
* Params:
* info = The StructInfo instance for the object that failed finalization.
* e = The exception thrown during finalization.
* file = The name of the file that signaled this error.
* line = The line number on which this error occurred.
*
* Throws:
* FinalizeError.
*/
extern (C) void onStructFinalizeError( StructInfo info, Throwable e, string file = __FILE__, size_t line = __LINE__ ) @safe pure nothrow
{
throw new StructFinalizeError( info, file, line, e );
}


/**
* A callback for hidden function errors in D. A $(LREF HiddenFuncError) will be
* thrown.
Expand Down
1 change: 1 addition & 0 deletions src/core/memory.d
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ struct GC
and is only implemented for data structures at least a page in size.
*/
NO_INTERIOR = 0b0001_0000,
STRUCT_FINALIZE = 0b0010_0000, /// Finalize the struct data in this block on collect.
}


Expand Down
88 changes: 72 additions & 16 deletions src/gc/gc.d
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ version = STACKGROWSDOWN; // growing the stack means subtracting from the

import gc.bits;
import gc.stats;
import gc.structinfo;
import gc.os;
import gc.config;

Expand Down Expand Up @@ -128,6 +129,8 @@ private
{
// to allow compilation of this module without access to the rt package,
// make these functions available from rt.lifetime
void rt_finalize_struct(void* p, StructInfo inf, bool resetMemory) nothrow;
int rt_hasStructFinalizerInSegment(void* p, StructInfo inf, in void[] segment) nothrow;
void rt_finalize2(void* p, bool det, bool resetMemory) nothrow;
int rt_hasFinalizerInSegment(void* p, in void[] segment) nothrow;

Expand Down Expand Up @@ -374,7 +377,7 @@ class GC
/**
*
*/
uint setAttr(void* p, uint mask) nothrow
uint setAttr(void* p, uint mask, StructInfo inf = null) nothrow
{
if (!p)
{
Expand All @@ -392,7 +395,7 @@ class GC
auto biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;

oldb = gcx.getBits(pool, biti);
gcx.setBits(pool, biti, mask);
gcx.setBits(pool, biti, mask, inf);
}
return oldb;
}
Expand Down Expand Up @@ -555,7 +558,10 @@ class GC

if (bits)
{
gcx.setBits(pool, cast(size_t)(sentinel_sub(p) - pool.baseAddr) >> pool.shiftBy, bits);
if (ti)
gcx.setBits(pool, cast(size_t)(sentinel_sub(p) - pool.baseAddr) >> pool.shiftBy, bits, cast(StructInfo)ti.next);
else
gcx.setBits(pool, cast(size_t)(sentinel_sub(p) - pool.baseAddr) >> pool.shiftBy, bits);
}
return p;
}
Expand Down Expand Up @@ -663,7 +669,7 @@ class GC
if (bits)
{
gcx.clrBits(pool, biti, ~BlkAttr.NONE);
gcx.setBits(pool, biti, bits);
gcx.setBits(pool, biti, bits, cast(StructInfo)ti);
}
else
{
Expand Down Expand Up @@ -715,7 +721,7 @@ class GC
{
immutable biti = cast(size_t)(p - pool.baseAddr) >> pool.shiftBy;
gcx.clrBits(pool, biti, ~BlkAttr.NONE);
gcx.setBits(pool, biti, bits);
gcx.setBits(pool, biti, bits, cast(StructInfo)ti);
}
alloc_size = newsz * PAGESIZE;
return p;
Expand All @@ -732,7 +738,7 @@ class GC
if (bits)
{
gcx.clrBits(pool, biti, ~BlkAttr.NONE);
gcx.setBits(pool, biti, bits);
gcx.setBits(pool, biti, bits, cast(StructInfo)ti);
}
else
{
Expand Down Expand Up @@ -1595,7 +1601,7 @@ struct Gcx
{
foreach (pool; pooltable[0 .. npools])
{
if (!pool.finals.nbits) continue;
if (!pool.finals.nbits && !pool.structFinals.nbits) continue;

if (pool.isLargeObject)
{
Expand All @@ -1607,11 +1613,18 @@ struct Gcx

auto p = pool.baseAddr + pn * PAGESIZE;

if (!pool.finals.test(biti) ||
!rt_hasFinalizerInSegment(sentinel_add(p), segment))
if (pool.structFinals.nbits && pool.structFinals.test(biti))
{
auto si = pool.structInfo.data[biti];
if (!rt_hasStructFinalizerInSegment(sentinel_add(p), si, segment))
continue;
rt_finalize_struct(sentinel_add(p), si, false);
}
else if (!pool.finals.nbits || !pool.finals.test(biti) || !rt_hasFinalizerInSegment(sentinel_add(p), segment))
continue;
else
rt_finalize2(sentinel_add(p), false, false);

rt_finalize2(sentinel_add(p), false, false);
clrBits(pool, biti, ~BlkAttr.NONE);

if (pn < pool.searchStart) pool.searchStart = pn;
Expand Down Expand Up @@ -1658,11 +1671,18 @@ struct Gcx
clearIndex = biti & GCBits.BITS_MASK;
}

if (!pool.finals.test(biti) ||
!rt_hasFinalizerInSegment(sentinel_add(p), segment))
if (pool.structFinals.nbits && pool.structFinals.test(biti))
{
auto si = pool.structInfo.data[biti];
if (!rt_hasStructFinalizerInSegment(sentinel_add(p), si, segment))
continue;
rt_finalize_struct(sentinel_add(p), si, false);
}
else if (!pool.finals.nbits || !pool.finals.test(biti) || !rt_hasFinalizerInSegment(sentinel_add(p), segment))
continue;
else
rt_finalize2(sentinel_add(p), false, false);

rt_finalize2(sentinel_add(p), false, false);
toClear |= GCBits.BITS_1 << clearIndex;

debug(COLLECT_PRINTF) printf("\tcollecting %p\n", p);
Expand Down Expand Up @@ -2653,8 +2673,12 @@ struct Gcx
{ byte *p = pool.baseAddr + pn * PAGESIZE;

sentinel_Invariant(sentinel_add(p));
if (pool.finals.nbits && pool.finals.testClear(biti))

if (pool.structFinals.nbits && pool.structFinals.testClear(biti))
rt_finalize_struct(sentinel_add(p), pool.structInfo.data[biti], false);
else if (pool.finals.nbits && pool.finals.testClear(biti))
rt_finalize2(sentinel_add(p), false, false);

clrBits(pool, biti, ~BlkAttr.NONE ^ BlkAttr.FINALIZE);

debug(COLLECT_PRINTF) printf("\tcollecting big %p\n", p);
Expand Down Expand Up @@ -2723,8 +2747,12 @@ struct Gcx
sentinel_Invariant(sentinel_add(p));

pool.freebits.set(biti);
if (pool.finals.nbits && pool.finals.test(biti))

if (pool.structFinals.nbits && pool.structFinals.test(biti))
rt_finalize_struct(sentinel_add(p), pool.structInfo.data[biti], false);
else if (pool.finals.nbits && pool.finals.test(biti))
rt_finalize2(sentinel_add(p), false, false);

toClear |= GCBits.BITS_1 << clearIndex;

List *list = cast(List *)p;
Expand Down Expand Up @@ -2873,6 +2901,9 @@ struct Gcx
if (pool.finals.nbits &&
pool.finals.test(biti))
bits |= BlkAttr.FINALIZE;
if (pool.structFinals.nbits &&
pool.structFinals.test(biti))
biti |= BlkAttr.STRUCT_FINALIZE;
if (pool.noscan.test(biti))
bits |= BlkAttr.NO_SCAN;
if (pool.nointerior.nbits && pool.nointerior.test(biti))
Expand All @@ -2889,7 +2920,7 @@ struct Gcx
/**
*
*/
void setBits(Pool* pool, size_t biti, uint mask) nothrow
void setBits(Pool* pool, size_t biti, uint mask, StructInfo inf = null) nothrow
in
{
assert(pool);
Expand All @@ -2908,6 +2939,16 @@ struct Gcx
pool.finals.alloc(pool.mark.nbits);
pool.finals.data[dataIndex] |= orWith;
}
else if (mask & BlkAttr.STRUCT_FINALIZE)
{
if (!pool.structFinals.nbits)
{
pool.structFinals.alloc(pool.mark.nbits);
pool.structInfo.alloc(pool.mark.nbits);
}
pool.structFinals.data[dataIndex] |= orWith;
pool.structInfo.data[biti] = inf;
}
if (mask & BlkAttr.NO_SCAN)
{
pool.noscan.data[dataIndex] |= orWith;
Expand Down Expand Up @@ -2948,6 +2989,11 @@ struct Gcx

if (mask & BlkAttr.FINALIZE && pool.finals.nbits)
pool.finals.data[dataIndex] &= keep;
else if (mask & BlkAttr.STRUCT_FINALIZE && pool.structFinals.nbits)
{
pool.structFinals.data[dataIndex] &= keep;
pool.structInfo.data[biti] = null;
}
if (mask & BlkAttr.NO_SCAN)
pool.noscan.data[dataIndex] &= keep;
// if (mask & BlkAttr.NO_MOVE && pool.nomove.nbits)
Expand All @@ -2968,6 +3014,11 @@ struct Gcx
immutable toKeep = ~toClear;
if (pool.finals.nbits)
pool.finals.data[dataIndex] &= toKeep;
if (pool.structFinals.nbits)
{
pool.structFinals.data[dataIndex] &= toKeep;
pool.structInfo.data[(dataIndex << GCBits.BITS_SHIFT) - 1] = null;
}

pool.noscan.data[dataIndex] &= toKeep;

Expand Down Expand Up @@ -3113,6 +3164,8 @@ struct Pool
GCBits scan; // entries that need to be scanned
GCBits freebits; // entries that are on the free list
GCBits finals; // entries that need finalizer run on them
GCBits structFinals;// struct entries that need a finalzier run on them
GCStructInfoArray structInfo; // struct info for struct entries
GCBits noscan; // entries that should not be scanned
GCBits appendable; // entries that are appendable
GCBits nointerior; // interior pointers should be ignored.
Expand Down Expand Up @@ -3233,6 +3286,8 @@ struct Pool
freebits.Dtor();
}
finals.Dtor();
structFinals.Dtor();
structInfo.Dtor();
noscan.Dtor();
appendable.Dtor();
}
Expand All @@ -3248,6 +3303,7 @@ struct Pool
//scan.Invariant();
//freebits.Invariant();
//finals.Invariant();
//structFinals.Invariant();
//noscan.Invariant();
//appendable.Invariant();
//nointerior.Invariant();
Expand Down
Loading

0 comments on commit 977c7f7

Please sign in to comment.