]> granicus.if.org Git - gc/commitdiff
Implement thread suspend/resume API (Linux threads only)
authorKeith Seitz <keiths@redhat.com>
Wed, 21 Jun 2006 20:56:37 +0000 (20:56 +0000)
committerIvan Maidanski <ivmai@mail.ru>
Thu, 19 Nov 2015 22:34:09 +0000 (01:34 +0300)
(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

include/gc.h
include/private/pthread_support.h
pthread_stop_world.c

index 7cf932034f6090038d7629c53b1c6d33ad645a4a..d9f4e397407dee63bdec54e1d668217e27048b8b 100644 (file)
@@ -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
index 294aeedb92d66e53cb237f83e0706915e71e8911..f835fa3c92384ef66dc5c3b0eeecd66afbc8149a 100644 (file)
@@ -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.                   */
 
index 63c93c0f2246ba4182503166fa938f23c9435daa..f9e86c55f69c5be7478fcbf30198c71094c50a79 100644 (file)
@@ -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