291 changes: 131 additions & 160 deletions src/gc/gc.d → src/gc/impl/conservative/gc.d

Large diffs are not rendered by default.

274 changes: 274 additions & 0 deletions src/gc/impl/manual/gc.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,274 @@
/**
* This module contains a minimal garbage collector implementation according to
* published requirements. This library is mostly intended to serve as an
* example, but it is usable in applications which do not rely on a garbage
* collector to clean up memory (ie. when dynamic array resizing is not used,
* and all memory allocated with 'new' is freed deterministically with
* 'delete').
*
* Please note that block attribute data must be tracked, or at a minimum, the
* FINALIZE bit must be tracked for any allocated memory block because calling
* rt_finalize on a non-object block can result in an access violation. In the
* allocator below, this tracking is done via a leading uint bitmask. A real
* allocator may do better to store this data separately, similar to the basic
* GC.
*
* Copyright: Copyright Sean Kelly 2005 - 2016.
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Sean Kelly
*/

/* Copyright Sean Kelly 2005 - 2016.
* 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.impl.manual.gc;

import gc.config;
import gc.stats;
import gc.gcinterface;

import rt.util.container.array;

import cstdlib = core.stdc.stdlib : calloc, free, malloc, realloc;

extern (C) void onOutOfMemoryError(void* pretend_sideffect = null) @trusted pure nothrow @nogc; /* dmd @@@BUG11461@@@ */

class ManualGC : GC
{
__gshared Array!Root roots;
__gshared Array!Range ranges;

static void initialize(ref GC gc)
{
import core.stdc.string;

if (config.gc != "manual")
return;

auto p = cstdlib.malloc(__traits(classInstanceSize, ManualGC));
if (!p)
onOutOfMemoryError();

auto init = typeid(ManualGC).initializer();
assert(init.length == __traits(classInstanceSize, ManualGC));
auto instance = cast(ManualGC) memcpy(p, init.ptr, init.length);
instance.__ctor();

gc = instance;
}

static void finalize(ref GC gc)
{
if (config.gc != "manual")
return;

auto instance = cast(ManualGC) gc;
instance.Dtor();
cstdlib.free(cast(void*) instance);
}

this()
{
}

void Dtor()
{
}

void enable()
{
}

void disable()
{
}

void collect() nothrow
{
}

void collectNoStack() nothrow
{
}

void minimize() nothrow
{
}

uint getAttr(void* p) nothrow
{
return 0;
}

uint setAttr(void* p, uint mask) nothrow
{
return 0;
}

uint clrAttr(void* p, uint mask) nothrow
{
return 0;
}

void* malloc(size_t size, uint bits, const TypeInfo ti) nothrow
{
void* p = cstdlib.malloc(size);

if (size && p is null)
onOutOfMemoryError();
return p;
}

BlkInfo qalloc(size_t size, uint bits, const TypeInfo ti) nothrow
{
BlkInfo retval;
retval.base = malloc(size, bits, ti);
retval.size = size;
retval.attr = bits;
return retval;
}

void* calloc(size_t size, uint bits, const TypeInfo ti) nothrow
{
void* p = cstdlib.calloc(1, size);

if (size && p is null)
onOutOfMemoryError();
return p;
}

void* realloc(void* p, size_t size, uint bits, const TypeInfo ti) nothrow
{
p = cstdlib.realloc(p, size);

if (size && p is null)
onOutOfMemoryError();
return p;
}

size_t extend(void* p, size_t minsize, size_t maxsize, const TypeInfo ti) nothrow
{
return 0;
}

size_t reserve(size_t size) nothrow
{
return 0;
}

void free(void* p) nothrow
{
cstdlib.free(p);
}

/**
* Determine the base address of the block containing p. If p is not a gc
* allocated pointer, return null.
*/
void* addrOf(void* p) nothrow
{
return null;
}

/**
* Determine the allocated size of pointer p. If p is an interior pointer
* or not a gc allocated pointer, return 0.
*/
size_t sizeOf(void* p) nothrow
{
return 0;
}

/**
* Determine the base address of the block containing p. If p is not a gc
* allocated pointer, return null.
*/
BlkInfo query(void* p) nothrow
{
return BlkInfo.init;
}

GCStats stats() nothrow
{
return GCStats.init;
}

void addRoot(void* p) nothrow @nogc
{
roots.insertBack(Root(p));
}

void removeRoot(void* p) nothrow @nogc
{
foreach (ref r; roots)
{
if (r is p)
{
r = roots.back;
roots.popBack();
return;
}
}
assert(false);
}

@property RootIterator rootIter() @nogc
{
return &rootsApply;
}

private int rootsApply(scope int delegate(ref Root) nothrow dg)
{
foreach (ref r; roots)
{
if (auto result = dg(r))
return result;
}
return 0;
}

void addRange(void* p, size_t sz, const TypeInfo ti = null) nothrow @nogc
{
ranges.insertBack(Range(p, p + sz, cast() ti));
}

void removeRange(void* p) nothrow @nogc
{
foreach (ref r; ranges)
{
if (r.pbot is p)
{
r = ranges.back;
ranges.popBack();
return;
}
}
assert(false);
}

@property RangeIterator rangeIter() @nogc
{
return &rangesApply;
}

private int rangesApply(scope int delegate(ref Range) nothrow dg)
{
foreach (ref r; ranges)
{
if (auto result = dg(r))
return result;
}
return 0;
}

void runFinalizers(in void[] segment) nothrow
{
}

bool inFinalizer() nothrow
{
return false;
}
}
253 changes: 66 additions & 187 deletions src/gc/proxy.d
Original file line number Diff line number Diff line change
@@ -1,118 +1,51 @@
/**
* Contains the external GC interface.
*
* Copyright: Copyright Digital Mars 2005 - 2013.
* Copyright: Copyright Digital Mars 2005 - 2016.
* License: $(WEB www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
* Authors: Walter Bright, Sean Kelly
*/

/* Copyright Digital Mars 2005 - 2013.
/* Copyright Digital Mars 2005 - 2016.
* 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.proxy;

import gc.gc;
import gc.impl.conservative.gc;
import gc.impl.manual.gc;
import gc.config;
import gc.stats;
import core.stdc.stdlib;
import gc.gcinterface;


private
{
__gshared GC _gc;

static import core.memory;
alias BlkInfo = core.memory.GC.BlkInfo;

extern (C) void thread_init();
extern (C) void thread_term();

struct Proxy
{
extern (C)
{
void function() gc_enable;
void function() gc_disable;
nothrow:
void function() gc_collect;
void function() gc_minimize;

uint function(void*) gc_getAttr;
uint function(void*, uint) gc_setAttr;
uint function(void*, uint) gc_clrAttr;

void* function(size_t, uint, const TypeInfo) gc_malloc;
BlkInfo function(size_t, uint, const TypeInfo) gc_qalloc;
void* function(size_t, uint, const TypeInfo) gc_calloc;
void* function(void*, size_t, uint ba, const TypeInfo) gc_realloc;
size_t function(void*, size_t, size_t, const TypeInfo) gc_extend;
size_t function(size_t) gc_reserve;
void function(void*) gc_free;

void* function(void*) gc_addrOf;
size_t function(void*) gc_sizeOf;

BlkInfo function(void*) gc_query;

void function(void*) gc_addRoot;
void function(void*, size_t, const TypeInfo ti) gc_addRange;

void function(void*) gc_removeRoot;
void function(void*) gc_removeRange;
void function(in void[]) gc_runFinalizers;

bool function() gc_inFinalizer;
}
}

__gshared Proxy pthis;
__gshared Proxy* proxy;

void initProxy()
{
pthis.gc_enable = &gc_enable;
pthis.gc_disable = &gc_disable;
pthis.gc_collect = &gc_collect;
pthis.gc_minimize = &gc_minimize;

pthis.gc_getAttr = &gc_getAttr;
pthis.gc_setAttr = &gc_setAttr;
pthis.gc_clrAttr = &gc_clrAttr;

pthis.gc_malloc = &gc_malloc;
pthis.gc_qalloc = &gc_qalloc;
pthis.gc_calloc = &gc_calloc;
pthis.gc_realloc = &gc_realloc;
pthis.gc_extend = &gc_extend;
pthis.gc_reserve = &gc_reserve;
pthis.gc_free = &gc_free;

pthis.gc_addrOf = &gc_addrOf;
pthis.gc_sizeOf = &gc_sizeOf;

pthis.gc_query = &gc_query;

pthis.gc_addRoot = &gc_addRoot;
pthis.gc_addRange = &gc_addRange;
__gshared GC instance;
__gshared GC proxiedGC; // used to iterate roots of Windows DLLs

pthis.gc_removeRoot = &gc_removeRoot;
pthis.gc_removeRange = &gc_removeRange;
pthis.gc_runFinalizers = &gc_runFinalizers;

pthis.gc_inFinalizer = &gc_inFinalizer;
}
}


extern (C)
{

void gc_init()
{
_gc.initialize();
config.initialize();
ManualGC.initialize(instance);
ConservativeGC.initialize(instance);

// NOTE: The GC must initialize the thread library
// before its first collection.
thread_init();
initProxy();
}

void gc_term()
Expand All @@ -126,229 +59,175 @@ extern (C)
//
// NOTE: Due to popular demand, this has been re-enabled. It still has
// the problems mentioned above though, so I guess we'll see.
_gc.fullCollectNoStack(); // not really a 'collect all' -- still scans
// static data area, roots, and ranges.

instance.collectNoStack(); // not really a 'collect all' -- still scans
// static data area, roots, and ranges.

thread_term();

_gc.Dtor();
ManualGC.finalize(instance);
ConservativeGC.finalize(instance);
}

void gc_enable()
{
if( proxy is null )
return _gc.enable();
return proxy.gc_enable();
instance.enable();
}

void gc_disable()
{
if( proxy is null )
return _gc.disable();
return proxy.gc_disable();
instance.disable();
}

void gc_collect() nothrow
{
if( proxy is null )
{
_gc.fullCollect();
return;
}
return proxy.gc_collect();
instance.collect();
}

void gc_minimize() nothrow
{
if( proxy is null )
return _gc.minimize();
return proxy.gc_minimize();
instance.minimize();
}

uint gc_getAttr( void* p ) nothrow
{
if( proxy is null )
return _gc.getAttr( p );
return proxy.gc_getAttr( p );
return instance.getAttr(p);
}

uint gc_setAttr( void* p, uint a ) nothrow
{
if( proxy is null )
return _gc.setAttr( p, a );
return proxy.gc_setAttr( p, a );
return instance.setAttr(p, a);
}

uint gc_clrAttr( void* p, uint a ) nothrow
{
if( proxy is null )
return _gc.clrAttr( p, a );
return proxy.gc_clrAttr( p, a );
return instance.clrAttr(p, a);
}

void* gc_malloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow
{
if( proxy is null )
return _gc.malloc( sz, ba, null, ti );
return proxy.gc_malloc( sz, ba, ti );
return instance.malloc(sz, ba, ti);
}

BlkInfo gc_qalloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow
{
if( proxy is null )
{
BlkInfo retval;
retval.base = _gc.malloc( sz, ba, &retval.size, ti );
retval.attr = ba;
return retval;
}
return proxy.gc_qalloc( sz, ba, ti );
return instance.qalloc( sz, ba, ti );
}

void* gc_calloc( size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow
{
if( proxy is null )
return _gc.calloc( sz, ba, null, ti );
return proxy.gc_calloc( sz, ba, ti );
return instance.calloc( sz, ba, ti );
}

void* gc_realloc( void* p, size_t sz, uint ba = 0, const TypeInfo ti = null ) nothrow
{
if( proxy is null )
return _gc.realloc( p, sz, ba, null, ti );
return proxy.gc_realloc( p, sz, ba, ti );
return instance.realloc( p, sz, ba, ti );
}

size_t gc_extend( void* p, size_t mx, size_t sz, const TypeInfo ti = null ) nothrow
{
if( proxy is null )
return _gc.extend( p, mx, sz, ti );
return proxy.gc_extend( p, mx, sz,ti );
return instance.extend( p, mx, sz,ti );
}

size_t gc_reserve( size_t sz ) nothrow
{
if( proxy is null )
return _gc.reserve( sz );
return proxy.gc_reserve( sz );
return instance.reserve( sz );
}

void gc_free( void* p ) nothrow
{
if( proxy is null )
return _gc.free( p );
return proxy.gc_free( p );
return instance.free( p );
}

void* gc_addrOf( void* p ) nothrow
{
if( proxy is null )
return _gc.addrOf( p );
return proxy.gc_addrOf( p );
return instance.addrOf( p );
}

size_t gc_sizeOf( void* p ) nothrow
{
if( proxy is null )
return _gc.sizeOf( p );
return proxy.gc_sizeOf( p );
return instance.sizeOf( p );
}

BlkInfo gc_query( void* p ) nothrow
{
if( proxy is null )
return _gc.query( p );
return proxy.gc_query( p );
return instance.query( p );
}

// NOTE: This routine is experimental. The stats or function name may change
// before it is made officially available.
GCStats gc_stats() nothrow
{
if( proxy is null )
{
GCStats stats = void;
_gc.getStats( stats );
return stats;
}
// TODO: Add proxy support for this once the layout of GCStats is
// finalized.
//return proxy.gc_stats();
return GCStats.init;
return instance.stats();
}

void gc_addRoot( void* p ) nothrow
{
if( proxy is null )
return _gc.addRoot( p );
return proxy.gc_addRoot( p );
return instance.addRoot( p );
}

void gc_addRange( void* p, size_t sz, const TypeInfo ti = null ) nothrow
{
if( proxy is null )
return _gc.addRange( p, sz, ti );
return proxy.gc_addRange( p, sz, ti );
return instance.addRange( p, sz, ti );
}

void gc_removeRoot( void* p ) nothrow
{
if( proxy is null )
return _gc.removeRoot( p );
return proxy.gc_removeRoot( p );
return instance.removeRoot( p );
}

void gc_removeRange( void* p ) nothrow
{
if( proxy is null )
return _gc.removeRange( p );
return proxy.gc_removeRange( p );
return instance.removeRange( p );
}

void gc_runFinalizers( in void[] segment ) nothrow
{
if( proxy is null )
return _gc.runFinalizers( segment );
return proxy.gc_runFinalizers( segment );
return instance.runFinalizers( segment );
}

bool gc_inFinalizer() nothrow
{
if( proxy is null )
return _gc.inFinalizer;
return proxy.gc_inFinalizer();
return instance.inFinalizer();
}

Proxy* gc_getProxy() nothrow
GC gc_getProxy() nothrow
{
return &pthis;
return instance;
}

export
{
void gc_setProxy( Proxy* p )
void gc_setProxy( GC proxy )
{
if( proxy !is null )
foreach(root; instance.rootIter)
{
proxy.addRoot(root);
}

foreach(range; instance.rangeIter)
{
// TODO: Decide if this is an error condition.
proxy.addRange(range.pbot, range.ptop - range.pbot, range.ti);
}
proxy = p;
foreach (r; _gc.rootIter)
proxy.gc_addRoot( r );

foreach (r; _gc.rangeIter)
proxy.gc_addRange( r.pbot, r.ptop - r.pbot, null );
proxiedGC = instance; // remember initial GC to later remove roots
instance = proxy;
}

void gc_clrProxy()
{
foreach (r; _gc.rangeIter)
proxy.gc_removeRange( r.pbot );
foreach(root; proxiedGC.rootIter)
{
instance.removeRoot(root);
}

foreach (r; _gc.rootIter)
proxy.gc_removeRoot( r );
foreach(range; proxiedGC.rangeIter)
{
instance.removeRange(range);
}

proxy = null;
instance = proxiedGC;
proxiedGC = null;
}
}

}