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
|
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
|
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%
|
### u3: reference counts
|
||||||
of what you need to know to program in u3 is just how to get your
|
|
||||||
refcounts right.
|
|
||||||
|
|
||||||
|
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:
|
u3 deals with reference-counted, immutable, acyclic nouns.
|
||||||
***
|
Unfortunately, we are not Apple and can't build reference
|
||||||
*** u3a_: fundamental allocators.
|
counting into your C compiler, so you need to count by hand.
|
||||||
*** 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.
|
|
||||||
**/
|
|
||||||
|
|
||||||
|
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
|
### u3: reference protocols
|
||||||
build directory:
|
|
||||||
|
|
||||||
g/ u3 implementation
|
*THIS IS THE MOST CRITICAL SECTION IN THE `u3` DOCUMENTATION.*
|
||||||
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
|
|
||||||
|
|
||||||
|
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_lo_grab("lo_exit", u3_none);
|
||||||
// u3_loom_save(u3A->ent_d);
|
// u3_loom_save(u3A->ent_d);
|
||||||
// u3_loom_exit();
|
// u3_loom_exit();
|
||||||
u3t_boff();
|
// u3t_boff();
|
||||||
u3_lo_exit();
|
u3_lo_exit();
|
||||||
|
|
||||||
exit(u3_Host.xit_i);
|
exit(u3_Host.xit_i);
|
||||||
@ -553,7 +553,7 @@ u3_lo_loop()
|
|||||||
|
|
||||||
_lo_init();
|
_lo_init();
|
||||||
u3_raft_init();
|
u3_raft_init();
|
||||||
u3t_boot(); // activate profiling
|
// u3t_boot(); // activate profiling
|
||||||
|
|
||||||
if ( c3n == u3_Host.ops_u.bat ) {
|
if ( c3n == u3_Host.ops_u.bat ) {
|
||||||
uv_run(u3L, UV_RUN_DEFAULT);
|
uv_run(u3L, UV_RUN_DEFAULT);
|
||||||
|
Loading…
Reference in New Issue
Block a user