mirror of
https://github.com/kanaka/mal.git
synced 2024-09-11 21:57:38 +03:00
- Reorder README to have implementation list after "learning tool" bullet. - This also moves tests/ and libs/ into impls. It would be preferrable to have these directories at the top level. However, this causes difficulties with the wasm implementations which need pre-open directories and have trouble with paths starting with "../../". So in lieu of that, symlink those directories to the top-level. - Move the run_argv_test.sh script into the tests directory for general hygiene.
197 lines
5.2 KiB
Vala
197 lines
5.2 KiB
Vala
abstract class GC.Object : GLib.Object {
|
|
public GC.Object? next;
|
|
public unowned GC.Object? prev;
|
|
public bool visited;
|
|
|
|
public delegate void VisitorFunc(GC.Object? obj);
|
|
|
|
construct {
|
|
next = null;
|
|
prev = null;
|
|
GC.Core.register_object(this);
|
|
}
|
|
public abstract void gc_traverse(VisitorFunc visitor);
|
|
}
|
|
|
|
class GC.Root : GLib.Object {
|
|
public weak GC.Root? next;
|
|
public weak GC.Root? prev;
|
|
|
|
public GC.Object? obj;
|
|
|
|
construct { GC.Core.register_root(this); }
|
|
~Root() { GC.Core.unregister_root(this); }
|
|
|
|
public Root.empty() { obj = null; }
|
|
public Root(GC.Object? obj_) { obj = obj_; }
|
|
}
|
|
|
|
class GC.Core : GLib.Object {
|
|
private struct ObjectQueue {
|
|
GC.Object? head;
|
|
GC.Object? tail;
|
|
|
|
public void unlink(GC.Object obj_) {
|
|
GC.Object obj = obj_;
|
|
|
|
if (obj.prev == null) {
|
|
assert(obj == head);
|
|
head = obj.next;
|
|
}
|
|
else
|
|
obj.prev.next = obj.next;
|
|
|
|
if (obj.next == null)
|
|
tail = obj.prev;
|
|
else
|
|
obj.next.prev = obj.prev;
|
|
}
|
|
|
|
public void link(GC.Object obj) {
|
|
if (tail != null) {
|
|
tail.next = obj;
|
|
obj.prev = tail;
|
|
} else {
|
|
head = obj;
|
|
obj.prev = null;
|
|
}
|
|
|
|
tail = obj;
|
|
obj.next = null;
|
|
}
|
|
}
|
|
|
|
private static ObjectQueue objects;
|
|
private static weak GC.Root? roots_head;
|
|
private static uint until_next_collection;
|
|
|
|
static construct {
|
|
objects.head = objects.tail = null;
|
|
roots_head = null;
|
|
}
|
|
|
|
public static void register_object(GC.Object obj) {
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: registered %p [%s]\n",
|
|
obj, Type.from_instance(obj).name());
|
|
#endif
|
|
objects.link(obj);
|
|
if (until_next_collection > 0)
|
|
until_next_collection--;
|
|
}
|
|
public static void register_root(GC.Root root) {
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: registered root %p\n", root);
|
|
#endif
|
|
root.next = roots_head;
|
|
root.prev = null;
|
|
if (roots_head != null)
|
|
roots_head.prev = root;
|
|
roots_head = root;
|
|
}
|
|
public static void unregister_root(GC.Root root) {
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: unregistered root %p\n", root);
|
|
#endif
|
|
if (root.prev == null)
|
|
roots_head = root.next;
|
|
else
|
|
root.prev.next = root.next;
|
|
if (root.next != null)
|
|
root.next.prev = root.prev;
|
|
}
|
|
|
|
private static void statistics(uint before, uint after, uint roots) {
|
|
#if GC_STATS
|
|
stderr.printf("GC: %u roots, %u -> %u objects\n",
|
|
roots, before, after);
|
|
#endif
|
|
}
|
|
|
|
public static void collect() {
|
|
uint orig = 0;
|
|
uint roots = 0;
|
|
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: started\n");
|
|
#endif
|
|
for (unowned GC.Object obj = objects.head; obj != null; obj = obj.next)
|
|
{
|
|
obj.visited = false;
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: considering %p [%s]\n",
|
|
obj, Type.from_instance(obj).name());
|
|
#endif
|
|
orig++;
|
|
}
|
|
|
|
ObjectQueue after = { null, null };
|
|
until_next_collection = 0;
|
|
|
|
for (unowned GC.Root root = roots_head; root != null; root = root.next)
|
|
{
|
|
roots++;
|
|
if (root.obj != null && !root.obj.visited) {
|
|
GC.Object obj = root.obj;
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: root %p -> %p [%s]\n",
|
|
root, obj, Type.from_instance(obj).name());
|
|
#endif
|
|
objects.unlink(obj);
|
|
after.link(obj);
|
|
obj.visited = true;
|
|
until_next_collection++;
|
|
}
|
|
}
|
|
|
|
for (GC.Object? obj = after.head; obj != null; obj = obj.next) {
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: traversing %p [%s]\n",
|
|
obj, Type.from_instance(obj).name());
|
|
#endif
|
|
obj.gc_traverse((obj2_) => {
|
|
GC.Object obj2 = obj2_;
|
|
if (obj2 == null)
|
|
return;
|
|
if (!obj2.visited) {
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: %p -> %p [%s]\n",
|
|
obj, obj2, Type.from_instance(obj2).name());
|
|
#endif
|
|
objects.unlink(obj2);
|
|
after.link(obj2);
|
|
obj2.visited = true;
|
|
until_next_collection++;
|
|
}
|
|
});
|
|
}
|
|
|
|
// Manually free everything, to avoid stack overflow while
|
|
// recursing down the list unreffing them all
|
|
objects.tail = null;
|
|
while (objects.head != null) {
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: collecting %p [%s]\n", objects.head,
|
|
Type.from_instance(objects.head).name());
|
|
#endif
|
|
objects.head = objects.head.next;
|
|
}
|
|
|
|
objects = after;
|
|
|
|
#if GC_DEBUG
|
|
stderr.printf("GC: finished\n");
|
|
#endif
|
|
|
|
statistics(orig, until_next_collection, roots);
|
|
}
|
|
|
|
public static void maybe_collect() {
|
|
#if !GC_ALWAYS
|
|
if (until_next_collection > 0)
|
|
return;
|
|
#endif
|
|
collect();
|
|
}
|
|
}
|