Show HN: A Simple Garbage Collector for C

github.com github.com3 years ago in #Dev Love393

gc is an implementation of a conservative, thread-local, mark-and-sweepgarbage collector. The implementation provides a fully functional replacementfor the standard POSIX malloc(), calloc(), realloc(), and free() calls. The focus of gc is to provide a conceptually clean implementation ofa mark-and-sweep GC, without delving into the depths of architecture-specificoptimization (see e.g. the Boehm GC for such an undertaking). Itshould be particularly suitable for learning purposes and is open for all kindsof optimization (PRs welcome!). The original motivation for gc is my desire to write my own LISPin C, entirely from scratch – and that required garbage collection. Acknowledgements This work would not have been possible without the ability to read the work ofothers, most notably the Boehm GC, orangeduck’s tgc (which alsofollows the ideals of being tiny and simple), and The Garbage CollectionHandbook. Table of contentsDocumentation OverviewQuickstartDownload and test$ git clone git@github.com:mkirchner/gc.git $ cd gc $ make test $ make coverage # to open the current coverage in a browser Basic usage… #include “gc.h“ … void some_fun() { … int* my_array = gc_calloc(gc, 1024, sizeof(int)); for (size_t i; i<1024; ++i) { my_array[i] = 42; } … // look ma, no free! } int main(int argc, char* argv[]) { gc = gc_start(gc, &argc); … some_fun(); … gc_stop(gc); return 0; }Core API This describes the core API, see gc.h for more details and the low-level API. Starting, stopping, pausing, resuming and running GC In order to initialize and start garbage collection, use the gc_start()function and pass a bottom-of-stack address: void gc_start(GarbageCollector* gc, void* bos); The bottom-of-stack parameter bos needs to point to a stack-allocatedvariable and marks the low end of the stack from where rootfinding (scanning) starts. Garbage collection can be stopped, paused and resumed with void gc_stop(GarbageCollector* gc); void gc_pause(GarbageCollector* gc); void gc_resume(GarbageCollector* gc); and manual garbage collection can be triggered with size_t gc_run(GarbageCollector* gc);Memory allocation and deallocation gc supports malloc(), calloc()and realloc()-style memory allocation.The respective funtion signatures mimick the POSIX functions (with theexception that we need to pass the garbage collector along as the firstargument): void* gc_malloc(GarbageCollector* gc, size_t size); void* gc_calloc(GarbageCollector* gc, size_t count, size_t size); void* gc_realloc(GarbageCollector* gc, void* ptr, size_t size); It is possible to pass a pointer to a desctructor function through theextended interface: void* dtor(void* obj) { // do some cleanup work obj->parent->deregister(); obj->db->disconnect() … // no need to free obj } … SomeObject* obj = gc_malloc_ext(gc, sizeof(SomeObject), dtor); … gc supports static allocations that are garbage collected only when theGC shuts down via gc_stop(). Just use the appropriate helper function: void* gc_malloc_static(GarbageCollector* gc, size_t size, void (*dtor)(void*)); Static allocation expects a pointer to a finalization function; just set toNULL if finalization is not required. Note that gc currently does not guarantee a specific ordering when itcollects static variables, If static vars need to be deallocated in aparticular order, the user should call gc_free() on them in the desiredsequence prior to calling gc_stop(), see below. It is also possible to trigger explicit memory deallocation using void gc_free(GarbageCollector* gc, void* ptr); Calling gc_free() is guaranteed to (a) finalize/destruct on the objectpointed to by ptr if applicable and (b) to free the memory that ptr points toirrespective of the current scheduling for garbage collection and will alsowork if GC has been paused using gc_pause() above. Helper functions gc also offers a strdup() implementation that returns a garbage-collectedcopy: char* gc_strdup (GarbageCollector* gc, const char* s);Basic Concepts The fundamental idea behind garbage collection is to automate the memoryallocation/deallocation cycle. This is accomplished by keeping track of allallocated memory and periodically triggering deallocation for memory that isstill allocated but unreachable. Many advanced garbage collectors also implement their own approach to memoryallocation (i.e….

Like to keep reading?

This article first appeared on github.com. If you'd like to keep reading, follow the white rabbit.

View Full Article

Leave a Reply