/* This is called when a thread hits an interrupt at a GC safe point. This means * that another thread is already trying to start a GC run, so we don't need to * try and do that, just enlist in the run. */ void MVM_gc_enter_from_interrupt(MVMThreadContext *tc) { MVMuint8 decr = 0; AO_t curr; tc->gc_work_count = 0; add_work(tc, tc); /* grab our child */ signal_child(tc); /* Count us in to the GC run. Wait for a vote to steal. */ GCORCH_LOG(tc, "Thread %d run %d : Entered from interrupt\n"); while ((curr = tc->instance->gc_start) < 2 || !MVM_trycas(&tc->instance->gc_start, curr, curr - 1)) { /* apr_sleep(1); apr_thread_yield();*/ } /* Wait for all threads to indicate readiness to collect. */ while (tc->instance->gc_start) { /* apr_sleep(1); apr_thread_yield();*/ } run_gc(tc, MVMGCWhatToDo_NoInstance); }
static void _sig_child() { pid_t pid; int status, errno_save = errno; while ((pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { // just ignore stop/continue signal if (WIFSTOPPED(status) || WIFCONTINUED(status)) continue; // called only when child process exit() normal, -2 : signal status = WIFEXITED(status) ? (char) WEXITSTATUS(status) : -2; signal_child(pid, status); } errno = errno_save; }
/* This is called when the allocator finds it has run out of memory and wants * to trigger a GC run. In this case, it's possible (probable, really) that it * will need to do that triggering, notifying other running threads that the * time has come to GC. */ void MVM_gc_enter_from_allocator(MVMThreadContext *tc) { GCORCH_LOG(tc, "Thread %d run %d : Entered from allocate\n"); /* Try to start the GC run. */ if (MVM_trycas(&tc->instance->gc_start, 0, 1)) { MVMThread *last_starter = NULL; MVMuint32 num_threads = 0; /* We are the winner of the GC starting race. This gives us some * extra responsibilities as well as doing the usual things. * First, increment GC sequence number. */ tc->instance->gc_seq_number++; GCORCH_LOG(tc, "Thread %d run %d : GC thread elected coordinator: starting gc seq %d\n", tc->instance->gc_seq_number); /* Ensure our stolen list is empty. */ tc->gc_work_count = 0; /* need to wait for other threads to reset their gc_status. */ while (tc->instance->gc_ack) apr_thread_yield(); add_work(tc, tc); /* grab our child */ signal_child(tc); do { if (tc->instance->threads && tc->instance->threads != last_starter) { MVMThread *head; MVMuint32 add; while (!MVM_trycas(&tc->instance->threads, (head = tc->instance->threads), NULL)); add = signal_all_but(tc, head, last_starter); last_starter = head; if (add) { GCORCH_LOG(tc, "Thread %d run %d : Found %d other threads\n", add); MVM_atomic_add(&tc->instance->gc_start, add); num_threads += add; } } } while (tc->instance->gc_start > 1); if (!MVM_trycas(&tc->instance->threads, NULL, last_starter)) MVM_panic(MVM_exitcode_gcorch, "threads list corrupted\n"); if (tc->instance->gc_finish != 0) MVM_panic(MVM_exitcode_gcorch, "finish votes was %d\n", tc->instance->gc_finish); tc->instance->gc_ack = tc->instance->gc_finish = num_threads + 1; GCORCH_LOG(tc, "Thread %d run %d : finish votes is %d\n", (int)tc->instance->gc_finish); /* signal to the rest to start */ if (MVM_atomic_decr(&tc->instance->gc_start) != 1) MVM_panic(MVM_exitcode_gcorch, "start votes was %d\n", tc->instance->gc_finish); run_gc(tc, MVMGCWhatToDo_All); } else { /* Another thread beat us to starting the GC sync process. Thus, act as * if we were interrupted to GC. */ GCORCH_LOG(tc, "Thread %d run %d : Lost coordinator election\n"); MVM_gc_enter_from_interrupt(tc); } }