diff --git a/pkg/noun/manage.c b/pkg/noun/manage.c index d167994a46..2cab5e3e44 100644 --- a/pkg/noun/manage.c +++ b/pkg/noun/manage.c @@ -486,6 +486,17 @@ u3m_mark(void) return qua_u; } +/* _pave_jets(): build jet tables. +*/ +static void +_pave_jets(void) +{ + u3R->jed.bas_p = u3h_new(); + u3R->jed.cod_p = u3h_new(); + u3R->jed.han_p = u3h_new(); + u3R->jed.war_p = u3h_new(); +} + /* _pave_parts(): build internal tables. */ static void @@ -493,11 +504,8 @@ _pave_parts(void) { u3R->cax.har_p = u3h_new_cache(u3C.hap_w); // transient u3R->cax.per_p = u3h_new_cache(u3C.per_w); // persistent - u3R->jed.war_p = u3h_new(); - u3R->jed.cod_p = u3h_new(); - u3R->jed.han_p = u3h_new(); - u3R->jed.bas_p = u3h_new(); u3R->byc.har_p = u3h_new(); + _pave_jets(); } /* _pave_road(): writes road boundaries to loom mem (stored at mat_w) @@ -692,6 +700,14 @@ u3m_pave(c3_o nuu_o) } } +/* u3m_pave_jets(): re-initialize jet dashboard hashtables. +*/ +void +u3m_pave_jets(void) +{ + _pave_jets(); +} + #if 0 /* u3m_clear(): clear all allocated data in road. */ diff --git a/pkg/noun/manage.h b/pkg/noun/manage.h index 8e543992b9..4a735794be 100644 --- a/pkg/noun/manage.h +++ b/pkg/noun/manage.h @@ -88,6 +88,11 @@ void u3m_pave(c3_o nuu_o); + /* u3m_pave_jets(): re-initialize jet dashboard hashtables. + */ + void + u3m_pave_jets(); + /* u3m_signal(): treat a nock-level exception as a signal interrupt. */ void diff --git a/pkg/noun/urth.c b/pkg/noun/urth.c index 6eb6f8fcc3..b276cab04f 100644 --- a/pkg/noun/urth.c +++ b/pkg/noun/urth.c @@ -12,6 +12,7 @@ #include "imprison.h" #include "jets.h" #include "manage.h" +#include "options.h" #include "retrieve.h" #include "serial.h" #include "ur/ur.h" @@ -460,6 +461,140 @@ u3u_meld(void) } #endif +/* BEGIN helper functions for u3u_melt + ------------------------------------------------------------------- +*/ +/* _cj_warm_tap(): tap war_p to rel +*/ +static void +_cj_warm_tap(u3_noun kev, void* wit) +{ + u3_noun* rel = wit; + *rel = u3nc(u3k(kev), *rel); +} + +static inline u3_weak +_cu_melt_get(u3p(u3h_root) set_p, u3_noun som) +{ + u3_post hav_p = u3h_git(set_p, som); + + if ( u3_none == hav_p ) { + return u3_none; + } + + // restore tag bits from [som] + // + return (hav_p >> u3a_vits) | (som & 0xc0000000); +} + +static inline void +_cu_melt_put(u3p(u3h_root) set_p, u3_noun som) +{ + // strip tag bits from [som] to skip refcounts + // + u3_post hav_p = u3a_to_off(som); + u3h_put(set_p, som, hav_p); +} + +static void +_cu_melt_noun(u3p(u3h_root) set_p, u3_noun* mos) +{ + u3_noun som = *mos; + u3_weak hav; + + // skip direct atoms + // + if ( c3y == u3a_is_cat(som) ) { + return; + } + + // [som] equals [hav], and [hav] is canonical + // + if ( u3_none != (hav = _cu_melt_get(set_p, som)) ) { + if ( hav != som ) { + u3z(som); + *mos = u3k(hav); + } + return; + } + + // traverse subtrees + // + if ( c3y == u3a_is_cell(som) ) { + u3a_cell *cel_u = u3a_to_ptr(som); + _cu_melt_noun(set_p, &cel_u->hed); + _cu_melt_noun(set_p, &cel_u->tel); + } + + // [som] is canonical + // + _cu_melt_put(set_p, som); +} + +/* u3u_melt(): globally deduplicate memory and pack in-place. +*/ +c3_w +u3u_melt(void) +{ + c3_w pre_w = u3a_open(u3R); + + // Verify that we're on the main road. + // + u3_assert( &(u3H->rod_u) == u3R ); + + // Store a cons list of the cold jet registrations in `cod` + // + u3_noun cod = u3_nul; + u3h_walk_with(u3R->jed.cod_p, _cj_warm_tap, &cod); + + u3m_reclaim(); // refresh the byte-code interpreter. + u3j_free(); // free cold & warm jet state + + u3h_free(u3R->cax.per_p); + u3R->cax.per_p = u3h_new_cache(u3C.per_w); + + u3z(u3A->yot); // Clear the hoon run-time cache + u3A->yot = 0; + + u3z(u3R->bug.mer); // Clear the "emergency" buffer. + u3R->bug.mer = 0; + + u3z(u3R->bug.tax); // Clear the stack traces. + u3R->bug.tax = 0; + + u3p(u3h_root) set_p = u3h_new(); // temp hashtable + + _cu_melt_noun(set_p, &cod); // melt the jets + _cu_melt_noun(set_p, &u3A->roc); // melt the kernel + + u3h_free(set_p); // release the temp hashtable + + // re-initialize the jets + // + u3j_boot(c3y); + u3m_pave_jets(); + + // Put the jet registrations back. Loop over cod putting them back into the cold jet + // dashboard. Then re-run the garbage collector. + // + u3_noun codc; + codc = cod; + + while(u3_nul != cod) { + u3_noun kev = u3h(cod); + u3h_put(u3R->jed.cod_p, u3h(kev), u3k(u3t(kev))); + cod = u3t(cod); + } + u3z(codc); + + // remove free space + // + u3j_ream(); + u3m_pack(); + + return (u3a_open(u3R) - pre_w); +} + /* _cu_rock_path(): format rock path. */ static c3_o diff --git a/pkg/noun/urth.h b/pkg/noun/urth.h index e2ab3e6f44..630fa002d0 100644 --- a/pkg/noun/urth.h +++ b/pkg/noun/urth.h @@ -12,6 +12,11 @@ c3_w u3u_meld(void); + /* u3u_melt(): globally deduplicate memory and pack in-place. + */ + c3_w + u3u_melt(void); + /* u3u_cram(): globably deduplicate memory, and write a rock to disk. */ c3_o diff --git a/pkg/vere/main.c b/pkg/vere/main.c index 3083aae5bf..dcb25a0f6f 100644 --- a/pkg/vere/main.c +++ b/pkg/vere/main.c @@ -801,6 +801,7 @@ _cw_usage(c3_c* bin_c) " %s grab %.*s measure memory usage:\n", " %s info %.*s print pier info:\n", " %s meld %.*s deduplicate snapshot:\n", + " %s melt %.*s deduplicate and minimize snapshot:\n", " %s pack %.*s defragment snapshot:\n", " %s play %.*s recompute events:\n", " %s prep %.*s prepare for upgrade:\n", @@ -2005,7 +2006,7 @@ _cw_queu(c3_i argc, c3_c* argv[]) } } -/* _cw_uniq(): deduplicate persistent nouns +/* _cw_meld(): deduplicate persistent nouns */ static void _cw_meld(c3_i argc, c3_c* argv[]) @@ -2101,6 +2102,94 @@ _cw_meld(c3_i argc, c3_c* argv[]) u3m_stop(); } +/* _cw_melt(): deduplicate persistent nouns and compress them to minimal size +*/ +static void +_cw_melt(c3_i argc, c3_c* argv[]) +{ + c3_i ch_i, lid_i; + c3_w arg_w; + + static struct option lop_u[] = { + { "loom", required_argument, NULL, c3__loom }, + { "no-demand", no_argument, NULL, 6 }, + { "swap", no_argument, NULL, 7 }, + { "swap-to", required_argument, NULL, 8 }, + { "gc-early", no_argument, NULL, 9 }, + { NULL, 0, NULL, 0 } + }; + + u3_Host.dir_c = _main_pier_run(argv[0]); + + while ( -1 != (ch_i=getopt_long(argc, argv, "", lop_u, &lid_i)) ) { + switch ( ch_i ) { + case c3__loom: { + if (_main_readw_loom("loom", &u3_Host.ops_u.lom_y)) { + exit(1); + } + } break; + + case 6: { // no-demand + u3_Host.ops_u.map = c3n; + u3C.wag_w |= u3o_no_demand; + } break; + + case 7: { // swap + u3_Host.ops_u.eph = c3y; + u3C.wag_w |= u3o_swap; + } break; + + case 8: { // swap-to + u3_Host.ops_u.eph = c3y; + u3C.wag_w |= u3o_swap; + u3C.eph_c = strdup(optarg); + break; + } + + case 9: { // gc-early + u3C.wag_w |= u3o_check_corrupt; + break; + } + + case '?': { + fprintf(stderr, "invalid argument\r\n"); + exit(1); + } break; + } + } + + // argv[optind] is always "melt" + // + + if ( !u3_Host.dir_c ) { + if ( optind + 1 < argc ) { + u3_Host.dir_c = argv[optind + 1]; + } + else { + fprintf(stderr, "invalid command, pier required\r\n"); + exit(1); + } + + optind++; + } + + if ( optind + 1 != argc ) { + fprintf(stderr, "invalid command\r\n"); + exit(1); + } + + u3C.wag_w |= u3o_hashless; + + u3_Host.eve_d = u3m_boot(u3_Host.dir_c, (size_t)1 << u3_Host.ops_u.lom_y); + u3_disk* log_u = _cw_disk_init(u3_Host.dir_c); // XX s/b try_aquire lock + + u3a_print_memory(stderr, "urbit: melt: gained", u3u_melt()); + + u3m_save(); + u3_disk_exit(log_u); + u3m_stop(); +} + /* _cw_next(): request upgrade */ static void @@ -3135,6 +3224,7 @@ _cw_utils(c3_i argc, c3_c* argv[]) case c3__info: _cw_info(argc, argv); return 1; case c3__meld: _cw_meld(argc, argv); return 1; + case c3__melt: _cw_melt(argc, argv); return 1; case c3__next: _cw_next(argc, argv); return 2; // continue on case c3__pack: _cw_pack(argc, argv); return 1; case c3__play: _cw_play(argc, argv); return 1;