Val _lib7_Sock_to_log (Task* task, Val arg) { //=============================================== // // Mythryl type: String -> Void // // Write string to currently open logfile via log_if from // // src/c/main/error-reporting.c ENTER_MYTHRYL_CALLABLE_C_FN(__func__); char* string = HEAP_STRING_AS_C_STRING( arg ); log_if ("%s", string); // Safer than doing just log_if(string) -- the string might have a '%' in it. EXIT_MYTHRYL_CALLABLE_C_FN(__func__); return HEAP_VOID; }
static void c_signal_handler (int sig, siginfo_t* si, void* c) { // ================ // // This is the C signal handler for // signals that are to be passed to // the Mythryl level via signal_handler in // // src/lib/std/src/nj/runtime-signals-guts.pkg // ucontext_t* scp /* This variable is unused on some platforms, so suppress 'unused var' compiler warning: */ __attribute__((unused)) = (ucontext_t*) c; Pthread* pthread = SELF_PTHREAD; // Sanity check: We compile in a MAX_POSIX_SIGNALS value but // have no way to ensure that we don't wind up getting run // on some custom kernel supporting more than MAX_POSIX_SIGNAL, // so we check here to be safe: // if (sig >= MAX_POSIX_SIGNALS) die ("posix-signal.c: c_signal_handler: sig d=%d >= MAX_POSIX_SIGNAL %d\n", sig, MAX_POSIX_SIGNALS ); // Remember that we have seen signal number 'sig'. // // This will eventually get noticed by choose_signal() in // // src/c/machine-dependent/signal-stuff.c // pthread->posix_signal_counts[sig].seen_count++; pthread->all_posix_signals.seen_count++; log_if( "posix-signal.c/c_signal_handler: signal d=%d seen_count d=%d done_count d=%d diff d=%d", sig, pthread->posix_signal_counts[sig].seen_count, pthread->posix_signal_counts[sig].done_count, pthread->posix_signal_counts[sig].seen_count - pthread->posix_signal_counts[sig].done_count ); #ifdef SIGNAL_DEBUG debug_say ("c_signal_handler: sig = %d, pending = %d, inHandler = %d\n", sig, pthread->posix_signal_pending, pthread->mythryl_handler_for_posix_signal_is_running); #endif // The following line is needed only when // currently executing "pure" C code, but // doing it anyway in all other cases will // not hurt: // pthread->ccall_limit_pointer_mask = 0; if ( pthread->executing_mythryl_code && ! pthread->posix_signal_pending && ! pthread->mythryl_handler_for_posix_signal_is_running ){ pthread->posix_signal_pending = TRUE; #ifdef USE_ZERO_LIMIT_PTR_FN // SIG_SavePC( pthread->task, scp ); SET_SIGNAL_PROGRAM_COUNTER( scp, Zero_Heap_Allocation_Limit ); #else SIG_Zero_Heap_Allocation_Limit( scp ); // OK to adjust the heap limit directly. #endif } }
void heapclean_agegroup0 (Task* task, Val** roots) { // =================== // // Do "garbage collection" on just agegroup0. // // 'roots' is a vector of live pointers into agegroup0, // harvested from the live register set, global variables // into the heap maintained by C code, etc. // // This fun is called (only) from: // // src/c/heapcleaner/call-heapcleaner.c // // NB: If we have multiple hostthreads running, // each has its own agegroup0, but we process // all of those during this call, by virtue // of being passed all the roots from all the // running hostthreads. Heap* heap = task->heap; Agegroup* age1 = heap->agegroup[0]; Vunt age1_tospace_top [ MAX_PLAIN_SIBS ]; // // Heapcleaner statistics support: We use this to note the // current start-of-freespace in each generation-one sib buffer. // At the bottom of this fn, the difference between this and // the new start-of-freespace gives us the amount of live stuff // we've copied into that sib. This is pure reportage; // our algorithms do not depend in any way on this information. long bytes_allocated = agegroup0_usedspace_in_bytes( task ); // INCREASE_BIGCOUNTER( &heap->total_bytes_allocated, bytes_allocated ); // // More heapcleaner statistics reportage. for (int i = 0; i < MAX_PLAIN_SIBS; i++) { // age1_tospace_top[i] = (Vunt) age1->sib[ i ]->tospace.first_free; } static int callcount = 0; ++callcount; #ifdef VERBOSE if (! (callcount & 0xf)) { // log_if ("Agegroup 1 before cleaning agegroup0: (call %d) -- heapclean-agegroup0.c", callcount); // for (int i = 0; i < MAX_PLAIN_SIBS; i++) { // log_if(" %s: to-space bottom = %#x, end of fromspace oldstuff = %#x, tospace.first_free = %#x", // sib_name__global[ i+1 ], // age1->sib[ i ]->tospace, age1->sib[ i ]->fromspace.seniorchunks_end, age1->sib[ i ]->tospace.first_free ); } } #endif // Scan the standard roots. These are pointers // to live data harvested from the live registers, // C globals etc, so all agegroup0 records pointed // to by them are definitely "live" (nongarbage): // { Sibid* b2s = book_to_sibid__global; // Cache global locally for speed. book_to_sibid__global def in src/c/heapcleaner/heapcleaner-initialization.c Val* rp; while ((rp = *roots++) != NULL) { // forward_to_agegroup1_if_in_agegroup0( b2s, age1, rp, task ); } } // The changelog records all writes to refcells or rw_vectors containing pointer data, // because such writes might introduce cross-generation pointers that we need to know // Scan the changelog -- if there are any new // about when doing partial heapcleanings. This makes each such write cost one CONS cell. // pointers into agegroup0 from other agegroups // // we need to know about them now: // Updates to tagged-integer values refcells or rw_vectors cannot introduce such pointers, // // so we do not track them in the changelog and they suffer no slowdown. // { for (int i = 0; i < MAX_HOSTTHREADS; i++) { // Potentially need to process one heap storelog per hostthread. // Hostthread* hostthread = hostthread_table__global[ i ]; // Task* task = hostthread->task; // if (hostthread->mode != HOSTTHREAD_IS_VOID) { // process_task_heap_changelog( task, heap ); } } } copy_all_remaining_reachable_values_in_agegroup0_to_agegroup1( age1, task ); ++heap->agegroup0_heapcleanings_count; null_out_newly_dead_weakrefs( heap ); // null_out_newly_dead_weakrefs def in src/c/heapcleaner/heapcleaner-stuff.c //////////////////////////////////////////////////////////// // At this point there is nothing left in the agegroup0 // buffer(s) that we care about, so we're done. Our caller // will reset the agegroup0 buffer(s) to empty and resume // allocating linearly in it/them from start to end. //////////////////////////////////////////////////////////// #ifdef VERBOSE if (! (callcount & 0xf)) { log_if ("Agegroup 1 after minorgc: (call %d) -- heapclean-agegroup0.c", callcount); for (int i = 0; i < MAX_PLAIN_SIBS; i++) { log_if (" %s: base = %#x, oldTop = %%#x, tospace.first_free = %#x", sib_name__global[i+1], age1->sib[i]->tospace, /* age1->sib[i]->oldTop, */ age1->sib[i]->tospace.first_free); } } #endif // Cleaner statistics stuff: { long bytes_copied = 0; for (int i = 0; i < MAX_PLAIN_SIBS; i++) { // int bytes = (Vunt) age1->sib[ i ]->tospace.first_free - age1_tospace_top[ i ]; bytes_copied += bytes; INCREASE_BIGCOUNTER( &heap->total_bytes_copied_to_sib[ 0 ][ i ], bytes ); } total_bytes_allocated__global += bytes_allocated; // Never used otherwise. total_bytes_copied__global += bytes_copied; // Never used otherwise. #ifndef VERBOSE if (! (callcount & 0xff)) { log_if ("DONE minorgc #%d: %d/%d (%5.2f%%) bytes copied; %%d updates (callcount %d) -- heapclean-agegroup0.c", callcount, bytes_copied, bytes_allocated, (bytes_allocated ? (double)(100*bytes_copied)/(double)bytes_allocated : 0.0) /* update_count__global - nUpdates */); } #endif } #ifdef CHECK_HEAP check_heap( heap, 1 ); // check_heap def in src/c/heapcleaner/check-heap.c #endif } // fun heapclean_agegroup0
static Val forward_agegroup0_chunk_to_agegroup1 (Agegroup* ag1, Val v, Task* task, int caller) { // 'task' arg is only for debugging, can be removed in production use. // ===================================== // // Forward pair/record/vector/string 'v' from agegroup0 to agegroup 1. // This involves: // // o Duplicating v in the appropriate agegroup 1 to-space buffer. // o Setting v's tagword to FORWARDED_CHUNK_TAGWORD. // o Setting v's first slot to point to the duplicate. // o Returning a pointer to the duplicate. Val* new_chunk; Vunt len_in_words; Sib* sib; Val* chunk = PTR_CAST(Val*, v); Val tagword = chunk[-1]; switch (GET_BTAG_FROM_TAGWORD( tagword )) { // case PAIRS_AND_RECORDS_BTAG: // len_in_words = GET_LENGTH_IN_WORDS_FROM_TAGWORD( tagword ); #ifdef NO_PAIR_STRIP // 'NO_PAIR_STRIP' appears nowhere else in the codebase. sib = ag1->sib[RO_POINTERS_SIB]; #else if (len_in_words != 2) { sib = ag1->sib[ RO_POINTERS_SIB ]; // This v is not a pair, so fall through to default code. } else { // // This v is a pair, so we'll use special-case code. sib = ag1->sib[ RO_CONSCELL_SIB ]; // We'll copy it into the dedicated pairs-only sib in agegroup1. new_chunk = sib->tospace.first_free; // Where to copy it in that sib. sib->tospace.first_free += 2; // Allocate the space for it. new_chunk[0] = chunk[0]; // Copy first word of pair. new_chunk[1] = chunk[1]; // Copy second word of pair. // Notice that we don't need to copy the tagword -- it is implicit in the fact that we're in the pairsib. // Set up the forward pointer in the old pair: // chunk[-1] = FORWARDED_CHUNK_TAGWORD; chunk[0] = (Val)(Vunt)new_chunk; return PTR_CAST( Val, new_chunk ); // Done! } #endif break; case RO_VECTOR_HEADER_BTAG: case RW_VECTOR_HEADER_BTAG: // len_in_words = 2; // sib = ag1->sib[ RO_POINTERS_SIB ]; break; // Fall through to generic-case code. case RW_VECTOR_DATA_BTAG: // len_in_words = GET_LENGTH_IN_WORDS_FROM_TAGWORD( tagword ); // sib = ag1->sib[ RW_POINTERS_SIB ]; // The RW_POINTERS_SIB allows updates, which the RO_POINTERS_SIB does not. break; // Fall through to generic-case code. case FOUR_BYTE_ALIGNED_NONPOINTER_DATA_BTAG: // len_in_words = GET_LENGTH_IN_WORDS_FROM_TAGWORD( tagword ); // sib = ag1->sib[ NONPTR_DATA_SIB ]; break; // Fall through to generic-case code. case EIGHT_BYTE_ALIGNED_NONPOINTER_DATA_BTAG: // len_in_words = GET_LENGTH_IN_WORDS_FROM_TAGWORD( tagword ); // sib = ag1->sib[ NONPTR_DATA_SIB ]; // #ifdef ALIGN_FLOAT64S # ifdef CHECK_HEAP if (((Vunt)sib->tospace.first_free & WORD_BYTESIZE) == 0) { // *sib->tospace.first_free++ = (Val) 0; } # else sib->tospace.first_free = (Val*) (((Vunt)sib->tospace.first_free) | WORD_BYTESIZE); # endif #endif break; // Fall through to generic-case code. case WEAK_POINTER_OR_SUSPENSION_BTAG: // return forward_special_chunk ( ag1, chunk, tagword ); case FORWARDED_CHUNK_BTAG: // return PTR_CAST( Val, FOLLOW_FORWARDING_POINTER(chunk)); // We've already copied this one to agegroup1, so just return pointer to copy. default: log_if("bad chunk tag %d, chunk = %#x, tagword = %#x -- forward_agegroup0_chunk_to_agegroup1() in src/c/heapcleaner/heapclean-agegroup0.c", GET_BTAG_FROM_TAGWORD(tagword), chunk, tagword); log_if("forward_agegroup0_chunk_to_agegroup1 was called by %s", caller ? "process_task_heap_changelog" : "forward_to_agegroup1_if_in_agegroup0"); dump_task(task,"forward_agegroup0_chunk_to_agegroup1/default"); die ("bad chunk tag %d, chunk = %#x, tagword = %#x -- forward_agegroup0_chunk_to_agegroup1() in src/c/heapcleaner/heapclean-agegroup0.c", GET_BTAG_FROM_TAGWORD(tagword), chunk, tagword); exit(1); // Cannot execute -- just to quiet gcc -Wall. } // Make an agegroup1 copy of the chunk // in the appropriate agegroup1 sib (buffer): // new_chunk = sib->tospace.first_free; // Figure out where copy will be located. sib->tospace.first_free += (len_in_words + 1); // Allocate space needed by the copy. *new_chunk++ = tagword; // Install tagword at start of copy. (Note that 'sib' cannot be RO_CONSCELL_SIB, we handled that case above.) ASSERT( sib->tospace.first_free <= sib->tospace.limit ); COPYLOOP(chunk, new_chunk, len_in_words); // Copy over the rest of the chunk. // COPYLOOP def in src/c/heapcleaner/copy-loop.h // Set up the forwarding pointer in the original // to the copy and return the new chunk: // chunk[-1] = FORWARDED_CHUNK_TAGWORD; chunk[ 0] = (Val) (Vunt) new_chunk; return PTR_CAST( Val, new_chunk ); } // fun forward_agegroup0_chunk_to_agegroup1