From e7ff4550db6886d80ecd524c67653657613c5ffe Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 5 Jun 2020 18:01:07 -0700 Subject: [PATCH 01/15] u3: fixes use of system malloc in u3i_chubs --- pkg/urbit/noun/imprison.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/noun/imprison.c b/pkg/urbit/noun/imprison.c index be9decee5..897145d84 100644 --- a/pkg/urbit/noun/imprison.c +++ b/pkg/urbit/noun/imprison.c @@ -56,7 +56,9 @@ u3_atom u3i_chubs(c3_w a_w, const c3_d* b_d) { - c3_w *b_w = c3_malloc(a_w * 8); + // XX efficiency + // + c3_w *b_w = u3a_malloc(a_w * 8); c3_w i_w; u3_atom p; @@ -65,7 +67,7 @@ u3i_chubs(c3_w a_w, b_w[(2 * i_w) + 1] = b_d[i_w] >> 32ULL; } p = u3i_words((a_w * 2), b_w); - c3_free(b_w); + u3a_free(b_w); return p; } From c78bc697589b5a46f0c1d6cf91afad3355b2ecf1 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Sat, 6 Jun 2020 00:29:01 -0700 Subject: [PATCH 02/15] u3: cleans up imprison.h/c --- pkg/urbit/include/noun/imprison.h | 78 +++----- pkg/urbit/noun/imprison.c | 299 ++++++++++++------------------ 2 files changed, 151 insertions(+), 226 deletions(-) diff --git a/pkg/urbit/include/noun/imprison.h b/pkg/urbit/include/noun/imprison.h index 363feb42e..c45ea87a2 100644 --- a/pkg/urbit/include/noun/imprison.h +++ b/pkg/urbit/include/noun/imprison.h @@ -1,60 +1,62 @@ -/* include/n/i.h +/* include/noun/imprison.h ** ** This file is in the public domain. */ /* General constructors. */ - /* u3i_words(): - ** - ** Copy [a] words from [b] into an atom. - */ - u3_noun - u3i_words(c3_w a_w, - const c3_w* b_w); - - /* u3i_bytes(): - ** - ** Copy `a` bytes from `b` to an LSB first atom. + /* u3i_bytes(): Copy [a] bytes from [b] to an LSB first atom. */ u3_noun u3i_bytes(c3_w a_w, const c3_y* b_y); - /* u3i_mp(): - ** - ** Copy the GMP integer `a` into an atom, and clear it. + /* u3i_words(): Copy [a] words from [b] into an atom. + */ + u3_noun + u3i_words(c3_w a_w, + const c3_w* b_w); + + /* u3i_chubs(): Copy [a] chubs from [b] into an atom. + */ + u3_atom + u3i_chubs(c3_w a_w, + const c3_d* b_d); + + /* u3i_mp(): Copy the GMP integer [a] into an atom, and clear it. */ u3_noun u3i_mp(mpz_t a_mp); - /* u3i_vint(): - ** - ** Create `a + 1`. + /* u3i_vint(): increment [a]. */ u3_noun u3i_vint(u3_noun a); - /* u3i_cell(): - ** - ** Produce the cell `[a b]`. + /* u3i_cell(): Produce the cell `[a b]`. */ u3_noun u3i_cell(u3_noun a, u3_noun b); - /* u3i_trel(): - ** - ** Produce the triple `[a b c]`. + /* u3i_trel(): Produce the triple `[a b c]`. */ u3_noun u3i_trel(u3_noun a, u3_noun b, u3_noun c); - /* u3i_qual(): - ** - ** Produce the cell `[a b c d]`. + /* u3i_qual(): Produce the cell `[a b c d]`. */ u3_noun u3i_qual(u3_noun a, u3_noun b, u3_noun c, u3_noun d); + /* u3i_string(): Produce an LSB-first atom from the C string [a]. + */ + u3_noun + u3i_string(const c3_c* a_c); + + /* u3i_tape(): from a C string, to a list of bytes. + */ + u3_atom + u3i_tape(const c3_c* txt_c); + /* u3i_edit(): ** ** Mutate `big` at axis `axe` with new value `som` @@ -63,13 +65,6 @@ u3_noun u3i_edit(u3_noun big, u3_noun axe, u3_noun som); - /* u3i_string(): - ** - ** Produce an LSB-first atom from the C string `a`. - */ - u3_noun - u3i_string(const c3_c* a_c); - /* u3i_molt(): ** ** Mutate `som` with a 0-terminated list of axis, noun pairs. @@ -77,18 +72,3 @@ */ u3_noun u3i_molt(u3_noun som, ...); - - /* u3i_chubs(): - ** - ** Construct `a` double-words from `b`, LSD first, as an atom. - */ - u3_atom - u3i_chubs(c3_w a_w, - const c3_d* b_d); - - /* u3i_tape(): from a C string, to a list of bytes. - */ - u3_atom - u3i_tape(const c3_c* txt_c); - - diff --git a/pkg/urbit/noun/imprison.c b/pkg/urbit/noun/imprison.c index 897145d84..4b01b1a94 100644 --- a/pkg/urbit/noun/imprison.c +++ b/pkg/urbit/noun/imprison.c @@ -1,92 +1,22 @@ -/* g/i.c +/* noun/imprison.c ** */ #include "all.h" -/* u3i_words(): -** -** Copy [a] words from [b] into an atom. -*/ -u3_noun -u3i_words(c3_w a_w, - const c3_w* b_w) -{ - /* Strip trailing zeroes. - */ - while ( a_w && !b_w[a_w - 1] ) { - a_w--; - } - - /* Check for cat. - */ - if ( !a_w ) { - return 0; - } - else if ( (a_w == 1) && !(b_w[0] >> 31) ) { - return b_w[0]; - } - - /* Allocate, fill, return. - */ - { - c3_w* nov_w = u3a_walloc(a_w + c3_wiseof(u3a_atom)); - u3a_atom* nov_u = (void*)nov_w; - - nov_u->mug_w = 0; - nov_u->len_w = a_w; - - /* Fill the words. - */ - { - c3_w i_w; - - for ( i_w=0; i_w < a_w; i_w++ ) { - nov_u->buf_w[i_w] = b_w[i_w]; - } - } - return u3a_to_pug(u3a_outa(nov_w)); - } -} - -/* u3i_chubs(): -** -** Construct `a` double-words from `b`, LSD first, as an atom. -*/ -u3_atom -u3i_chubs(c3_w a_w, - const c3_d* b_d) -{ - // XX efficiency - // - c3_w *b_w = u3a_malloc(a_w * 8); - c3_w i_w; - u3_atom p; - - for ( i_w = 0; i_w < a_w; i_w++ ) { - b_w[(2 * i_w)] = b_d[i_w] & 0xffffffffULL; - b_w[(2 * i_w) + 1] = b_d[i_w] >> 32ULL; - } - p = u3i_words((a_w * 2), b_w); - u3a_free(b_w); - return p; -} - -/* u3i_bytes(): -** -** Copy `a` bytes from `b` to an LSB first atom. +/* u3i_bytes(): Copy [a] bytes from [b] to an LSB first atom. */ u3_noun u3i_bytes(c3_w a_w, - const c3_y* b_y) + const c3_y* b_y) { - /* Strip trailing zeroes. - */ + // Strip trailing zeroes. + // while ( a_w && !b_y[a_w - 1] ) { a_w--; } - /* Check for cat. - */ + // Check for cat. + // if ( a_w <= 4 ) { if ( !a_w ) { return 0; @@ -105,18 +35,18 @@ u3i_bytes(c3_w a_w, } } - /* Allocate, fill, return. - */ + // Allocate, fill, return. + // { - c3_w len_w = (a_w + 3) >> 2; - c3_w* nov_w = u3a_walloc((len_w + c3_wiseof(u3a_atom))); + c3_w len_w = (a_w + 3) >> 2; + c3_w* nov_w = u3a_walloc((len_w + c3_wiseof(u3a_atom))); u3a_atom* nov_u = (void*)nov_w; nov_u->mug_w = 0; nov_u->len_w = len_w; - /* Clear the words. - */ + // Clear the words. + // { c3_w i_w; @@ -125,8 +55,8 @@ u3i_bytes(c3_w a_w, } } - /* Fill the bytes. - */ + // Fill the bytes. + // { c3_w i_w; @@ -138,9 +68,71 @@ u3i_bytes(c3_w a_w, } } -/* u3i_mp(): -** -** Copy the GMP integer `a` into an atom, and clear it. +/* u3i_words(): Copy [a] words from [b] into an atom. +*/ +u3_noun +u3i_words(c3_w a_w, + const c3_w* b_w) +{ + // Strip trailing zeroes. + // + while ( a_w && !b_w[a_w - 1] ) { + a_w--; + } + + // Check for cat. + // + if ( !a_w ) { + return 0; + } + else if ( (a_w == 1) && !(b_w[0] >> 31) ) { + return b_w[0]; + } + + // Allocate, fill, return. + // + { + c3_w* nov_w = u3a_walloc(a_w + c3_wiseof(u3a_atom)); + u3a_atom* nov_u = (void*)nov_w; + + nov_u->mug_w = 0; + nov_u->len_w = a_w; + + // Fill the words. + // + { + c3_w i_w; + + for ( i_w=0; i_w < a_w; i_w++ ) { + nov_u->buf_w[i_w] = b_w[i_w]; + } + } + return u3a_to_pug(u3a_outa(nov_w)); + } +} + +/* u3i_chubs(): Copy [a] chubs from [b] into an atom. +*/ +u3_atom +u3i_chubs(c3_w a_w, + const c3_d* b_d) +{ + // XX efficiency + // + c3_w *b_w = u3a_malloc(a_w * 8); + c3_w i_w; + u3_atom p; + + for ( i_w = 0; i_w < a_w; i_w++ ) { + b_w[(2 * i_w)] = b_d[i_w] & 0xffffffffULL; + b_w[(2 * i_w) + 1] = b_d[i_w] >> 32ULL; + } + p = u3i_words((a_w * 2), b_w); + u3a_free(b_w); + return p; +} + +/* u3i_mp(): Copy the GMP integer [a] into an atom, and clear it. */ u3_noun u3i_mp(mpz_t a_mp) @@ -154,9 +146,7 @@ u3i_mp(mpz_t a_mp) return u3a_malt(buz_w); } -/* u3i_vint(): -** -** Create `a + 1`. +/* u3i_vint(): increment [a]. */ u3_noun u3i_vint(u3_noun a) @@ -178,18 +168,14 @@ u3i_vint(u3_noun a) mpz_t a_mp; u3r_mp(a_mp, a); - u3a_lose(a); + u3z(a); mpz_add_ui(a_mp, a_mp, 1); return u3i_mp(a_mp); } } -c3_w BAD; - -/* u3i_cell(): -** -** Produce the cell `[a b]`. +/* u3i_cell(): Produce the cell `[a b]`. */ u3_noun u3i_cell(u3_noun a, u3_noun b) @@ -199,9 +185,9 @@ u3i_cell(u3_noun a, u3_noun b) #ifdef U3_CPU_DEBUG u3R->pro.cel_d++; #endif + { - // c3_w* nov_w = u3a_walloc(c3_wiseof(u3a_cell)); - c3_w* nov_w = u3a_celloc(); + c3_w* nov_w = u3a_celloc(); u3a_cell* nov_u = (void *)nov_w; u3_noun pro; @@ -210,35 +196,13 @@ u3i_cell(u3_noun a, u3_noun b) nov_u->tel = b; pro = u3a_to_pom(u3a_outa(nov_w)); -#if 0 - if ( (0x730e66cc == u3r_mug(pro)) && - (c3__tssg == u3h(u3t(u3t(pro)))) ) { - static c3_w xuc_w; - u3l_log("BAD %x %p\r\n", pro, u3a_to_ptr(a)); - BAD = pro; - if ( xuc_w == 1 ) u3m_bail(c3__exit); - xuc_w++; - } -#endif -#if 1 + u3t_off(mal_o); return pro; -#else - if ( !FOO ) return u3a_to_pom(u3a_outa(nov_w)); - else { - u3_noun pro = u3a_to_pom(u3a_outa(nov_w)); - - u3m_p("leaked", pro); - u3l_log("pro %u, %x\r\n", pro, u3r_mug(pro)); - abort(); - } -#endif } } -/* u3i_trel(): -** -** Produce the triple `[a b c]`. +/* u3i_trel(): Produce the triple `[a b c]`. */ u3_noun u3i_trel(u3_noun a, u3_noun b, u3_noun c) @@ -246,9 +210,7 @@ u3i_trel(u3_noun a, u3_noun b, u3_noun c) return u3i_cell(a, u3i_cell(b, c)); } -/* u3i_qual(): -** -** Produce the cell `[a b c d]`. +/* u3i_qual(): Produce the cell `[a b c d]`. */ u3_noun u3i_qual(u3_noun a, u3_noun b, u3_noun c, u3_noun d) @@ -256,6 +218,32 @@ u3i_qual(u3_noun a, u3_noun b, u3_noun c, u3_noun d) return u3i_cell(a, u3i_trel(b, c, d)); } +/* u3i_string(): Produce an LSB-first atom from the C string [a]. +*/ +u3_noun +u3i_string(const c3_c* a_c) +{ + return u3i_bytes(strlen(a_c), (c3_y *)a_c); +} + +/* u3i_tape(): from a C string, to a list of bytes. +*/ +u3_atom +u3i_tape(const c3_c* txt_c) +{ + if ( !*txt_c ) { + return u3_nul; + } else return u3i_cell(*txt_c, u3i_tape(txt_c + 1)); +} + +/* u3i_list(): +** +** Generate a null-terminated list, with `u3_none` as terminator. +*/ +u3_noun +u3i_list(u3_weak one, ...); + + static u3_noun _edit_cat(u3_noun big, c3_l axe_l, u3_noun som) { @@ -400,48 +388,6 @@ u3i_edit(u3_noun big, u3_noun axe, u3_noun som) } } -/* u3i_string(): -** -** Produce an LSB-first atom from the C string `a`. -*/ -u3_noun -u3i_string(const c3_c* a_c) -{ - return u3i_bytes(strlen(a_c), (c3_y *)a_c); -} - -/* u3i_tape(): from a C string, to a list of bytes. -*/ -u3_atom -u3i_tape(const c3_c* txt_c) -{ - if ( !*txt_c ) { - return u3_nul; - } else return u3i_cell(*txt_c, u3i_tape(txt_c + 1)); -} - -/* u3i_decimal(): -** -** Parse `a` as a list of decimal digits. -*/ -u3_atom -u3i_decimal(u3_noun a); - -/* u3i_heximal(): -** -** Parse `a` as a list of hex digits. -*/ -u3_noun -u3i_heximal(u3_noun a); - -/* u3i_list(): -** -** Generate a null-terminated list, with `u3_none` as terminator. -*/ -u3_noun -u3i_list(u3_weak one, ...); - - /* u3i_molt(): ** ** Mutate `som` with a 0-terminated list of axis, noun pairs. @@ -478,7 +424,7 @@ u3i_list(u3_weak one, ...); struct _molt_pair* pms_m) // transfer { if ( len_w == 0 ) { - return u3a_gain(som); + return u3k(som); } else if ( (len_w == 1) && (1 == pms_m[0].axe_w) ) { return pms_m[0].som; @@ -505,8 +451,8 @@ u3i_molt(u3_noun som, ...) struct _molt_pair* pms_m; u3_noun pro; - /* Count. - */ + // Count. + // len_w = 0; { va_start(ap, som); @@ -523,8 +469,8 @@ u3i_molt(u3_noun som, ...) c3_assert( 0 != len_w ); pms_m = alloca(len_w * sizeof(struct _molt_pair)); - /* Install. - */ + // Install. + // { c3_w i_w; @@ -536,10 +482,9 @@ u3i_molt(u3_noun som, ...) va_end(ap); } - /* Apply. - */ + // Apply. + // pro = _molt_apply(som, len_w, pms_m); - u3a_lose(som); + u3z(som); return pro; } - From 1d1a263e489b4a7240f71020ae4aafc25436d105 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Sat, 6 Jun 2020 00:59:26 -0700 Subject: [PATCH 03/15] u3: rewrites u3i_chubs() for efficiency --- pkg/urbit/noun/imprison.c | 67 ++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/pkg/urbit/noun/imprison.c b/pkg/urbit/noun/imprison.c index 4b01b1a94..e36b2257a 100644 --- a/pkg/urbit/noun/imprison.c +++ b/pkg/urbit/noun/imprison.c @@ -117,19 +117,64 @@ u3_atom u3i_chubs(c3_w a_w, const c3_d* b_d) { - // XX efficiency + // Strip trailing zeroes. // - c3_w *b_w = u3a_malloc(a_w * 8); - c3_w i_w; - u3_atom p; - - for ( i_w = 0; i_w < a_w; i_w++ ) { - b_w[(2 * i_w)] = b_d[i_w] & 0xffffffffULL; - b_w[(2 * i_w) + 1] = b_d[i_w] >> 32ULL; + while ( a_w && !b_d[a_w - 1] ) { + a_w--; + } + + // Check for cat. + // + if ( !a_w ) { + return 0; + } + else if ( (1 == a_w) && !(b_d[0] >> 31) ) { + return (c3_w)b_d[0]; + } + + // Allocate, fill, return. + // + { + c3_w len_w = 2 * a_w; + + if ( !(b_d[a_w - 1] >> 32) ) { + len_w--; + } + + c3_w* nov_w = u3a_walloc(len_w + c3_wiseof(u3a_atom)); + u3a_atom* nov_u = (void*)nov_w; + + nov_u->mug_w = 0; + nov_u->len_w = len_w; + + // Fill the words. + // + { + c3_w i_w, x_w, max_w = a_w - 1; + c3_d i_d; + + for ( i_w = 0; i_w < max_w; i_w++ ) { + i_d = b_d[i_w]; + x_w = 2 * i_w; + nov_u->buf_w[x_w] = i_d & 0xffffffffULL; + x_w++; + nov_u->buf_w[x_w] = i_d >> 32; + } + + { + i_d = b_d[i_w]; + x_w = 2 * i_w; + nov_u->buf_w[x_w] = i_d & 0xffffffffULL; + x_w++; + } + + if ( x_w < len_w ) { + nov_u->buf_w[x_w] = i_d >> 32; + } + } + + return u3a_to_pug(u3a_outa(nov_w)); } - p = u3i_words((a_w * 2), b_w); - u3a_free(b_w); - return p; } /* u3i_mp(): Copy the GMP integer [a] into an atom, and clear it. From 406a823ddd7827598f6bbaaadd75a3fc91572c5e Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Sat, 6 Jun 2020 01:00:24 -0700 Subject: [PATCH 04/15] u3: adds allocator profiling labels to u3i_* atom functions --- pkg/urbit/noun/imprison.c | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/pkg/urbit/noun/imprison.c b/pkg/urbit/noun/imprison.c index e36b2257a..0baad7257 100644 --- a/pkg/urbit/noun/imprison.c +++ b/pkg/urbit/noun/imprison.c @@ -9,6 +9,9 @@ u3_noun u3i_bytes(c3_w a_w, const c3_y* b_y) { + u3_noun pro; + u3t_on(mal_o); + // Strip trailing zeroes. // while ( a_w && !b_y[a_w - 1] ) { @@ -64,8 +67,12 @@ u3i_bytes(c3_w a_w, nov_u->buf_w[i_w >> 2] |= (b_y[i_w] << ((i_w & 3) * 8)); } } - return u3a_to_pug(u3a_outa(nov_w)); + + pro = u3a_to_pug(u3a_outa(nov_w)); } + + u3t_off(mal_o); + return pro; } /* u3i_words(): Copy [a] words from [b] into an atom. @@ -74,6 +81,9 @@ u3_noun u3i_words(c3_w a_w, const c3_w* b_w) { + u3_noun pro; + u3t_on(mal_o); + // Strip trailing zeroes. // while ( a_w && !b_w[a_w - 1] ) { @@ -107,8 +117,12 @@ u3i_words(c3_w a_w, nov_u->buf_w[i_w] = b_w[i_w]; } } - return u3a_to_pug(u3a_outa(nov_w)); + + pro = u3a_to_pug(u3a_outa(nov_w)); } + + u3t_off(mal_o); + return pro; } /* u3i_chubs(): Copy [a] chubs from [b] into an atom. @@ -117,6 +131,9 @@ u3_atom u3i_chubs(c3_w a_w, const c3_d* b_d) { + u3_noun pro; + u3t_on(mal_o); + // Strip trailing zeroes. // while ( a_w && !b_d[a_w - 1] ) { @@ -173,8 +190,11 @@ u3i_chubs(c3_w a_w, } } - return u3a_to_pug(u3a_outa(nov_w)); + pro = u3a_to_pug(u3a_outa(nov_w)); } + + u3t_off(mal_o); + return pro; } /* u3i_mp(): Copy the GMP integer [a] into an atom, and clear it. @@ -225,6 +245,7 @@ u3i_vint(u3_noun a) u3_noun u3i_cell(u3_noun a, u3_noun b) { + u3_noun pro; u3t_on(mal_o); #ifdef U3_CPU_DEBUG @@ -234,17 +255,16 @@ u3i_cell(u3_noun a, u3_noun b) { c3_w* nov_w = u3a_celloc(); u3a_cell* nov_u = (void *)nov_w; - u3_noun pro; nov_u->mug_w = 0; nov_u->hed = a; nov_u->tel = b; pro = u3a_to_pom(u3a_outa(nov_w)); - - u3t_off(mal_o); - return pro; } + + u3t_off(mal_o); + return pro; } /* u3i_trel(): Produce the triple `[a b c]`. From e7eceba9652f708de2747a621cdfcf95e3c33265 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Sat, 6 Jun 2020 01:54:33 -0700 Subject: [PATCH 05/15] u3: implements u3i_list()/u3nl() varargs list constructor --- pkg/urbit/include/noun/aliases.h | 5 +++++ pkg/urbit/include/noun/imprison.h | 5 +++++ pkg/urbit/noun/imprison.c | 33 +++++++++++++++++++++++++++---- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/pkg/urbit/include/noun/aliases.h b/pkg/urbit/include/noun/aliases.h index c8d0e852f..5e98714d4 100644 --- a/pkg/urbit/include/noun/aliases.h +++ b/pkg/urbit/include/noun/aliases.h @@ -84,6 +84,11 @@ # define u3nt(a, b, c) u3i_trel(a, b, c) # define u3nq(a, b, c, d) u3i_qual(a, b, c, d) + + /* u3nl(), u3_none-terminated varargs list + */ +# define u3nl u3i_list + /* u3du(), u3ud(): noun/cell test. */ # define u3du(som) (u3r_du(som)) diff --git a/pkg/urbit/include/noun/imprison.h b/pkg/urbit/include/noun/imprison.h index c45ea87a2..714bf15c6 100644 --- a/pkg/urbit/include/noun/imprison.h +++ b/pkg/urbit/include/noun/imprison.h @@ -57,6 +57,11 @@ u3_atom u3i_tape(const c3_c* txt_c); + /* u3i_list(): list from `u3_none`-terminated varargs. + */ + u3_noun + u3i_list(u3_weak som, ...); + /* u3i_edit(): ** ** Mutate `big` at axis `axe` with new value `som` diff --git a/pkg/urbit/noun/imprison.c b/pkg/urbit/noun/imprison.c index 0baad7257..6eb533c43 100644 --- a/pkg/urbit/noun/imprison.c +++ b/pkg/urbit/noun/imprison.c @@ -301,13 +301,38 @@ u3i_tape(const c3_c* txt_c) } else return u3i_cell(*txt_c, u3i_tape(txt_c + 1)); } -/* u3i_list(): -** -** Generate a null-terminated list, with `u3_none` as terminator. +/* u3i_list(): list from `u3_none`-terminated varargs. */ u3_noun -u3i_list(u3_weak one, ...); +u3i_list(u3_weak som, ...) +{ + u3_noun lit = u3_nul; + va_list ap; + if ( u3_none == som ) { + return lit; + } + else { + lit = u3nc(som, lit); + } + + { + u3_noun tem; + + va_start(ap, som); + while ( 1 ) { + if ( u3_none == (tem = va_arg(ap, u3_weak)) ) { + break; + } + else { + lit = u3nc(tem, lit); + } + } + va_end(ap); + } + + return u3kb_flop(lit); +} static u3_noun _edit_cat(u3_noun big, c3_l axe_l, u3_noun som) From 2c5a1adea3d81887f7ab900ff05178dde17658cf Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Sat, 6 Jun 2020 16:50:00 -0700 Subject: [PATCH 06/15] u3: adds failing u3r_mug_words tests --- pkg/urbit/tests/mug_tests.c | 81 +++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/pkg/urbit/tests/mug_tests.c b/pkg/urbit/tests/mug_tests.c index 760f20d06..ff7736bc0 100644 --- a/pkg/urbit/tests/mug_tests.c +++ b/pkg/urbit/tests/mug_tests.c @@ -94,6 +94,87 @@ _test_mug(void) c3_free(str_w); } + { + c3_w som_w[4]; + u3_noun som; + + { + som_w[0] = 0; + som_w[1] = 0; + som_w[2] = 0; + som_w[3] = 1; + som = u3i_words(4, som_w); + + if ( 0x519bd45c != u3r_mug(som) ) { + fprintf(stderr, "fail (j) (1)\r\n"); + exit(1); + } + + if ( 0x519bd45c != u3r_mug_words(som_w, 4) ) { + fprintf(stderr, "fail (j) (2)\r\n"); + exit(1); + } + u3z(som); + } + + { + som_w[0] = 0; + som_w[1] = 1; + som_w[2] = 0; + som_w[3] = 1; + som = u3i_words(4, som_w); + + if ( 0x540eb8a9 != u3r_mug(som) ) { + fprintf(stderr, "fail (k) (1)\r\n"); + exit(1); + } + + if ( 0x540eb8a9 != u3r_mug_words(som_w, 4) ) { + fprintf(stderr, "fail (k) (2)\r\n"); + exit(1); + } + u3z(som); + } + + { + som_w[0] = 1; + som_w[1] = 1; + som_w[2] = 0; + som_w[3] = 1; + som = u3i_words(4, som_w); + + if ( 0x319d28f9 != u3r_mug(som) ) { + fprintf(stderr, "fail (l) (1)\r\n"); + exit(1); + } + + if ( 0x319d28f9 != u3r_mug_words(som_w, 4) ) { + fprintf(stderr, "fail (l) (2)\r\n"); + exit(1); + } + u3z(som); + } + + { + som_w[0] = 0; + som_w[1] = 0; + som_w[2] = 0; + som_w[3] = 0xffff; + som = u3i_words(4, som_w); + + if ( 0x5230a260 != u3r_mug(som) ) { + fprintf(stderr, "fail (m) (1)\r\n"); + exit(1); + } + + if ( 0x5230a260 != u3r_mug_words(som_w, 4) ) { + fprintf(stderr, "fail (m) (2)\r\n"); + exit(1); + } + u3z(som); + } + } + fprintf(stderr, "test_mug: ok\n"); } From 9e305da03cb24bb2d015a4c4a9258be5b48890be Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Sat, 6 Jun 2020 16:51:21 -0700 Subject: [PATCH 07/15] u3: rewrites u3r_mug_words, correct for arbitrary input --- pkg/urbit/noun/retrieve.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/pkg/urbit/noun/retrieve.c b/pkg/urbit/noun/retrieve.c index 7da63cbcc..7015770d1 100644 --- a/pkg/urbit/noun/retrieve.c +++ b/pkg/urbit/noun/retrieve.c @@ -1405,14 +1405,30 @@ u3r_mug_string(const c3_c *a_c) c3_w u3r_mug_words(const c3_w* key_w, c3_w len_w) { - c3_w byt_w = 0; - c3_w wor_w; + c3_w byt_w; - while ( 0 < len_w ) { - wor_w = key_w[--len_w]; - byt_w += _(u3a_is_cat(wor_w)) ? u3r_met(3, wor_w) : 4; + // ignore trailing zeros + // + while ( len_w && !key_w[len_w - 1] ) { + len_w--; } + // calculate byte-width a la u3r_met(3, ...) + // + if ( !len_w ) { + byt_w = 0; + } + else { + c3_w gal_w = len_w - 1; + c3_w daz_w = key_w[gal_w]; + + byt_w = (gal_w << 2) + + ((daz_w >> 24) ? 4 : (daz_w >> 16) ? 3 : (daz_w >> 8) ? 2 : 1); + + } + + // XX: assumes little-endian + // return u3r_mug_bytes((c3_y*)key_w, byt_w); } From 27a9dbe1c0a7a95f7807db17f056377d98e39f65 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Sat, 6 Jun 2020 16:52:05 -0700 Subject: [PATCH 08/15] u3: use u3r_mug_words for all atoms --- pkg/urbit/noun/retrieve.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pkg/urbit/noun/retrieve.c b/pkg/urbit/noun/retrieve.c index 7015770d1..14c0fe14c 100644 --- a/pkg/urbit/noun/retrieve.c +++ b/pkg/urbit/noun/retrieve.c @@ -1387,7 +1387,7 @@ u3r_mug_chub(c3_d num_d) c3_w buf_w[2]; buf_w[0] = (c3_w)(num_d & 0xffffffffULL); - buf_w[1] = (c3_w)(num_d >> 32ULL); + buf_w[1] = (c3_w)(num_d >> 32); return u3r_mug_words(buf_w, 2); } @@ -1438,8 +1438,7 @@ c3_w u3r_mug_both(c3_w lef_w, c3_w rit_w) { c3_w ham_w = lef_w ^ (0x7fffffff ^ rit_w); - - return u3r_mug_words(&ham_w, (0 == ham_w) ? 0 : 1); + return u3r_mug_words(&ham_w, 1); } /* u3r_mug_cell(): Compute the mug of the cell `[hed tel]`. @@ -1547,7 +1546,7 @@ u3r_mug(u3_noun veb) // veb is a direct atom, mug is not memoized // if ( _(u3a_is_cat(veb)) ) { - mug_w = u3r_mug_bytes((c3_y*)&veb, u3r_met(3, veb)); + mug_w = u3r_mug_words(&veb, 1); goto retreat; } // veb is indirect, a pointer into the loom @@ -1565,7 +1564,7 @@ u3r_mug(u3_noun veb) // else if ( _(u3a_is_atom(veb)) ) { u3a_atom* vat_u = (u3a_atom*)veb_u; - mug_w = u3r_mug_bytes((c3_y*)vat_u->buf_w, u3r_met(3, veb)); + mug_w = u3r_mug_words(vat_u->buf_w, vat_u->len_w); vat_u->mug_w = mug_w; goto retreat; } From 4cfaf083487c1d8e2bff887dfd978216c775faf6 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 4 Jun 2020 11:09:50 -0700 Subject: [PATCH 09/15] newt: print errors to stderr --- pkg/urbit/vere/newt.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/urbit/vere/newt.c b/pkg/urbit/vere/newt.c index c60083a44..8340c9033 100644 --- a/pkg/urbit/vere/newt.c +++ b/pkg/urbit/vere/newt.c @@ -87,9 +87,9 @@ _newt_gain_mess(u3_moat* mot_u) // very likely to be a bad write, we can't jam anything this big // if ( 0xFFFFFFFFULL < nel_d ) { - u3l_log("newt: %d warn: large read %" PRIu64 "\r\n", - getpid(), - nel_d); + fprintf(stderr, "newt: %d warn: large read %" PRIu64 "\r\n", + getpid(), + nel_d); } mot_u->len_d -= 8ULL; @@ -316,7 +316,7 @@ _newt_write_cb(uv_write_t* wri_u, c3_i sas_i) c3_free(req_u); if ( 0 != sas_i ) { - u3l_log("newt: bad write %d\r\n", sas_i); + fprintf(stderr, "newt: bad write %d\r\n", sas_i); moj_u->bal_f(vod_p, uv_strerror(sas_i)); } } From a31f27a57586f4cae9341a835a52b1362ac3eac5 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Fri, 5 Jun 2020 17:55:58 -0700 Subject: [PATCH 10/15] newt: cleans up api, allocates less on write --- pkg/urbit/include/vere/vere.h | 16 ++-- pkg/urbit/tests/newt_tests.c | 43 ++++++++--- pkg/urbit/vere/daemon.c | 6 +- pkg/urbit/vere/lord.c | 6 +- pkg/urbit/vere/newt.c | 137 ++++++++++++++++------------------ pkg/urbit/worker/main.c | 5 +- 6 files changed, 111 insertions(+), 102 deletions(-) diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index 1640ba924..79e75e329 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -68,7 +68,7 @@ typedef struct _u3_moat { uv_pipe_t pyp_u; // input stream u3_moor_bail bal_f; // error response function - void* vod_p; // callback pointer + void* ptr_v; // callback pointer u3_moor_poke pok_f; // action function struct _u3_mess* mes_u; // message in progress c3_d len_d; // length of stray bytes @@ -78,15 +78,16 @@ /* u3_mojo: outbound message stream. */ typedef struct _u3_mojo { - uv_pipe_t pyp_u; // output stream + uv_pipe_t pyp_u; // output stream u3_moor_bail bal_f; // error response function + void* ptr_v; // callback pointer } u3_mojo; /* u3_moor: two-way message stream, linked list */ typedef struct _u3_moor { uv_pipe_t pyp_u; u3_moor_bail bal_f; - void* vod_p; + void* ptr_v; u3_moor_poke pok_f; struct _u3_mess* mes_u; c3_d len_d; @@ -1057,11 +1058,6 @@ /** Stream messages. **/ - /* u3_newt_encode(): encode an atom to a length-prefixed byte buffer - */ - c3_y* - u3_newt_encode(u3_atom mat, c3_w* len_w); - /* u3_newt_decode(): decode a (partial) length-prefixed byte buffer */ void @@ -1070,9 +1066,7 @@ /* u3_newt_write(): write atom to stream; free atom. */ void - u3_newt_write(u3_mojo* moj_u, - u3_atom mat, - void* vod_p); + u3_newt_write(u3_mojo* moj_u, u3_atom mat); /* u3_newt_read(): activate reading on input stream. */ diff --git a/pkg/urbit/tests/newt_tests.c b/pkg/urbit/tests/newt_tests.c index 41f20eb40..f279fec23 100644 --- a/pkg/urbit/tests/newt_tests.c +++ b/pkg/urbit/tests/newt_tests.c @@ -13,6 +13,31 @@ _setup(void) static c3_w pok_w; static c3_w bal_w; +/* _newt_encode(): synchronous serialization into a single buffer, for test purposes +*/ +static c3_y* +_newt_encode(u3_atom mat, c3_w* len_w) +{ + c3_w met_w = u3r_met(3, mat); + c3_y* buf_y; + + *len_w = 8 + met_w; + buf_y = c3_malloc(*len_w); + + // write header; c3_d is futureproofing + // + buf_y[0] = ((met_w >> 0) & 0xff); + buf_y[1] = ((met_w >> 8) & 0xff); + buf_y[2] = ((met_w >> 16) & 0xff); + buf_y[3] = ((met_w >> 24) & 0xff); + buf_y[4] = buf_y[5] = buf_y[6] = buf_y[7] = 0; + + u3r_bytes(0, met_w, buf_y + 8, mat); + u3z(mat); + + return buf_y; +} + static void _moat_poke_cb(void* vod_p, u3_atom a) { @@ -48,7 +73,7 @@ _test_newt_smol(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); u3_newt_decode(&mot_u, buf_y, len_w); if ( 1 != pok_w ) { @@ -63,7 +88,7 @@ _test_newt_smol(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); buf_y = c3_realloc(buf_y, 2 * len_w); memcpy(buf_y + len_w, buf_y, len_w); @@ -84,7 +109,7 @@ _test_newt_smol(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); end_y = c3_malloc(1); end_y[0] = buf_y[len_w - 1]; @@ -113,7 +138,7 @@ _test_newt_smol(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); dub_w = 2 * len_w; haf_w = len_w / 2; @@ -168,7 +193,7 @@ _test_newt_vast(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); u3_newt_decode(&mot_u, buf_y, len_w); if ( 1 != pok_w ) { @@ -183,7 +208,7 @@ _test_newt_vast(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); buf_y = c3_realloc(buf_y, 2 * len_w); memcpy(buf_y + len_w, buf_y, len_w); @@ -203,7 +228,7 @@ _test_newt_vast(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); { c3_y* cop_y = c3_malloc(len_w); @@ -243,7 +268,7 @@ _test_newt_vast(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); dub_w = 2 * len_w; haf_w = len_w / 2; @@ -281,7 +306,7 @@ _test_newt_vast(void) pok_w = 0; bal_w = 0; - buf_y = u3_newt_encode(u3k(a), &len_w); + buf_y = _newt_encode(u3k(a), &len_w); dub_w = 2 * len_w; diff --git a/pkg/urbit/vere/daemon.c b/pkg/urbit/vere/daemon.c index 844cb1c99..7e7bf44e7 100644 --- a/pkg/urbit/vere/daemon.c +++ b/pkg/urbit/vere/daemon.c @@ -413,14 +413,14 @@ _daemon_socket_connect(uv_stream_t *sock, int status) if ( u3K.cli_u == 0 ) { u3K.cli_u = c3_malloc(sizeof(u3_moor)); mor_u = u3K.cli_u; - mor_u->vod_p = 0; + mor_u->ptr_v = 0; mor_u->nex_u = 0; } else { for (mor_u = u3K.cli_u; mor_u->nex_u; mor_u = mor_u->nex_u); mor_u->nex_u = c3_malloc(sizeof(u3_moor)); - mor_u->nex_u->vod_p = mor_u; + mor_u->nex_u->ptr_v = mor_u; mor_u = mor_u->nex_u; mor_u->nex_u = 0; } @@ -841,7 +841,7 @@ _boothack_cb(uv_connect_t* con_u, c3_i sas_i) else { u3_noun dom = u3nc(c3__doom, _boothack_doom()); u3_atom mat = u3ke_jam(dom); - u3_newt_write(moj_u, mat, 0); + u3_newt_write(moj_u, mat); c3_free(con_u); diff --git a/pkg/urbit/vere/lord.c b/pkg/urbit/vere/lord.c index 5e0b4757a..68bd83b0a 100644 --- a/pkg/urbit/vere/lord.c +++ b/pkg/urbit/vere/lord.c @@ -657,9 +657,8 @@ _lord_writ_jam(u3_lord* god_u, u3_writ* wit_u) static void _lord_writ_send(u3_lord* god_u, u3_writ* wit_u) { - _lord_writ_jam(god_u, wit_u); - u3_newt_write(&god_u->inn_u, wit_u->mat, 0); + u3_newt_write(&god_u->inn_u, wit_u->mat); wit_u->mat = 0; // ignore subprocess error on shutdown @@ -943,12 +942,13 @@ u3_lord_init(c3_c* pax_c, c3_w wag_w, c3_d key_d[4], u3_lord_cb cb_u) // start reading from proc // { - god_u->out_u.vod_p = god_u; + god_u->out_u.ptr_v = god_u; god_u->out_u.pok_f = _lord_plea; god_u->out_u.bal_f = _lord_bail; // XX distinguish from out_u.bal_f ? // + god_u->inn_u.ptr_v = god_u; god_u->inn_u.bal_f = _lord_bail; u3_newt_read(&god_u->out_u); diff --git a/pkg/urbit/vere/newt.c b/pkg/urbit/vere/newt.c index 8340c9033..9aa6919b7 100644 --- a/pkg/urbit/vere/newt.c +++ b/pkg/urbit/vere/newt.c @@ -179,7 +179,7 @@ _newt_poke_mess(u3_moat* mot_u) // { u3_noun mat = u3i_bytes((c3_w)len_d, buf_y); - mot_u->pok_f(mot_u->vod_p, mat); + mot_u->pok_f(mot_u->ptr_v, mat); } c3_free(buf_y); @@ -238,21 +238,9 @@ u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_w len_w) } } -/* _raft_alloc(): libuv-style allocator for raft. -*/ -static void -_newt_alloc(uv_handle_t* had_u, - size_t len_i, - uv_buf_t* buf_u) -{ - void* ptr_v = c3_malloc(len_i); - - *buf_u = uv_buf_init(ptr_v, len_i); -} - /* _newt_read_cb(): stream input callback. */ -void +static void _newt_read_cb(uv_stream_t* str_u, ssize_t len_i, const uv_buf_t* buf_u) @@ -262,7 +250,8 @@ _newt_read_cb(uv_stream_t* str_u, if ( 0 > len_i ) { c3_free(buf_u->base); uv_read_stop(str_u); - mot_u->bal_f(mot_u->vod_p, uv_strerror(len_i)); + fprintf(stderr, "newt: read failed %s\r\n", uv_strerror(len_i)); + mot_u->bal_f(mot_u->ptr_v, uv_strerror(len_i)); } // EAGAIN/EWOULDBLOCK // @@ -274,100 +263,100 @@ _newt_read_cb(uv_stream_t* str_u, } } +/* _newt_alloc(): libuv-style allocator. +*/ +static void +_newt_alloc(uv_handle_t* had_u, + size_t len_i, + uv_buf_t* buf_u) +{ + // XX pick an appropriate size + // + void* ptr_v = c3_malloc(len_i); + + *buf_u = uv_buf_init(ptr_v, len_i); +} + /* u3_newt_read(): start stream reading. */ void u3_newt_read(u3_moat* mot_u) { - c3_i err_i; - mot_u->mes_u = 0; mot_u->len_d = 0; mot_u->rag_y = 0; - err_i = uv_read_start((uv_stream_t*) &mot_u->pyp_u, - _newt_alloc, - _newt_read_cb); + { + c3_i sas_i; - if ( err_i != 0 ) { - mot_u->bal_f(mot_u, uv_strerror(err_i)); + if ( 0 != (sas_i = uv_read_start((uv_stream_t*)&mot_u->pyp_u, + _newt_alloc, + _newt_read_cb)) ) + { + fprintf(stderr, "newt: read failed %s\r\n", uv_strerror(sas_i)); + mot_u->bal_f(mot_u->ptr_v, uv_strerror(sas_i)); + } } } -/* u3_write_t: write request for newt +/* n_req: write request for newt */ -typedef struct _u3_write_t { +typedef struct _n_req { uv_write_t wri_u; u3_mojo* moj_u; - void* vod_p; - c3_y* buf_y; -} u3_write_t; + c3_y buf_y[0]; +} n_req; /* _newt_write_cb(): generic write callback. */ static void _newt_write_cb(uv_write_t* wri_u, c3_i sas_i) { - u3_write_t* req_u = (struct _u3_write_t*)wri_u; - void* vod_p = req_u->vod_p; - u3_mojo* moj_u = req_u->moj_u; + n_req* req_u = (n_req*)wri_u; + u3_mojo* moj_u = req_u->moj_u; - c3_free(req_u->buf_y); c3_free(req_u); if ( 0 != sas_i ) { - fprintf(stderr, "newt: bad write %d\r\n", sas_i); - moj_u->bal_f(vod_p, uv_strerror(sas_i)); + fprintf(stderr, "newt: write failed %s\r\n", uv_strerror(sas_i)); + moj_u->bal_f(moj_u->ptr_v, uv_strerror(sas_i)); } } -/* u3_newt_encode(): encode an atom to a length-prefixed byte buffer -*/ -c3_y* -u3_newt_encode(u3_atom mat, c3_w* len_w) -{ - c3_w met_w = u3r_met(3, mat); - c3_y* buf_y; - - *len_w = 8 + met_w; - buf_y = c3_malloc(*len_w); - - // write header; c3_d is futureproofing - // - buf_y[0] = ((met_w >> 0) & 0xff); - buf_y[1] = ((met_w >> 8) & 0xff); - buf_y[2] = ((met_w >> 16) & 0xff); - buf_y[3] = ((met_w >> 24) & 0xff); - buf_y[4] = buf_y[5] = buf_y[6] = buf_y[7] = 0; - - u3r_bytes(0, met_w, buf_y + 8, mat); - u3z(mat); - - return buf_y; -} - /* u3_newt_write(): write atom to stream; free atom. */ void -u3_newt_write(u3_mojo* moj_u, - u3_atom mat, - void* vod_p) +u3_newt_write(u3_mojo* moj_u, u3_atom mat) { - u3_write_t* req_u = c3_malloc(sizeof(*req_u)); - c3_w len_w; - c3_y* buf_y = u3_newt_encode(mat, &len_w); - uv_buf_t buf_u; - c3_i err_i; - + c3_w len_w = u3r_met(3, mat); + n_req* req_u = c3_malloc(8 + len_w + sizeof(*req_u)); req_u->moj_u = moj_u; - req_u->buf_y = buf_y; - buf_u = uv_buf_init((c3_c*)buf_y, len_w); - if ( 0 != (err_i = uv_write((uv_write_t*)req_u, - (uv_stream_t*)&moj_u->pyp_u, - &buf_u, 1, - _newt_write_cb)) ) + // write header; c3_d is futureproofing + // + req_u->buf_y[0] = ((len_w >> 0) & 0xff); + req_u->buf_y[1] = ((len_w >> 8) & 0xff); + req_u->buf_y[2] = ((len_w >> 16) & 0xff); + req_u->buf_y[3] = ((len_w >> 24) & 0xff); + req_u->buf_y[4] = req_u->buf_y[5] = req_u->buf_y[6] = req_u->buf_y[7] = 0; + + // write payload + // + u3r_bytes(0, len_w, req_u->buf_y + 8, mat); + u3z(mat); + { - moj_u->bal_f(moj_u, uv_strerror(err_i)); + uv_buf_t buf_u = uv_buf_init((c3_c*)req_u->buf_y, 8 + len_w); + c3_i sas_i; + + if ( 0 != (sas_i = uv_write(&req_u->wri_u, + (uv_stream_t*)&moj_u->pyp_u, + &buf_u, 1, + _newt_write_cb)) ) + { + c3_free(req_u); + fprintf(stderr, "newt: write failed %s\r\n", uv_strerror(sas_i)); + moj_u->bal_f(moj_u->ptr_v, uv_strerror(sas_i)); + } } } diff --git a/pkg/urbit/worker/main.c b/pkg/urbit/worker/main.c index c50d12728..ccde6dda8 100644 --- a/pkg/urbit/worker/main.c +++ b/pkg/urbit/worker/main.c @@ -40,7 +40,7 @@ _newt_fail(void* vod_p, const c3_c* wut_c) static void _newt_send(u3_noun pel) { - u3_newt_write(&out_u, u3ke_jam(pel), 0); + u3_newt_write(&out_u, u3ke_jam(pel)); } /* _newt_send_slog(): send hint output (hod is [priority tank]). @@ -155,11 +155,12 @@ main(c3_i argc, c3_c* argv[]) // set up writing // + out_u.ptr_v = &u3V; out_u.bal_f = _newt_fail; // set up reading // - inn_u.vod_p = &u3V; + inn_u.ptr_v = &u3V; inn_u.pok_f = _newt_writ; inn_u.bal_f = _newt_fail; From 3f26140cf4721ae3e47dd3ac712f10cd134308cb Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 4 Jun 2020 11:09:22 -0700 Subject: [PATCH 11/15] newt: delivers inbound messages asynchronously --- pkg/urbit/include/vere/vere.h | 58 ++++--- pkg/urbit/tests/newt_tests.c | 82 ++++------ pkg/urbit/vere/daemon.c | 2 + pkg/urbit/vere/lord.c | 1 + pkg/urbit/vere/newt.c | 298 ++++++++++++++-------------------- pkg/urbit/worker/main.c | 2 + 6 files changed, 202 insertions(+), 241 deletions(-) diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index 79e75e329..4c8cee2f6 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -46,15 +46,6 @@ */ typedef void (*u3_moor_bail)(void*, const c3_c* err_c); - /* u3_mess: blob message in process. - */ - typedef struct _u3_mess { - c3_d len_d; // blob length in bytes - c3_d has_d; // currently held - struct _u3_meat* meq_u; // exit of message queue - struct _u3_meat* qem_u; // entry of message queue - } u3_mess; - /* u3_meat: blob message block. */ typedef struct _u3_meat { @@ -63,6 +54,29 @@ c3_y hun_y[0]; } u3_meat; + /* u3_mess_type: in-process message type. + */ + typedef enum { + u3_mess_head = 0, // awaiting header + u3_mess_tail = 1 // awaiting body + } u3_mess_type; + + /* u3_mess: blob message in process. + */ + typedef struct _u3_mess { + u3_mess_type sat_e; // msg type + union { // + struct { // awaiting header: + c3_y len_y[8]; // header bytes + c3_y has_y; // length + } hed_u; // + struct { // awaiting body + u3_meat* met_u; // partial message + c3_d has_d; // length + } tal_u; // + }; + } u3_mess; + /* u3_moat: inbound message stream. */ typedef struct _u3_moat { @@ -70,9 +84,10 @@ u3_moor_bail bal_f; // error response function void* ptr_v; // callback pointer u3_moor_poke pok_f; // action function - struct _u3_mess* mes_u; // message in progress - c3_d len_d; // length of stray bytes - c3_y* rag_y; // stray bytes + u3_mess mes_u; // message in progress + uv_timer_t tim_u; // queue timer + u3_meat* ent_u; // entry of message queue + u3_meat* ext_u; // exit of message queue } u3_moat; /* u3_mojo: outbound message stream. @@ -85,14 +100,15 @@ /* u3_moor: two-way message stream, linked list */ typedef struct _u3_moor { - uv_pipe_t pyp_u; - u3_moor_bail bal_f; - void* ptr_v; - u3_moor_poke pok_f; - struct _u3_mess* mes_u; - c3_d len_d; - c3_y* rag_y; - struct _u3_moor* nex_u; + uv_pipe_t pyp_u; // duplex stream + u3_moor_bail bal_f; // error response function + void* ptr_v; // callback pointer + u3_moor_poke pok_f; // action function + u3_mess mes_u; // message in progress + uv_timer_t tim_u; // queue timer + u3_meat* ent_u; // entry of message queue + u3_meat* ext_u; // exit of message queue + struct _u3_moor* nex_u; // next in list } u3_moor; /* u3_dent: directory entry. @@ -1061,7 +1077,7 @@ /* u3_newt_decode(): decode a (partial) length-prefixed byte buffer */ void - u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_w len_w); + u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_d len_d); /* u3_newt_write(): write atom to stream; free atom. */ diff --git a/pkg/urbit/tests/newt_tests.c b/pkg/urbit/tests/newt_tests.c index f279fec23..639cc80e5 100644 --- a/pkg/urbit/tests/newt_tests.c +++ b/pkg/urbit/tests/newt_tests.c @@ -10,9 +10,6 @@ _setup(void) u3m_pave(c3y, c3n); } -static c3_w pok_w; -static c3_w bal_w; - /* _newt_encode(): synchronous serialization into a single buffer, for test purposes */ static c3_y* @@ -38,17 +35,18 @@ _newt_encode(u3_atom mat, c3_w* len_w) return buf_y; } -static void -_moat_poke_cb(void* vod_p, u3_atom a) +static c3_w +_moat_length(u3_moat* mot_u) { - pok_w++; - u3z(a); -} + u3_meat* met_u = mot_u->ext_u; + c3_w len_w = 0; -static void -_moat_bail_cb(void* vod_p, const c3_c* err_c) -{ - bal_w++; + while ( met_u ) { + met_u = met_u->nex_u; + len_w++; + } + + return len_w; } /* _test_newt_smol(): various scenarios with small messages @@ -64,19 +62,16 @@ _test_newt_smol(void) c3_y* buf_y; memset(&mot_u, 0, sizeof(u3_moat)); - mot_u.pok_f = _moat_poke_cb; - mot_u.bal_f = _moat_bail_cb; // one message one buffer // { - pok_w = 0; - bal_w = 0; + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); u3_newt_decode(&mot_u, buf_y, len_w); - if ( 1 != pok_w ) { + if ( 1 != _moat_length(&mot_u) ) { fprintf(stderr, "newt smol fail (a)\n"); exit(1); } @@ -85,8 +80,7 @@ _test_newt_smol(void) // two messages one buffer // { - pok_w = 0; - bal_w = 0; + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); @@ -96,7 +90,7 @@ _test_newt_smol(void) u3_newt_decode(&mot_u, buf_y, len_w); - if ( 2 != pok_w ) { + if ( 2 != _moat_length(&mot_u) ) { fprintf(stderr, "newt smol fail (b)\n"); exit(1); } @@ -106,8 +100,8 @@ _test_newt_smol(void) // { c3_y* end_y; - pok_w = 0; - bal_w = 0; + + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); @@ -116,14 +110,14 @@ _test_newt_smol(void) u3_newt_decode(&mot_u, buf_y, len_w - 1); - if ( 0 != pok_w ) { + if ( 0 != _moat_length(&mot_u) ) { fprintf(stderr, "newt smol fail (c)\n"); exit(1); } u3_newt_decode(&mot_u, end_y, 1); - if ( 1 != pok_w ) { + if ( 1 != _moat_length(&mot_u) ) { fprintf(stderr, "newt smol fail (d)\n"); exit(1); } @@ -135,8 +129,7 @@ _test_newt_smol(void) c3_y* haf_y; c3_w haf_w, dub_w; - pok_w = 0; - bal_w = 0; + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); @@ -155,14 +148,14 @@ _test_newt_smol(void) u3_newt_decode(&mot_u, buf_y, dub_w - haf_w); - if ( 1 != pok_w ) { + if ( 1 != _moat_length(&mot_u) ) { fprintf(stderr, "newt smol fail (e)\n"); exit(1); } u3_newt_decode(&mot_u, haf_y, haf_w); - if ( 2 != pok_w ) { + if ( 2 != _moat_length(&mot_u) ) { fprintf(stderr, "newt smol fail (f)\n"); exit(1); } @@ -184,19 +177,16 @@ _test_newt_vast(void) c3_y* buf_y; memset(&mot_u, 0, sizeof(u3_moat)); - mot_u.pok_f = _moat_poke_cb; - mot_u.bal_f = _moat_bail_cb; // one message one buffer // { - pok_w = 0; - bal_w = 0; + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); u3_newt_decode(&mot_u, buf_y, len_w); - if ( 1 != pok_w ) { + if ( 1 != _moat_length(&mot_u) ) { fprintf(stderr, "newt vast fail (a)\n"); exit(1); } @@ -205,8 +195,7 @@ _test_newt_vast(void) // two messages one buffer // { - pok_w = 0; - bal_w = 0; + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); @@ -216,7 +205,7 @@ _test_newt_vast(void) u3_newt_decode(&mot_u, buf_y, len_w); - if ( 2 != pok_w ) { + if ( 2 != _moat_length(&mot_u) ) { fprintf(stderr, "newt vast fail (b)\n"); exit(1); } @@ -225,8 +214,7 @@ _test_newt_vast(void) // one message many buffers // { - pok_w = 0; - bal_w = 0; + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); @@ -241,7 +229,7 @@ _test_newt_vast(void) c3_y* end_y = c3_malloc(1); end_y[0] = cop_y[haf_w]; - if ( 0 != pok_w ) { + if ( 0 != _moat_length(&mot_u) ) { fprintf(stderr, "newt vast fail (c) %u\n", haf_w); exit(1); } @@ -253,7 +241,7 @@ _test_newt_vast(void) c3_free(cop_y); } - if ( 1 != pok_w ) { + if ( 1 != _moat_length(&mot_u) ) { fprintf(stderr, "newt vast fail (d)\n"); exit(1); } @@ -265,8 +253,7 @@ _test_newt_vast(void) c3_y* haf_y; c3_w haf_w, dub_w; - pok_w = 0; - bal_w = 0; + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); @@ -285,14 +272,14 @@ _test_newt_vast(void) u3_newt_decode(&mot_u, buf_y, dub_w - haf_w); - if ( 1 != pok_w ) { + if ( 1 != _moat_length(&mot_u) ) { fprintf(stderr, "newt vast fail (e)\n"); exit(1); } u3_newt_decode(&mot_u, haf_y, haf_w); - if ( 2 != pok_w ) { + if ( 2 != _moat_length(&mot_u) ) { fprintf(stderr, "newt vast fail (f)\n"); exit(1); } @@ -303,8 +290,7 @@ _test_newt_vast(void) { c3_w dub_w; - pok_w = 0; - bal_w = 0; + mot_u.ent_u = mot_u.ext_u = 0; buf_y = _newt_encode(u3k(a), &len_w); @@ -326,7 +312,7 @@ _test_newt_vast(void) c3_y* end_y = c3_malloc(1); end_y[0] = cop_y[haf_w]; - if ( 1 != pok_w ) { + if ( 1 != _moat_length(&mot_u) ) { fprintf(stderr, "newt vast fail (g) %u\n", haf_w); exit(1); } @@ -338,7 +324,7 @@ _test_newt_vast(void) c3_free(cop_y); } - if ( 2 != pok_w ) { + if ( 2 != _moat_length(&mot_u) ) { fprintf(stderr, "newt vast fail (h)\n"); exit(1); } diff --git a/pkg/urbit/vere/daemon.c b/pkg/urbit/vere/daemon.c index 7e7bf44e7..bcb60e56b 100644 --- a/pkg/urbit/vere/daemon.c +++ b/pkg/urbit/vere/daemon.c @@ -425,6 +425,7 @@ _daemon_socket_connect(uv_stream_t *sock, int status) mor_u->nex_u = 0; } + uv_timer_init(u3L, &mor_u->tim_u); uv_pipe_init(u3L, &mor_u->pyp_u, 0); mor_u->pok_f = _daemon_fate; mor_u->bal_f = _daemon_bail; @@ -866,6 +867,7 @@ _daemon_loop_init() u3_moor* mor_u = c3_malloc(sizeof(u3_moor)); uv_connect_t* con_u = c3_malloc(sizeof(uv_connect_t)); con_u->data = mor_u; + uv_timer_init(u3L, &mor_u->tim_u); uv_pipe_init(u3L, &mor_u->pyp_u, 0); uv_pipe_connect(con_u, &mor_u->pyp_u, u3K.soc_c, _boothack_cb); } diff --git a/pkg/urbit/vere/lord.c b/pkg/urbit/vere/lord.c index 68bd83b0a..ffa1fcc88 100644 --- a/pkg/urbit/vere/lord.c +++ b/pkg/urbit/vere/lord.c @@ -914,6 +914,7 @@ u3_lord_init(c3_c* pax_c, c3_w wag_w, c3_d key_d[4], u3_lord_cb cb_u) arg_c[5] = 0; uv_pipe_init(u3L, &god_u->inn_u.pyp_u, 0); + uv_timer_init(u3L, &god_u->out_u.tim_u); uv_pipe_init(u3L, &god_u->out_u.pyp_u, 0); god_u->cod_u[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; diff --git a/pkg/urbit/vere/newt.c b/pkg/urbit/vere/newt.c index 9aa6919b7..92618016f 100644 --- a/pkg/urbit/vere/newt.c +++ b/pkg/urbit/vere/newt.c @@ -31,209 +31,152 @@ #include "all.h" #include "vere/vere.h" -/* _newt_gain_meat(): add a block to an existing message +/* _newt_mess_head(): await next msg header. */ static void -_newt_gain_meat(u3_moat* mot_u) +_newt_mess_head(u3_mess* mes_u) { - c3_assert( 0 != mot_u->mes_u ); - - // create block - // - u3_meat* met_u = c3_malloc(mot_u->len_d + (c3_d) sizeof(u3_meat)); - met_u->nex_u = 0; - met_u->len_d = mot_u->len_d; - memcpy(met_u->hun_y, mot_u->rag_y, mot_u->len_d); - - // enqueue block - // - if ( !mot_u->mes_u->meq_u ) { - mot_u->mes_u->meq_u = mot_u->mes_u->qem_u = met_u; - } - else { - mot_u->mes_u->qem_u->nex_u = met_u; - mot_u->mes_u->qem_u = met_u; - } - mot_u->mes_u->has_d += met_u->len_d; - - // free consumed stray bytes - // - c3_free(mot_u->rag_y); - mot_u->len_d = 0; - mot_u->rag_y = 0; + mes_u->sat_e = u3_mess_head; + mes_u->hed_u.has_y = 0; } -/* _newt_gain_mess(): begin parsing a new message +/* _newt_mess_tail(): await msg body. */ static void -_newt_gain_mess(u3_moat* mot_u) +_newt_mess_tail(u3_mess* mes_u, c3_d len_d) { - c3_assert( 8ULL <= mot_u->len_d ); - c3_assert( 0 == mot_u->mes_u ); + u3_meat* met_u = c3_malloc(len_d + sizeof(*met_u)); + met_u->nex_u = 0; + met_u->len_d = len_d; - c3_d nel_d = 0ULL; + mes_u->sat_e = u3_mess_tail; + mes_u->tal_u.has_d = 0; + mes_u->tal_u.met_u = met_u; +} - nel_d |= ((c3_d) mot_u->rag_y[0]) << 0ULL; - nel_d |= ((c3_d) mot_u->rag_y[1]) << 8ULL; - nel_d |= ((c3_d) mot_u->rag_y[2]) << 16ULL; - nel_d |= ((c3_d) mot_u->rag_y[3]) << 24ULL; - nel_d |= ((c3_d) mot_u->rag_y[4]) << 32ULL; - nel_d |= ((c3_d) mot_u->rag_y[5]) << 40ULL; - nel_d |= ((c3_d) mot_u->rag_y[6]) << 48ULL; - nel_d |= ((c3_d) mot_u->rag_y[7]) << 56ULL; - - c3_assert( 0ULL != nel_d ); - - // very likely to be a bad write, we can't jam anything this big - // - if ( 0xFFFFFFFFULL < nel_d ) { - fprintf(stderr, "newt: %d warn: large read %" PRIu64 "\r\n", - getpid(), - nel_d); - } - - mot_u->len_d -= 8ULL; - - mot_u->mes_u = c3_malloc(sizeof(u3_mess)); - mot_u->mes_u->len_d = nel_d; - mot_u->mes_u->has_d = 0; - mot_u->mes_u->meq_u = mot_u->mes_u->qem_u = 0; - - if ( 0ULL == mot_u->len_d ) { - c3_free(mot_u->rag_y); - mot_u->rag_y = 0; +/* _newt_meat_plan(): enqueue complete msg. +*/ +static void +_newt_meat_plan(u3_moat* mot_u, u3_meat* met_u) +{ + if ( mot_u->ent_u ) { + mot_u->ent_u->nex_u = met_u; + mot_u->ent_u = met_u; } else { - // remove consumed length from stray bytes - // - c3_y* buf_y = c3_malloc(mot_u->len_d); - memcpy(buf_y, mot_u->rag_y + 8, mot_u->len_d); - - c3_free(mot_u->rag_y); - mot_u->rag_y = buf_y; + mot_u->ent_u = mot_u->ext_u = met_u; } } -/* _newt_poke_mess(): pass message to [mot_u] callback +static void +_newt_meat_next_cb(uv_timer_t* tim_u); + +/* _newt_meat_poke(): deliver completed msg. */ static void -_newt_poke_mess(u3_moat* mot_u) +_newt_meat_poke(u3_moat* mot_u) { - c3_assert( 0 != mot_u->mes_u ); - c3_assert( mot_u->mes_u->has_d >= mot_u->mes_u->len_d ); + u3_meat* met_u = mot_u->ext_u; - c3_d len_d = mot_u->mes_u->len_d; - c3_y* buf_y = c3_malloc(len_d); - c3_d pat_d = 0; - u3_meat* met_u; + if ( met_u ) { + mot_u->ext_u = met_u->nex_u; - // we should have just cleared this - // - c3_assert(!mot_u->rag_y); - c3_assert(!mot_u->len_d); - - // collect queue blocks, cleaning them up; return any spare meat - // to the rag. - // - { - met_u = mot_u->mes_u->meq_u; - - while ( met_u && (pat_d < len_d) ) { - u3_meat* nex_u = met_u->nex_u; - c3_d end_d = (pat_d + met_u->len_d); - c3_d eat_d; - c3_d rem_d; - - eat_d = c3_min(len_d, end_d) - pat_d; - memcpy(buf_y + pat_d, met_u->hun_y, eat_d); - pat_d += eat_d; - - rem_d = (met_u->len_d - eat_d); - if ( rem_d ) { - mot_u->rag_y = c3_malloc(rem_d); - memcpy(mot_u->rag_y, met_u->hun_y + eat_d, rem_d); - mot_u->len_d = rem_d; - - // one: unless we got a bad length, this has to be the last - // block in the message. - // - // two: bad data on a newt channel can cause us to assert. - // that's actually the right thing for a private channel. - /// - c3_assert(0 == nex_u); - } - - c3_free(met_u); - met_u = nex_u; + if ( mot_u->ext_u ) { + uv_timer_start(&mot_u->tim_u, _newt_meat_next_cb, 0, 0); + } + else { + mot_u->ent_u = 0; } - c3_assert(pat_d == len_d); - - // clear the message - // - c3_free(mot_u->mes_u); - mot_u->mes_u = 0; - } - - // build and send the object - // - { - u3_noun mat = u3i_bytes((c3_w)len_d, buf_y); + u3_noun mat = u3i_bytes((c3_w)met_u->len_d, met_u->hun_y); mot_u->pok_f(mot_u->ptr_v, mat); + c3_free(met_u); } +} - c3_free(buf_y); +/* _newt_meat_next_cb(): handle next msg after timer. +*/ +static void +_newt_meat_next_cb(uv_timer_t* tim_u) +{ + u3_moat* mot_u = tim_u->data; + _newt_meat_poke(mot_u); } /* u3_newt_decode(): decode a (partial) length-prefixed byte buffer */ void -u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_w len_w) +u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_d len_d) { - // grow read buffer by `len_d` bytes - // - if ( mot_u->rag_y ) { - // XX check SIZE_MAX? - // - c3_d nel_d = mot_u->len_d + len_w; + u3_mess* mes_u = &mot_u->mes_u; - mot_u->rag_y = c3_realloc(mot_u->rag_y, nel_d); - memcpy(mot_u->rag_y + mot_u->len_d, buf_y, len_w); + while ( len_d ) { + switch( mes_u->sat_e ) { - mot_u->len_d = nel_d; - c3_free(buf_y); - } - else { - mot_u->rag_y = buf_y; - mot_u->len_d = (c3_d)len_w; - } - - // process stray bytes, trying to create a new message - // or add a block to an existing one. - // - while ( mot_u->rag_y ) { - // no message - // - if ( !mot_u->mes_u ) { - // but enough stray bytes to start one + // read up to 8 length bytes as needed // - if ( 8ULL <= mot_u->len_d ) { - _newt_gain_mess(mot_u); - } - else { - break; - } - } - else { - // there is a live message, add a block to the queue. - // - _newt_gain_meat(mot_u); + case u3_mess_head: { + c3_y* len_y = mes_u->hed_u.len_y; + c3_y has_y = mes_u->hed_u.has_y; + c3_y ned_y = 8 - has_y; + c3_y cop_y = c3_min(ned_y, len_d); - // check for message completions - // - if ( mot_u->mes_u->has_d >= mot_u->mes_u->len_d ) { - _newt_poke_mess(mot_u); - } + memcpy(len_y + has_y, buf_y, cop_y); + buf_y += cop_y; + len_d -= cop_y; + ned_y -= cop_y; + + // moar bytes needed, yield + // + if ( ned_y ) { + mes_u->hed_u.has_y = (has_y + cop_y); + } + // length known, allocate message + // + else { + c3_d met_d = (((c3_d)len_y[0]) << 0) + | (((c3_d)len_y[1]) << 8) + | (((c3_d)len_y[2]) << 16) + | (((c3_d)len_y[3]) << 24) + | (((c3_d)len_y[4]) << 32) + | (((c3_d)len_y[5]) << 40) + | (((c3_d)len_y[6]) << 48) + | (((c3_d)len_y[7]) << 56); + + // must be non-zero, only 32 bits supported + // + c3_assert( met_d ); + c3_assert( 0xFFFFFFFFULL > met_d ); + + // await body + // + _newt_mess_tail(mes_u, met_d); + } + } break; + + case u3_mess_tail: { + u3_meat* met_u = mes_u->tal_u.met_u; + c3_d has_d = mes_u->tal_u.has_d; + c3_d ned_d = met_u->len_d - has_d; + c3_d cop_d = c3_min(ned_d, len_d); + + memcpy(met_u->hun_y + has_d, buf_y, cop_d); + buf_y += cop_d; + len_d -= cop_d; + ned_d -= cop_d; + + // moar bytes needed, yield + // + if ( ned_d ) { + mes_u->tal_u.has_d = (has_d + cop_d); + } + // message completed, enqueue and await next header + // + else { + _newt_meat_plan(mot_u, met_u); + _newt_mess_head(mes_u); + } + } break; } } } @@ -259,7 +202,10 @@ _newt_read_cb(uv_stream_t* str_u, c3_free(buf_u->base); } else { - u3_newt_decode(mot_u, (c3_y*)buf_u->base, (c3_w)len_i); + u3_newt_decode(mot_u, (c3_y*)buf_u->base, (c3_d)len_i); + c3_free(buf_u->base); + + _newt_meat_poke(mot_u); } } @@ -282,9 +228,17 @@ _newt_alloc(uv_handle_t* had_u, void u3_newt_read(u3_moat* mot_u) { - mot_u->mes_u = 0; - mot_u->len_d = 0; - mot_u->rag_y = 0; + // zero-initialize completed msg queue + // + mot_u->ent_u = mot_u->ext_u = 0; + + // store pointer for queue timer callback + // + mot_u->tim_u.data = mot_u; + + // await next msg header + // + _newt_mess_head(&mot_u->mes_u); { c3_i sas_i; diff --git a/pkg/urbit/worker/main.c b/pkg/urbit/worker/main.c index ccde6dda8..c4bca1c0d 100644 --- a/pkg/urbit/worker/main.c +++ b/pkg/urbit/worker/main.c @@ -144,6 +144,8 @@ main(c3_i argc, c3_c* argv[]) { c3_i err_i; + err_i = uv_timer_init(lup_u, &inn_u.tim_u); + c3_assert(!err_i); err_i = uv_pipe_init(lup_u, &inn_u.pyp_u, 0); c3_assert(!err_i); uv_pipe_open(&inn_u.pyp_u, inn_i); From 8ef8987b5432b458e8b36b2d3167eb0e6dc849df Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Mon, 8 Jun 2020 20:14:39 -0700 Subject: [PATCH 12/15] newt: adds synchronous read, used in serf with blocking writes --- pkg/urbit/include/vere/vere.h | 7 +- pkg/urbit/vere/newt.c | 125 ++++++++++++++++++++++++++-------- pkg/urbit/worker/main.c | 4 +- 3 files changed, 104 insertions(+), 32 deletions(-) diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index 4c8cee2f6..a61e497c8 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -1084,7 +1084,12 @@ void u3_newt_write(u3_mojo* moj_u, u3_atom mat); - /* u3_newt_read(): activate reading on input stream. + /* u3_newt_read_sync(): start reading; multiple msgs synchronous. + */ + void + u3_newt_read_sync(u3_moat* mot_u); + + /* u3_newt_read(): start reading; each msg asynchronous. */ void u3_newt_read(u3_moat* mot_u); diff --git a/pkg/urbit/vere/newt.c b/pkg/urbit/vere/newt.c index 92618016f..62ff6a218 100644 --- a/pkg/urbit/vere/newt.c +++ b/pkg/urbit/vere/newt.c @@ -68,13 +68,39 @@ _newt_meat_plan(u3_moat* mot_u, u3_meat* met_u) } } -static void -_newt_meat_next_cb(uv_timer_t* tim_u); - /* _newt_meat_poke(): deliver completed msg. */ static void -_newt_meat_poke(u3_moat* mot_u) +_newt_meat_poke(u3_moat* mot_u, u3_meat* met_u) +{ + u3_noun mat = u3i_bytes((c3_w)met_u->len_d, met_u->hun_y); + mot_u->pok_f(mot_u->ptr_v, mat); + c3_free(met_u); +} + +/* _newt_meat_next_sync(): deliver completed msgs, synchronously. +*/ +static void +_newt_meat_next_sync(u3_moat* mot_u) +{ + u3_meat* met_u = mot_u->ext_u; + + while ( met_u ) { + u3_meat* nex_u = met_u->nex_u; + _newt_meat_poke(mot_u, met_u); + met_u = nex_u; + } + + mot_u->ent_u = mot_u->ext_u = 0; +} + +static void +_newt_meat_next_cb(uv_timer_t* tim_u); + +/* _newt_meat_next(): deliver completed msgs, asynchronously. +*/ +static void +_newt_meat_next(u3_moat* mot_u) { u3_meat* met_u = mot_u->ext_u; @@ -88,9 +114,7 @@ _newt_meat_poke(u3_moat* mot_u) mot_u->ent_u = 0; } - u3_noun mat = u3i_bytes((c3_w)met_u->len_d, met_u->hun_y); - mot_u->pok_f(mot_u->ptr_v, mat); - c3_free(met_u); + _newt_meat_poke(mot_u, met_u); } } @@ -100,7 +124,7 @@ static void _newt_meat_next_cb(uv_timer_t* tim_u) { u3_moat* mot_u = tim_u->data; - _newt_meat_poke(mot_u); + _newt_meat_next(mot_u); } /* u3_newt_decode(): decode a (partial) length-prefixed byte buffer @@ -181,7 +205,48 @@ u3_newt_decode(u3_moat* mot_u, c3_y* buf_y, c3_d len_d) } } -/* _newt_read_cb(): stream input callback. +/* _newt_read(): handle async read result. +*/ +static c3_o +_newt_read(u3_moat* mot_u, + ssize_t len_i, + const uv_buf_t* buf_u) +{ + if ( 0 > len_i ) { + c3_free(buf_u->base); + uv_read_stop((uv_stream_t*)&mot_u->pyp_u); + fprintf(stderr, "newt: read failed %s\r\n", uv_strerror(len_i)); + mot_u->bal_f(mot_u->ptr_v, uv_strerror(len_i)); + return c3n; + } + // EAGAIN/EWOULDBLOCK + // + else if ( 0 == len_i ) { + c3_free(buf_u->base); + return c3n; + } + else { + u3_newt_decode(mot_u, (c3_y*)buf_u->base, (c3_d)len_i); + c3_free(buf_u->base); + return c3y; + } +} + +/* _newt_read_sync_cb(): async read callback, sync msg delivery. +*/ +static void +_newt_read_sync_cb(uv_stream_t* str_u, + ssize_t len_i, + const uv_buf_t* buf_u) +{ + u3_moat* mot_u = (void *)str_u; + + if ( c3y == _newt_read(mot_u, len_i, buf_u) ) { + _newt_meat_next_sync(mot_u); + } +} + +/* _newt_read_cb(): async read callback, async msg delivery. */ static void _newt_read_cb(uv_stream_t* str_u, @@ -190,22 +255,8 @@ _newt_read_cb(uv_stream_t* str_u, { u3_moat* mot_u = (void *)str_u; - if ( 0 > len_i ) { - c3_free(buf_u->base); - uv_read_stop(str_u); - fprintf(stderr, "newt: read failed %s\r\n", uv_strerror(len_i)); - mot_u->bal_f(mot_u->ptr_v, uv_strerror(len_i)); - } - // EAGAIN/EWOULDBLOCK - // - else if ( 0 == len_i ) { - c3_free(buf_u->base); - } - else { - u3_newt_decode(mot_u, (c3_y*)buf_u->base, (c3_d)len_i); - c3_free(buf_u->base); - - _newt_meat_poke(mot_u); + if ( c3y == _newt_read(mot_u, len_i, buf_u) ) { + _newt_meat_next(mot_u); } } @@ -223,10 +274,8 @@ _newt_alloc(uv_handle_t* had_u, *buf_u = uv_buf_init(ptr_v, len_i); } -/* u3_newt_read(): start stream reading. -*/ -void -u3_newt_read(u3_moat* mot_u) +static void +_newt_read_init(u3_moat* mot_u, uv_read_cb read_cb_f) { // zero-initialize completed msg queue // @@ -245,7 +294,7 @@ u3_newt_read(u3_moat* mot_u) if ( 0 != (sas_i = uv_read_start((uv_stream_t*)&mot_u->pyp_u, _newt_alloc, - _newt_read_cb)) ) + read_cb_f)) ) { fprintf(stderr, "newt: read failed %s\r\n", uv_strerror(sas_i)); mot_u->bal_f(mot_u->ptr_v, uv_strerror(sas_i)); @@ -253,6 +302,22 @@ u3_newt_read(u3_moat* mot_u) } } +/* u3_newt_read_sync(): start reading; multiple msgs synchronous. +*/ +void +u3_newt_read_sync(u3_moat* mot_u) +{ + _newt_read_init(mot_u, _newt_read_sync_cb); +} + +/* u3_newt_read(): start reading; each msg asynchronous. +*/ +void +u3_newt_read(u3_moat* mot_u) +{ + _newt_read_init(mot_u, _newt_read_cb); +} + /* n_req: write request for newt */ typedef struct _n_req { diff --git a/pkg/urbit/worker/main.c b/pkg/urbit/worker/main.c index c4bca1c0d..98fc6cbe9 100644 --- a/pkg/urbit/worker/main.c +++ b/pkg/urbit/worker/main.c @@ -153,6 +153,8 @@ main(c3_i argc, c3_c* argv[]) err_i = uv_pipe_init(lup_u, &out_u.pyp_u, 0); c3_assert(!err_i); uv_pipe_open(&out_u.pyp_u, out_i); + + uv_stream_set_blocking((uv_stream_t*)&out_u.pyp_u, 1); } // set up writing @@ -241,7 +243,7 @@ main(c3_i argc, c3_c* argv[]) // start reading // - u3_newt_read(&inn_u); + u3_newt_read_sync(&inn_u); // enter loop // From 00fefce3344d3c3f2e015b4b979292cb8cca3ca4 Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 11 Jun 2020 14:45:36 -0700 Subject: [PATCH 13/15] serf: plugs leak of error-notification event --- pkg/urbit/worker/serf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index 9a22b937e..a0a4577a8 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -583,7 +583,7 @@ _serf_work(u3_serf* sef_u, u3_noun job) c3_assert( sef_u->sen_d == sef_u->dun_d); sef_u->sen_d++; - gon = _serf_poke(sef_u, "work", job); + gon = _serf_poke(sef_u, "work", job); // retain // event accepted // @@ -613,7 +613,7 @@ _serf_work(u3_serf* sef_u, u3_noun job) // job = _serf_make_crud(job, dud); - gon = _serf_poke(sef_u, "crud", u3k(job)); + gon = _serf_poke(sef_u, "crud", job); // retain // error notification accepted // From f48dd41ca8dd66063c23213915cf6a823f2452de Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 11 Jun 2020 15:14:03 -0700 Subject: [PATCH 14/15] serf: refactors %work $writ handling --- pkg/urbit/worker/serf.c | 61 +++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index a0a4577a8..1c0ea2114 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -488,6 +488,24 @@ _serf_sure_core(u3_serf* sef_u, u3_noun cor) sef_u->mut_o = c3y; } +/* _serf_sure(): event succeeded, save state and process effects. +*/ +static u3_noun +_serf_sure(u3_serf* sef_u, c3_w pre_w, u3_noun par) +{ + // vir/(list ovum) list of effects + // cor/arvo arvo core + // + u3_noun vir, cor; + u3x_cell(par, &vir, &cor); + + _serf_sure_core(sef_u, u3k(cor)); + vir = _serf_sure_feck(sef_u, pre_w, u3k(vir)); + + u3z(par); + return vir; +} + /* _serf_make_crud(): */ static u3_noun @@ -572,11 +590,7 @@ static u3_noun _serf_work(u3_serf* sef_u, u3_noun job) { u3_noun gon; - c3_w pre_w = u3a_open(u3R); - - // %work must be performed against an extant kernel - // - c3_assert( 0 != sef_u->mug_l); + c3_w pre_w = u3a_open(u3R); // event numbers must be continuous // @@ -588,18 +602,11 @@ _serf_work(u3_serf* sef_u, u3_noun job) // event accepted // if ( u3_blip == u3h(gon) ) { - // vir/(list ovum) list of effects - // cor/arvo arvo core - // - u3_noun vir, cor; - u3x_trel(gon, 0, &vir, &cor); - - _serf_sure_core(sef_u, u3k(cor)); - vir = _serf_sure_feck(sef_u, pre_w, u3k(vir)); + u3_noun vir = _serf_sure(sef_u, pre_w, u3k(u3t(gon))); u3z(gon); u3z(job); return u3nc(c3__done, u3nt(u3i_chubs(1, &sef_u->dun_d), - u3i_words(1, &sef_u->mug_l), + sef_u->mug_l, vir)); } // event rejected @@ -609,7 +616,7 @@ _serf_work(u3_serf* sef_u, u3_noun job) // u3_noun dud = u3k(gon); - // XX reclaim/pack on %meme first? + // XX reclaim on %meme first? // job = _serf_make_crud(job, dud); @@ -618,18 +625,11 @@ _serf_work(u3_serf* sef_u, u3_noun job) // error notification accepted // if ( u3_blip == u3h(gon) ) { - // vir/(list ovum) list of effects - // cor/arvo arvo core - // - u3_noun vir, cor; - u3x_trel(gon, 0, &vir, &cor); - - _serf_sure_core(sef_u, u3k(cor)); - vir = _serf_sure_feck(sef_u, pre_w, u3k(vir)); + u3_noun vir = _serf_sure(sef_u, pre_w, u3k(u3t(gon))); u3z(gon); u3z(dud); return u3nc(c3__swap, u3nq(u3i_chubs(1, &sef_u->dun_d), - u3i_words(1, &sef_u->mug_l), + sef_u->mug_l, job, vir)); } @@ -638,7 +638,7 @@ _serf_work(u3_serf* sef_u, u3_noun job) else { sef_u->sen_d = sef_u->dun_d; - // XX reclaim/pack on %meme ? + // XX reclaim on %meme ? // u3z(job); @@ -673,6 +673,10 @@ u3_serf_work(u3_serf* sef_u, u3_noun job) u3t_event_trace(lab_c, 'B'); } + // %work must be performed against an extant kernel + // + c3_assert( 0 != sef_u->mug_l); + pro = u3nc(c3__work, _serf_work(sef_u, job)); if ( tac_t ) { @@ -810,13 +814,13 @@ _serf_play_list(u3_serf* sef_u, u3_noun eve) // u3z(vev); return u3nc(c3__bail, u3nt(u3i_chubs(1, &sef_u->dun_d), - u3i_words(1, &sef_u->mug_l), + sef_u->mug_l, dud)); } } u3z(vev); - return u3nc(c3__done, u3i_words(1, &sef_u->mug_l)); + return u3nc(c3__done, sef_u->mug_l); } /* u3_serf_play(): apply event list, producing status. @@ -1092,8 +1096,7 @@ _serf_ripe(u3_serf* sef_u) ? 0 : u3r_mug(u3A->roc); - return u3nc(u3i_chubs(1, &sef_u->dun_d), - u3i_words(1, &sef_u->mug_l)); + return u3nc(u3i_chubs(1, &sef_u->dun_d), sef_u->mug_l); } /* u3_serf_init(): init or restore, producing status. From 19da74d1460713069e2cffcc92d86bbf3b3ad06c Mon Sep 17 00:00:00 2001 From: Joe Bryan Date: Thu, 11 Jun 2020 16:01:19 -0700 Subject: [PATCH 15/15] serf: disables auto |pack, refactors loading from rock (-r) --- pkg/urbit/include/vere/serf.h | 7 ++- pkg/urbit/include/vere/vere.h | 5 ++ pkg/urbit/vere/pier.c | 3 - pkg/urbit/vere/term.c | 3 + pkg/urbit/worker/main.c | 49 +-------------- pkg/urbit/worker/serf.c | 113 ++++++++++++++++++++-------------- 6 files changed, 83 insertions(+), 97 deletions(-) diff --git a/pkg/urbit/include/vere/serf.h b/pkg/urbit/include/vere/serf.h index b49533e90..9fb6ef0c6 100644 --- a/pkg/urbit/include/vere/serf.h +++ b/pkg/urbit/include/vere/serf.h @@ -12,7 +12,7 @@ c3_d dun_d; // last event processed c3_l mug_l; // hash of state c3_o pac_o; // pack kernel - c3_o rec_o; // reclaim cash + c3_o rec_o; // reclaim cache c3_o mut_o; // mutated kerne u3_noun sac; // space measurementl } u3_serf; @@ -24,6 +24,11 @@ u3_noun u3_serf_init(u3_serf* sef_u); + /* u3_serf_unpack(): initialize from rock at [eve_d]. + */ + void + u3_serf_unpack(u3_serf* sef_u, c3_d eve_d); + /* u3_serf_writ(): apply writ [wit], producing plea [*pel] on c3y. */ c3_o diff --git a/pkg/urbit/include/vere/vere.h b/pkg/urbit/include/vere/vere.h index a61e497c8..32b5cfc84 100644 --- a/pkg/urbit/include/vere/vere.h +++ b/pkg/urbit/include/vere/vere.h @@ -1133,6 +1133,11 @@ c3_o u3_pier_save(u3_pier* pir_u); + /* u3_pier_pack(): save a portable snapshot. + */ + c3_o + u3_pier_pack(u3_pier* pir_u); + /* u3_pier_stub(): get the One Pier for unreconstructed code. */ u3_pier* diff --git a/pkg/urbit/vere/pier.c b/pkg/urbit/vere/pier.c index 7b98fdfab..6932e641e 100644 --- a/pkg/urbit/vere/pier.c +++ b/pkg/urbit/vere/pier.c @@ -574,9 +574,6 @@ _pier_play_read(u3_play* pay_u) } } -c3_o -u3_pier_pack(u3_pier* pir_u); - /* _pier_play(): send a batch of events to the worker for log replay. */ static void diff --git a/pkg/urbit/vere/term.c b/pkg/urbit/vere/term.c index d29ee128b..36c3ce090 100644 --- a/pkg/urbit/vere/term.c +++ b/pkg/urbit/vere/term.c @@ -1401,6 +1401,9 @@ _term_io_kick(u3_auto* car_u, u3_noun wir, u3_noun cad) // case c3__pack: { ret_o = c3y; + // XX would be + // + // u3_assure(u3_pier_pack(car_u->pir_u)); } break; } } diff --git a/pkg/urbit/worker/main.c b/pkg/urbit/worker/main.c index 98fc6cbe9..d4f89a0f8 100644 --- a/pkg/urbit/worker/main.c +++ b/pkg/urbit/worker/main.c @@ -175,54 +175,7 @@ main(c3_i argc, c3_c* argv[]) u3V.sen_d = u3V.dun_d = u3m_boot(dir_c); if ( eve_d ) { - c3_o roc_o; - c3_c nam_c[8193]; - snprintf(nam_c, 8192, "%s/.urb/roc/%" PRIu64 ".jam", u3V.dir_c, eve_d); - - struct stat buf_b; - c3_i fid_i = open(nam_c, O_RDONLY, 0644); - - if ( (fid_i < 0) || (fstat(fid_i, &buf_b) < 0) ) { - fprintf(stderr, "serf: rock: %s not found\r\n", nam_c); - roc_o = c3n; - } - else { - fprintf(stderr, "serf: rock: %s found\r\n", nam_c); - roc_o = c3y; - } - - close(fid_i); - - - if ( c3y == roc_o ) { - if ( c3n == u3e_hold() ) { - fprintf(stderr, "serf: unable to backup checkpoint\r\n"); - } - else { - u3m_wipe(); - - if ( c3n == u3m_rock_load(u3V.dir_c, eve_d) ) { - fprintf(stderr, "serf: compaction failed, restoring checkpoint\r\n"); - - if ( c3n == u3e_fall() ) { - fprintf(stderr, "serf: unable to restore checkpoint\r\n"); - c3_assert(0); - } - } - - if ( c3n == u3e_drop() ) { - fprintf(stderr, "serf: warning: orphaned backup checkpoint file\r\n"); - } - - fprintf(stderr, "serf (%" PRIu64 "): compacted loom\r\n", eve_d); - - u3V.sen_d = u3V.dun_d = eve_d; - - // save now for flexibility - // - u3e_save(); - } - } + u3_serf_unpack(&u3V, eve_d); } } diff --git a/pkg/urbit/worker/serf.c b/pkg/urbit/worker/serf.c index 1c0ea2114..0c0f6fd11 100644 --- a/pkg/urbit/worker/serf.c +++ b/pkg/urbit/worker/serf.c @@ -290,9 +290,7 @@ _serf_static_grab(void) static void _serf_pack(u3_serf* sef_u) { - // skip for now - // - // _serf_static_grab(); + _serf_static_grab(); u3l_log("serf (%" PRIu64 "): compacting loom\r\n", sef_u->dun_d); @@ -301,39 +299,11 @@ _serf_pack(u3_serf* sef_u) return; } - if ( c3n == u3e_hold() ) { - u3l_log("serf: unable to backup checkpoint\r\n"); - return; - } - - u3m_wipe(); - - if ( c3n == u3m_rock_load(sef_u->dir_c, sef_u->dun_d) ) { - u3l_log("serf: compaction failed, restoring checkpoint\r\n"); - - if ( c3n == u3e_fall() ) { - fprintf(stderr, "serf: unable to restore checkpoint\r\n"); - c3_assert(0); - } - } - - if ( c3n == u3e_drop() ) { - u3l_log("serf: warning: orphaned backup checkpoint file\r\n"); - } - - // leave these for now - // - // if ( c3n == u3m_rock_drop(sef_u->dir_c, sef_u->dun_d) ) { - // u3l_log("serf: warning: orphaned state file\r\n"); - // } + u3_serf_unpack(sef_u, sef_u->dun_d); u3l_log("serf (%" PRIu64 "): compacted loom\r\n", sef_u->dun_d); _serf_static_grab(); - - // save now for flexibility - // - u3e_save(); } /* u3_serf_post(): update serf state post-writ. @@ -346,7 +316,7 @@ u3_serf_post(u3_serf* sef_u) sef_u->rec_o = c3n; } - // XX this runs on replay too + // XX this runs on replay too, |mass s/b elsewhere // if ( c3y == sef_u->mut_o ) { sef_u->mut_o = c3n; @@ -432,14 +402,11 @@ _serf_sure_feck(u3_serf* sef_u, c3_w pre_w, u3_noun vir) if ( (pre_w > low_w) && !(pos_w > low_w) ) { // XX set flag(s) in u3V so we don't repeat endlessly? - // XX pack here too? // - pac_o = c3y; rec_o = c3y; pri = 1; } else if ( (pre_w > hig_w) && !(pos_w > hig_w) ) { - pac_o = c3y; rec_o = c3y; pri = 0; } @@ -453,12 +420,6 @@ _serf_sure_feck(u3_serf* sef_u, c3_w pre_w, u3_noun vir) rec_o = c3y; } - // pack every 20K events - // - if ( 0 == (sef_u->dun_d % 20000ULL) ) { - pac_o = c3y; - } - // notify daemon of memory pressure via "fake" effect // if ( u3_none != pri ) { @@ -782,7 +743,7 @@ _serf_play_list(u3_serf* sef_u, u3_noun eve) _serf_sure_core(sef_u, u3k(cor)); - // process effects to set pack/reclaim flags + // process effects to set u3_serf_post flags // u3z(_serf_sure_feck(sef_u, pre_w, u3k(vir))); @@ -806,8 +767,7 @@ _serf_play_list(u3_serf* sef_u, u3_noun eve) u3z(gon); - // XX reclaim/pack on meme - // XX retry? + // XX reclaim on meme ? // // send failure notification @@ -964,6 +924,8 @@ u3_serf_live(u3_serf* sef_u, u3_noun com, u3_noun* ret) return c3y; } + // NB: the %pack $writ only saves the rock, it doesn't load it + // case c3__pack: { c3_d eve_d; @@ -1099,6 +1061,67 @@ _serf_ripe(u3_serf* sef_u) return u3nc(u3i_chubs(1, &sef_u->dun_d), sef_u->mug_l); } +/* u3_serf_unpack(): initialize from rock at [eve_d]. +*/ +void +u3_serf_unpack(u3_serf* sef_u, c3_d eve_d) +{ + c3_o roc_o; + c3_c nam_c[8193]; + snprintf(nam_c, 8192, "%s/.urb/roc/%" PRIu64 ".jam", sef_u->dir_c, eve_d); + + struct stat buf_b; + c3_i fid_i = open(nam_c, O_RDONLY, 0644); + + if ( (fid_i < 0) || (fstat(fid_i, &buf_b) < 0) ) { + fprintf(stderr, "serf: rock: %s not found\r\n", nam_c); + roc_o = c3n; + } + else { + fprintf(stderr, "serf: rock: %s found\r\n", nam_c); + roc_o = c3y; + } + + close(fid_i); + + + if ( c3y == roc_o ) { + if ( c3n == u3e_hold() ) { + fprintf(stderr, "serf: unable to backup checkpoint\r\n"); + } + else { + u3m_wipe(); + + if ( c3n == u3m_rock_load(sef_u->dir_c, eve_d) ) { + fprintf(stderr, "serf: compaction failed, restoring checkpoint\r\n"); + + if ( c3n == u3e_fall() ) { + fprintf(stderr, "serf: unable to restore checkpoint\r\n"); + c3_assert(0); + } + } + + if ( c3n == u3e_drop() ) { + fprintf(stderr, "serf: warning: orphaned backup checkpoint file\r\n"); + } + + // leave rocks on disk + // + // if ( c3n == u3m_rock_drop(sef_u->dir_c, sef_u->dun_d) ) { + // u3l_log("serf: warning: orphaned state file\r\n"); + // } + + fprintf(stderr, "serf (%" PRIu64 "): compacted loom\r\n", eve_d); + + sef_u->sen_d = sef_u->dun_d = eve_d; + + // save now for flexibility + // + u3e_save(); + } + } +} + /* u3_serf_init(): init or restore, producing status. */ u3_noun