summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKeith Seitz <keiths@redhat.com>2006-06-21 20:56:37 +0000
committerIvan Maidanski <ivmai@mail.ru>2015-11-20 01:34:09 +0300
commit62097c3ea1261ad5ffefb8996b2c9bd09baeeab9 (patch)
tree20fd36a787e3363976f1210f511c835733709b92
parentc62a70c6c449361ee3dbb87ffe54d45ecab2a207 (diff)
downloadbdwgc-62097c3ea1261ad5ffefb8996b2c9bd09baeeab9.tar.gz
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
-rw-r--r--include/gc.h11
-rw-r--r--include/private/pthread_support.h4
-rw-r--r--pthread_stop_world.c73
3 files changed, 85 insertions, 3 deletions
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