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 <[email protected]>
Signed-off-by: Pekka Enberg <[email protected]>
---
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
[email protected]
https://lists.sourceforge.net/lists/listinfo/jatovm-devel