mirror of
https://github.com/ilyakooo0/urbit.git
synced 2024-12-18 20:31:40 +03:00
Merge remote-tracking branch 'origin/test' into dish
This commit is contained in:
commit
3bdcfedb2a
174
Spec/u3.md
174
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
|
||||
|
||||
|
4
v/loop.c
4
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);
|
||||
|
Loading…
Reference in New Issue
Block a user