diff --git a/Spec/u3.md b/Spec/u3.md index bb51dcc37..ed03df0dc 100644 --- a/Spec/u3.md +++ b/Spec/u3.md @@ -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. diff --git a/i/n/j.h b/i/n/j.h index 6ba6825d4..25a0b2b62 100644 --- a/i/n/j.h +++ b/i/n/j.h @@ -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 ,* diff --git a/i/n/o.h b/i/n/o.h new file mode 100644 index 000000000..d822da6ef --- /dev/null +++ b/i/n/o.h @@ -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); + diff --git a/n/j.c b/n/j.c index d53740747..d86862107 100644 --- a/n/j.c +++ b/n/j.c @@ -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(); +} +