Val _util_NetDB_mknetent (Task *task, struct netent* nentry) { //==================== // // Allocate a Mythryl value of type // Null_Or( (String, List(String), Addr_Family, Sysword) ) // to represent a struct netent value. if (nentry == NULL) return OPTION_NULL; // Build the return result: // If our agegroup0 buffer is more than half full, // empty it by doing a heapcleaning. This is very // conservative -- which is the way I like it. :-) // if (agegroup0_freespace_in_bytes( task ) < agegroup0_usedspace_in_bytes( task ) ){ call_heapcleaner( task, 0 ); } Val name = make_ascii_string_from_c_string__may_heapclean( task, nentry->n_name, NULL ); Roots roots1 = { &name, NULL }; Val aliases = make_ascii_strings_from_vector_of_c_strings__may_heapclean( task, nentry->n_aliases, &roots1 ); Roots roots2 = { &aliases, &roots1 }; Val af = make_system_constant__may_heapclean( task, &_Sock_AddrFamily, nentry->n_addrtype, &roots2 ); // Roots roots3 = { &af, &roots2 }; Val net = make_one_word_unt( task, (Vunt) (nentry->n_net) ); // Roots roots4 = { &net, &roots3 }; Val result = make_four_slot_record( task, name, aliases, af, net ); return OPTION_THE( task, result ); }
Val _util_NetDB_mkservent (Task* task, struct servent* sentry) { //===================== // // Mythryl type: // // Allocate an Lib7 value of type: // Null_Or( (String, List(String), Int, String) ) // to represent a struct servent value. Note that the port number is returned // in network byteorder, so we need to map it to host order. if (sentry == NULL) return OPTION_NULL; // If our agegroup0 buffer is more than half full, // empty it by doing a heapcleaning. This is very // conservative -- which is the way I like it. :-) // if (agegroup0_freespace_in_bytes( task ) < agegroup0_usedspace_in_bytes( task ) ){ call_heapcleaner( task, 0 ); } // Build the return result: Val name = make_ascii_string_from_c_string__may_heapclean( task, sentry->s_name, NULL ); Roots roots1 = { &name, NULL }; Val aliases = make_ascii_strings_from_vector_of_c_strings__may_heapclean( task, sentry->s_aliases, &roots1); Roots roots2 = { &aliases, &roots1 }; Val proto = make_ascii_string_from_c_string__may_heapclean( task, sentry->s_proto, &roots2); // Roots roots3 = { &proto, &roots2 }; Val port = TAGGED_INT_FROM_C_INT( ntohs(sentry->s_port) ); Val result = make_four_slot_record(task, name, aliases, port, proto); return OPTION_THE( task, result ); }
static Val read_in_compiled_file_list__may_heapclean ( // ========================================= // Task* task, const char* compiled_files_to_load_filename, int* return_max_boot_path_len, Roots* extra_roots ){ // Open given file and read from it the list of // filenames of compiled_files to be later loaded. // Return them as a Mythryl list of Mythryl strings: #define BUF_LEN 1024 // "This should be plenty for two numbers." "640K should be enough for anyone." char buf[ BUF_LEN ]; // Val* file_names = NULL; char* name_buf = NULL; int max_num_boot_files = MAX_NUMBER_OF_BOOT_FILES; int max_boot_path_len = MAX_LENGTH_FOR_A_BOOTFILE_PATHNAME; int file_count = 0; FILE* list_fd = open_file( compiled_files_to_load_filename, FALSE ); fprintf ( stderr, " load-compiledfiles.c: Reading file %s\n", compiled_files_to_load_filename ); if (log_fd) { // fprintf ( log_fd, " load-compiledfiles.c: Reading file %s\n", compiled_files_to_load_filename ); } Val file_list = LIST_NIL; Roots roots1 = { &file_list, extra_roots }; if (list_fd) { // Read header: // for (;;) { // if (!fgets (buf, BUF_LEN, list_fd)) { die ( "compiled_files_to_load file \"%s\" ends before end-of-header (first empty line)", compiled_files_to_load_filename ); } { char* p = buf; while (*p == ' ' || *p == '\t') ++p; // Skip leading whitespace. if (p[0] == '\n') break; // Header ends at first empty line. if (p[0] == '#') continue; // Ignore comment lines. if (strstr( p,"FILES=") == p) { // max_num_boot_files = strtoul(p+6, NULL, 0); continue; } if (strstr(p,"MAX_LINE_LENGTH=") == p) { // max_boot_path_len = strtoul(p+16, NULL, 0) +2; continue; } die ( "compiled_files_to_load file \"%s\" contains unrecognized header line \"%s\"", compiled_files_to_load_filename, p ); } } if (max_num_boot_files < 0) { // die("compiled_files_to_load file \"%s\" contains negative files count?! (%d)", compiled_files_to_load_filename, max_num_boot_files ); } if (max_boot_path_len < 0) { // die("compiled_file_to_load file \"%s\" contains negative boot path len?! (%d)", compiled_files_to_load_filename, max_boot_path_len ); } *return_max_boot_path_len = max_boot_path_len; // Tell the calling function. if (!(name_buf = MALLOC( max_boot_path_len ))) { // die ("unable to allocate space for .compiled file filenames"); } // if (!(file_names = MALLOC( max_num_boot_files * sizeof(char*) ))) { // // // die ("Unable to allocate space for compiledfiles-to-load name table"); // } // Read in the file names, converting them to // Mythryl strings and saving them in a list: // while (fgets( name_buf, max_boot_path_len, list_fd )) { // Skip leading whitespace: // char* p = name_buf; while (*p == ' ' || *p == '\t') ++p; // Ignore empty lines and comment lines: // if (*p == '\n') continue; if (*p == '#') continue; // Strip any trailing newline: // { int j = strlen(p)-1; // if (p[j] == '\n') p[j] = '\0'; } if (file_count >= max_num_boot_files) die ("too many files\n"); // If our agegroup0 buffer is more than half full, // empty it by doing a heapcleaning. This is very // conservative -- which is the way I like it. *grin* // if (agegroup0_freespace_in_bytes( task ) < agegroup0_usedspace_in_bytes( task ) ){ call_heapcleaner_with_extra_roots( task, 0, &roots1 ); } Val file_name = make_ascii_string_from_c_string__may_heapclean(task, p, &roots1 ); file_list = LIST_CONS(task, file_name, file_list); } if (name_buf) FREE( name_buf ); fclose( list_fd ); } // Reverse filename list (to restore // original order) and return it: // { Val file_list2 = LIST_NIL; Roots roots2 = { &file_list2, &roots1 }; // for (; file_list != LIST_NIL; file_list = LIST_TAIL(file_list)) { // Val file_name = LIST_HEAD(file_list); // file_list2 = LIST_CONS(task, file_name, file_list2); // Again, if our agegroup0 buffer is more than // half full, empty it by doing a heapcleaning: // if (agegroup0_freespace_in_bytes( task ) < agegroup0_usedspace_in_bytes( task ) ){ call_heapcleaner_with_extra_roots( task, 0, &roots2 ); } } return file_list2; } }
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