summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Maidanski <ivmai@mail.ru>2015-10-29 20:33:48 +0300
committerIvan Maidanski <ivmai@mail.ru>2015-11-20 01:37:52 +0300
commit9f48082eafc4d54ca33390a72924715b03d7c1dd (patch)
tree9d508a0056c8ebab09d02f283eaf1ba3d311adab
parentd34d5b53d67b3ef6fa2d70a945e21f023cb9e3e7 (diff)
downloadbdwgc-9f48082eafc4d54ca33390a72924715b03d7c1dd.tar.gz
Code refactoring of thread suspend/resume API support
* CMakeLists.txt (enable_gcj_support): Define GC_ENABLE_SUSPEND_THREAD. * configure.ac (enable_gcj_support): Likewise. * doc/README.macros (GC_ENABLE_SUSPEND_THREAD): Document. * include/gc.h (GC_suspend_thread, GC_resume_thread, GC_is_thread_suspended): Move to javaxfc.h. * include/gc_pthread_redirects.h (GC_SUSPEND_THREAD_ID): New macro. * include/javaxfc.h (GC_SUSPEND_THREAD_ID): Likewise. * include/javaxfc.h (GC_suspend_thread, GC_resume_thread, GC_is_thread_suspended): Define if and only if GC_THREADS; refine comment. * include/javaxfc.h (GC_suspend_thread, GC_resume_thread, GC_is_thread_suspended): Decorate with GC_CALL; change argument type from pthread_t to GC_SUSPEND_THREAD_ID. * pthread_stop_world.c (GC_suspend_thread, GC_resume_thread, GC_is_thread_suspended): Likewise. * pthread_stop_world.c (GC_suspend_handler): Move check for SUSPENDED_EXT to GC_suspend_handler_inner (to avoid duplicate GC_lookup_thread call). * pthread_stop_world.c (suspend_self_inner, GC_TIME_LIMIT, GC_brief_async_signal_safe_sleep, GC_suspend_thread, GC_resume_thread, GC_is_thread_suspended): Do not defined unless GC_ENABLE_SUSPEND_THREAD. * pthread_stop_world.c (suspend_self): Remove (invoke GC_do_blocking(suspend_self_inner) directly). * pthread_stop_world.c (GC_brief_async_signal_safe_sleep): Decorate with STATIC. * pthread_stop_world.c (GC_suspend_thread, GC_resume_thread, GC_is_thread_suspended): Wrap code into LOCK/UNLOCK (because, at least, GC_lookup_thread should be called with the allocation lock held). * pthread_stop_world.c (GC_suspend_thread, GC_resume_thread): Do not ABORT if thread is unregistered in GC (just no-op instead). * pthread_stop_world.c (GC_is_thread_suspended): Return 0 if thread is not registered in GC.
-rw-r--r--CMakeLists.txt1
-rw-r--r--configure.ac2
-rw-r--r--doc/README.macros3
-rw-r--r--include/gc.h12
-rw-r--r--include/gc_pthread_redirects.h4
-rw-r--r--include/javaxfc.h15
-rw-r--r--pthread_stop_world.c157
7 files changed, 112 insertions, 82 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bd7b9270..b7919c90 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -203,6 +203,7 @@ ENDIF(CMAKE_USE_WIN32_THREADS_INIT)
OPTION(enable_gcj_support "Support for gcj" NO)
IF(enable_gcj_support)
ADD_DEFINITIONS("-DGC_GCJ_SUPPORT")
+ ADD_DEFINITIONS("-DGC_ENABLE_SUSPEND_THREAD")
ENDIF(enable_gcj_support)
diff --git a/configure.ac b/configure.ac
index 3260e7da..4ca7b0ab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -682,6 +682,8 @@ AC_ARG_ENABLE(gcj-support,
[Disable support for gcj.])])
if test x"$enable_gcj_support" != xno; then
AC_DEFINE(GC_GCJ_SUPPORT, 1, [Define to include support for gcj.])
+ AC_DEFINE([GC_ENABLE_SUSPEND_THREAD], 1,
+ [Define to turn on GC_suspend_thread support.])
fi
dnl Interaction with other programs that might use signals.
diff --git a/doc/README.macros b/doc/README.macros
index c3d27437..90ef5128 100644
--- a/doc/README.macros
+++ b/doc/README.macros
@@ -403,6 +403,9 @@ PARALLEL_MARK Allows the marker to run in multiple threads. Recommended
GC_ALWAYS_MULTITHREADED Force multi-threaded mode at GC initialization.
(Turns GC_allow_register_threads into a no-op routine.)
+GC_ENABLE_SUSPEND_THREAD (Linux only) Turn on thread suspend/resume API
+support.
+
GC_WINMAIN_REDIRECT (Win32 only) Redirect (rename) an application
WinMain to GC_WinMain; implement the "real" WinMain which starts a new
thread to call GC_WinMain after initializing the GC. Useful for WinCE.
diff --git a/include/gc.h b/include/gc.h
index f8f764e1..7cf93203 100644
--- a/include/gc.h
+++ b/include/gc.h
@@ -1912,18 +1912,6 @@ GC_API void GC_CALL GC_win32_free_heap(void);
(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page)
#endif /* _AMIGA && !GC_AMIGA_MAKINGLIB */
-/* External thread suspension support. These functions do not implement
- * suspension counts or any other higher-level abstraction. Threads which
- * have been suspended numerous times will resume with the very first call
- * to GC_resume_thread.
- */
-#if defined(GC_PTHREADS) && !defined(__native_client__) \
- && !defined(GC_WIN32_THREADS) && !defined(GC_DARWIN_THREADS)
-GC_API void GC_suspend_thread(pthread_t);
-GC_API void GC_resume_thread(pthread_t);
-GC_API int GC_is_thread_suspended(pthread_t);
-#endif
-
#ifdef __cplusplus
} /* end of extern "C" */
#endif
diff --git a/include/gc_pthread_redirects.h b/include/gc_pthread_redirects.h
index 0d571e91..e069c578 100644
--- a/include/gc_pthread_redirects.h
+++ b/include/gc_pthread_redirects.h
@@ -33,6 +33,10 @@
#ifndef GC_PTHREAD_REDIRECTS_ONLY
# include <pthread.h>
+# ifndef GC_SUSPEND_THREAD_ID
+# define GC_SUSPEND_THREAD_ID pthread_t
+# endif
+
# ifndef GC_NO_DLOPEN
# include <dlfcn.h>
GC_API void *GC_dlopen(const char * /* path */, int /* mode */);
diff --git a/include/javaxfc.h b/include/javaxfc.h
index 99eaf9ad..9fd2f188 100644
--- a/include/javaxfc.h
+++ b/include/javaxfc.h
@@ -40,6 +40,21 @@
*/
GC_API void GC_CALL GC_finalize_all(void);
+#ifdef GC_THREADS
+ /* External thread suspension support. No thread suspension count */
+ /* (so a thread which has been suspended numerous times will be */
+ /* resumed with the very first call to GC_resume_thread). */
+ /* Acquire the allocation lock. Thread should be registered in GC */
+ /* (otherwise no-op, GC_is_thread_suspended returns false). */
+ /* Unimplemented on some platforms. Not recommended for general use. */
+# ifndef GC_SUSPEND_THREAD_ID
+# define GC_SUSPEND_THREAD_ID void*
+# endif
+ GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID);
+ GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID);
+ GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID);
+#endif /* GC_THREADS */
+
#ifdef __cplusplus
} /* end of extern "C" */
#endif
diff --git a/pthread_stop_world.c b/pthread_stop_world.c
index d8096332..632c8ad4 100644
--- a/pthread_stop_world.c
+++ b/pthread_stop_world.c
@@ -50,7 +50,9 @@
/* It's safe to call original pthread_sigmask() here. */
#undef pthread_sigmask
-void suspend_self();
+#ifdef GC_ENABLE_SUSPEND_THREAD
+ static void *GC_CALLBACK suspend_self_inner(void *client_data);
+#endif
#ifdef DEBUG_THREADS
# ifndef NSIG
@@ -212,11 +214,6 @@ STATIC void GC_suspend_handler_inner(ptr_t dummy, void *context);
#endif
{
int old_errno = errno;
- GC_thread me = GC_lookup_thread (pthread_self());
- if (me -> flags & SUSPENDED_EXT) {
- suspend_self();
- return;
- }
if (sig != GC_sig_suspend) {
# if defined(GC_FREEBSD_THREADS)
@@ -267,6 +264,19 @@ STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED,
/* of a thread which holds the allocation lock in order */
/* to stop the world. Thus concurrent modification of the */
/* data structure is impossible. */
+
+# ifdef GC_ENABLE_SUSPEND_THREAD
+ if ((me -> flags & SUSPENDED_EXT) != 0) {
+ /* TODO: GC_with_callee_saves_pushed is redundant here. */
+ (void)GC_do_blocking(suspend_self_inner, me);
+# ifdef DEBUG_THREADS
+ GC_log_printf("Continuing %p on GC_resume_thread\n", (void *)self);
+# endif
+ RESTORE_CANCEL(cancel_state);
+ return;
+ }
+# endif
+
if (me -> stop_info.last_stop_count == my_stop_count) {
/* Duplicate signal. OK if we are retrying. */
if (!GC_retry_signals) {
@@ -346,79 +356,86 @@ STATIC void GC_restart_handler(int sig)
# endif
}
-#ifndef GC_TIME_LIMIT
-# define GC_TIME_LIMIT 50
-#endif
-
-void GC_brief_async_signal_safe_sleep()
-{
- struct timeval tv;
- tv.tv_sec = 0;
- tv.tv_usec = 1000 * GC_TIME_LIMIT / 2;
- select(0, 0, 0, 0, &tv);
-}
-
-static void *GC_CALLBACK suspend_self_inner(void *client_data) {
- GC_thread me = (GC_thread)client_data;
-
- while (me -> flags & SUSPENDED_EXT)
- GC_brief_async_signal_safe_sleep();
- return NULL;
-}
+# ifdef GC_ENABLE_SUSPEND_THREAD
+# ifndef GC_TIME_LIMIT
+# define GC_TIME_LIMIT 50
+# endif
-void suspend_self() {
- GC_thread me = GC_lookup_thread(pthread_self());
- if (me == NULL)
- ABORT("attempting to suspend unknown thread");
+ STATIC void GC_brief_async_signal_safe_sleep(void)
+ {
+ struct timeval tv;
+ tv.tv_sec = 0;
+ tv.tv_usec = 1000 * GC_TIME_LIMIT / 2;
+ select(0, 0, 0, 0, &tv);
+ }
- me -> flags |= SUSPENDED_EXT;
- (void)GC_do_blocking(suspend_self_inner, me);
-}
+ static void *GC_CALLBACK suspend_self_inner(void *client_data) {
+ GC_thread me = (GC_thread)client_data;
-#ifdef USE_TKILL_ON_ANDROID
- static int android_thread_kill(pid_t tid, int sig);
-#endif
-
-void GC_suspend_thread(pthread_t thread) {
- if (thread == pthread_self())
- suspend_self();
- else {
- int result;
- GC_thread t = GC_lookup_thread(thread);
- if (t == NULL)
- ABORT("attempting to suspend unknown thread");
+ while ((me -> flags & SUSPENDED_EXT) != 0) {
+ /* TODO: Use sigsuspend() instead. */
+ GC_brief_async_signal_safe_sleep();
+ }
+ return NULL;
+ }
- t -> flags |= SUSPENDED_EXT;
-# ifndef USE_TKILL_ON_ANDROID
- result = pthread_kill(t -> id, GC_sig_suspend);
-# else
- result = android_thread_kill(t -> kernel_id, GC_sig_suspend);
+# ifdef USE_TKILL_ON_ANDROID
+ static int android_thread_kill(pid_t tid, int sig);
# endif
- switch (result) {
- case ESRCH:
- case 0:
- break;
- default:
- ABORT("pthread_kill failed");
- }
- }
-}
-void GC_resume_thread(pthread_t thread) {
- GC_thread t = GC_lookup_thread(thread);
- if (t == NULL)
- ABORT("attempting to resume unknown thread");
+ GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID thread) {
+ GC_thread t;
+ int result;
+ DCL_LOCK_STATE;
+
+ LOCK();
+ t = GC_lookup_thread((pthread_t)thread);
+ if (t != NULL) {
+ t -> flags |= SUSPENDED_EXT;
+ if ((pthread_t)thread == pthread_self()) {
+ (void)GC_do_blocking(suspend_self_inner, t);
+ } else {
+# ifndef USE_TKILL_ON_ANDROID
+ result = pthread_kill(t -> id, GC_sig_suspend);
+# else
+ result = android_thread_kill(t -> kernel_id, GC_sig_suspend);
+# endif
+ switch (result) {
+ case ESRCH:
+ case 0:
+ break;
+ default:
+ ABORT("pthread_kill failed");
+ }
+ }
+ }
+ UNLOCK();
+ }
- t -> flags &= ~SUSPENDED_EXT;
-}
+ GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID thread) {
+ GC_thread t;
+ DCL_LOCK_STATE;
-int GC_is_thread_suspended(pthread_t thread) {
- GC_thread t = GC_lookup_thread(thread);
- if (t == NULL)
- ABORT("querying suspension state of unknown thread");
+ LOCK();
+ t = GC_lookup_thread((pthread_t)thread);
+ if (t != NULL)
+ t -> flags &= ~SUSPENDED_EXT;
+ UNLOCK();
+ }
- return (t -> flags & SUSPENDED_EXT);
-}
+ GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID thread) {
+ GC_thread t;
+ int flags = 0;
+ DCL_LOCK_STATE;
+
+ LOCK();
+ t = GC_lookup_thread((pthread_t)thread);
+ if (t != NULL)
+ flags = t -> flags;
+ UNLOCK();
+ return (flags & SUSPENDED_EXT) != 0;
+ }
+# endif /* GC_ENABLE_SUSPEND_THREAD */
#endif /* !GC_OPENBSD_UTHREADS && !NACL */