{"id":13836135,"url":"https://github.com/bullno1/ugc","last_synced_at":"2026-03-03T13:48:21.814Z","repository":{"id":38854896,"uuid":"93270733","full_name":"bullno1/ugc","owner":"bullno1","description":"A single-header incremental garbage collector library","archived":false,"fork":false,"pushed_at":"2024-08-28T07:39:28.000Z","size":23,"stargazers_count":291,"open_issues_count":0,"forks_count":14,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-10-13T09:56:48.628Z","etag":null,"topics":["garbage-collection","garbage-collector","header-only","memory-management","single-header","single-header-lib"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bullno1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-03T19:52:15.000Z","updated_at":"2025-09-23T00:23:17.000Z","dependencies_parsed_at":"2024-11-20T22:55:17.507Z","dependency_job_id":null,"html_url":"https://github.com/bullno1/ugc","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/bullno1/ugc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bullno1%2Fugc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bullno1%2Fugc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bullno1%2Fugc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bullno1%2Fugc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bullno1","download_url":"https://codeload.github.com/bullno1/ugc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bullno1%2Fugc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30047839,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-03T10:53:31.691Z","status":"ssl_error","status_checked_at":"2026-03-03T10:53:22.041Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["garbage-collection","garbage-collector","header-only","memory-management","single-header","single-header-lib"],"created_at":"2024-08-04T15:00:36.444Z","updated_at":"2026-03-03T13:48:21.795Z","avatar_url":"https://github.com/bullno1.png","language":"C","funding_links":[],"categories":["C","Memory Management"],"sub_categories":[],"readme":"# μgc\n\n[![License](https://img.shields.io/badge/license-BSD-blue.svg)](LICENSE)\n\n*μgc* is a single-header garbage collector library.\nIt is designed to be embedded in a programming language's runtime.\n\n## Features\n\n* Fully incremental using tri-color marking.\n* No external dependencies.\n* No memory allocation.\n* Overhead of 2 pointers per object.\n* Thoroughly tested: [unit test](munit.c) and [property-based test](theft.c)\n\n## Usage\n\n### Building\n\nPut `ugc.h` into your project.\n\nWrite in *one* C file:\n\n```c\n#define UGC_IMPLEMENTATION\n#include \"ugc.h\"\n```\n\n### Integrating with your runtime\n\nAll heap allocated objects must include `ugc_header_t`, usually as the first member:\n\n```c\nstruct my_heap_obj_s\n{\n\tugc_header_t header;\n\n\t// Other fields\n\ttype_info_t type;\n\tvoid* external_ref;\n\t// ...\n};\n```\n\nInitialize a garbage collector with:\n\n```\nugc_t gc;\n\nugc_init(\u0026gc, scan_fn, free_fn);\n\n// Each gc instance has a userdata field.\ngc.userdata = language_runtime;\n```\n\n`ugc_init` requires two callback functions: `scan_fn` and `free_fn`.\n\n`scan_fn` is a function that will be called to help μgc trace an object's reference and the root set.\nIt should behave as follow:\n\n```c\nstatic void\nscan_gc_obj(ugc_t* gc, ugc_header_t* header)\n{\n\tif(header == NULL) // Scan the root set\n\t{\n\t\t// ugc_visit needs to be called on each pointer in the stack and global\n\t\t// environment\n\n\t\tstruct my_language_runtime_s* runtime = gc-\u003euserdata;\n\n\t\t// Scan the stack\n\t\tfor(unsigned int i = 0; i \u003c runtime-\u003estack_len; ++i)\n\t\t{\n\t\t\tif(runtime-\u003estack[i]) { ugc_visit(gc, runtime-\u003estack[i]); }\n\t\t}\n\n\t\t// If the language is similar to Lua where the global environment is a\n\t\t// first-class object, call ugc_visit on the object instead of its fields\n\t\tugc_visit(gc, runtime-\u003eglobal);\n\t}\n\telse // Scan the given object\n\t{\n\t\t// ugc_visit needs to be called on each external reference of this\n\t\t// object.\n\n\t\tstruct my_heap_obj_s* obj = (struct my_heap_obj_s*)header;\n\t\tif(obj-\u003eexternal_ref) { ugc_visit(gc, obj-\u003eexternal_ref); }\n\t}\n}\n```\n\nThe second parameter of `ugc_visit` must not be NULL and must point to a `ugc_header_t`.\n\n`free_fn` will be called when μgc has determined that a language's object is garbage.\nIt should release an object's resources:\n\n```c\nstatic void\nfree_gc_obj(ugc_t* gc, ugc_header_t* header)\n{\n\tstruct my_language_runtime_s* runtime = gc-\u003euserdata;\n\truntime-\u003efree(header);\n}\n```\n\nWhen a new object is allocated, it needs to be registered with μgc using:\n\n```\nugc_register(gc, new_object);\n```\n\nWhenever an object receives a reference to another (i.e: `src.field = dst`), μgc must be informed:\n\n```\nugc_write_barrier(gc, direction, src, dst);\n```\n\nThere are two types of write barriers: \"forward\" and \"backward\".\n\n[LuaJIT wiki](http://wiki.luajit.org/New-Garbage-Collector#gc-algorithms_tri-color-incremental-mark-sweep) states the following about \"backward\" barrier:\n\n\u003e This is moving the barrier \"back\", because the object has to be reprocessed later on.\n\u003e This is beneficial for container objects, because they usually receive several stores in succession.\n\u003e This avoids a barrier for the next objects that are stored into it (which are likely white, too).\n\nAnd \"forward\" barrier:\n\n\u003e This moves the barrier \"forward\", because it implicitly drives the GC forward.\n\u003e This works best for objects that only receive isolated stores.\n\nIn the above language example, stores to the stack/local variables do not require a write barrier but stores to global variables do.\n\n### Controlling garbage collection\n\nμgc does not start collection automatically because there are many factors (e.g: heap size, number of objects, time limit...) that need to be considered.\nIt is best left to the language implementer to decide.\nThus, it provides two functions to control the garbage collector:\n\n`ugc_step(gc)` performs a single atomic step (e.g: mark/free one object, scan the root set).\nOne typically calls it several times per allocation or regularly after a trigger.\n\"Push/pop GC pause\" function usually found in some language's API can be implemented by simply maintaining a counter and not calling `ugc_step` if it is non-zero.\nThe current state of the GC is stored in `ugc_t::state`.\nOne can use that to give different speeds to different phases.\n\n`ugc_collect(gc)` finishes the *current* collection cycle.\nThis means:\n\n- If the GC has alread started, it will return once the current cycle ends.\n- If the GC is idle (`ugc_t::state == UGC_IDLE`), it will start a cycle and finish it.\n\nTherefore, if the GC is already in the `UGC_SWEEP` phase, any new garbage will be left to the next cycle.\nOne needs to pay attention to this when calling `gc_collect` in case of emergency (e.g: `malloc` returns `NULL`).\nThe first call to `gc_collect` may reclaim some but not all memory.\nIt is possible for `malloc` to return `NULL` again.\n`gc_collect` should be called a second time.\nThe runtime should only panic if `malloc` still fails.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbullno1%2Fugc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbullno1%2Fugc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbullno1%2Fugc/lists"}