From ac0244e4936ad099c2f4c3f72e518733f7fac2fe Mon Sep 17 00:00:00 2001 From: Mathias Lang Date: Thu, 5 Oct 2017 20:24:29 +0200 Subject: [PATCH] Implement a monitor system for the GC Allow one to hook the start and end of a fullcollect, potentially gathering statistics / logging the event as one see fit. This was present in the D1 runtime but got stripped for unknown reason (git history has no mention of it). Sociomantic relies on it heavily for some real-time apps. --- src/core/memory.d | 23 +++++++++++++++++++++++ src/gc/gcinterface.d | 11 +++++++++++ src/gc/impl/conservative/gc.d | 19 ++++++++++++++++++- src/gc/impl/manual/gc.d | 5 +++++ 4 files changed, 57 insertions(+), 1 deletion(-) 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));