summaryrefslogtreecommitdiff
path: root/thread_pthread.c
diff options
context:
space:
mode:
authoryugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-06-10 12:51:37 +0000
committeryugui <yugui@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-06-10 12:51:37 +0000
commitc3f3d77225499eba2e2f562024413dd2794c0139 (patch)
tree70d92934a0490f55e20dbf50aae805f5d9e75706 /thread_pthread.c
parentfa5cd00340d81b9771866cf635ddc4266546a15f (diff)
Fixes threading on NativeClient.
* thread_pthread.c (timer_thread_sleep): Extracted out a function from thread_timer(). Added an alternative implementation for platforms that lacks select(2) or pipe(2). (rb_thread_create_timer_thread, native_cond_initialize, native_cond_destroy): Replaced wrong HAVE_XXX checks. * configure.in (pthread_attr_init): New check. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36022 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'thread_pthread.c')
-rw-r--r--thread_pthread.c197
1 files changed, 132 insertions, 65 deletions
diff --git a/thread_pthread.c b/thread_pthread.c
index 383d41b732..f434d16922 100644
--- a/thread_pthread.c
+++ b/thread_pthread.c
@@ -52,6 +52,14 @@ static pthread_t timer_thread_id;
#define USE_MONOTONIC_COND 0
#endif
+#ifdef __native_client__
+/* Doesn't have select(1). */
+# define USE_SLEEPY_TIMER_THREAD 0
+#else
+/* The timer thread sleeps while only one Ruby thread is running. */
+# define USE_SLEEPY_TIMER_THREAD 1
+#endif
+
static void
gvl_acquire_common(rb_vm_t *vm)
{
@@ -140,11 +148,9 @@ static void
gvl_init(rb_vm_t *vm)
{
native_mutex_initialize(&vm->gvl.lock);
-#ifdef HAVE_PTHREAD_CONDATTR_INIT
native_cond_initialize(&vm->gvl.cond, RB_CONDATTR_CLOCK_MONOTONIC);
native_cond_initialize(&vm->gvl.switch_cond, RB_CONDATTR_CLOCK_MONOTONIC);
native_cond_initialize(&vm->gvl.switch_wait_cond, RB_CONDATTR_CLOCK_MONOTONIC);
-#endif
vm->gvl.acquired = 0;
vm->gvl.waiting = 0;
vm->gvl.need_yield = 0;
@@ -153,11 +159,9 @@ gvl_init(rb_vm_t *vm)
static void
gvl_destroy(rb_vm_t *vm)
{
-#ifdef HAVE_PTHREAD_CONDATTR_INIT
native_cond_destroy(&vm->gvl.switch_wait_cond);
native_cond_destroy(&vm->gvl.switch_cond);
native_cond_destroy(&vm->gvl.cond);
-#endif
native_mutex_destroy(&vm->gvl.lock);
}
@@ -239,19 +243,17 @@ native_mutex_destroy(pthread_mutex_t *lock)
}
}
-#ifdef HAVE_PTHREAD_CONDATTR_INIT
-int pthread_condattr_init(pthread_condattr_t *attr);
-
static void
native_cond_initialize(rb_thread_cond_t *cond, int flags)
{
#ifdef HAVE_PTHREAD_COND_INIT
int r;
+# ifdef HAVE_PTHREAD_COND_ATTR_INIT
pthread_condattr_t attr;
pthread_condattr_init(&attr);
-#if USE_MONOTONIC_COND
+# if USE_MONOTONIC_COND
cond->clockid = CLOCK_REALTIME;
if (flags & RB_CONDATTR_CLOCK_MONOTONIC) {
r = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
@@ -259,9 +261,12 @@ native_cond_initialize(rb_thread_cond_t *cond, int flags)
cond->clockid = CLOCK_MONOTONIC;
}
}
-#endif
+# endif
r = pthread_cond_init(&cond->cond, &attr);
+# else
+ r = pthread_cond_init(&cond->cond, NULL);
+# endif
if (r != 0) {
rb_bug_errno("pthread_cond_init", r);
}
@@ -280,7 +285,6 @@ native_cond_destroy(rb_thread_cond_t *cond)
}
#endif
}
-#endif
/*
* In OS X 10.7 (Lion), pthread_cond_signal and pthread_cond_broadcast return
@@ -819,21 +823,27 @@ native_thread_create(rb_thread_t *th)
th->machine_register_stack_maxsize = th->machine_stack_maxsize;
#endif
+#ifdef HAVE_PTHREAD_ATTR_INIT
CHECK_ERR(pthread_attr_init(&attr));
-#ifdef PTHREAD_STACK_MIN
+# ifdef PTHREAD_STACK_MIN
thread_debug("create - stack size: %lu\n", (unsigned long)stack_size);
CHECK_ERR(pthread_attr_setstacksize(&attr, stack_size));
-#endif
+# endif
-#ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
+# ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
-#endif
+# endif
CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
err = pthread_create(&th->thread_id, &attr, thread_start_func_1, th);
+#else
+ err = pthread_create(&th->thread_id, NULL, thread_start_func_1, th);
+#endif
thread_debug("create: %p (%d)\n", (void *)th, err);
+#ifdef HAVE_PTHREAD_ATTR_INIT
CHECK_ERR(pthread_attr_destroy(&attr));
+#endif
}
return err;
}
@@ -1078,12 +1088,18 @@ static void ping_signal_thread_list(void) { return; }
static int check_signal_thread_list(void) { return 0; }
#endif /* USE_SIGNAL_THREAD_LIST */
+#define TT_DEBUG 0
+#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
+
+/* 100ms. 10ms is too small for user level thread scheduling
+ * on recent Linux (tested on 2.6.35)
+ */
+#define TIME_QUANTUM_USEC (100 * 1000)
+
+#if USE_SLEEPY_TIMER_THREAD
static int timer_thread_pipe[2] = {-1, -1};
static int timer_thread_pipe_owner_process;
-#define TT_DEBUG 0
-
-#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
/* only use signal-safe system calls here */
void
@@ -1146,17 +1162,77 @@ close_communication_pipe(void)
timer_thread_pipe[0] = timer_thread_pipe[1] = -1;
}
-/* 100ms. 10ms is too small for user level thread scheduling
- * on recent Linux (tested on 2.6.35)
+/**
+ * Let the timer thread sleep a while.
+ *
+ * The timer thread sleeps until woken up by rb_thread_wakeup_timer_thread() if only one Ruby thread is running.
+ * @pre the calling context is in the timer thread.
*/
-#define TIME_QUANTUM_USEC (100 * 1000)
+static inline void
+timer_thread_sleep(rb_global_vm_lock_t* gvl)
+{
+ int result;
+ int need_polling;
+ struct timeval timeout;
+ fd_set rfds;
+ FD_ZERO(&rfds);
+ FD_SET(timer_thread_pipe[0], &rfds);
+
+ need_polling = check_signal_thread_list();
+
+ if (gvl->waiting > 0 || need_polling) {
+ timeout.tv_sec = 0;
+ timeout.tv_usec = TIME_QUANTUM_USEC;
+
+ /* polling (TIME_QUANTUM_USEC usec) */
+ result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, &timeout);
+ }
+ else {
+ /* wait (infinite) */
+ result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, 0);
+ }
+
+ if (result == 0) {
+ /* maybe timeout */
+ }
+ else if (result > 0) {
+ consume_communication_pipe();
+ }
+ else { /* result < 0 */
+ switch (errno) {
+ case EBADF:
+ case EINVAL:
+ case ENOMEM: /* from Linux man */
+ case EFAULT: /* from FreeBSD man */
+ rb_async_bug_errno("thread_timer: select", errno);
+ default:
+ /* ignore */;
+ }
+ }
+}
+
+#else /* USE_SLEEPY_TIMER_THREAD */
+# define PER_NANO 1000000000
+void rb_thread_wakeup_timer_thread(void) {}
+
+static pthread_mutex_t timer_thread_lock;
+static rb_thread_cond_t timer_thread_cond;
+
+static inline void
+timer_thread_sleep(rb_global_vm_lock_t* unused) {
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = TIME_QUANTUM_USEC * 1000;
+ ts = native_cond_timeout(&timer_thread_cond, ts);
+
+ native_cond_timedwait(&timer_thread_cond, &timer_thread_lock, &ts);
+}
+#endif /* USE_SLEEPY_TIMER_THREAD */
static void *
thread_timer(void *p)
{
rb_global_vm_lock_t *gvl = (rb_global_vm_lock_t *)p;
- int result;
- struct timeval timeout;
if (TT_DEBUG) WRITE_CONST(2, "start timer thread\n");
@@ -1164,51 +1240,27 @@ thread_timer(void *p)
prctl(PR_SET_NAME, "ruby-timer-thr");
#endif
+#if !USE_SLEEPY_TIMER_THREAD
+ native_mutex_initialize(&timer_thread_lock);
+ native_cond_initialize(&timer_thread_cond, RB_CONDATTR_CLOCK_MONOTONIC);
+ native_mutex_lock(&timer_thread_lock);
+#endif
while (system_working > 0) {
- fd_set rfds;
- int need_polling;
/* timer function */
ping_signal_thread_list();
timer_thread_function(0);
- need_polling = check_signal_thread_list();
if (TT_DEBUG) WRITE_CONST(2, "tick\n");
- /* wait */
- FD_ZERO(&rfds);
- FD_SET(timer_thread_pipe[0], &rfds);
-
- if (gvl->waiting > 0 || need_polling) {
- timeout.tv_sec = 0;
- timeout.tv_usec = TIME_QUANTUM_USEC;
-
- /* polling (TIME_QUANTUM_USEC usec) */
- result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, &timeout);
- }
- else {
- /* wait (infinite) */
- result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, 0);
- }
-
- if (result == 0) {
- /* maybe timeout */
- }
- else if (result > 0) {
- consume_communication_pipe();
- }
- else { /* result < 0 */
- switch (errno) {
- case EBADF:
- case EINVAL:
- case ENOMEM: /* from Linux man */
- case EFAULT: /* from FreeBSD man */
- rb_async_bug_errno("thread_timer: select", errno);
- default:
- /* ignore */;
- }
- }
+ /* wait */
+ timer_thread_sleep(gvl);
}
+#if !USE_SLEEPY_TIMER_THREAD
+ native_mutex_unlock(&timer_thread_lock);
+ native_cond_destroy(&timer_thread_cond);
+ native_mutex_destroy(&timer_thread_lock);
+#endif
if (TT_DEBUG) WRITE_CONST(2, "finish timer thread\n");
return NULL;
@@ -1217,13 +1269,16 @@ thread_timer(void *p)
static void
rb_thread_create_timer_thread(void)
{
-#ifndef __native_client__
if (!timer_thread_id) {
- pthread_attr_t attr;
int err;
+#ifdef HAVE_PTHREAD_ATTR_INIT
+ pthread_attr_t attr;
- pthread_attr_init(&attr);
-#ifdef PTHREAD_STACK_MIN
+ if (pthread_attr_init(&attr)) {
+ fprintf(stderr, "[FATAL] Failed to initialize pthread attr(errno: %d)\n", err);
+ exit(EXIT_FAILURE);
+ }
+# ifdef PTHREAD_STACK_MIN
if (PTHREAD_STACK_MIN < 4096 * 3) {
/* Allocate the machine stack for the timer thread
* at least 12KB (3 pages). FreeBSD 8.2 AMD64 causes
@@ -1236,8 +1291,10 @@ rb_thread_create_timer_thread(void)
pthread_attr_setstacksize(&attr,
PTHREAD_STACK_MIN + (THREAD_DEBUG ? BUFSIZ : 0));
}
+# endif
#endif
+#if USE_SLEEPY_TIMER_THREAD
/* communication pipe with timer thread and signal handler */
if (timer_thread_pipe_owner_process != getpid()) {
if (timer_thread_pipe[0] != -1) {
@@ -1251,7 +1308,7 @@ rb_thread_create_timer_thread(void)
}
rb_update_max_fd(timer_thread_pipe[0]);
rb_update_max_fd(timer_thread_pipe[1]);
-#if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
+# if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) && defined(O_NONBLOCK)
{
int oflags;
int err;
@@ -1264,24 +1321,30 @@ rb_thread_create_timer_thread(void)
if (err == -1)
rb_sys_fail(0);
}
-#endif /* defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) */
+# endif /* defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) */
/* validate pipe on this process */
timer_thread_pipe_owner_process = getpid();
}
+#endif /* USE_SLEEPY_TIMER_THREAD */
/* create timer thread */
if (timer_thread_id) {
rb_bug("rb_thread_create_timer_thread: Timer thread was already created\n");
}
+#ifdef HAVE_PTHREAD_ATTR_INIT
err = pthread_create(&timer_thread_id, &attr, thread_timer, &GET_VM()->gvl);
+#else
+ err = pthread_create(&timer_thread_id, NULL, thread_timer, &GET_VM()->gvl);
+#endif
if (err != 0) {
fprintf(stderr, "[FATAL] Failed to create timer thread (errno: %d)\n", err);
exit(EXIT_FAILURE);
}
+#ifdef HAVE_PTHREAD_ATTR_INIT
pthread_attr_destroy(&attr);
- }
#endif
+ }
}
static int
@@ -1357,6 +1420,7 @@ ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr)
int
rb_reserved_fd_p(int fd)
{
+#if USE_SLEEPY_TIMER_THRAED
if (fd == timer_thread_pipe[0] ||
fd == timer_thread_pipe[1]) {
return 1;
@@ -1364,6 +1428,9 @@ rb_reserved_fd_p(int fd)
else {
return 0;
}
+#else
+ return 0;
+#endif
}
#endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */