diff --git a/Spec/u3.md b/Spec/u3.md index 9543ec94d..29583d19c 100644 --- a/Spec/u3.md +++ b/Spec/u3.md @@ -159,87 +159,117 @@ Regular symbols follow this pattern: u3q[a-g] jets (retain, C args) i/j/q.h j/[a-g]/*.c u3w[a-g] jets (retain, nock core) i/j/w.h j/[a-g]/*.c +Irregular symbols always start with `u3` and obey no other rules. +They're defined in `i/n/u.h`. Finally, `i/all.h` includes all +these headers (fast compilers, yay) and is all you need to +program in `u3`. -u3 deals with reference-counted, immutable, acyclic nouns. 90% -of what you need to know to program in u3 is just how to get your -refcounts right. +### u3: reference counts +The only really essential thing you need to know about `u3` is +how to handle reference counts. Everything else, you can skip +and just get to work. - /** Prefix definitions: - *** - *** u3a_: fundamental allocators. - *** u3c_: constants. - *** u3e_: checkpointing. - *** u3h_: HAMT hash tables. - *** u3i_: noun constructors - *** u3j_: jets. - *** u3k*: direct jet calls (modern C convention) - *** u3m_: system management etc. - *** u3n_: nock interpreter. - *** u3o_: fundamental macros. - *** u3q*: direct jet calls (archaic C convention) - *** u3r_: read functions which never bail out. - *** u3s_: structures and definitions. - *** u3t_: tracing. - *** u3w_: direct jet calls (core noun convention) - *** u3x_: read functions which do bail out. - *** u3v_: arvo specific structures. - *** u3z_: memoization. - *** - *** u3_cr_, u3_cx_, u3_cz_ functions use retain conventions; the caller - *** retains ownership of passed-in nouns, the callee preserves - *** ownership of returned nouns. - *** - *** Unless documented otherwise, all other functions use transfer - *** conventions; the caller logically releases passed-in nouns, - *** the callee logically releases returned nouns. - *** - *** In general, exceptions to the transfer convention all occur - *** when we're using a noun as a key. - **/ +u3 deals with reference-counted, immutable, acyclic nouns. +Unfortunately, we are not Apple and can't build reference +counting into your C compiler, so you need to count by hand. +Every allocated noun contains a counter which counts the number +of references to it - typically variables with type `u3_noun`. +When this counter goes to 0, the noun is freed. +To tell `u3` that you've added a reference to a noun, call the +function `u3a_gain()` or its shorthand `u3k()`. (For your +convenience, this function returns its argument.) To tell `u3` +that you've destroyed a reference, call `u3a_lose()` or `u3z()`. +(If you screw up by decrementing the counter too much, `u3` will +dump core in horrible ways. If you screw up by incrementing it +too much, `u3` will leak memory. To check for memory leaks, +set the `bug_o` flag in `u3e_boot()` - eg, run `vere` with `-g`. +Memory leaks are difficult to debug - the best way to handle +leaks is just to revert to a version that didn't have them, and +look over your code again.) -The best way to introduce `u3` is with a simple map of the Urbit -build directory: +### u3: reference protocols - g/ u3 implementation - g/a.c allocation - g/e.c persistence - g/h.c hashtables - g/i.c noun construction - g/j.c jet control - g/m.c master state - g/n.c nock execution - g/r.c noun access, error returns - g/t.c tracing/profiling - g/v.c arvo kernel - g/x.c noun access, error crashes - g/z.c memoization/caching - i/ all includes - i/v vere systems headers - i/g u3 headers (matching g/ names) - i/c c3 headers - i/c/defs.h miscellaneous c3 macros - i/c/motes.h symbolic constants - i/c/portable.h portability definitions - i/c/types.h c3 types - i/j jet headers - i/j/k.h jet interfaces (transfer, args) - i/j/q.h jet interfaces (retain, args) - i/j/w.h jet interfaces (retain, core) - j/ jet code - j/dash.c jet structures - j/1 tier 1 jets: basic math - j/2 tier 2 jets: lists - j/3 tier 3 jets: bit twiddling - j/4 tier 4 jets: containers - j/5 tier 5 jets: misc - j/6 tier 6 jets: hoon - v/ vere systems code - outside/ all external bundled code +*THIS IS THE MOST CRITICAL SECTION IN THE `u3` DOCUMENTATION.* +The key question when calling a C function in a refcounted world +is what the function will do to the noun refcounts - and, if the +function returns a noun, what it does to the return. +There are two semantic patterns, `transfer` and `retain`. In +`transfer` semantics, the caller "gives" a use count to the +callee, which "gives back" any return. For instance, if I have + { + u3_noun foo = u3i_string("foobar"); + u3_noun bar; + + bar = u3f_futz(foo); + [...] + u3z(bar); + } + +Suppose `u3f_futz()` has `transfer` semantics. At `[...]`, my +code holds one reference to `bar` and zero references to `foo` - +which has been freed, unless it's part of `bar`. My code now +owns `bar` and gets to work with it until it's done, at which +point a `u3z()` is required. + +On the other hand, if `u3f_futz()` has `retain` semantics, we +need to write + + { + u3_noun foo = u3i_string("foobar"); + u3_noun bar; + + bar = u3f_futz(foo); + [...] + u3z(foo); + } + +because calling `u3f_futz()` does not release our ownership of +`foo`, which we have to free ourselves. + +But if we free `bar`, we are making a great mistake, because our +reference to it is not in any way registered in the memory +manager (which cannot track references in C variables, of +course). It is normal and healthy to have these uncounted +C references, but they must be treated with care. + +The bottom line is that it's essential for the caller to know +the refcount semantics of any function which takes or returns a +noun. (In some unusual circumstances, different arguments or +returns in one function may be handled differently.) + +Broadly speaking, as a design question, retain semantics are more +appropriate for functions which inspect or query nouns. For +instance, `u3h()` (which takes the head of a noun) retains, so +that we can traverse a noun tree without constantly incrementing +and decrementing. + +Transfer semantics are more appropriate for functions which make +nouns, which is obviously what most functions do. + +In general, though, in most places it's not worth thinking about +what your function does. There is probably a convention for it. +Follow the convention. + +### u3: reference conventions + +The `u3` convention is that, unless otherwise specified, *all +functions have transfer semantics* - with the exception of the +prefixes: `u3r`, `u3x`, `u3z`, `u3q` and `u3w`. Also, within +jet directories `a` through `f` (but not `g`), internal functions +retain (for historical reasons). + +If functions outside this set have retain semantics, they need to +be commented, both in the `.h` and `.c` file, with `RETAIN` in +all caps. Yes, it's this important. + +### u3: system and memory architecture + +Describing diff --git a/v/loop.c b/v/loop.c index 51f381af2..cf34c935c 100644 --- a/v/loop.c +++ b/v/loop.c @@ -385,7 +385,7 @@ u3_lo_shut(c3_o inn) // u3_lo_grab("lo_exit", u3_none); // u3_loom_save(u3A->ent_d); // u3_loom_exit(); - u3t_boff(); + // u3t_boff(); u3_lo_exit(); exit(u3_Host.xit_i); @@ -553,7 +553,7 @@ u3_lo_loop() _lo_init(); u3_raft_init(); - u3t_boot(); // activate profiling + // u3t_boot(); // activate profiling if ( c3n == u3_Host.ops_u.bat ) { uv_run(u3L, UV_RUN_DEFAULT);