1
1
mirror of https://github.com/kanaka/mal.git synced 2024-08-17 17:50:24 +03:00
mal/impls/vala/gc.vala
Joel Martin 8a19f60386 Move implementations into impls/ dir
- 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.
2020-02-10 23:50:16 -06:00

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();
}
}