]> granicus.if.org Git - gc/commitdiff
Code refactoring of thread suspend/resume API support
authorIvan Maidanski <ivmai@mail.ru>
Thu, 29 Oct 2015 17:33:48 +0000 (20:33 +0300)
committerIvan Maidanski <ivmai@mail.ru>
Thu, 19 Nov 2015 22:37:52 +0000 (01:37 +0300)
* 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
configure.ac
doc/README.macros
include/gc.h
include/gc_pthread_redirects.h
include/javaxfc.h
pthread_stop_world.c

index bd7b927035c6c392da4c4e295207b8e4d3870862..b7919c906f71ee7fb241a66b7c53c86f1b46cfc5 100644 (file)
@@ -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)
 
 
index 3260e7dabe2f65a26b18eaf7a056349efb88d124..4ca7b0ab6a1eb6e9b6805ad6bd6b95f2ddcdd1cb 100644 (file)
@@ -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.
index c3d27437cb6fe1556b4ce4c29aaea5de9090c75a..90ef5128a5d274df753a310ef4f3a1c7bb2a2899 100644 (file)
@@ -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.
index f8f764e124632af0bc4d6ded04e4cdee59fa474d..7cf932034f6090038d7629c53b1c6d33ad645a4a 100644 (file)
@@ -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
index 0d571e913e3957b0160dc484e2da819d9e50b211..e069c578bfd0f295101bcab7c75ca48dda154b88 100644 (file)
 #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 */);
index 99eaf9ad8716daefcaf705e8115a97eab0c41e95..9fd2f18849d528847e4c0aa55e5fe815f0f7ace8 100644 (file)
  */
 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
index d80963320fa1feb777a95ff76e26860d875fc4a1..632c8ad4aac97aa59728ceb17cac340b196bef3c 100644 (file)
@@ -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 */