From 9f48082eafc4d54ca33390a72924715b03d7c1dd Mon Sep 17 00:00:00 2001 From: Ivan Maidanski Date: Thu, 29 Oct 2015 20:33:48 +0300 Subject: [PATCH] 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. --- CMakeLists.txt | 1 + configure.ac | 2 + doc/README.macros | 3 + include/gc.h | 12 --- include/gc_pthread_redirects.h | 4 + include/javaxfc.h | 15 ++++ pthread_stop_world.c | 157 ++++++++++++++++++--------------- 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 +# ifndef GC_SUSPEND_THREAD_ID +# define GC_SUSPEND_THREAD_ID pthread_t +# endif + # ifndef GC_NO_DLOPEN # include 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 */ -- 2.40.0