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