Skip to content

Commit

Permalink
Merge branch 'thread-suspend'
Browse files Browse the repository at this point in the history
  • Loading branch information
ivmai committed Jun 21, 2016
2 parents 81cae56 + c651715 commit 4f6d0ac
Show file tree
Hide file tree
Showing 10 changed files with 204 additions and 50 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -210,6 +210,7 @@ Kai Tietz <ktietz70@googlemail.com>
Kaz Kojima <kkojima@rr.iij4u.or.jp>
Kazu Hirata <kazu@codesourcery.com>
Kazuhiro Inaoka <inaoka.kazuhiro@renesas.com>
Keith Seitz <keiths@redhat.com>
Kenjiro Taura <tau@eidos.ic.i.u-tokyo.ac.jp>
Kenneth Schalk <schalk@cadtls.hlo.dec.com>
Kevin Kenny <kenny@m.cs.uiuc.edu>
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Expand Up @@ -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)


Expand Down
2 changes: 2 additions & 0 deletions configure.ac
Expand Up @@ -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.
Expand Down
3 changes: 3 additions & 0 deletions doc/README.macros
Expand Up @@ -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.
Expand Down
4 changes: 4 additions & 0 deletions include/gc_pthread_redirects.h
Expand Up @@ -33,6 +33,10 @@
#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 */);
Expand Down
15 changes: 15 additions & 0 deletions include/javaxfc.h
Expand Up @@ -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
4 changes: 1 addition & 3 deletions include/private/pthread_support.h
Expand Up @@ -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. */

Expand Down
194 changes: 147 additions & 47 deletions pthread_stop_world.c
Expand Up @@ -50,6 +50,10 @@
/* It's safe to call original pthread_sigmask() here. */
#undef pthread_sigmask

#ifdef GC_ENABLE_SUSPEND_THREAD
static void *GC_CALLBACK suspend_self_inner(void *client_data);
#endif

#ifdef DEBUG_THREADS
# ifndef NSIG
# if defined(MAXSIG)
Expand Down Expand Up @@ -260,6 +264,27 @@ 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) {
# ifdef SPARC
me -> stop_info.stack_ptr = GC_save_regs_in_stack();
# else
me -> stop_info.stack_ptr = GC_approx_sp();
# ifdef IA64
me -> backing_store_ptr = GC_save_regs_in_stack();
# endif
# endif
sem_post(&GC_suspend_ack_sem);
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) {
Expand Down Expand Up @@ -339,6 +364,121 @@ STATIC void GC_restart_handler(int sig)
# endif
}

# ifdef USE_TKILL_ON_ANDROID
extern int tkill(pid_t tid, int sig); /* from sys/linux-unistd.h */

static int android_thread_kill(pid_t tid, int sig)
{
int ret;
int old_errno = errno;

ret = tkill(tid, sig);
if (ret < 0) {
ret = errno;
errno = old_errno;
}
return ret;
}

# define THREAD_SYSTEM_ID(t) (t)->kernel_id
# define RAISE_SIGNAL(t, sig) android_thread_kill(THREAD_SYSTEM_ID(t), sig)
# else
# define THREAD_SYSTEM_ID(t) (t)->id
# define RAISE_SIGNAL(t, sig) pthread_kill(THREAD_SYSTEM_ID(t), sig)
# endif /* !USE_TKILL_ON_ANDROID */

# ifdef GC_ENABLE_SUSPEND_THREAD
# ifndef GC_TIME_LIMIT
# define GC_TIME_LIMIT 50
# endif

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);
}

static void *GC_CALLBACK suspend_self_inner(void *client_data) {
GC_thread me = (GC_thread)client_data;

while ((me -> flags & SUSPENDED_EXT) != 0) {
/* TODO: Use sigsuspend() instead. */
GC_brief_async_signal_safe_sleep();
}
return NULL;
}

GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID thread) {
GC_thread t;
DCL_LOCK_STATE;

LOCK();
t = GC_lookup_thread((pthread_t)thread);
if (t == NULL || (t -> flags & SUSPENDED_EXT) != 0) {
UNLOCK();
return;
}

t -> flags |= SUSPENDED_EXT;
if ((pthread_t)thread == pthread_self()) {
UNLOCK();
/* It is safe as "t" cannot become invalid here (no race with */
/* GC_unregister_my_thread). */
(void)GC_do_blocking(suspend_self_inner, t);
return;
}

/* TODO: Support GC_retry_signals */
switch (RAISE_SIGNAL(t, GC_sig_suspend)) {
case ESRCH:
/* Not really there anymore (terminated but not joined yet). */
/* No need to wait but leave the suspension flag on. */
GC_ASSERT((t -> flags & FINISHED) != 0);
UNLOCK();
return;
case 0:
break;
default:
ABORT("pthread_kill failed");
}

/* Wait for the thread to complete threads table lookup and */
/* stack_ptr assignment. */
GC_ASSERT(GC_thr_initialized);
while (sem_wait(&GC_suspend_ack_sem) != 0) {
if (errno != EINTR)
ABORT("sem_wait for handler failed (suspend_self)");
}
UNLOCK();
}

GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID thread) {
GC_thread t;
DCL_LOCK_STATE;

LOCK();
t = GC_lookup_thread((pthread_t)thread);
if (t != NULL)
t -> flags &= ~SUSPENDED_EXT;
UNLOCK();
}

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 */

#ifdef IA64
Expand Down Expand Up @@ -449,24 +589,6 @@ GC_INNER void GC_push_all_stacks(void)
int GC_stopping_pid = 0;
#endif

#ifdef USE_TKILL_ON_ANDROID
extern int tkill(pid_t tid, int sig); /* from sys/linux-unistd.h */

static int android_thread_kill(pid_t tid, int sig)
{
int ret;
int old_errno = errno;

ret = tkill(tid, sig);
if (ret < 0) {
ret = errno;
errno = old_errno;
}

return ret;
}
#endif /* USE_TKILL_ON_ANDROID */

/* We hold the allocation lock. Suspend all threads that might */
/* still be running. Return the number of suspend signals that */
/* were sent. */
Expand All @@ -475,11 +597,6 @@ STATIC int GC_suspend_all(void)
int n_live_threads = 0;
int i;
# ifndef NACL
# ifndef USE_TKILL_ON_ANDROID
pthread_t thread_id;
# else
pid_t thread_id;
# endif
GC_thread p;
# ifndef GC_OPENBSD_UTHREADS
int result;
Expand All @@ -493,7 +610,7 @@ STATIC int GC_suspend_all(void)
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (!THREAD_EQUAL(p -> id, self)) {
if (p -> flags & FINISHED) continue;
if ((p -> flags & (FINISHED | SUSPENDED_EXT)) != 0) continue;
if (p -> thread_blocked) /* Will wait */ continue;
# ifndef GC_OPENBSD_UTHREADS
if (p -> stop_info.last_stop_count == GC_stop_count) continue;
Expand All @@ -516,13 +633,7 @@ STATIC int GC_suspend_all(void)
(void *)p->id);
}
# else
# ifndef USE_TKILL_ON_ANDROID
thread_id = p -> id;
result = pthread_kill(thread_id, GC_sig_suspend);
# else
thread_id = p -> kernel_id;
result = android_thread_kill(thread_id, GC_sig_suspend);
# endif
result = RAISE_SIGNAL(p, GC_sig_suspend);
switch(result) {
case ESRCH:
/* Not really there anymore. Possible? */
Expand All @@ -531,8 +642,8 @@ STATIC int GC_suspend_all(void)
case 0:
if (GC_on_thread_event)
GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED,
(void *)(word)thread_id);
/* Note: thread_id might be truncated. */
(void *)(word)THREAD_SYSTEM_ID(p));
/* Note: thread id might be truncated. */
break;
default:
ABORT_ARG1("pthread_kill failed at suspend",
Expand Down Expand Up @@ -846,11 +957,6 @@ GC_INNER void GC_start_world(void)
register int n_live_threads = 0;
register int result;
# endif
# ifndef USE_TKILL_ON_ANDROID
pthread_t thread_id;
# else
pid_t thread_id;
# endif
# ifdef GC_NETBSD_THREADS_WORKAROUND
int code;
# endif
Expand All @@ -865,7 +971,7 @@ GC_INNER void GC_start_world(void)
for (i = 0; i < THREAD_TABLE_SZ; i++) {
for (p = GC_threads[i]; p != 0; p = p -> next) {
if (!THREAD_EQUAL(p -> id, self)) {
if (p -> flags & FINISHED) continue;
if ((p -> flags & (FINISHED | SUSPENDED_EXT)) != 0) continue;
if (p -> thread_blocked) continue;
# ifndef GC_OPENBSD_UTHREADS
n_live_threads++;
Expand All @@ -880,13 +986,7 @@ GC_INNER void GC_start_world(void)
if (GC_on_thread_event)
GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, (void *)p->id);
# else
# ifndef USE_TKILL_ON_ANDROID
thread_id = p -> id;
result = pthread_kill(thread_id, GC_sig_thr_restart);
# else
thread_id = p -> kernel_id;
result = android_thread_kill(thread_id, GC_sig_thr_restart);
# endif
result = RAISE_SIGNAL(p, GC_sig_thr_restart);
switch(result) {
case ESRCH:
/* Not really there anymore. Possible? */
Expand All @@ -895,7 +995,7 @@ GC_INNER void GC_start_world(void)
case 0:
if (GC_on_thread_event)
GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED,
(void *)(word)thread_id);
(void *)(word)THREAD_SYSTEM_ID(p));
break;
default:
ABORT_ARG1("pthread_kill failed at resume",
Expand Down
1 change: 1 addition & 0 deletions pthread_support.c
Expand Up @@ -1637,6 +1637,7 @@ GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base *sb)
} else if ((me -> flags & FINISHED) != 0) {
/* This code is executed when a thread is registered from the */
/* client thread key destructor. */
GC_ASSERT((me -> flags & SUSPENDED_EXT) == 0);
GC_record_stack_base(me, sb);
me -> flags &= ~FINISHED; /* but not DETACHED */
# ifdef GC_EXPLICIT_SIGNALS_UNBLOCK
Expand Down

0 comments on commit 4f6d0ac

Please sign in to comment.