From: Keith Seitz Date: Wed, 21 Jun 2006 20:56:37 +0000 (+0000) Subject: Implement thread suspend/resume API (Linux threads only) X-Git-Tag: gc7_6_0~29^2~7 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=62097c3ea1261ad5ffefb8996b2c9bd09baeeab9;p=gc Implement thread suspend/resume API (Linux threads only) (Cherry-picked commit 42fe54a from 'gcc_boehmgc' branch.) * pthread_stop_world.c (GC_suspend_handler): Redirect to suspension routine if signal is received and thread is flagged SUSPENDED_EXT. * pthread_stop_world.c (GC_brief_async_signal_safe_sleep, suspend_self_inner, suspend_self, GC_suspend_thread, GC_resume_thread): New function. * include/gc.h (GC_suspend_thread, GC_resume_thread): Declare. * include/private/pthread_support.h (SUSPENDED_EXT): Update comment. Conflicts: * ChangeLog * include/gc.h * include/private/pthread_support.h * pthread_stop_world.c --- diff --git a/include/gc.h b/include/gc.h index 7cf93203..d9f4e397 100644 --- a/include/gc.h +++ b/include/gc.h @@ -1912,6 +1912,17 @@ 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); +#endif + #ifdef __cplusplus } /* end of extern "C" */ #endif diff --git a/include/private/pthread_support.h b/include/private/pthread_support.h index 294aeedb..f835fa3c 100644 --- a/include/private/pthread_support.h +++ b/include/private/pthread_support.h @@ -62,9 +62,7 @@ typedef struct GC_Thread_Rep { /* it unregisters itself, since it */ /* may not return a GC pointer. */ # define MAIN_THREAD 4 /* True for the original thread only. */ -# define SUSPENDED_EXT 8 /* Thread was suspended externally */ - /* (this is not used by the unmodified */ - /* GC itself at present). */ +# define SUSPENDED_EXT 8 /* Thread was suspended externally. */ # define DISABLED_GC 0x10 /* Collections are disabled while the */ /* thread is exiting. */ diff --git a/pthread_stop_world.c b/pthread_stop_world.c index 63c93c0f..f9e86c55 100644 --- a/pthread_stop_world.c +++ b/pthread_stop_world.c @@ -50,6 +50,8 @@ /* It's safe to call original pthread_sigmask() here. */ #undef pthread_sigmask +void suspend_self(); + #ifdef DEBUG_THREADS # ifndef NSIG # if defined(MAXSIG) @@ -210,6 +212,11 @@ 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) @@ -339,6 +346,72 @@ 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; +} + +void suspend_self() { + GC_thread me = GC_lookup_thread(pthread_self()); + if (me == NULL) + ABORT("attempting to suspend unknown thread"); + + me -> flags |= SUSPENDED_EXT; + (void)GC_do_blocking(suspend_self_inner, me); +} + +#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"); + + 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); +# 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"); + + t -> flags &= ~SUSPENDED_EXT; +} + #endif /* !GC_OPENBSD_UTHREADS && !NACL */ #ifdef IA64