/* For debugging: */ void GC_print_static_roots() { register int i; size_t total = 0; for (i = 0; i < n_root_sets; i++) { GC_printf2("From 0x%lx to 0x%lx ", (unsigned long) GC_static_roots[i].r_start, (unsigned long) GC_static_roots[i].r_end); if (GC_static_roots[i].r_tmp) { GC_printf0(" (temporary)\n"); } else { GC_printf0("\n"); } total += GC_static_roots[i].r_end - GC_static_roots[i].r_start; } GC_printf1("Total size: %ld\n", (unsigned long) total); if (GC_root_size != total) { GC_printf1("GC_root_size incorrect: %ld!!\n", (unsigned long) GC_root_size); } }
void GC_print_sig_mask() { sigset_t blocked; int i; if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0) ABORT("pthread_sigmask"); GC_printf0("Blocked: "); for (i = 1; i <= MAXSIG; i++) { if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); } } GC_printf0("\n"); }
static void start_mark_threads() { unsigned i; pthread_attr_t attr; if (GC_markers > MAX_MARKERS) { WARN("Limiting number of mark threads\n", 0); GC_markers = MAX_MARKERS; } if (0 != pthread_attr_init(&attr)) ABORT("pthread_attr_init failed"); if (0 != pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) ABORT("pthread_attr_setdetachstate failed"); # if defined(HPUX) || defined(GC_DGUX386_THREADS) /* Default stack size is usually too small: fix it. */ /* Otherwise marker threads or GC may run out of */ /* space. */ # define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word)) { size_t old_size; int code; if (pthread_attr_getstacksize(&attr, &old_size) != 0) ABORT("pthread_attr_getstacksize failed\n"); if (old_size < MIN_STACK_SIZE) { if (pthread_attr_setstacksize(&attr, MIN_STACK_SIZE) != 0) ABORT("pthread_attr_setstacksize failed\n"); } } # endif /* HPUX || GC_DGUX386_THREADS */ # ifdef CONDPRINT if (GC_print_stats) { GC_printf1("Starting %ld marker threads\n", GC_markers - 1); } # endif for (i = 0; i < GC_markers - 1; ++i) { if (0 != PTHREAD_CREATE(GC_mark_threads + i, &attr, GC_mark_thread, (void *)(word)i)) { WARN("Marker thread creation failed, errno = %ld.\n", errno); } } }
void * GC_mark_thread(void * id) { word my_mark_no = 0; marker_sp[(word)id] = GC_approx_sp(); for (;; ++my_mark_no) { /* GC_mark_no is passed only to allow GC_help_marker to terminate */ /* promptly. This is important if it were called from the signal */ /* handler or from the GC lock acquisition code. Under Linux, it's */ /* not safe to call it from a signal handler, since it uses mutexes */ /* and condition variables. Since it is called only here, the */ /* argument is unnecessary. */ if (my_mark_no < GC_mark_no || my_mark_no > GC_mark_no + 2) { /* resynchronize if we get far off, e.g. because GC_mark_no */ /* wrapped. */ my_mark_no = GC_mark_no; } # ifdef DEBUG_THREADS GC_printf1("Starting mark helper for mark number %ld\n", my_mark_no); # endif GC_help_marker(my_mark_no); } }
/* * This may be called from DllMain, and hence operates under unusual * constraints. */ static GC_thread GC_new_thread(void) { int i; /* It appears to be unsafe to acquire a lock here, since this */ /* code is apparently not preeemptible on some systems. */ /* (This is based on complaints, not on Microsoft's official */ /* documentation, which says this should perform "only simple */ /* initialization tasks".) */ /* Hence we make do with nonblocking synchronization. */ /* The following should be a noop according to the win32 */ /* documentation. There is empirical evidence that it */ /* isn't. - HB */ # if defined(MPROTECT_VDB) if (GC_incremental) SetUnhandledExceptionFilter(GC_write_fault_handler); # endif /* cast away volatile qualifier */ for (i = 0; InterlockedExchange((IE_t)&thread_table[i].in_use,1) != 0; i++) { /* Compare-and-swap would make this cleaner, but that's not */ /* supported before Windows 98 and NT 4.0. In Windows 2000, */ /* InterlockedExchange is supposed to be replaced by */ /* InterlockedExchangePointer, but that's not really what I */ /* want here. */ if (i == MAX_THREADS - 1) ABORT("too many threads"); } /* Update GC_max_thread_index if necessary. The following is safe, */ /* and unlike CompareExchange-based solutions seems to work on all */ /* Windows95 and later platforms. */ /* Unfortunately, GC_max_thread_index may be temporarily out of */ /* bounds, so readers have to compensate. */ while (i > GC_max_thread_index) { InterlockedIncrement((IE_t)&GC_max_thread_index); } if (GC_max_thread_index >= MAX_THREADS) { /* We overshot due to simultaneous increments. */ /* Setting it to MAX_THREADS-1 is always safe. */ GC_max_thread_index = MAX_THREADS - 1; } # ifdef CYGWIN32 thread_table[i].pthread_id = pthread_self(); # endif if (!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), (HANDLE*)&thread_table[i].handle, 0, 0, DUPLICATE_SAME_ACCESS)) { DWORD last_error = GetLastError(); GC_printf1("Last error code: %lx\n", last_error); ABORT("DuplicateHandle failed"); } thread_table[i].stack_base = GC_get_stack_base(); /* Up until this point, GC_push_all_stacks considers this thread */ /* invalid. */ if (thread_table[i].stack_base == NULL) ABORT("Failed to find stack base in GC_new_thread"); /* Up until this point, this entry is viewed as reserved but invalid */ /* by GC_delete_thread. */ thread_table[i].id = GetCurrentThreadId(); /* If this thread is being created while we are trying to stop */ /* the world, wait here. Hopefully this can't happen on any */ /* systems that don't allow us to block here. */ while (GC_please_stop) Sleep(20); GC_ASSERT(!thread_table[i]->thread_blocked); return thread_table + i; }
/* make it cheaper. */ GC_API void GC_register_finalizer_inner(void * obj, GC_finalization_proc fn, void *cd, GC_finalization_proc *ofn, void **ocd, finalization_mark_proc mp, /* PLTSCHEME: eager_level */ int eager_level) { ptr_t base; struct finalizable_object * curr_fo, * prev_fo; size_t index; struct finalizable_object *new_fo; hdr *hhdr; DCL_LOCK_STATE; # ifdef THREADS LOCK(); # endif if (log_fo_table_size == -1 || GC_fo_entries > ((word)1 << log_fo_table_size)) { GC_grow_table((struct hash_chain_entry ***)(&fo_head), &log_fo_table_size); # ifdef PRINTSTATS GC_printf1("Grew fo table to %lu entries\n", (unsigned long)(1 << log_fo_table_size)); # endif } /* in the THREADS case signals are disabled and we hold allocation */ /* lock; otherwise neither is true. Proceed carefully. */ base = (ptr_t)obj; index = HASH2(base, log_fo_table_size); prev_fo = 0; curr_fo = fo_head[index]; while (curr_fo != 0) { GC_ASSERT(GC_size(curr_fo) >= sizeof(struct finalizable_object)); if (curr_fo -> fo_hidden_base == HIDE_POINTER(base)) { /* Interruption by a signal in the middle of this */ /* should be safe. The client may see only *ocd */ /* updated, but we'll declare that to be his */ /* problem. */ if (ocd) *ocd = (void *) curr_fo -> fo_client_data; if (ofn) *ofn = curr_fo -> fo_fn; /* Delete the structure for base. */ if (prev_fo == 0) { fo_head[index] = fo_next(curr_fo); } else { fo_set_next(prev_fo, fo_next(curr_fo)); } if (fn == 0) { GC_fo_entries--; /* May not happen if we get a signal. But a high */ /* estimate will only make the table larger than */ /* necessary. */ # if !defined(THREADS) && !defined(DBG_HDRS_ALL) GC_free((void *)curr_fo); # endif } else { curr_fo -> fo_fn = fn; curr_fo -> fo_client_data = (ptr_t)cd; curr_fo -> fo_mark_proc = mp; curr_fo -> eager_level = eager_level; /* PLTSCHEME */ /* Reinsert it. We deleted it first to maintain */ /* consistency in the event of a signal. */ if (prev_fo == 0) { fo_head[index] = curr_fo; } else { fo_set_next(prev_fo, curr_fo); } } # ifdef THREADS UNLOCK(); # endif return; } prev_fo = curr_fo; curr_fo = fo_next(curr_fo); } if (ofn) *ofn = 0; if (ocd) *ocd = 0; if (fn == 0) { /* PLTSCHEME: */ /* If this item is already queued, de-queue it. */ #if 1 if (GC_finalize_now) { ptr_t real_ptr; struct finalizable_object * curr_fo, *prev_fo; prev_fo = NULL; curr_fo = GC_finalize_now; while (curr_fo != 0) { real_ptr = (ptr_t)(curr_fo -> fo_hidden_base); if (real_ptr == obj) { if (prev_fo) fo_set_next(prev_fo, fo_next(curr_fo)); else GC_finalize_now = fo_next(curr_fo); GC_free((void *)curr_fo); break; } prev_fo = curr_fo; curr_fo = fo_next(curr_fo); } } #endif # ifdef THREADS UNLOCK(); # endif return; } GET_HDR(base, hhdr); if (0 == hhdr) { /* We won't collect it, hence finalizer wouldn't be run. */ # ifdef THREADS UNLOCK(); # endif return; } new_fo = (struct finalizable_object *) GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL); if (EXPECT(0 == new_fo, FALSE)) { # ifdef THREADS UNLOCK(); # endif new_fo = (struct finalizable_object *) GC_oom_fn(sizeof(struct finalizable_object)); if (0 == new_fo) { GC_finalization_failures++; return; } /* It's not likely we'll make it here, but ... */ # ifdef THREADS LOCK(); # endif } new_fo -> fo_hidden_base = (word)HIDE_POINTER(base); new_fo -> fo_fn = fn; new_fo -> fo_client_data = (ptr_t)cd; new_fo -> fo_object_size = hhdr -> hb_sz; new_fo -> fo_mark_proc = mp; new_fo -> eager_level = eager_level; /* PLTSCHEME */ fo_set_next(new_fo, fo_head[index]); GC_fo_entries++; fo_head[index] = new_fo; # ifdef THREADS UNLOCK(); # endif }