Update some jet stuff, and docs

This commit is contained in:
C. Guy Yarvin 2014-11-28 12:29:10 -08:00
parent ec15c99566
commit bdebf8f657
4 changed files with 310 additions and 180 deletions

View File

@ -93,8 +93,9 @@ modularize the definitions. Keep them alphabetical, though.
### c3: variables and variable naming
The C3 style uses Hoon style TLV variable names, with a quasi
Hungarian syntax. This is weird, but works really well, as
long as what you're doing isn't hideous.
Hungarian syntax. This is weird, but works really well, as long
as what you're doing isn't hideously complicated. (Then it works
badly, but we shouldn't need anything hideous in u3.)
A TLV variable name is a random pronounceable three-letter
string, sometimes with some vague relationship to its meaning,
@ -295,7 +296,7 @@ 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 a convention for it, which
depends on where it is, not what it is. Follow the convention.
depends on where it is, not what it does. Follow the convention.
### u3: reference conventions
@ -653,7 +654,9 @@ debugging printfs: `u3m_pretty()`, `u3m_p()`, `u3m_tape()` and
It's sometimes nice to run a mark-and-sweep garbage collector,
`u3m_grab()`, which collects the world from a list of roots,
and asserts if it finds any leaks or incorrect refcounts.
and asserts if it finds any leaks or incorrect refcounts. This
tool is for debugging and long-term maintenance only; refcounts
should never err.
### u3j: jets
@ -662,9 +665,9 @@ a useful computing environment. Except perhaps `u3a` (there is
really no such thing as a trivial allocator, though `u3a` is
dumber than most) - `u3j` is the most interesting code in `u3`.
Let's consider the minor miracle of jet binding which lets `u3j`
work - and decrement not be `O(n)` - without violating the
precisely defined semantics of pure Nock, *ever*.
Let's consider the minor miracle of driver-to-battery binding
which lets `u3j` work - and decrement not be `O(n)` - without
violating the precisely defined semantics of pure Nock, *ever*.
It's easy to assume that jets represent an architectural coupling
between Hoon language semantics and Nock interpreter internals.
@ -682,7 +685,29 @@ itself.
Except for the arbitrary decision to make a core `[code data]`,
(or as we sometimes say, `[battery payload]`), instead of `[data
code]`, any high-level language transforming itself to Nock would
use this design. So jets are in fact fully general.
use this design.
So jets are in fact fully general. Broadly speaking, the jet
system works by matching a C *driver* to a battery. When the
battery is invoked with Nock operator `9`, it must be found in
associative memory and linked to its driver. Then we link the
formula axis of the operation (`a` in `[9 a b]`) to a specific
function in the driver.
To validate this jet binding, we need to know two things. One,
we need to know the C function actually is a perfect semantic
match for the Nock formula. This can be developed with driver
test flags, which work, and locked down with a secure formula
hash in the driver, which we haven't bothered with just yet.
(You could also try to develop a formal method for verifying
that C functions and Nock formulas are equivalent, but this is
a research problem for the future.)
Two, we need to validate that the payload is appropriate for the
battery. We should note that jets are a Nock feature and have no
reference to Hoon. A driver which relies on the Hoon type system
to only pair it with valid payloads is a broken driver, and
breaks the Nock compliance of the system as a whole. So don't.
Now, a casual observer might look at `[battery payload]` and
expect the simplest case of it to be `[formula subject]`. That
@ -764,7 +789,7 @@ numbering itself on process initialization. This structure -
which embeds function pointers to all the jets - is defined
in `j/tree.c`. The data structures:
/* u3j_harm: jet arm.
/* u3j_harm: driver arm.
*/
typedef struct _u3j_harm {
c3_c* fcs_c; // `.axe` or name
@ -774,7 +799,7 @@ in `j/tree.c`. The data structures:
c3_o liv; // live (enabled)
} u3j_harm;
/* u3j_core: driver definition.
/* u3j_core: C core driver.
*/
typedef struct _u3j_core {
c3_c* cos_c; // control string
@ -788,9 +813,9 @@ in `j/tree.c`. The data structures:
*/
typedef struct _u3e_dash {
u3j_core* dev_u; // null-terminated static list
c3_l len_l; // dynamic array length
c3_l all_l; // allocated length
u3j_core* ray_u; // dynamic array by axis
c3_l len_l; // ray_u filled length
c3_l all_l; // ray_u allocated length
u3j_core* ray_u; // dynamic driver array
} u3j_dash;
Warm and cold state is *per road*. In other words, as we nest
@ -805,12 +830,13 @@ In case you understand Hoon, `das` (cold state) is a `++dash`,
and `har_p` (warm state) is a map from battery to `++calx`:
++ bane ,@tas :: battery name
++ bash ,@uvH :: ctx identity hash
++ bash ,@uvH :: label hash
++ bosh ,@uvH :: local battery hash
++ batt ,* :: battery
++ calf ::
$: jax=,@ud :: hot core index
hap=(map ,@ud ,@ud) :: axis/hot arm index
lab=path :: label as path
jit=* :: arbitrary data
== ::
++ calx (trel calf (pair bash cope) club) :: cached by battery
@ -821,7 +847,7 @@ and `har_p` (warm state) is a map from battery to `++calx`:
++ corp (each core batt) :: parent or static
++ dash (map bash clog) :: jet system
The jet index `jax` in a `++calx` is an index into `ray_u` in the
The driver index `jax` in a `++calx` is an index into `ray_u` in the
dashboard - ie, a pointer into hot state. This is why the warm
state has to be reset when we reload the pier in a new process.
@ -897,9 +923,22 @@ instance, if the core is a Hoon gate - a function - we will call
### u3j: the cold jet dashboard
For even more fun, the jet tree is not actually a tree of
batteries. Rather, *multiple batteries* may share any location
in the jet tree. For instance, it's normal to have two
equivalent Nock batteries at the same time in one pier: one
batteries. It's a tree of battery *labels*, where a label is
an [axis term] path from the root of the tree. (At the root,
if the core pattern is always followed properly, is a core whose
payload is an atomic constant, conventionally the Hoon version.)
Under each of these labels, it's normal to have an arbitrary
number of different Nock batteries (not just multiple copies
of the same noun, a situation we *do* strive to avoid). For
instance, one might be compiled with debugging hints, one not.
We might even have changed the semantics of the battery without
changing the label - so long as those semantics don't invalidate
any attached driver.
et tree. For instance, it's normal to have
two equivalent Nock batteries at the same time in one pier: one
battery compiled with debugging hints, one not.
Rather, the jet tree is a semantic hierarchy. The root of the
@ -981,11 +1020,12 @@ from battery to `++calx`.
A `calx` is a triple of a `++calf`, a `[bash cope]` cell, and a
`club`. The latter two are all straight from cold state.
The `calf` contains data dependent on hot state. It's a triple
of `jax`, the hot jet index (in `ray_u` in `u3j_dash`); `hap`, a
table from arm axis (ie, the axis of each formula within the
battery) to jet arm index (into `arm_u` in `u3j_core`); and
`jit`, any other dynamic data that may speed up execution.
The `calf` contains warm data dependent on hot state. It's a
quadruple: of `jax`, the hot driver index (in `ray_u` in
`u3j_dash`); `hap`, a table from arm axis (ie, the axis of each
formula within the battery) to driver arm index (into `arm_u` in
`u3j_core`); `lab`, the complete label path; and `jit`, any
other dynamic data that may speed up execution.
We construct `hap`, when we create the calx, by iterating through
the arms registered in the `u3j_core`. Note the way a `u3j_harm`
@ -999,8 +1039,8 @@ an `fcs_c` with a named arm, it's sufficient to make sure the
name is bound to a formula `[0 axis]` in the hook table.
`jit`, as its name suggests, is a stub where any sort of
optimization data computed on jet registration might go.
To use it, fill in the `_cj_jit()` function.
optimization data computed on battery registration might go. To
use it, fill in the `_cj_jit()` function.
### u3j: the hot dashboard
@ -1012,21 +1052,22 @@ will try to execute them.
Because nouns with a reference count of 1 are precious,
`u3j_kick()` has a tricky reference control definition. It
reserves the right to return `u3_none` in the case where there is
no jet, or the jet does not apply for this case; in this case, it
does not consume its argument `cor`. Otherwise, it does.
no driver, or the driver does not apply for this case; in this
case, it retains argument `cor`. If it succeeds, though, it
transfers `cor`.
`u3j_kick()` searches for the battery (always the head of the
core, of course) in the hot dashboard. If the battery is
registered, it searches for the axis in `hap` in the `calx`.
If it exists, the core has a driver and the driver has a jet for
this arm, which we can try to call. If not, we return `u3_none`.
If it exists, the core matches a driver and the driver jets this
arm. If not, we return `u3_none`.
Otherwise, we call `fun_f` in our `u3j_harm`. This obeys the
same protocol as `u3j_kick()`; it can refuse to function by
returning `u3_none`, or consume the noun.
Besides the actual function pointer `fun_f`, we have some flags
in the `u3j_harm` which tell us how to call the jet.
in the `u3j_harm` which tell us how to call the arm function.
If `ice` is yes (`&`, `0`), the jet is known to be perfect and we
can just trust the product of `fun_f`. Otherwise, we need to run
@ -1039,13 +1080,13 @@ When auto-testing jets in this way, the principle is that the
test is on the outermost layer of recursion.)
(Note also that anyone who multi-threads this execution
environment has a slight locking problem with these flags if jet
environment has a slight locking problem with these flags if arm
testing is multi-threaded.)
If `tot` is yes, (`&`, `0`), the jet is *total* and has to return
properly (though it can still return *u3_none*). Otherwise, it
is *partial* and can `u3_cm_bail()` out with c3__punt. This
feature has a cost: the jet runs in a subroad.
If `tot` is yes, (`&`, `0`), the arm function is *total* and has
to return properly (though it can still return *u3_none*).
Otherwise, it is *partial* and can `u3_cm_bail()` out with
c3__punt. This feature has a cost: the jet runs in a subroad.
Finally, if `liv` is no (`|`, 1), the jet is off and doesn't run.
@ -1061,7 +1102,7 @@ then appears ready for action.
### u3j: jet functions
At present, all jets are compiled statically into `u3`. This is
At present, all drivers are compiled statically into `u3`. This is
not a long-term permanent solution or anything. However, it will
always be the case with a certain amount of core functionality.

View File

@ -6,16 +6,17 @@
**/
#if 0
++ bane ,@tas :: battery name
++ bash ,@uvH :: ctx identity hash
++ bosh ,@uvH :: local battery hash
++ bash ,@uvH :: label hash
++ bosh ,@uvH :: battery hash
++ batt ,* :: battery
++ calf ::
$: jax=,@ud :: hot core index
hap=(map ,@ud ,@ud) :: axis/hot arm index
lab=path :: label as path
jit=* :: arbitrary data
== ::
++ calx (trel calf (pair bash cope) club) :: cached by battery
++ clog (pair cope (map batt club)) :: identity record
++ clog (pair cope (map batt club)) :: label record
++ club (pair corp (map term nock)) :: battery pattern
++ cope (trel bane axis (each bash noun)) :: core pattern
++ core ,*

83
i/n/o.h Normal file
View File

@ -0,0 +1,83 @@
/* i/n/o.h
**
** This file is in the public domain.
*/
/** Data structures.
**/
/* u3o_config: process / system configuration.
*/
/* u3e_line: control line.
*/
typedef struct _u3e_line {
c3_w pag_w;
c3_w mug_w;
} u3e_line;
/* u3e_control: memory change, control file.
*/
typedef struct _u3e_control {
c3_d evt_d; // event number
c3_w nor_w; // new page count north
c3_w sou_w; // new page count south
c3_w pgs_w; // number of changed pages
u3e_line mem_u[0]; // per page
} u3e_control;
/* u3_cs_patch: memory change, top level.
*/
typedef struct _u3_cs_patch {
c3_i ctl_i;
c3_i mem_i;
u3e_control* con_u;
} u3_ce_patch;
/* u3e_image: memory segment, open file.
*/
typedef struct _u3e_image {
c3_c* nam_c; // segment name
c3_i fid_i; // open file, or 0
c3_w pgs_w; // length in pages
} u3e_image;
/* u3e_pool: entire memory system.
*/
typedef struct _u3e_pool {
c3_c* dir_c; // path to
c3_d evt_d; // last patch written at event
c3_w dit_w[u3a_pages >> 5]; // touched since last save
u3e_image nor_u; // north segment
u3e_image sou_u; // south segment
} u3e_pool;
/** Globals.
**/
/* u3_Pool / u3P: global memory control.
*/
c3_global u3e_pool u3e_Pool;
# define u3P u3e_Pool
/** Functions.
**/
/* u3e_fault(): handle a memory event with libsigsegv protocol.
*/
c3_i
u3e_fault(void* adr_v, c3_i ser_i);
/* u3e_save():
*/
void
u3e_save(void);
/* u3e_live(): start the persistence system. Return c3y if no image.
*/
c3_o
u3e_live(c3_o nuu_o, c3_c* dir_c);
/* u3e_dirty(): count dirty pages.
*/
c3_w
u3e_dirty(void);

287
n/j.c
View File

@ -305,6 +305,7 @@ _cj_hot_mean(c3_l par_l, u3_noun mop, u3_noun bat)
return 0;
}
/* _cj_hot_mine(): in hoting state, declare a core. RETAINS.
*/
static c3_l
@ -331,144 +332,6 @@ _cj_hot_mine(u3_noun mop, u3_noun cor)
}
}
static c3_l _cj_warm_ream_at(u3_noun soh, u3_noun* lab, u3_noun cag);
/* _cj_warm_ream_be(): install battery; RETAINS.
*/
static void
_cj_warm_ream_be(c3_l jax_l,
u3_noun soh,
u3_noun lab,
u3_noun mop,
u3_noun bat,
u3_noun cuz)
{
u3h_put(u3R->jed.har_p,
bat,
u3nt(u3nq(jax_l,
_cj_warm_hump(jax_l, u3t(cuz)),
u3k(lab),
u3_nul),
u3nc(u3k(soh), u3k(mop)),
u3k(cuz)));
}
/* _cj_warm_ream_is(): reream battery; RETAINS.
*/
static void
_cj_warm_ream_is(c3_l jax_l,
u3_noun soh,
u3_noun lab,
u3_noun mop,
u3_noun sab)
{
if ( u3_nul != sab ) {
u3_noun n_sab, l_sab, r_sab, pn_sab, qn_sab;
u3x_trel(sab, &n_sab, &l_sab, &r_sab);
u3x_cell(n_sab, &pn_sab, &qn_sab);
_cj_warm_ream_be(jax_l, soh, lab, mop, pn_sab, qn_sab);
_cj_warm_ream_is(jax_l, soh, lab, mop, l_sab);
_cj_warm_ream_is(jax_l, soh, lab, mop, r_sab);
}
}
/* _cj_warm_ream_un(): reream under `soh`; RETAINS, transfers `*lab`.
*/
static c3_l
_cj_warm_ream_un(u3_noun soh, u3_noun* lab)
{
u3_noun cag = u3kdb_got(u3k(u3R->jed.das), u3k(soh));
u3_noun sab = u3t(cag);
u3_noun cax;
c3_l jax_l;
if ( u3_none != (cax = u3h_get(u3R->jed.har_p, u3h(u3h(sab)))) ) {
jax_l = u3h(u3h(cax));
*lab = u3k(u3h(u3t(u3t(u3h(cax)))));
u3z(cax);
}
else {
jax_l = _cj_warm_ream_at(soh, lab, cag);
}
u3z(cag);
return jax_l;
}
/* _cj_warm_ream_at(): reream at `soh` and `cag`; RETAINS, transfers `*lab`.
*/
static c3_l
_cj_warm_ream_at(u3_noun soh, u3_noun* lab, u3_noun cag)
{
u3_noun mop = u3h(cag);
u3_noun sab = u3t(cag);
u3_noun p_mop, q_mop, r_mop, hr_mop, tr_mop;
u3x_trel(mop, &p_mop, &q_mop, &r_mop);
u3x_cell(r_mop, &hr_mop, &tr_mop);
{
c3_l par_l, jax_l;
u3_noun pal = u3_nul;
if ( c3y == hr_mop ) {
par_l = _cj_warm_ream_un(tr_mop, &pal);
}
else {
par_l = 0;
pal = u3_nul;
}
*lab = u3nc(u3k(p_mop), pal);
u3m_p("lab", *lab);
jax_l = _cj_hot_mean(par_l, mop, 0);
_cj_warm_ream_is(jax_l, soh, *lab, mop, sab);
return jax_l;
}
}
/* _cj_warm_ream_in(): reream in `taw`; RETAINS.
*/
static void
_cj_warm_ream_in(u3_noun taw)
{
if ( u3_nul != taw ) {
u3_noun n_taw, l_taw, r_taw, pn_taw, qn_taw;
u3_noun lab;
u3x_trel(taw, &n_taw, &l_taw, &r_taw);
u3x_cell(n_taw, &pn_taw, &qn_taw);
_cj_warm_ream_at(pn_taw, &lab, qn_taw);
u3z(lab);
_cj_warm_ream_in(l_taw);
_cj_warm_ream_in(r_taw);
}
}
/* _cj_warm_ream(): reream warm from cold state.
*/
static void
_cj_warm_ream(void)
{
c3_assert(u3R == &(u3H->rod_u));
{
_cj_warm_ream_in(u3R->jed.das);
}
}
/* u3j_ream(): reream after restoring from checkpoint.
*/
void
u3j_ream(void)
{
u3h_free(u3R->jed.har_p);
u3R->jed.har_p = u3h_new();
_cj_warm_ream();
}
/* u3j_boot(): initialize jet system.
*/
void
@ -799,7 +662,7 @@ u3j_kink(u3_noun cor,
/* _cj_jit(): generate arbitrary warm jet-associated data. RETAIN.
*/
static u3_noun
_cj_jit(c3_l jax_l, u3_noun cor)
_cj_jit(c3_l jax_l, u3_noun bat)
{
return u3_nul;
}
@ -820,10 +683,12 @@ _cj_mine(u3_noun cey, u3_noun cor)
u3_noun cup; // ++corp
u3_noun soh; // ++bash
u3_noun cuz; // ++club
u3_noun lab; // ++path
if ( 0 == q_cey ) {
mop = u3nq(u3k(p_cey), 3, c3n, u3k(u3t(cor)));
cup = u3nc(c3n, u3k(cor));
lab = u3_nul;
}
else {
u3_weak rah = u3r_at(q_cey, cor);
@ -854,6 +719,7 @@ _cj_mine(u3_noun cey, u3_noun cor)
else {
cup = u3nc(c3n, u3k(tab));
}
lab = u3k(u3h(u3t(u3t(cax))));
u3z(cax);
}
}
@ -885,9 +751,10 @@ _cj_mine(u3_noun cey, u3_noun cor)
u3h_put(u3R->jed.har_p,
bat,
u3nt(u3nt(jax_l,
u3nt(u3nq(jax_l,
_cj_warm_hump(jax_l, r_cey),
_cj_jit(jax_l, cor)),
u3nc(u3k(p_cey), lab),
_cj_jit(jax_l, bat)),
u3nc(soh, mop),
cuz));
}
@ -1012,3 +879,141 @@ u3j_reap(u3_noun das, u3p(u3h_root) har_p)
_cj_cold_reap_in(das);
u3h_walk(har_p, _cj_warm_reap);
}
static c3_l _cj_warm_ream_at(u3_noun soh, u3_noun* lab, u3_noun cag);
/* _cj_warm_ream_be(): install battery; RETAINS.
*/
static void
_cj_warm_ream_be(c3_l jax_l,
u3_noun soh,
u3_noun lab,
u3_noun mop,
u3_noun bat,
u3_noun cuz)
{
u3h_put(u3R->jed.har_p,
bat,
u3nt(u3nq(jax_l,
_cj_warm_hump(jax_l, u3t(cuz)),
u3k(lab),
_cj_jit(jax_l, bat)),
u3nc(u3k(soh), u3k(mop)),
u3k(cuz)));
}
/* _cj_warm_ream_is(): reream battery; RETAINS.
*/
static void
_cj_warm_ream_is(c3_l jax_l,
u3_noun soh,
u3_noun lab,
u3_noun mop,
u3_noun sab)
{
if ( u3_nul != sab ) {
u3_noun n_sab, l_sab, r_sab, pn_sab, qn_sab;
u3x_trel(sab, &n_sab, &l_sab, &r_sab);
u3x_cell(n_sab, &pn_sab, &qn_sab);
_cj_warm_ream_be(jax_l, soh, lab, mop, pn_sab, qn_sab);
_cj_warm_ream_is(jax_l, soh, lab, mop, l_sab);
_cj_warm_ream_is(jax_l, soh, lab, mop, r_sab);
}
}
/* _cj_warm_ream_un(): reream under `soh`; RETAINS, transfers `*lab`.
*/
static c3_l
_cj_warm_ream_un(u3_noun soh, u3_noun* lab)
{
u3_noun cag = u3kdb_got(u3k(u3R->jed.das), u3k(soh));
u3_noun sab = u3t(cag);
u3_noun cax;
c3_l jax_l;
if ( u3_none != (cax = u3h_get(u3R->jed.har_p, u3h(u3h(sab)))) ) {
jax_l = u3h(u3h(cax));
*lab = u3k(u3h(u3t(u3t(u3h(cax)))));
u3z(cax);
}
else {
jax_l = _cj_warm_ream_at(soh, lab, cag);
}
u3z(cag);
return jax_l;
}
/* _cj_warm_ream_at(): reream at `soh` and `cag`; RETAINS, transfers `*lab`.
*/
static c3_l
_cj_warm_ream_at(u3_noun soh, u3_noun* lab, u3_noun cag)
{
u3_noun mop = u3h(cag);
u3_noun sab = u3t(cag);
u3_noun p_mop, q_mop, r_mop, hr_mop, tr_mop;
u3x_trel(mop, &p_mop, &q_mop, &r_mop);
u3x_cell(r_mop, &hr_mop, &tr_mop);
{
c3_l par_l, jax_l;
u3_noun pal = u3_nul;
if ( c3y == hr_mop ) {
par_l = _cj_warm_ream_un(tr_mop, &pal);
}
else {
par_l = 0;
pal = u3_nul;
}
*lab = u3nc(u3k(p_mop), pal);
jax_l = _cj_hot_mean(par_l, mop, 0);
_cj_warm_ream_is(jax_l, soh, *lab, mop, sab);
return jax_l;
}
}
/* _cj_warm_ream_in(): reream in `taw`; RETAINS.
*/
static void
_cj_warm_ream_in(u3_noun taw)
{
if ( u3_nul != taw ) {
u3_noun n_taw, l_taw, r_taw, pn_taw, qn_taw;
u3_noun lab;
u3x_trel(taw, &n_taw, &l_taw, &r_taw);
u3x_cell(n_taw, &pn_taw, &qn_taw);
_cj_warm_ream_at(pn_taw, &lab, qn_taw);
u3z(lab);
_cj_warm_ream_in(l_taw);
_cj_warm_ream_in(r_taw);
}
}
/* _cj_warm_ream(): reream warm from cold state.
*/
static void
_cj_warm_ream(void)
{
c3_assert(u3R == &(u3H->rod_u));
{
_cj_warm_ream_in(u3R->jed.das);
}
}
/* u3j_ream(): reream after restoring from checkpoint.
*/
void
u3j_ream(void)
{
u3h_free(u3R->jed.har_p);
u3R->jed.har_p = u3h_new();
_cj_warm_ream();
}