This fixes various of deadlocks and race conditions in gc_start() when called from object allocator:
(1) Don't deadlock during early bootstrap when there are no running threads. (2) Use vm_nr_threads_running() that accurately keeps track of running threads so that we don't deadlock on a thread that waits on a monitor. (3) Make gc_start() re-entrant while waiting for threads to arrive at a safepoint by turning gc_start() into a safepoint. (4) Make sure all threads exit their safepoint before letting a thread stop the world. Cc: Vegard Nossum <vegard.nos...@gmail.com> Signed-off-by: Pekka Enberg <penb...@cs.helsinki.fi> --- vm/gc.c | 118 +++++++++++++++++++++++++++++++++++++++++++------------------- 1 files changed, 82 insertions(+), 36 deletions(-) diff --git a/vm/gc.c b/vm/gc.c index 949a6c9..7ab37b7 100644 --- a/vm/gc.c +++ b/vm/gc.c @@ -4,6 +4,7 @@ #include <pthread.h> #include "lib/guard-page.h" +#include "vm/thread.h" #include "vm/die.h" #include "vm/gc.h" @@ -14,11 +15,11 @@ static pthread_mutex_t safepoint_mutex = PTHREAD_MUTEX_INITIALIZER; /* Protected by safepoint_mutex */ static pthread_cond_t everyone_in_cond = PTHREAD_COND_INITIALIZER; static pthread_cond_t everyone_out_cond = PTHREAD_COND_INITIALIZER; -static unsigned int nr_threads = 0; -static unsigned int nr_in_safepoint = 0; +static unsigned int nr_exiting_safepoint; +static unsigned int nr_in_safepoint; static pthread_cond_t can_continue_cond = PTHREAD_COND_INITIALIZER; -static bool can_continue; +static bool can_continue = true; void gc_init(void) { @@ -29,18 +30,10 @@ void gc_init(void) void gc_attach_thread(void) { - pthread_mutex_lock(&safepoint_mutex); - ++nr_threads; - pthread_mutex_unlock(&safepoint_mutex); } void gc_detach_thread(void) { - assert(nr_threads > 0); - - pthread_mutex_lock(&safepoint_mutex); - --nr_threads; - pthread_mutex_unlock(&safepoint_mutex); } static void hide_safepoint_guard_page(void) @@ -53,48 +46,101 @@ static void unhide_safepoint_guard_page(void) unhide_guard_page(gc_safepoint_page); } -void gc_start(void) +static void do_gc_reclaim(void) { - pthread_mutex_lock(&safepoint_mutex); + /* TODO: Do main GC work here. */ +} - assert(nr_in_safepoint == 0); +/* Callers must hold safepoint_mutex */ +static void gc_safepoint_exit(void) +{ + assert(nr_exiting_safepoint > 0); + + /* + * Don't let threads stop the world until everyone is out of their + * safepoint. + */ + if (--nr_exiting_safepoint == 0) + pthread_cond_broadcast(&everyone_out_cond); +} - can_continue = false; - hide_safepoint_guard_page(); +/* Callers must hold safepoint_mutex */ +static void do_gc_safepoint(void) +{ + /* Only the GC thread will be waiting for this. */ + if (++nr_in_safepoint == vm_nr_threads_running()) + pthread_cond_signal(&everyone_in_cond); - while (nr_in_safepoint != nr_threads) - pthread_cond_wait(&everyone_in_cond, &safepoint_mutex); + /* Block until GC has finished */ + while (!can_continue) + pthread_cond_wait(&can_continue_cond, &safepoint_mutex); - /* At this point, we know that everyone is in the safepoint. */ - unhide_safepoint_guard_page(); + assert(nr_in_safepoint > 0); + --nr_in_safepoint; +} - /* TODO: Do main GC work here. */ +void gc_safepoint(void) +{ + pthread_mutex_lock(&safepoint_mutex); - /* Resume other threads */ - can_continue = true; - pthread_cond_broadcast(&can_continue_cond); + do_gc_safepoint(); - while (nr_in_safepoint != 0) - pthread_cond_wait(&everyone_out_cond, &safepoint_mutex); + gc_safepoint_exit(); pthread_mutex_unlock(&safepoint_mutex); } -void gc_safepoint(void) +/* + * This is the main entrypoint to the stop-the-world GC. + */ +void gc_start(void) { pthread_mutex_lock(&safepoint_mutex); - /* Only the GC thread will be waiting for this. */ - if (++nr_in_safepoint == nr_threads) - pthread_cond_signal(&everyone_in_cond); + /* Don't deadlock during early boostrap. */ + if (vm_nr_threads_running() == 0) + goto out_unlock; - /* Block until GC has finished */ - while (!can_continue) - pthread_cond_wait(&can_continue_cond, &safepoint_mutex); + /* + * Wait until all threads have exited their safepoint before stopping + * the world again. + */ + while (nr_exiting_safepoint > 0) + pthread_cond_wait(&everyone_out_cond, &safepoint_mutex); - /* Only the GC thread will be waiting for this. */ - if (--nr_in_safepoint == 0) - pthread_cond_signal(&everyone_out_cond); + /* + * If someone stopped the world before us, put the current thread in a + * safepoint. + */ + if (nr_in_safepoint != 0) { + do_gc_safepoint(); + goto out_exit_safepoint; + } + + /* Only one thread can stop the world at a time. */ + assert(can_continue); + + ++nr_in_safepoint; + can_continue = false; + hide_safepoint_guard_page(); + + /* Wait for all other threads to enter a safepoint. */ + while (nr_in_safepoint != vm_nr_threads_running()) + pthread_cond_wait(&everyone_in_cond, &safepoint_mutex); + + /* At this point, we know that everyone is in the safepoint. */ + unhide_safepoint_guard_page(); + + do_gc_reclaim(); + + /* Resume other threads */ + assert(nr_in_safepoint > 0); + nr_exiting_safepoint = nr_in_safepoint--; + can_continue = true; + pthread_cond_broadcast(&can_continue_cond); +out_exit_safepoint: + gc_safepoint_exit(); +out_unlock: pthread_mutex_unlock(&safepoint_mutex); } -- 1.5.6.3 ------------------------------------------------------------------------------ Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july _______________________________________________ Jatovm-devel mailing list Jatovm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jatovm-devel