Garbage collection is done in a separate thread.

Before the main GC work is started, thread creation and destruction
must be disabled and all mutator threads must be suspended. The
SIGUSR2 signal is sent to all threads. The signal handler determines
whether a thread is running JIT code or native code. In the former
case the handler returns to let the thread reach a safepoint. In the
latter case it is put into safepoint immediately.  When thread is
suspended in safepoint it waits for another SIGUSR2 signal which is
sent by the GC thread to wake threads up.

We must not use mutexes from signal handlers because
pthread_mutex_lock() is not reentrant. Using it from a signal handler
may result in a deadlock. This approach uses semaphores to notify GC
when a thread enters or leaves a safepoint.

Signed-off-by: Tomek Grabiec <tgrab...@gmail.com>
---
 arch/x86/include/arch/memory.h    |   13 +++
 include/vm/gc.h                   |    4 +-
 include/vm/thread.h               |    9 ++-
 regression/jvm/GcTortureTest.java |    2 +-
 vm/gc.c                           |  216 ++++++++++++++++++++++---------------
 vm/signal.c                       |    9 +--
 vm/thread.c                       |   85 +++++++--------
 7 files changed, 194 insertions(+), 144 deletions(-)

diff --git a/arch/x86/include/arch/memory.h b/arch/x86/include/arch/memory.h
index e27c31b..30f8c0f 100644
--- a/arch/x86/include/arch/memory.h
+++ b/arch/x86/include/arch/memory.h
@@ -3,6 +3,19 @@
 
 #include <stdint.h>
 
+/*
+ * Memory barriers.
+ */
+#ifdef CONFIG_X86_32
+#define mb() asm volatile("lock; addl $0,0(%esp)")
+#define rmb() asm volatile("lock; addl $0,0(%esp)")
+#define wmb() asm volatile("lock; addl $0,0(%esp)")
+#else
+#define mb()   asm volatile("mfence":::"memory")
+#define rmb()  asm volatile("lfence":::"memory")
+#define wmb()  asm volatile("sfence" ::: "memory")
+#endif
+
 static inline void cpu_write_u32(unsigned char *p, uint32_t val)
 {
        *((uint32_t*)p) = val;
diff --git a/include/vm/gc.h b/include/vm/gc.h
index 06bebc6..7d23f5b 100644
--- a/include/vm/gc.h
+++ b/include/vm/gc.h
@@ -13,9 +13,7 @@ void gc_init(void);
 
 void *gc_alloc(size_t size);
 
-void gc_attach_thread(void);
-void gc_detach_thread(void);
-
 void gc_safepoint(struct register_state *);
+void suspend_handler(int, siginfo_t *, void *);
 
 #endif
diff --git a/include/vm/thread.h b/include/vm/thread.h
index f6e6551..97f672e 100644
--- a/include/vm/thread.h
+++ b/include/vm/thread.h
@@ -36,7 +36,7 @@ struct vm_exec_env {
        struct vm_thread *thread;
 };
 
-unsigned int vm_nr_threads_running(void);
+unsigned int vm_nr_threads(void);
 
 extern __thread struct vm_exec_env current_exec_env;
 
@@ -59,5 +59,12 @@ char *vm_thread_get_name(struct vm_thread *thread);
 bool vm_thread_is_interrupted(struct vm_thread *thread);
 bool vm_thread_interrupted(struct vm_thread *thread);
 void vm_thread_interrupt(struct vm_thread *thread);
+void vm_lock_thread_count(void);
+void vm_unlock_thread_count(void);
+
+extern struct list_head thread_list;
+extern pthread_mutex_t threads_mutex;
+
+#define vm_thread_for_each(this) list_for_each_entry(this, &thread_list, 
list_node)
 
 #endif
diff --git a/regression/jvm/GcTortureTest.java 
b/regression/jvm/GcTortureTest.java
index 33eef15..9f00232 100644
--- a/regression/jvm/GcTortureTest.java
+++ b/regression/jvm/GcTortureTest.java
@@ -7,7 +7,7 @@ public class GcTortureTest {
         for (int i = 0; i < threads.length; i++) {
             threads[i] = new Thread(new Runnable() {
                 public void run() {
-                    for (int i = 0; i < 1000000; i++)
+                    for (int i = 0; i < 100000; i++)
                       new Object();
                 }
             });
diff --git a/vm/gc.c b/vm/gc.c
index 0815d59..3de8f21 100644
--- a/vm/gc.c
+++ b/vm/gc.c
@@ -1,45 +1,37 @@
 #include <assert.h>
+#include <pthread.h>
+#include <semaphore.h>
 #include <stdbool.h>
 #include <stdio.h>
-#include <pthread.h>
 
+#include "arch/memory.h"
 #include "arch/registers.h"
+#include "arch/signal.h"
 #include "lib/guard-page.h"
 #include "vm/thread.h"
 #include "vm/stdlib.h"
 #include "vm/die.h"
 #include "vm/gc.h"
+#include "vm/trace.h"
 
 void *gc_safepoint_page;
 
-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_exiting_safepoint;
-static unsigned int nr_in_safepoint;
+static pthread_mutex_t gc_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t gc_cond = PTHREAD_COND_INITIALIZER;
 
-static pthread_cond_t can_continue_cond = PTHREAD_COND_INITIALIZER;
-static bool can_continue = true;
+/* Protected by gc_mutex */
+static unsigned int nr_threads;
+static bool gc_started;
 
-bool verbose_gc;
-bool gc_enabled;
+static sem_t safepoint_sem;
 
-void gc_init(void)
-{
-       gc_safepoint_page = alloc_guard_page(false);
-       if (!gc_safepoint_page)
-               die("Couldn't allocate GC safepoint guard page");
-}
+static volatile sig_atomic_t can_continue;
+static __thread sig_atomic_t in_safepoint;
 
-void gc_attach_thread(void)
-{
-}
+static pthread_t gc_thread_id;
 
-void gc_detach_thread(void)
-{
-}
+bool verbose_gc;
+bool gc_enabled;
 
 static void hide_safepoint_guard_page(void)
 {
@@ -59,98 +51,150 @@ static void do_gc_reclaim(void)
        /* TODO: Do main GC work here. */
 }
 
-/* Callers must hold safepoint_mutex */
-static void gc_safepoint_exit(void)
+void gc_safepoint(struct register_state *regs)
+{
+       sigset_t mask;
+       int sig;
+
+       in_safepoint = true;
+       sem_post(&safepoint_sem);
+
+       sigemptyset(&mask);
+       sigaddset(&mask, SIGUSR2);
+
+       rmb();
+       while (!can_continue) {
+               sigwait(&mask, &sig);
+               rmb();
+       }
+
+       in_safepoint = false;
+       sem_post(&safepoint_sem);
+}
+
+void suspend_handler(int sig, siginfo_t *si, void *ctx)
 {
-       assert(nr_exiting_safepoint > 0);
+       /*
+        * Ignore this signal if we are already in safepoint. This
+        * happens when a thread executes a safepoint poll in JIT
+        * before the GC thread sends suspend signals.
+        */
+       if (in_safepoint)
+               return;
 
        /*
-        * Don't let threads stop the world until everyone is out of their
-        * safepoint.
+        * Force native code to enter a safepoint and restart non-native
+        * threads. The latter will suspend themselves once they reach a GC
+        * point.
         */
-       if (--nr_exiting_safepoint == 0)
-               pthread_cond_broadcast(&everyone_out_cond);
+
+       if (signal_from_native(ctx)) {
+               struct register_state thread_register_state;
+               ucontext_t *uc = ctx;
+
+               save_signal_registers(&thread_register_state, 
uc->uc_mcontext.gregs);
+               gc_safepoint(&thread_register_state);
+       }
 }
 
-/* Callers must hold safepoint_mutex */
-static void do_gc_safepoint(void)
+static void gc_suspend_rest(void)
 {
-       /* Only the GC thread will be waiting for this. */
-       if (++nr_in_safepoint == vm_nr_threads_running())
-               pthread_cond_signal(&everyone_in_cond);
+       struct vm_thread *thread;
 
-       /* Block until GC has finished */
-       while (!can_continue)
-               pthread_cond_wait(&can_continue_cond, &safepoint_mutex);
+       sem_init(&safepoint_sem, true, 1 - nr_threads);
+       can_continue = false;
+       wmb();
 
-       assert(nr_in_safepoint > 0);
-       --nr_in_safepoint;
+       vm_thread_for_each(thread) {
+               if (pthread_kill(thread->posix_id, SIGUSR2) != 0)
+                       die("pthread_kill");
+       }
+
+       /* Wait for all threads to enter a safepoint. */
+       sem_wait(&safepoint_sem);
 }
 
-void gc_safepoint(struct register_state *regs)
+static void gc_resume_rest(void)
 {
-       pthread_mutex_lock(&safepoint_mutex);
+       struct vm_thread *thread;
+
+       sem_init(&safepoint_sem, true, 1 - nr_threads);
 
-       do_gc_safepoint();
+       can_continue = true;
+       wmb();
 
-       gc_safepoint_exit();
+       vm_thread_for_each(thread) {
+               if (pthread_kill(thread->posix_id, SIGUSR2) != 0)
+                       die("pthread_kill");
+       }
 
-       pthread_mutex_unlock(&safepoint_mutex);
+       /* Wait for all threads to leave a safepoint. */
+       sem_wait(&safepoint_sem);
 }
 
-/*
- * This is the main entrypoint to the stop-the-world GC.
- */
-static void gc_start(struct register_state *regs)
+static void do_gc(void)
 {
-       pthread_mutex_lock(&safepoint_mutex);
+       vm_lock_thread_count();
+
+       nr_threads = vm_nr_threads();
 
        /* Don't deadlock during early boostrap. */
-       if (vm_nr_threads_running() == 0)
-               goto out_unlock;
+       if (nr_threads == 0)
+               goto out;
 
-       /*
-        * 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);
+       hide_safepoint_guard_page();
+       gc_suspend_rest();
+       unhide_safepoint_guard_page();
 
-       /*
-        * 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;
+       do_gc_reclaim();
+       gc_resume_rest();
+
+out:
+       vm_unlock_thread_count();
+}
+
+static void *gc_thread(void *arg)
+{
+       pthread_mutex_lock(&gc_mutex);
+
+       for (;;) {
+               while (!gc_started)
+                       pthread_cond_wait(&gc_cond, &gc_mutex);
+
+               do_gc();
+
+               gc_started = false;
+               pthread_cond_broadcast(&gc_cond);
        }
 
-       /* Only one thread can stop the world at a time. */
-       assert(can_continue);
+       pthread_mutex_unlock(&gc_mutex);
+       return NULL;
+}
 
-       ++nr_in_safepoint;
-       can_continue = false;
-       hide_safepoint_guard_page();
+/*
+ * This wakes up the GC thread and suspends until garbage collection is done.
+ */
+static void gc_start(struct register_state *regs)
+{
+       pthread_mutex_lock(&gc_mutex);
 
-       /* 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);
+       gc_started = true;
+       pthread_cond_broadcast(&gc_cond);
 
-       /* At this point, we know that everyone is in the safepoint. */
-       unhide_safepoint_guard_page();
+       while (gc_started)
+               pthread_cond_wait(&gc_cond, &gc_mutex);
 
-       do_gc_reclaim();
+       pthread_mutex_unlock(&gc_mutex);
+}
 
-       /* Resume other threads */
-       assert(nr_in_safepoint > 0);
-       nr_exiting_safepoint = nr_in_safepoint--;
-       can_continue = true;
-       pthread_cond_broadcast(&can_continue_cond);
+void gc_init(void)
+{
+       gc_safepoint_page = alloc_guard_page(false);
+       if (!gc_safepoint_page)
+               die("Couldn't allocate GC safepoint guard page");
 
-out_exit_safepoint:
-       gc_safepoint_exit();
-out_unlock:
-       pthread_mutex_unlock(&safepoint_mutex);
+       if (pthread_create(&gc_thread_id, NULL, &gc_thread, NULL))
+               die("Couldn't create GC thread");
 }
 
 void *gc_alloc(size_t size)
diff --git a/vm/signal.c b/vm/signal.c
index 63fe0d9..e3dc112 100644
--- a/vm/signal.c
+++ b/vm/signal.c
@@ -166,11 +166,6 @@ static void sigsegv_handler(int sig, siginfo_t *si, void 
*ctx)
        print_backtrace_and_die(sig, si, ctx);
 }
 
-static void signal_handler(int sig, siginfo_t *si, void *ctx)
-{
-       print_backtrace_and_die(sig, si, ctx);
-}
-
 void setup_signal_handlers(void)
 {
        struct sigaction sa;
@@ -184,6 +179,6 @@ void setup_signal_handlers(void)
        sa.sa_sigaction = sigfpe_handler;
        sigaction(SIGFPE, &sa, NULL);
 
-       sa.sa_sigaction = signal_handler;
-       sigaction(SIGUSR1, &sa, NULL);
+       sa.sa_sigaction = suspend_handler;
+       sigaction(SIGUSR2, &sa, NULL);
 }
diff --git a/vm/thread.c b/vm/thread.c
index a0770b1..8cc1fd3 100644
--- a/vm/thread.c
+++ b/vm/thread.c
@@ -45,18 +45,18 @@ __thread struct vm_exec_env current_exec_env;
 static struct vm_object *main_thread_group;
 
 /* This mutex protects global operations on thread structures. */
-static pthread_mutex_t threads_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t threads_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 /* Condition variable used for waiting on any thread's death. */
 static pthread_cond_t thread_terminate_cond = PTHREAD_COND_INITIALIZER;
 
+static unsigned int nr_threads;
 static int nr_non_daemons;
 
-static unsigned int nr_threads_running;
+struct list_head thread_list;
 
-static struct list_head thread_list;
-
-#define thread_for_each(this) list_for_each_entry(this, &thread_list, 
list_node)
+static bool thread_count_locked;
+static pthread_cond_t thread_count_lock_cond = PTHREAD_COND_INITIALIZER;
 
 static void vm_thread_free(struct vm_thread *thread)
 {
@@ -107,36 +107,10 @@ static bool vm_thread_is_daemon(struct vm_thread *thread)
        return field_get_int(jthread, vm_java_lang_Thread_daemon) != 0;
 }
 
-unsigned int vm_nr_threads_running(void)
-{
-       unsigned int nr;
-
-       pthread_mutex_lock(&threads_mutex);
-       nr = nr_threads_running;
-       pthread_mutex_unlock(&threads_mutex);
-
-       return nr;
-}
-
-static void update_thread_count(enum vm_thread_state state)
+/* Must hold threads_mutex */
+unsigned int vm_nr_threads(void)
 {
-       pthread_mutex_lock(&threads_mutex);
-
-       switch (state) {
-       case VM_THREAD_STATE_NEW:
-               break;
-       case VM_THREAD_STATE_RUNNABLE:
-               nr_threads_running++;
-               break;
-       case VM_THREAD_STATE_BLOCKED:
-       case VM_THREAD_STATE_TERMINATED:
-       case VM_THREAD_STATE_TIMED_WAITING:
-       case VM_THREAD_STATE_WAITING:
-               nr_threads_running--;
-               break;
-       }
-
-       pthread_mutex_unlock(&threads_mutex);
+       return nr_threads;
 }
 
 void vm_thread_set_state(struct vm_thread *thread, enum vm_thread_state state)
@@ -144,41 +118,34 @@ void vm_thread_set_state(struct vm_thread *thread, enum 
vm_thread_state state)
        pthread_mutex_lock(&thread->mutex);
        thread->state = state;
        pthread_mutex_unlock(&thread->mutex);
-
-       update_thread_count(state);
 }
 
 static void vm_thread_attach_thread(struct vm_thread *thread)
 {
-       pthread_mutex_lock(&threads_mutex);
        list_add(&thread->list_node, &thread_list);
-       nr_threads_running++;
-       pthread_mutex_unlock(&threads_mutex);
-
-       gc_attach_thread();
+       nr_threads++;
 }
 
+/* The caller must hold threads_mutex */
 static void vm_thread_detach_thread(struct vm_thread *thread)
 {
        vm_thread_set_state(thread, VM_THREAD_STATE_TERMINATED);
 
-       gc_detach_thread();
-
-       pthread_mutex_lock(&threads_mutex);
-
        list_del(&thread->list_node);
 
        if (!vm_thread_is_daemon(thread))
                nr_non_daemons--;
 
+       nr_threads--;
+
        pthread_cond_broadcast(&thread_terminate_cond);
-       pthread_mutex_unlock(&threads_mutex);
 }
 
 int init_threading(void)
 {
        INIT_LIST_HEAD(&thread_list);
        nr_non_daemons = 0;
+       nr_threads = 0;
 
        main_thread_group = vm_object_alloc(vm_java_lang_ThreadGroup);
        if (!main_thread_group)
@@ -254,7 +221,12 @@ static void *vm_thread_entry(void *arg)
        if (exception_occurred())
                vm_print_exception(exception_occurred());
 
+       pthread_mutex_lock(&threads_mutex);
+       while (thread_count_locked)
+               pthread_cond_wait(&thread_count_lock_cond, &threads_mutex);
+
        vm_thread_detach_thread(vm_thread_self());
+       pthread_mutex_unlock(&threads_mutex);
 
        return NULL;
 }
@@ -284,13 +256,19 @@ int vm_thread_start(struct vm_object *vmthread)
                pthread_mutex_unlock(&threads_mutex);
        }
 
+       pthread_mutex_lock(&threads_mutex);
+       while (thread_count_locked)
+               pthread_cond_wait(&thread_count_lock_cond, &threads_mutex);
+
        vm_thread_attach_thread(thread);
 
        if (pthread_create(&thread->posix_id, NULL, &vm_thread_entry, thread)) {
                vm_thread_detach_thread(thread);
+               pthread_mutex_unlock(&threads_mutex);
                return -1;
        }
 
+       pthread_mutex_unlock(&threads_mutex);
        return 0;
 }
 
@@ -359,3 +337,18 @@ void vm_thread_interrupt(struct vm_thread *thread)
        pthread_cond_broadcast(&mon->cond);
        pthread_mutex_unlock(&mon->mutex);
 }
+
+void vm_lock_thread_count(void)
+{
+       pthread_mutex_lock(&threads_mutex);
+       thread_count_locked = true;
+       pthread_mutex_unlock(&threads_mutex);
+}
+
+void vm_unlock_thread_count(void)
+{
+       pthread_mutex_lock(&threads_mutex);
+       thread_count_locked = false;
+       pthread_cond_broadcast(&thread_count_lock_cond);
+       pthread_mutex_unlock(&threads_mutex);
+}
-- 
1.6.0.4


------------------------------------------------------------------------------
This SF.Net email is sponsored by the Verizon Developer Community
Take advantage of Verizon's best-in-class app development support
A streamlined, 14 day to market process makes app distribution fast and easy
Join now and get one step closer to millions of Verizon customers
http://p.sf.net/sfu/verizon-dev2dev 
_______________________________________________
Jatovm-devel mailing list
Jatovm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jatovm-devel

Reply via email to