README-INTERNALS.md
1This document describes some general patterns in the implementation.
2
3# Resource reclamation.
4
5The library is written such that all resources will be released when the
6garbage collector claims an object. However, we also export Close()
7methods on each type that needs explicit cleanup. This is because the Go
8garbage collector [isn't always able to make the right decisions about
9objects with pointers to C memory][1].
10
11## Tracking dependencies
12
13Each notmuch object has a corresponding wrapper object:
14notmuch_database_t is wrapped by DB, notmuch_query_t is wrapped by Query
15and so on. Each of these wrappers is an alias for the type cStruct,
16which holds a pointer to the underlying C object, and also to the
17wrappers for any objects referenced by the underlying C object (via the
18`parent` field). This keeps the GC from collecting parent objects if
19the children are still in use.
20
21## Creating objects
22
23When creating an object, the caller should set the `parent` field to a
24pointer to the object's immediate parent object, and set cptr to the
25underlying c pointer. Finally, calling setGcClose on the object will
26cause it to be released properly by the garbage collector.
27
28## Cleaning up
29
30Calling the `Close()` method on an object a second time is a no-op, and
31thus always safe. The primary reason for this is that it makes dealing
32with mixing GC and manual reclamation simple.
33
34The Close methods also clear all of their references to parent objects
35explicitly. While this isn't strictly necessary, it means the GC
36will know that they are unreachable sooner, if that becomes the case.
37Per the documentation for `runtime.SetFinalizer`, once the finalizer is
38called, the object will stick around until the next time the GC looks at
39it. Because of this, it won't otherwise even consider parent objects
40until the third pass at least.
41
42In some cases, invoking the `*_destroy` function on an object also
43cleans up its children, in which case it becomes unsafe to then invoke
44their `*_destroy` function. cStruct handles all of the bookkeeping and
45synchronization necessary to deal with this; derived types just need to
46make proper use of doClose() in their Close() implementation (see the
47comments in cstruct.go)
48
49[1]: https://gist.github.com/dwbuiten/c9865c4afb38f482702e#cleaning
50