111 changes: 88 additions & 23 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 @@ -130,6 +131,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 @@ -371,7 +374,7 @@ class GC
/**
*
*/
uint setAttr(void* p, uint mask) nothrow
uint setAttr(void* p, uint mask, StructInfo inf = null) nothrow
{
if (!p)
{
Expand All @@ -389,7 +392,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 @@ -482,7 +485,7 @@ class GC
if (gcx.running)
onInvalidMemoryOperationError();

auto p = gcx.alloc(size + SENTINEL_EXTRA, alloc_size, bits);
auto p = gcx.alloc(size + SENTINEL_EXTRA, alloc_size, bits, ti);
if (!p)
onOutOfMemoryError();

Expand Down Expand Up @@ -600,7 +603,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 @@ -652,7 +655,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 @@ -669,7 +672,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 @@ -1538,7 +1541,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 @@ -1550,11 +1553,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 @@ -1601,11 +1611,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 @@ -2064,14 +2081,14 @@ struct Gcx
}


void* alloc(size_t size, ref size_t alloc_size, uint bits) nothrow
void* alloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti = null) nothrow
{
immutable bin = findBin(size);
return bin < B_PAGE ? smallAlloc(bin, alloc_size, bits) :
bigAlloc(size, alloc_size, bits);
return bin < B_PAGE ? smallAlloc(bin, alloc_size, bits, ti) :
bigAlloc(size, alloc_size, bits, ti);
}

void* smallAlloc(Bins bin, ref size_t alloc_size, uint bits) nothrow
void* smallAlloc(Bins bin, ref size_t alloc_size, uint bits, const TypeInfo ti = null) nothrow
{
alloc_size = binsize[bin];

Expand Down Expand Up @@ -2110,7 +2127,13 @@ struct Gcx
// Return next item from free list
bucket[bin] = (cast(List*)p).next;
auto pool = (cast(List*)p).pool;
if (bits) setBits(pool, (p - pool.baseAddr) >> pool.shiftBy, bits);
if (bits)
{
if (ti)
setBits(pool, (p - pool.baseAddr) >> pool.shiftBy, bits, cast(StructInfo)ti.next);
else
setBits(pool, (p - pool.baseAddr) >> pool.shiftBy, bits);
}
//debug(PRINTF) printf("\tmalloc => %p\n", p);
debug (MEMSTOMP) memset(p, 0xF0, size);
return p;
Expand All @@ -2120,7 +2143,7 @@ struct Gcx
* Allocate a chunk of memory that is larger than a page.
* Return null if out of memory.
*/
void* bigAlloc(size_t size, ref size_t alloc_size, uint bits) nothrow
void* bigAlloc(size_t size, ref size_t alloc_size, uint bits, const TypeInfo ti = null) nothrow
{
debug(PRINTF) printf("In bigAlloc. Size: %d\n", size);

Expand Down Expand Up @@ -2191,7 +2214,13 @@ struct Gcx
alloc_size = npages * PAGESIZE;
//debug(PRINTF) printf("\tp = %p\n", p);

if (bits) setBits(pool, pn * PAGESIZE >> pool.shiftBy, bits);
if (bits)
{
if (ti)
setBits(pool, pn * PAGESIZE >> pool.shiftBy, bits, cast(StructInfo)ti.next);
else
setBits(pool, pn * PAGESIZE >> pool.shiftBy, bits);
}
return p;
}

Expand Down Expand Up @@ -2630,8 +2659,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 @@ -2700,8 +2733,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 @@ -2851,6 +2888,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 @@ -2867,7 +2907,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 @@ -2886,6 +2926,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 @@ -2926,6 +2976,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 @@ -2946,6 +3001,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 @@ -3091,6 +3151,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 @@ -3211,6 +3273,8 @@ struct Pool
freebits.Dtor();
}
finals.Dtor();
structFinals.Dtor();
structInfo.Dtor();
noscan.Dtor();
appendable.Dtor();
}
Expand All @@ -3226,6 +3290,7 @@ struct Pool
//scan.Invariant();
//freebits.Invariant();
//finals.Invariant();
//structFinals.Invariant();
//noscan.Invariant();
//appendable.Invariant();
//nointerior.Invariant();
Expand Down
47 changes: 47 additions & 0 deletions src/gc/structinfo.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Contains an array of struct infos used by the GC.
*
* Copyright: Copyright Digital Mars 2005 - 2013.
* License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
* Authors: Orvid King
*/

/* Copyright Digital Mars 2005 - 2013.
* Distributed under the Boost Software License, Version 1.0.
* (See accompanying file LICENSE or copy at
* http://www.boost.org/LICENSE_1_0.txt)
*/
module gc.structinfo;


import core.stdc.string;
import core.stdc.stdlib;
import core.exception : onOutOfMemoryError;

struct GCStructInfoArray
{
StructInfo* data = null;
size_t length = 0;

void Dtor() nothrow
{
if (data)
{
free(data);
data = null;
length = 0;
}
}

invariant()
{
}

void alloc(size_t nEntries) nothrow
{
this.length = nEntries;
data = cast(typeof(data[0])*)calloc(nEntries, data[0].sizeof);
if (!data)
onOutOfMemoryError();
}
}
1 change: 1 addition & 0 deletions src/object.di
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ class TypeInfo_Interface : TypeInfo
ClassInfo info;
}

alias TypeInfo_Struct StructInfo;
class TypeInfo_Struct : TypeInfo
{
string name;
Expand Down
5 changes: 5 additions & 0 deletions src/object_.d
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ struct Interface
*/
alias TypeInfo_Class Classinfo;

/**
* Runtime type information about a struct.
*/
alias TypeInfo_Struct StructInfo;

/**
* Array of pairs giving the offset and type information for each
* member in an aggregate.
Expand Down
45 changes: 41 additions & 4 deletions src/rt/lifetime.d
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ static import rt.tlsgc;

alias BlkInfo = GC.BlkInfo;
alias BlkAttr = GC.BlkAttr;
import core.exception : onOutOfMemoryError, onFinalizeError;
import core.exception : onOutOfMemoryError, onFinalizeError, onStructFinalizeError;

private
{
Expand Down Expand Up @@ -53,7 +53,6 @@ extern (C) void* _d_allocmemory(size_t sz)
return GC.malloc(sz);
}


/**
*
*/
Expand Down Expand Up @@ -1011,6 +1010,10 @@ extern (C) void[] _d_newarraymiT(const TypeInfo ti, size_t ndims, ...)
extern (C) void* _d_newitemT(TypeInfo ti)
{
auto size = ti.tsize; // array element size
auto size = ti.tsize; // array element size
auto baseFlags = !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0;
if (auto si = cast(StructInfo)ti)
baseFlags |= si.xdtor ? BlkAttr.STRUCT_FINALIZE : 0;

debug(PRINTF) printf("_d_newitemT(size = %d)\n", size);
/* not sure if we need this...
Expand All @@ -1019,7 +1022,7 @@ extern (C) void* _d_newitemT(TypeInfo ti)
else
{*/
// allocate a block to hold this item
auto ptr = GC.malloc(size, !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0, ti);
auto ptr = GC.malloc(size, baseFlags, ti);
debug(PRINTF) printf(" p = %p\n", ptr);
if(size == ubyte.sizeof)
*cast(ubyte*)ptr = 0;
Expand All @@ -1037,6 +1040,9 @@ extern (C) void* _d_newitemT(TypeInfo ti)
extern (C) void* _d_newitemiT(TypeInfo ti)
{
auto size = ti.tsize; // array element size
auto baseFlags = !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0;
if (auto si = cast(StructInfo)ti)
baseFlags |= si.xdtor ? BlkAttr.STRUCT_FINALIZE : 0;

debug(PRINTF) printf("_d_newitemiT(size = %d)\n", size);

Expand All @@ -1048,7 +1054,7 @@ extern (C) void* _d_newitemiT(TypeInfo ti)
auto isize = initializer.length;
auto q = initializer.ptr;

auto ptr = GC.malloc(size, !(ti.flags & 1) ? BlkAttr.NO_SCAN : 0, ti);
auto ptr = GC.malloc(size, baseFlags, ti);
debug(PRINTF) printf(" p = %p\n", ptr);
if (isize == 1)
*cast(ubyte*)ptr = *cast(ubyte*)q;
Expand Down Expand Up @@ -1210,6 +1216,37 @@ extern (C) int rt_hasFinalizerInSegment(void* p, in void[] segment) nothrow
return false;
}

extern (C) int rt_hasStructFinalizerInSegment(void* p, StructInfo inf, in void[] segment) nothrow
{
if(!p)
return false;

return cast(size_t)(cast(void*)inf.xdtor - segment.ptr) < segment.length;
}

extern (C) void rt_finalize_struct(void* p, StructInfo inf, bool resetMemory = true) nothrow
{
debug(PRINTF) printf("rt_finalize_struct(p = %p)\n", p);

if(!p)
return;

try
{
if (inf.xdtor)
inf.xdtor(p); // call destructor

if(resetMemory)
{
ubyte[] w = cast(ubyte[])inf.m_init;
(cast(ubyte*) p)[0 .. w.length] = w[];
}
}
catch (Throwable e)
{
onStructFinalizeError(inf, e);
}
}

/**
*
Expand Down