diff --git a/src/core/memory.d b/src/core/memory.d index 67148b43c0..57f180b107 100644 --- a/src/core/memory.d +++ b/src/core/memory.d @@ -140,6 +140,12 @@ private extern (C) BlkInfo_ gc_query( void* p ) pure nothrow; extern (C) GC.Stats gc_stats ( ) nothrow @nogc; + // Alias used because DMD thinks the delegate are extern(C) as well otherwise + alias CollectionStartHook = void delegate() nothrow @nogc; + alias CollectionEndHook = void delegate(size_t, size_t) nothrow @nogc; + extern (C) void gc_monitor ( CollectionStartHook on_start, + CollectionEndHook on_end) nothrow @nogc; + extern (C) void gc_addRoot( in void* p ) nothrow @nogc; extern (C) void gc_addRange( in void* p, size_t sz, const TypeInfo ti = null ) nothrow @nogc; @@ -680,6 +686,23 @@ struct GC return gc_stats(); } + /** + * Provides a mean to hook the GC on the beggining and end of a collection + * + * If one of the event is of no interest, `null` can be provided. + * + * Params: + * on_start = A delegate to call whenever a collection starts + * on_end = A delegate to call whenever a collection ends. + * The first argument is the number of bytes freed overall, + * the second the number of bytes freed within full pages. + */ + static void monitor(CollectionStartHook on_start, CollectionEndHook on_end) + nothrow @nogc + { + gc_monitor(on_start, on_end); + } + /** * Adds an internal root pointing to the GC memory block referenced by p. * As a result, the block referenced by p itself and any blocks accessible diff --git a/src/gc/gcinterface.d b/src/gc/gcinterface.d index c162041994..49a573b21d 100644 --- a/src/gc/gcinterface.d +++ b/src/gc/gcinterface.d @@ -37,6 +37,10 @@ struct Range interface GC { + /// Provided for implementation's convenience, not intended for public usage + protected alias CollectionStartHook = void delegate() nothrow @nogc; + /// Ditto + protected alias CollectionEndHook = void delegate(size_t, size_t) nothrow @nogc; /* * @@ -148,6 +152,13 @@ interface GC */ core.memory.GC.Stats stats() nothrow; + /** + * Track beginning and end of allocation + * Useful for debugging and tuning. + */ + void monitor (CollectionStartHook on_start, CollectionEndHook on_end) + nothrow @nogc; + /** * add p to list of roots */ diff --git a/src/gc/impl/conservative/gc.d b/src/gc/impl/conservative/gc.d index d9b51004a7..0aec484cd9 100644 --- a/src/gc/impl/conservative/gc.d +++ b/src/gc/impl/conservative/gc.d @@ -255,6 +255,10 @@ class ConservativeGC : GC __gshared size_t line; __gshared char* file; + /// Hook used for debugging application-side + __gshared CollectionStartHook on_collect_start; + __gshared CollectionEndHook on_collect_end; + Gcx *gcx; // implementation import core.internal.spinlock; @@ -1200,6 +1204,16 @@ class ConservativeGC : GC return ret; } + override void monitor (CollectionStartHook on_start, CollectionEndHook on_end) + nothrow @nogc + { + static void store (CollectionStartHook start, CollectionEndHook end) + { + on_collect_start = start; + on_collect_end = end; + } + runLocked!(store)(on_start, on_end); + } // // @@ -2392,6 +2406,8 @@ struct Gcx } debug(COLLECT_PRINTF) printf("Gcx.fullcollect()\n"); + if (ConservativeGC.on_collect_start !is null) + ConservativeGC.on_collect_start(); //printf("\tpool address range = %p .. %p\n", minAddr, maxAddr); { @@ -2456,6 +2472,8 @@ struct Gcx updateCollectThresholds(); + if (ConservativeGC.on_collect_end !is null) + ConservativeGC.on_collect_end(freedLargePages, freedSmallPages); return freedLargePages + freedSmallPages; } @@ -3443,4 +3461,3 @@ unittest GC.free(z); GC.minimize(); // release huge pool } - diff --git a/src/gc/impl/manual/gc.d b/src/gc/impl/manual/gc.d index 3bcde758e3..6c14e82efc 100644 --- a/src/gc/impl/manual/gc.d +++ b/src/gc/impl/manual/gc.d @@ -195,6 +195,11 @@ class ManualGC : GC return typeof(return).init; } + /// This implementation is a no-op as there is no collection to monitor + override void monitor (CollectionStartHook, CollectionEndHook) nothrow @nogc + { + } + void addRoot(void* p) nothrow @nogc { roots.insertBack(Root(p));