This patch fixes various problems with safepoints. (1) We must increase ->nr_in_safepoint from gc_start(); otherwise we'll deadlock as the tread that called gc_start() will never enter gc_safepoint().
(2) Don't let threads enter gc_start() until we've returned from gc_start() for a previous safepoint. (3) Let only one thread wait in gc_start() when it's called concurrently. Only the first thread will wait and others will be be treated just as if they had entered gc_safepoint(). (4) Fix gc_start() when called during early bootstrap code when no threads are started. With the above fixes, GcTortureTest passes when gc_start() is called from object allocator. Cc: Vegard Nossum <vegard.nos...@gmail.com> Signed-off-by: Pekka Enberg <penb...@cs.helsinki.fi> --- vm/gc.c | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 files changed, 114 insertions(+), 22 deletions(-) diff --git a/vm/gc.c b/vm/gc.c index 949a6c9..921add3 100644 --- a/vm/gc.c +++ b/vm/gc.c @@ -20,6 +20,14 @@ static unsigned int nr_in_safepoint = 0; static pthread_cond_t can_continue_cond = PTHREAD_COND_INITIALIZER; static bool can_continue; +static pthread_cond_t can_attach_cond = PTHREAD_COND_INITIALIZER; +static bool can_attach = true; + +static pthread_barrier_t safepoint_barrier; + +bool gc_enabled; +bool verbose_gc; + void gc_init(void) { gc_safepoint_page = alloc_guard_page(false); @@ -30,7 +38,24 @@ void gc_init(void) void gc_attach_thread(void) { pthread_mutex_lock(&safepoint_mutex); + + /* Block until GC has finished */ + while (!can_attach) + pthread_cond_wait(&can_attach_cond, &safepoint_mutex); + + if (nr_in_safepoint != 0) + die("threads in safepoint"); + + if (nr_threads > 0) { + if (pthread_barrier_destroy(&safepoint_barrier) != 0) + die("pthread_barrier_destroy"); + } + ++nr_threads; + + if (pthread_barrier_init(&safepoint_barrier, NULL, nr_threads) < 0) + die("pthread_barrier_init"); + pthread_mutex_unlock(&safepoint_mutex); } @@ -39,10 +64,46 @@ void gc_detach_thread(void) assert(nr_threads > 0); pthread_mutex_lock(&safepoint_mutex); + + if (nr_in_safepoint != 0) + die("threads in safepoint"); + + if (nr_threads > 0) { + if (pthread_barrier_destroy(&safepoint_barrier) != 0) + die("pthread_barrier_destroy"); + } + --nr_threads; + + if (pthread_barrier_init(&safepoint_barrier, NULL, nr_threads) < 0) + die("pthread_barrier_init"); + pthread_mutex_unlock(&safepoint_mutex); } +static void gc_safepoint_barrier_wait(void) +{ + int err; + + /* + * Ensure nobody enters a new safepoint until everyone is out of the + * current one. + */ + err = pthread_barrier_wait(&safepoint_barrier); + switch (err) { + case PTHREAD_BARRIER_SERIAL_THREAD: { + pthread_mutex_lock(&safepoint_mutex); + can_attach = true; + pthread_cond_broadcast(&can_attach_cond); + pthread_mutex_unlock(&safepoint_mutex); + } + case 0: + break; + default: + die("pthread_barrier_wait"); + } +} + static void hide_safepoint_guard_page(void) { hide_guard_page(gc_safepoint_page); @@ -53,12 +114,59 @@ static void unhide_safepoint_guard_page(void) unhide_guard_page(gc_safepoint_page); } -void gc_start(void) +static void do_gc_reclaim(void) +{ + /* TODO: Do main GC work here. */ +} + +/* Callers must hold safepoint_mutex */ +static void do_gc_safepoint(void) +{ + /* Only the GC thread will be waiting for this. */ + if (++nr_in_safepoint == nr_threads) + pthread_cond_signal(&everyone_in_cond); + + /* Block until GC has finished */ + while (!can_continue) + pthread_cond_wait(&can_continue_cond, &safepoint_mutex); + + /* Only the GC thread will be waiting for this. */ + if (--nr_in_safepoint == 0) + pthread_cond_signal(&everyone_out_cond); +} + +void gc_safepoint(void) { pthread_mutex_lock(&safepoint_mutex); - assert(nr_in_safepoint == 0); + do_gc_safepoint(); + + pthread_mutex_unlock(&safepoint_mutex); + + gc_safepoint_barrier_wait(); +} +void gc_start(void) +{ + pthread_mutex_lock(&safepoint_mutex); + + /* There are no threads running during early bootstrap. */ + if (nr_threads == 0) { + pthread_mutex_unlock(&safepoint_mutex); + return; + } + + /* + * If someone entered the reclaim path before us, make the current + * thread enter a regular safepoint. + */ + if (nr_in_safepoint != 0) { + do_gc_safepoint(); + goto out; + } + + ++nr_in_safepoint; + can_attach = false; can_continue = false; hide_safepoint_guard_page(); @@ -68,33 +176,17 @@ void gc_start(void) /* At this point, we know that everyone is in the safepoint. */ unhide_safepoint_guard_page(); - /* TODO: Do main GC work here. */ + do_gc_reclaim(); /* Resume other threads */ + --nr_in_safepoint; can_continue = true; pthread_cond_broadcast(&can_continue_cond); while (nr_in_safepoint != 0) pthread_cond_wait(&everyone_out_cond, &safepoint_mutex); - +out: pthread_mutex_unlock(&safepoint_mutex); -} -void gc_safepoint(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); - - /* Block until GC has finished */ - while (!can_continue) - pthread_cond_wait(&can_continue_cond, &safepoint_mutex); - - /* Only the GC thread will be waiting for this. */ - if (--nr_in_safepoint == 0) - pthread_cond_signal(&everyone_out_cond); - - pthread_mutex_unlock(&safepoint_mutex); + gc_safepoint_barrier_wait(); } -- 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