diff --git a/pkg/urbit/noun/retrieve.c b/pkg/urbit/noun/retrieve.c index 34100b7ed9..8ce5e38265 100644 --- a/pkg/urbit/noun/retrieve.c +++ b/pkg/urbit/noun/retrieve.c @@ -316,16 +316,25 @@ _sing_one(u3_noun* a, u3_noun* b) static void _sung_one(u3_noun* a, u3_noun* b) { - + // already unified, we're done + // if ( *a == *b ) { return; - } else { + } + else { + // stash our road pointer + // + // XX do we need signal exclusion to safely mutate u3R? + // u3_road* rod_u = u3R; + while ( 1 ) { // // we can't perform this kind of butchery on the home road, // where asynchronous things can allocate. // + // XX anything other than u3t_samp? + // if ( u3R == &u3H->rod_u ) { break; } @@ -333,7 +342,14 @@ _sung_one(u3_noun* a, u3_noun* b) c3_o asr_o = u3a_is_senior(u3R, *a); c3_o bsr_o = u3a_is_senior(u3R, *b); - if ( _(asr_o) && _(bsr_o) ) { + // both [a] and [b] are in a senior road + // + // we can't switch to a senior road if GC is enabled, + // as we may be forced to (temporarily) leak a reference. + // + if ( (c3y == asr_o) && + (c3y == bsr_o) ) + { // // when unifying on a higher road, we can't free nouns, // because we can't track junior nouns that point into @@ -349,46 +365,70 @@ _sung_one(u3_noun* a, u3_noun* b) // itself, constantly running into duplicates. // #ifdef U3_MEMORY_DEBUG - return; + break; #else u3R = u3to(u3_road, u3R->par_p); continue; #endif } - - if ( _(asr_o) && !_(bsr_o) ){ - if ( u3R == rod_u ) { u3z(*b); } - *b = *a; - } - if ( _(bsr_o) && !_(asr_o) ) { - if ( u3R == rod_u ) { u3z(*a); } - *a = *b; - } - if ( u3a_is_north(u3R) ) { - if ( *a <= *b ) { - u3k(*a); - if ( u3R == rod_u ) { u3z(*b); } - *b = *a; - } else { - u3k(*b); - if ( u3R == rod_u ) { u3z(*a); } - *a = *b; - } - } else { - if ( *a >= *b ) { - u3k(*a); - if ( u3R == rod_u ) { u3z(*b); } + // if we're on our road, we can free + // + // it's safe to leak on inner roads; + // memory will be reclaimed when we return home. + // + // XX we should stash [b] on its road and lose it + // when return to that road. + // + c3_t loc_t = ( u3R == rod_u ); + + // keep [a]; it's senior + // + if ( (c3y == asr_o) && + (c3n == bsr_o) ) + { + if ( loc_t ) { u3z(*b); } *b = *a; - } else { - u3k(*b); - if ( u3R == rod_u ) { u3z(*a); } + } + // keep [b]; it's senior + // + else if ( (c3n == asr_o) && + (c3y == bsr_o) ) + { + if ( loc_t ) { u3z(*a); } *a = *b; } + // both are on the current road; gain a reference + // to whichever we keep + // + else { + // keep [a]; it's deeper in the heap + // + // (N && >) || (S && <) + // + if ( (*a > *b) == + (c3y == u3a_is_north(u3R)) ) + { + u3k(*a); + if ( loc_t ) { u3z(*b); } + *b = *a; + } + // keep [b]; it's deeper in the heap + // + else { + u3k(*b); + if ( loc_t ) { u3z(*a); } + *a = *b; + } + } } + break; } } + + // restore our road pointer + // u3R = rod_u; } }