ruby-changes:23971
From: yugui <ko1@a...>
Date: Sun, 10 Jun 2012 21:51:46 +0900 (JST)
Subject: [ruby-changes:23971] yugui:r36022 (trunk): Fixes threading on NativeClient.
yugui 2012-06-10 21:51:37 +0900 (Sun, 10 Jun 2012) New Revision: 36022 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=36022 Log: 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. Modified files: trunk/ChangeLog trunk/configure.in trunk/thread_pthread.c Index: configure.in =================================================================== --- configure.in (revision 36021) +++ configure.in (revision 36022) @@ -2015,6 +2015,11 @@ thr_stksegment pthread_stackseg_np pthread_getthrds_np \ pthread_cond_init pthread_condattr_setclock pthread_condattr_init \ pthread_sigmask) + if test "${host_os}" = "nacl"; then + ac_cv_func_pthread_attr_init=no + else + AC_CHECK_FUNCS(pthread_attr_init) + fi fi if test x"$ac_cv_header_ucontext_h" = xyes; then if test x"$rb_with_pthread" = xyes; then Index: ChangeLog =================================================================== --- ChangeLog (revision 36021) +++ ChangeLog (revision 36022) @@ -1,3 +1,15 @@ +Sun Jun 10 20:23:14 2012 Yuki Sonoda (Yugui) <yugui@y...> + + 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. + Sun Jun 10 21:30:11 2012 Tanaka Akira <akr@f...> * process.c (rb_exec_without_timer_thread): renamed from rb_exec_err. Index: thread_pthread.c =================================================================== --- thread_pthread.c (revision 36021) +++ thread_pthread.c (revision 36022) @@ -52,6 +52,14 @@ #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 @@ 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 @@ 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 @@ } } -#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 @@ 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 @@ } #endif } -#endif /* * In OS X 10.7 (Lion), pthread_cond_signal and pthread_cond_broadcast return @@ -819,21 +823,27 @@ 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,13 +1088,19 @@ 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 rb_thread_wakeup_timer_thread(void) @@ -1146,17 +1162,77 @@ 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 @@ 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 @@ static void rb_thread_create_timer_thread(void) { -#ifndef __native_client__ if (!timer_thread_id) { + int err; +#ifdef HAVE_PTHREAD_ATTR_INIT pthread_attr_t attr; - int err; - 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 @@ 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_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 @@ 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 } -#endif } static int @@ -1357,6 +1420,7 @@ 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 @@ else { return 0; } +#else + return 0; +#endif } #endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */ -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/