Ruby  1.9.3p385(2013-02-06revision39114)
thread_pthread.c
Go to the documentation of this file.
00001 /* -*-c-*- */
00002 /**********************************************************************
00003 
00004   thread_pthread.c -
00005 
00006   $Author: usa $
00007 
00008   Copyright (C) 2004-2007 Koichi Sasada
00009 
00010 **********************************************************************/
00011 
00012 #ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
00013 
00014 #include "gc.h"
00015 
00016 #ifdef HAVE_SYS_RESOURCE_H
00017 #include <sys/resource.h>
00018 #endif
00019 #ifdef HAVE_THR_STKSEGMENT
00020 #include <thread.h>
00021 #endif
00022 #if HAVE_FCNTL_H
00023 #include <fcntl.h>
00024 #elif HAVE_SYS_FCNTL_H
00025 #include <sys/fcntl.h>
00026 #endif
00027 
00028 static void native_mutex_lock(pthread_mutex_t *lock);
00029 static void native_mutex_unlock(pthread_mutex_t *lock);
00030 static int native_mutex_trylock(pthread_mutex_t *lock);
00031 static void native_mutex_initialize(pthread_mutex_t *lock);
00032 static void native_mutex_destroy(pthread_mutex_t *lock);
00033 static void native_cond_signal(rb_thread_cond_t *cond);
00034 static void native_cond_broadcast(rb_thread_cond_t *cond);
00035 static void native_cond_wait(rb_thread_cond_t *cond, pthread_mutex_t *mutex);
00036 static void native_cond_initialize(rb_thread_cond_t *cond, int flags);
00037 static void native_cond_destroy(rb_thread_cond_t *cond);
00038 static pthread_t timer_thread_id;
00039 
00040 #define RB_CONDATTR_CLOCK_MONOTONIC 1
00041 
00042 #if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && defined(HAVE_CLOCKID_T) && \
00043     defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && defined(HAVE_CLOCK_GETTIME)
00044 #define USE_MONOTONIC_COND 1
00045 #else
00046 #define USE_MONOTONIC_COND 0
00047 #endif
00048 
00049 static void
00050 gvl_acquire_common(rb_vm_t *vm)
00051 {
00052     if (vm->gvl.acquired) {
00053 
00054         vm->gvl.waiting++;
00055         if (vm->gvl.waiting == 1) {
00056             /* transit to polling mode */
00057             rb_thread_wakeup_timer_thread();
00058         }
00059 
00060         while (vm->gvl.acquired) {
00061             native_cond_wait(&vm->gvl.cond, &vm->gvl.lock);
00062         }
00063 
00064         vm->gvl.waiting--;
00065 
00066         if (vm->gvl.need_yield) {
00067             vm->gvl.need_yield = 0;
00068             native_cond_signal(&vm->gvl.switch_cond);
00069         }
00070     }
00071 
00072     vm->gvl.acquired = 1;
00073 }
00074 
00075 static void
00076 gvl_acquire(rb_vm_t *vm, rb_thread_t *th)
00077 {
00078     native_mutex_lock(&vm->gvl.lock);
00079     gvl_acquire_common(vm);
00080     native_mutex_unlock(&vm->gvl.lock);
00081 }
00082 
00083 static void
00084 gvl_release_common(rb_vm_t *vm)
00085 {
00086     vm->gvl.acquired = 0;
00087     if (vm->gvl.waiting > 0)
00088         native_cond_signal(&vm->gvl.cond);
00089 }
00090 
00091 static void
00092 gvl_release(rb_vm_t *vm)
00093 {
00094     native_mutex_lock(&vm->gvl.lock);
00095     gvl_release_common(vm);
00096     native_mutex_unlock(&vm->gvl.lock);
00097 }
00098 
00099 static void
00100 gvl_yield(rb_vm_t *vm, rb_thread_t *th)
00101 {
00102     native_mutex_lock(&vm->gvl.lock);
00103 
00104     gvl_release_common(vm);
00105 
00106     /* An another thread is processing GVL yield. */
00107     if (UNLIKELY(vm->gvl.wait_yield)) {
00108         while (vm->gvl.wait_yield)
00109             native_cond_wait(&vm->gvl.switch_wait_cond, &vm->gvl.lock);
00110         goto acquire;
00111     }
00112 
00113     if (vm->gvl.waiting > 0) {
00114         /* Wait until another thread task take GVL. */
00115         vm->gvl.need_yield = 1;
00116         vm->gvl.wait_yield = 1;
00117         while (vm->gvl.need_yield)
00118             native_cond_wait(&vm->gvl.switch_cond, &vm->gvl.lock);
00119         vm->gvl.wait_yield = 0;
00120     }
00121     else {
00122         native_mutex_unlock(&vm->gvl.lock);
00123         sched_yield();
00124         native_mutex_lock(&vm->gvl.lock);
00125     }
00126 
00127     native_cond_broadcast(&vm->gvl.switch_wait_cond);
00128   acquire:
00129     gvl_acquire_common(vm);
00130     native_mutex_unlock(&vm->gvl.lock);
00131 }
00132 
00133 static void
00134 gvl_init(rb_vm_t *vm)
00135 {
00136     native_mutex_initialize(&vm->gvl.lock);
00137     native_cond_initialize(&vm->gvl.cond, RB_CONDATTR_CLOCK_MONOTONIC);
00138     native_cond_initialize(&vm->gvl.switch_cond, RB_CONDATTR_CLOCK_MONOTONIC);
00139     native_cond_initialize(&vm->gvl.switch_wait_cond, RB_CONDATTR_CLOCK_MONOTONIC);
00140     vm->gvl.acquired = 0;
00141     vm->gvl.waiting = 0;
00142     vm->gvl.need_yield = 0;
00143     vm->gvl.wait_yield = 0;
00144 }
00145 
00146 static void
00147 gvl_destroy(rb_vm_t *vm)
00148 {
00149     native_cond_destroy(&vm->gvl.switch_wait_cond);
00150     native_cond_destroy(&vm->gvl.switch_cond);
00151     native_cond_destroy(&vm->gvl.cond);
00152     native_mutex_destroy(&vm->gvl.lock);
00153 }
00154 
00155 static void
00156 gvl_atfork(rb_vm_t *vm)
00157 {
00158     gvl_init(vm);
00159     gvl_acquire(vm, GET_THREAD());
00160 }
00161 
00162 #define NATIVE_MUTEX_LOCK_DEBUG 0
00163 
00164 static void
00165 mutex_debug(const char *msg, pthread_mutex_t *lock)
00166 {
00167     if (NATIVE_MUTEX_LOCK_DEBUG) {
00168         int r;
00169         static pthread_mutex_t dbglock = PTHREAD_MUTEX_INITIALIZER;
00170 
00171         if ((r = pthread_mutex_lock(&dbglock)) != 0) {exit(EXIT_FAILURE);}
00172         fprintf(stdout, "%s: %p\n", msg, (void *)lock);
00173         if ((r = pthread_mutex_unlock(&dbglock)) != 0) {exit(EXIT_FAILURE);}
00174     }
00175 }
00176 
00177 static void
00178 native_mutex_lock(pthread_mutex_t *lock)
00179 {
00180     int r;
00181     mutex_debug("lock", lock);
00182     if ((r = pthread_mutex_lock(lock)) != 0) {
00183         rb_bug_errno("pthread_mutex_lock", r);
00184     }
00185 }
00186 
00187 static void
00188 native_mutex_unlock(pthread_mutex_t *lock)
00189 {
00190     int r;
00191     mutex_debug("unlock", lock);
00192     if ((r = pthread_mutex_unlock(lock)) != 0) {
00193         rb_bug_errno("pthread_mutex_unlock", r);
00194     }
00195 }
00196 
00197 static inline int
00198 native_mutex_trylock(pthread_mutex_t *lock)
00199 {
00200     int r;
00201     mutex_debug("trylock", lock);
00202     if ((r = pthread_mutex_trylock(lock)) != 0) {
00203         if (r == EBUSY) {
00204             return EBUSY;
00205         }
00206         else {
00207             rb_bug_errno("pthread_mutex_trylock", r);
00208         }
00209     }
00210     return 0;
00211 }
00212 
00213 static void
00214 native_mutex_initialize(pthread_mutex_t *lock)
00215 {
00216     int r = pthread_mutex_init(lock, 0);
00217     mutex_debug("init", lock);
00218     if (r != 0) {
00219         rb_bug_errno("pthread_mutex_init", r);
00220     }
00221 }
00222 
00223 static void
00224 native_mutex_destroy(pthread_mutex_t *lock)
00225 {
00226     int r = pthread_mutex_destroy(lock);
00227     mutex_debug("destroy", lock);
00228     if (r != 0) {
00229         rb_bug_errno("pthread_mutex_destroy", r);
00230     }
00231 }
00232 
00233 static void
00234 native_cond_initialize(rb_thread_cond_t *cond, int flags)
00235 {
00236     int r;
00237     pthread_condattr_t attr;
00238 
00239     pthread_condattr_init(&attr);
00240 
00241 #if USE_MONOTONIC_COND
00242     cond->clockid = CLOCK_REALTIME;
00243     if (flags & RB_CONDATTR_CLOCK_MONOTONIC) {
00244         r = pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
00245         if (r == 0) {
00246             cond->clockid = CLOCK_MONOTONIC;
00247         }
00248     }
00249 #endif
00250 
00251     r = pthread_cond_init(&cond->cond, &attr);
00252     pthread_condattr_destroy(&attr);
00253     if (r != 0) {
00254         rb_bug_errno("pthread_cond_init", r);
00255     }
00256 
00257     return;
00258  }
00259 
00260 static void
00261 native_cond_destroy(rb_thread_cond_t *cond)
00262 {
00263     int r = pthread_cond_destroy(&cond->cond);
00264     if (r != 0) {
00265         rb_bug_errno("pthread_cond_destroy", r);
00266     }
00267 }
00268 
00269 /*
00270  * In OS X 10.7 (Lion), pthread_cond_signal and pthread_cond_broadcast return
00271  * EAGAIN after retrying 8192 times.  You can see them in the following page:
00272  *
00273  * http://www.opensource.apple.com/source/Libc/Libc-763.11/pthreads/pthread_cond.c
00274  *
00275  * The following native_cond_signal and native_cond_broadcast functions
00276  * need to retrying until pthread functions don't return EAGAIN.
00277  */
00278 
00279 static void
00280 native_cond_signal(rb_thread_cond_t *cond)
00281 {
00282     int r;
00283     do {
00284         r = pthread_cond_signal(&cond->cond);
00285     } while (r == EAGAIN);
00286     if (r != 0) {
00287         rb_bug_errno("pthread_cond_signal", r);
00288     }
00289 }
00290 
00291 static void
00292 native_cond_broadcast(rb_thread_cond_t *cond)
00293 {
00294     int r;
00295     do {
00296         r = pthread_cond_broadcast(&cond->cond);
00297     } while (r == EAGAIN);
00298     if (r != 0) {
00299         rb_bug_errno("native_cond_broadcast", r);
00300     }
00301 }
00302 
00303 static void
00304 native_cond_wait(rb_thread_cond_t *cond, pthread_mutex_t *mutex)
00305 {
00306     int r = pthread_cond_wait(&cond->cond, mutex);
00307     if (r != 0) {
00308         rb_bug_errno("pthread_cond_wait", r);
00309     }
00310 }
00311 
00312 static int
00313 native_cond_timedwait(rb_thread_cond_t *cond, pthread_mutex_t *mutex, struct timespec *ts)
00314 {
00315     int r;
00316 
00317     /*
00318      * An old Linux may return EINTR. Even though POSIX says
00319      *   "These functions shall not return an error code of [EINTR]".
00320      *   http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cond_timedwait.html
00321      * Let's hide it from arch generic code.
00322      */
00323     do {
00324         r = pthread_cond_timedwait(&cond->cond, mutex, ts);
00325     } while (r == EINTR);
00326 
00327     if (r != 0 && r != ETIMEDOUT) {
00328         rb_bug_errno("pthread_cond_timedwait", r);
00329     }
00330 
00331     return r;
00332 }
00333 
00334 #if SIZEOF_TIME_T == SIZEOF_LONG
00335 typedef unsigned long unsigned_time_t;
00336 #elif SIZEOF_TIME_T == SIZEOF_INT
00337 typedef unsigned int unsigned_time_t;
00338 #elif SIZEOF_TIME_T == SIZEOF_LONG_LONG
00339 typedef unsigned LONG_LONG unsigned_time_t;
00340 #else
00341 # error cannot find integer type which size is same as time_t.
00342 #endif
00343 
00344 #define TIMET_MAX (~(time_t)0 <= 0 ? (time_t)((~(unsigned_time_t)0) >> 1) : (time_t)(~(unsigned_time_t)0))
00345 
00346 static struct timespec
00347 native_cond_timeout(rb_thread_cond_t *cond, struct timespec timeout_rel)
00348 {
00349     int ret;
00350     struct timeval tv;
00351     struct timespec timeout;
00352     struct timespec now;
00353 
00354 #if USE_MONOTONIC_COND
00355     if (cond->clockid == CLOCK_MONOTONIC) {
00356         ret = clock_gettime(cond->clockid, &now);
00357         if (ret != 0)
00358             rb_sys_fail("clock_gettime()");
00359         goto out;
00360     }
00361 
00362     if (cond->clockid != CLOCK_REALTIME)
00363         rb_bug("unsupported clockid %d", cond->clockid);
00364 #endif
00365 
00366     ret = gettimeofday(&tv, 0);
00367     if (ret != 0)
00368         rb_sys_fail(0);
00369     now.tv_sec = tv.tv_sec;
00370     now.tv_nsec = tv.tv_usec * 1000;
00371 
00372 #if USE_MONOTONIC_COND
00373   out:
00374 #endif
00375     timeout.tv_sec = now.tv_sec;
00376     timeout.tv_nsec = now.tv_nsec;
00377     timeout.tv_sec += timeout_rel.tv_sec;
00378     timeout.tv_nsec += timeout_rel.tv_nsec;
00379 
00380     if (timeout.tv_nsec >= 1000*1000*1000) {
00381         timeout.tv_sec++;
00382         timeout.tv_nsec -= 1000*1000*1000;
00383     }
00384 
00385     if (timeout.tv_sec < now.tv_sec)
00386         timeout.tv_sec = TIMET_MAX;
00387 
00388     return timeout;
00389 }
00390 
00391 #define native_cleanup_push pthread_cleanup_push
00392 #define native_cleanup_pop  pthread_cleanup_pop
00393 #ifdef HAVE_SCHED_YIELD
00394 #define native_thread_yield() (void)sched_yield()
00395 #else
00396 #define native_thread_yield() ((void)0)
00397 #endif
00398 
00399 #if defined(SIGVTALRM) && !defined(__CYGWIN__) && !defined(__SYMBIAN32__)
00400 #define USE_SIGNAL_THREAD_LIST 1
00401 #endif
00402 #ifdef USE_SIGNAL_THREAD_LIST
00403 static void add_signal_thread_list(rb_thread_t *th);
00404 static void remove_signal_thread_list(rb_thread_t *th);
00405 static rb_thread_lock_t signal_thread_list_lock;
00406 #endif
00407 
00408 static pthread_key_t ruby_native_thread_key;
00409 
00410 static void
00411 null_func(int i)
00412 {
00413     /* null */
00414 }
00415 
00416 static rb_thread_t *
00417 ruby_thread_from_native(void)
00418 {
00419     return pthread_getspecific(ruby_native_thread_key);
00420 }
00421 
00422 static int
00423 ruby_thread_set_native(rb_thread_t *th)
00424 {
00425     return pthread_setspecific(ruby_native_thread_key, th) == 0;
00426 }
00427 
00428 static void native_thread_init(rb_thread_t *th);
00429 
00430 void
00431 Init_native_thread(void)
00432 {
00433     rb_thread_t *th = GET_THREAD();
00434 
00435     pthread_key_create(&ruby_native_thread_key, NULL);
00436     th->thread_id = pthread_self();
00437     native_thread_init(th);
00438 #ifdef USE_SIGNAL_THREAD_LIST
00439     native_mutex_initialize(&signal_thread_list_lock);
00440 #endif
00441     posix_signal(SIGVTALRM, null_func);
00442 }
00443 
00444 static void
00445 native_thread_init(rb_thread_t *th)
00446 {
00447     native_cond_initialize(&th->native_thread_data.sleep_cond, RB_CONDATTR_CLOCK_MONOTONIC);
00448     ruby_thread_set_native(th);
00449 }
00450 
00451 static void
00452 native_thread_destroy(rb_thread_t *th)
00453 {
00454     native_cond_destroy(&th->native_thread_data.sleep_cond);
00455 }
00456 
00457 #define USE_THREAD_CACHE 0
00458 
00459 #if USE_THREAD_CACHE
00460 static rb_thread_t *register_cached_thread_and_wait(void);
00461 #endif
00462 
00463 #if defined HAVE_PTHREAD_GETATTR_NP || defined HAVE_PTHREAD_ATTR_GET_NP
00464 #define STACKADDR_AVAILABLE 1
00465 #elif defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP
00466 #define STACKADDR_AVAILABLE 1
00467 #elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP
00468 #define STACKADDR_AVAILABLE 1
00469 #elif defined HAVE_PTHREAD_GETTHRDS_NP
00470 #define STACKADDR_AVAILABLE 1
00471 #endif
00472 
00473 #ifdef STACKADDR_AVAILABLE
00474 /*
00475  * Get the initial address and size of current thread's stack
00476  */
00477 static int
00478 get_stack(void **addr, size_t *size)
00479 {
00480 #define CHECK_ERR(expr)                         \
00481     {int err = (expr); if (err) return err;}
00482 #ifdef HAVE_PTHREAD_GETATTR_NP /* Linux */
00483     pthread_attr_t attr;
00484     size_t guard = 0;
00485     STACK_GROW_DIR_DETECTION;
00486     CHECK_ERR(pthread_getattr_np(pthread_self(), &attr));
00487 # ifdef HAVE_PTHREAD_ATTR_GETSTACK
00488     CHECK_ERR(pthread_attr_getstack(&attr, addr, size));
00489     STACK_DIR_UPPER((void)0, (void)(*addr = (char *)*addr + *size));
00490 # else
00491     CHECK_ERR(pthread_attr_getstackaddr(&attr, addr));
00492     CHECK_ERR(pthread_attr_getstacksize(&attr, size));
00493 # endif
00494     CHECK_ERR(pthread_attr_getguardsize(&attr, &guard));
00495     *size -= guard;
00496     pthread_attr_destroy(&attr);
00497 #elif defined HAVE_PTHREAD_ATTR_GET_NP /* FreeBSD, DragonFly BSD, NetBSD */
00498     pthread_attr_t attr;
00499     CHECK_ERR(pthread_attr_init(&attr));
00500     CHECK_ERR(pthread_attr_get_np(pthread_self(), &attr));
00501 # ifdef HAVE_PTHREAD_ATTR_GETSTACK
00502     CHECK_ERR(pthread_attr_getstack(&attr, addr, size));
00503     STACK_DIR_UPPER((void)0, (void)(*addr = (char *)*addr + *size));
00504 # else
00505     CHECK_ERR(pthread_attr_getstackaddr(&attr, addr));
00506     CHECK_ERR(pthread_attr_getstacksize(&attr, size));
00507     STACK_DIR_UPPER((void)0, (void)(*addr = (char *)*addr + *size));
00508 # endif
00509     pthread_attr_destroy(&attr);
00510 #elif (defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP) /* MacOS X */
00511     pthread_t th = pthread_self();
00512     *addr = pthread_get_stackaddr_np(th);
00513     *size = pthread_get_stacksize_np(th);
00514 #elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP
00515     stack_t stk;
00516 # if defined HAVE_THR_STKSEGMENT /* Solaris */
00517     CHECK_ERR(thr_stksegment(&stk));
00518 # else /* OpenBSD */
00519     CHECK_ERR(pthread_stackseg_np(pthread_self(), &stk));
00520 # endif
00521     *addr = stk.ss_sp;
00522     *size = stk.ss_size;
00523 #elif defined HAVE_PTHREAD_GETTHRDS_NP /* AIX */
00524     pthread_t th = pthread_self();
00525     struct __pthrdsinfo thinfo;
00526     char reg[256];
00527     int regsiz=sizeof(reg);
00528     CHECK_ERR(pthread_getthrds_np(&th, PTHRDSINFO_QUERY_ALL,
00529                                    &thinfo, sizeof(thinfo),
00530                                    &reg, &regsiz));
00531     *addr = thinfo.__pi_stackaddr;
00532     *size = thinfo.__pi_stacksize;
00533     STACK_DIR_UPPER((void)0, (void)(*addr = (char *)*addr + *size));
00534 #else
00535 #error STACKADDR_AVAILABLE is defined but not implemented.
00536 #endif
00537     return 0;
00538 #undef CHECK_ERR
00539 }
00540 #endif
00541 
00542 static struct {
00543     rb_thread_id_t id;
00544     size_t stack_maxsize;
00545     VALUE *stack_start;
00546 #ifdef __ia64
00547     VALUE *register_stack_start;
00548 #endif
00549 } native_main_thread;
00550 
00551 #ifdef STACK_END_ADDRESS
00552 extern void *STACK_END_ADDRESS;
00553 #endif
00554 
00555 #undef ruby_init_stack
00556 void
00557 ruby_init_stack(volatile VALUE *addr
00558 #ifdef __ia64
00559     , void *bsp
00560 #endif
00561     )
00562 {
00563     native_main_thread.id = pthread_self();
00564 #ifdef STACK_END_ADDRESS
00565     native_main_thread.stack_start = STACK_END_ADDRESS;
00566 #else
00567     if (!native_main_thread.stack_start ||
00568         STACK_UPPER((VALUE *)(void *)&addr,
00569                     native_main_thread.stack_start > addr,
00570                     native_main_thread.stack_start < addr)) {
00571         native_main_thread.stack_start = (VALUE *)addr;
00572     }
00573 #endif
00574 #ifdef __ia64
00575     if (!native_main_thread.register_stack_start ||
00576         (VALUE*)bsp < native_main_thread.register_stack_start) {
00577         native_main_thread.register_stack_start = (VALUE*)bsp;
00578     }
00579 #endif
00580     {
00581         size_t size = 0;
00582         size_t space = 0;
00583 #if defined(STACKADDR_AVAILABLE)
00584         void* stackaddr;
00585         STACK_GROW_DIR_DETECTION;
00586         get_stack(&stackaddr, &size);
00587         space = STACK_DIR_UPPER((char *)addr - (char *)stackaddr, (char *)stackaddr - (char *)addr);
00588 #elif defined(HAVE_GETRLIMIT)
00589         struct rlimit rlim;
00590         if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
00591             size = (size_t)rlim.rlim_cur;
00592         }
00593         space = size > 5 * 1024 * 1024 ? 1024 * 1024 : size / 5;
00594 #endif
00595         native_main_thread.stack_maxsize = size - space;
00596     }
00597 }
00598 
00599 #define CHECK_ERR(expr) \
00600     {int err = (expr); if (err) {rb_bug_errno(#expr, err);}}
00601 
00602 static int
00603 native_thread_init_stack(rb_thread_t *th)
00604 {
00605     rb_thread_id_t curr = pthread_self();
00606 
00607     if (pthread_equal(curr, native_main_thread.id)) {
00608         th->machine_stack_start = native_main_thread.stack_start;
00609         th->machine_stack_maxsize = native_main_thread.stack_maxsize;
00610     }
00611     else {
00612 #ifdef STACKADDR_AVAILABLE
00613         void *start;
00614         size_t size;
00615 
00616         if (get_stack(&start, &size) == 0) {
00617             th->machine_stack_start = start;
00618             th->machine_stack_maxsize = size;
00619         }
00620 #else
00621         rb_raise(rb_eNotImpError, "ruby engine can initialize only in the main thread");
00622 #endif
00623     }
00624 #ifdef __ia64
00625     th->machine_register_stack_start = native_main_thread.register_stack_start;
00626     th->machine_stack_maxsize /= 2;
00627     th->machine_register_stack_maxsize = th->machine_stack_maxsize;
00628 #endif
00629     return 0;
00630 }
00631 
00632 #ifndef __CYGWIN__
00633 #define USE_NATIVE_THREAD_INIT 1
00634 #endif
00635 
00636 static void *
00637 thread_start_func_1(void *th_ptr)
00638 {
00639 #if USE_THREAD_CACHE
00640   thread_start:
00641 #endif
00642     {
00643         rb_thread_t *th = th_ptr;
00644 #if !defined USE_NATIVE_THREAD_INIT
00645         VALUE stack_start;
00646 #endif
00647 
00648 #if defined USE_NATIVE_THREAD_INIT
00649         native_thread_init_stack(th);
00650 #endif
00651         native_thread_init(th);
00652         /* run */
00653 #if defined USE_NATIVE_THREAD_INIT
00654         thread_start_func_2(th, th->machine_stack_start, rb_ia64_bsp());
00655 #else
00656         thread_start_func_2(th, &stack_start, rb_ia64_bsp());
00657 #endif
00658     }
00659 #if USE_THREAD_CACHE
00660     if (1) {
00661         /* cache thread */
00662         rb_thread_t *th;
00663         if ((th = register_cached_thread_and_wait()) != 0) {
00664             th_ptr = (void *)th;
00665             th->thread_id = pthread_self();
00666             goto thread_start;
00667         }
00668     }
00669 #endif
00670     return 0;
00671 }
00672 
00673 struct cached_thread_entry {
00674     volatile rb_thread_t **th_area;
00675     rb_thread_cond_t *cond;
00676     struct cached_thread_entry *next;
00677 };
00678 
00679 
00680 #if USE_THREAD_CACHE
00681 static pthread_mutex_t thread_cache_lock = PTHREAD_MUTEX_INITIALIZER;
00682 struct cached_thread_entry *cached_thread_root;
00683 
00684 static rb_thread_t *
00685 register_cached_thread_and_wait(void)
00686 {
00687     rb_thread_cond_t cond = { PTHREAD_COND_INITIALIZER, };
00688     volatile rb_thread_t *th_area = 0;
00689     struct cached_thread_entry *entry =
00690       (struct cached_thread_entry *)malloc(sizeof(struct cached_thread_entry));
00691 
00692     struct timeval tv;
00693     struct timespec ts;
00694     gettimeofday(&tv, 0);
00695     ts.tv_sec = tv.tv_sec + 60;
00696     ts.tv_nsec = tv.tv_usec * 1000;
00697 
00698     pthread_mutex_lock(&thread_cache_lock);
00699     {
00700         entry->th_area = &th_area;
00701         entry->cond = &cond;
00702         entry->next = cached_thread_root;
00703         cached_thread_root = entry;
00704 
00705         native_cond_timedwait(&cond, &thread_cache_lock, &ts);
00706 
00707         {
00708             struct cached_thread_entry *e = cached_thread_root;
00709             struct cached_thread_entry *prev = cached_thread_root;
00710 
00711             while (e) {
00712                 if (e == entry) {
00713                     if (prev == cached_thread_root) {
00714                         cached_thread_root = e->next;
00715                     }
00716                     else {
00717                         prev->next = e->next;
00718                     }
00719                     break;
00720                 }
00721                 prev = e;
00722                 e = e->next;
00723             }
00724         }
00725 
00726         free(entry); /* ok */
00727         native_cond_destroy(&cond);
00728     }
00729     pthread_mutex_unlock(&thread_cache_lock);
00730 
00731     return (rb_thread_t *)th_area;
00732 }
00733 #endif
00734 
00735 static int
00736 use_cached_thread(rb_thread_t *th)
00737 {
00738     int result = 0;
00739 #if USE_THREAD_CACHE
00740     struct cached_thread_entry *entry;
00741 
00742     if (cached_thread_root) {
00743         pthread_mutex_lock(&thread_cache_lock);
00744         entry = cached_thread_root;
00745         {
00746             if (cached_thread_root) {
00747                 cached_thread_root = entry->next;
00748                 *entry->th_area = th;
00749                 result = 1;
00750             }
00751         }
00752         if (result) {
00753             native_cond_signal(entry->cond);
00754         }
00755         pthread_mutex_unlock(&thread_cache_lock);
00756     }
00757 #endif
00758     return result;
00759 }
00760 
00761 enum {
00762 #ifdef __SYMBIAN32__
00763     RUBY_STACK_MIN_LIMIT = 64 * 1024,  /* 64KB: Let's be slightly more frugal on mobile platform */
00764 #else
00765     RUBY_STACK_MIN_LIMIT = 512 * 1024, /* 512KB */
00766 #endif
00767     RUBY_STACK_SPACE_LIMIT = 1024 * 1024
00768 };
00769 
00770 #ifdef PTHREAD_STACK_MIN
00771 #define RUBY_STACK_MIN ((RUBY_STACK_MIN_LIMIT < PTHREAD_STACK_MIN) ? \
00772                         PTHREAD_STACK_MIN * 2 : RUBY_STACK_MIN_LIMIT)
00773 #else
00774 #define RUBY_STACK_MIN (RUBY_STACK_MIN_LIMIT)
00775 #endif
00776 #define RUBY_STACK_SPACE (RUBY_STACK_MIN/5 > RUBY_STACK_SPACE_LIMIT ? \
00777                           RUBY_STACK_SPACE_LIMIT : RUBY_STACK_MIN/5)
00778 
00779 static int
00780 native_thread_create(rb_thread_t *th)
00781 {
00782     int err = 0;
00783 
00784     if (use_cached_thread(th)) {
00785         thread_debug("create (use cached thread): %p\n", (void *)th);
00786     }
00787     else {
00788         pthread_attr_t attr;
00789         const size_t stack_size = RUBY_STACK_MIN;
00790         const size_t space = RUBY_STACK_SPACE;
00791 
00792         th->machine_stack_maxsize = stack_size - space;
00793 #ifdef __ia64
00794         th->machine_stack_maxsize /= 2;
00795         th->machine_register_stack_maxsize = th->machine_stack_maxsize;
00796 #endif
00797 
00798         CHECK_ERR(pthread_attr_init(&attr));
00799 
00800 #ifdef PTHREAD_STACK_MIN
00801         thread_debug("create - stack size: %lu\n", (unsigned long)stack_size);
00802         CHECK_ERR(pthread_attr_setstacksize(&attr, stack_size));
00803 #endif
00804 
00805 #ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
00806         CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
00807 #endif
00808         CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
00809 
00810         err = pthread_create(&th->thread_id, &attr, thread_start_func_1, th);
00811         thread_debug("create: %p (%d)\n", (void *)th, err);
00812         CHECK_ERR(pthread_attr_destroy(&attr));
00813     }
00814     return err;
00815 }
00816 
00817 static void
00818 native_thread_join(pthread_t th)
00819 {
00820     int err = pthread_join(th, 0);
00821     if (err) {
00822         rb_raise(rb_eThreadError, "native_thread_join() failed (%d)", err);
00823     }
00824 }
00825 
00826 
00827 #if USE_NATIVE_THREAD_PRIORITY
00828 
00829 static void
00830 native_thread_apply_priority(rb_thread_t *th)
00831 {
00832 #if defined(_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING > 0)
00833     struct sched_param sp;
00834     int policy;
00835     int priority = 0 - th->priority;
00836     int max, min;
00837     pthread_getschedparam(th->thread_id, &policy, &sp);
00838     max = sched_get_priority_max(policy);
00839     min = sched_get_priority_min(policy);
00840 
00841     if (min > priority) {
00842         priority = min;
00843     }
00844     else if (max < priority) {
00845         priority = max;
00846     }
00847 
00848     sp.sched_priority = priority;
00849     pthread_setschedparam(th->thread_id, policy, &sp);
00850 #else
00851     /* not touched */
00852 #endif
00853 }
00854 
00855 #endif /* USE_NATIVE_THREAD_PRIORITY */
00856 
00857 static void
00858 ubf_pthread_cond_signal(void *ptr)
00859 {
00860     rb_thread_t *th = (rb_thread_t *)ptr;
00861     thread_debug("ubf_pthread_cond_signal (%p)\n", (void *)th);
00862     native_cond_signal(&th->native_thread_data.sleep_cond);
00863 }
00864 
00865 static void
00866 native_sleep(rb_thread_t *th, struct timeval *timeout_tv)
00867 {
00868     struct timespec timeout;
00869     pthread_mutex_t *lock = &th->interrupt_lock;
00870     rb_thread_cond_t *cond = &th->native_thread_data.sleep_cond;
00871 
00872     if (timeout_tv) {
00873         struct timespec timeout_rel;
00874 
00875         timeout_rel.tv_sec = timeout_tv->tv_sec;
00876         timeout_rel.tv_nsec = timeout_tv->tv_usec * 1000;
00877 
00878         /* Solaris cond_timedwait() return EINVAL if an argument is greater than
00879          * current_time + 100,000,000.  So cut up to 100,000,000.  This is
00880          * considered as a kind of spurious wakeup.  The caller to native_sleep
00881          * should care about spurious wakeup.
00882          *
00883          * See also [Bug #1341] [ruby-core:29702]
00884          * http://download.oracle.com/docs/cd/E19683-01/816-0216/6m6ngupgv/index.html
00885          */
00886         if (timeout_rel.tv_sec > 100000000) {
00887             timeout_rel.tv_sec = 100000000;
00888             timeout_rel.tv_nsec = 0;
00889         }
00890 
00891         timeout = native_cond_timeout(cond, timeout_rel);
00892     }
00893 
00894     GVL_UNLOCK_BEGIN();
00895     {
00896         pthread_mutex_lock(lock);
00897         th->unblock.func = ubf_pthread_cond_signal;
00898         th->unblock.arg = th;
00899 
00900         if (RUBY_VM_INTERRUPTED(th)) {
00901             /* interrupted.  return immediate */
00902             thread_debug("native_sleep: interrupted before sleep\n");
00903         }
00904         else {
00905             if (!timeout_tv)
00906                 native_cond_wait(cond, lock);
00907             else
00908                 native_cond_timedwait(cond, lock, &timeout);
00909         }
00910         th->unblock.func = 0;
00911         th->unblock.arg = 0;
00912 
00913         pthread_mutex_unlock(lock);
00914     }
00915     GVL_UNLOCK_END();
00916 
00917     thread_debug("native_sleep done\n");
00918 }
00919 
00920 #ifdef USE_SIGNAL_THREAD_LIST
00921 struct signal_thread_list {
00922     rb_thread_t *th;
00923     struct signal_thread_list *prev;
00924     struct signal_thread_list *next;
00925 };
00926 
00927 static struct signal_thread_list signal_thread_list_anchor = {
00928     0, 0, 0,
00929 };
00930 
00931 #define FGLOCK(lock, body) do { \
00932     native_mutex_lock(lock); \
00933     { \
00934         body; \
00935     } \
00936     native_mutex_unlock(lock); \
00937 } while (0)
00938 
00939 #if 0 /* for debug */
00940 static void
00941 print_signal_list(char *str)
00942 {
00943     struct signal_thread_list *list =
00944       signal_thread_list_anchor.next;
00945     thread_debug("list (%s)> ", str);
00946     while(list){
00947         thread_debug("%p (%p), ", list->th, list->th->thread_id);
00948         list = list->next;
00949     }
00950     thread_debug("\n");
00951 }
00952 #endif
00953 
00954 static void
00955 add_signal_thread_list(rb_thread_t *th)
00956 {
00957     if (!th->native_thread_data.signal_thread_list) {
00958         FGLOCK(&signal_thread_list_lock, {
00959             struct signal_thread_list *list =
00960               malloc(sizeof(struct signal_thread_list));
00961 
00962             if (list == 0) {
00963                 fprintf(stderr, "[FATAL] failed to allocate memory\n");
00964                 exit(EXIT_FAILURE);
00965             }
00966 
00967             list->th = th;
00968 
00969             list->prev = &signal_thread_list_anchor;
00970             list->next = signal_thread_list_anchor.next;
00971             if (list->next) {
00972                 list->next->prev = list;
00973             }
00974             signal_thread_list_anchor.next = list;
00975             th->native_thread_data.signal_thread_list = list;
00976         });
00977     }
00978 }
00979 
00980 static void
00981 remove_signal_thread_list(rb_thread_t *th)
00982 {
00983     if (th->native_thread_data.signal_thread_list) {
00984         FGLOCK(&signal_thread_list_lock, {
00985             struct signal_thread_list *list =
00986               (struct signal_thread_list *)
00987                 th->native_thread_data.signal_thread_list;
00988 
00989             list->prev->next = list->next;
00990             if (list->next) {
00991                 list->next->prev = list->prev;
00992             }
00993             th->native_thread_data.signal_thread_list = 0;
00994             list->th = 0;
00995             free(list); /* ok */
00996         });
00997     }
00998 }
00999 
01000 static void
01001 ubf_select_each(rb_thread_t *th)
01002 {
01003     thread_debug("ubf_select_each (%p)\n", (void *)th->thread_id);
01004     if (th) {
01005         pthread_kill(th->thread_id, SIGVTALRM);
01006     }
01007 }
01008 
01009 static void
01010 ubf_select(void *ptr)
01011 {
01012     rb_thread_t *th = (rb_thread_t *)ptr;
01013     add_signal_thread_list(th);
01014     if (pthread_self() != timer_thread_id)
01015         rb_thread_wakeup_timer_thread(); /* activate timer thread */
01016     ubf_select_each(th);
01017 }
01018 
01019 static void
01020 ping_signal_thread_list(void) {
01021     if (signal_thread_list_anchor.next) {
01022         FGLOCK(&signal_thread_list_lock, {
01023             struct signal_thread_list *list;
01024 
01025             list = signal_thread_list_anchor.next;
01026             while (list) {
01027                 ubf_select_each(list->th);
01028                 list = list->next;
01029             }
01030         });
01031     }
01032 }
01033 
01034 static int
01035 check_signal_thread_list(void)
01036 {
01037     if (signal_thread_list_anchor.next)
01038         return 1;
01039     else
01040         return 0;
01041 }
01042 #else /* USE_SIGNAL_THREAD_LIST */
01043 static void add_signal_thread_list(rb_thread_t *th) { }
01044 static void remove_signal_thread_list(rb_thread_t *th) { }
01045 #define ubf_select 0
01046 static void ping_signal_thread_list(void) { return; }
01047 static int check_signal_thread_list(void) { return 0; }
01048 #endif /* USE_SIGNAL_THREAD_LIST */
01049 
01050 static int timer_thread_pipe[2] = {-1, -1};
01051 static int timer_thread_pipe_owner_process;
01052 
01053 #define TT_DEBUG 0
01054 
01055 #define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
01056 
01057 /* only use signal-safe system calls here */
01058 void
01059 rb_thread_wakeup_timer_thread(void)
01060 {
01061     ssize_t result;
01062 
01063     /* already opened */
01064     if (timer_thread_pipe_owner_process == getpid()) {
01065         const char *buff = "!";
01066       retry:
01067         if ((result = write(timer_thread_pipe[1], buff, 1)) <= 0) {
01068             switch (errno) {
01069               case EINTR: goto retry;
01070               case EAGAIN:
01071 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
01072               case EWOULDBLOCK:
01073 #endif
01074                 break;
01075               default:
01076                 rb_async_bug_errno("rb_thread_wakeup_timer_thread - write", errno);
01077             }
01078         }
01079         if (TT_DEBUG) WRITE_CONST(2, "rb_thread_wakeup_timer_thread: write\n");
01080     }
01081     else {
01082         /* ignore wakeup */
01083     }
01084 }
01085 
01086 /* VM-dependent API is not available for this function */
01087 static void
01088 consume_communication_pipe(void)
01089 {
01090 #define CCP_READ_BUFF_SIZE 1024
01091     /* buffer can be shared because no one refers to them. */
01092     static char buff[CCP_READ_BUFF_SIZE];
01093     ssize_t result;
01094 
01095   retry:
01096     result = read(timer_thread_pipe[0], buff, CCP_READ_BUFF_SIZE);
01097     if (result < 0) {
01098         switch (errno) {
01099           case EINTR: goto retry;
01100           default:
01101             rb_async_bug_errno("consume_communication_pipe: read\n", errno);
01102         }
01103     }
01104 }
01105 
01106 static void
01107 close_communication_pipe(void)
01108 {
01109     if (close(timer_thread_pipe[0]) < 0) {
01110         rb_bug_errno("native_stop_timer_thread - close(ttp[0])", errno);
01111     }
01112     if (close(timer_thread_pipe[1]) < 0) {
01113         rb_bug_errno("native_stop_timer_thread - close(ttp[1])", errno);
01114     }
01115     timer_thread_pipe[0] = timer_thread_pipe[1] = -1;
01116 }
01117 
01118 /* 100ms.  10ms is too small for user level thread scheduling
01119  * on recent Linux (tested on 2.6.35)
01120  */
01121 #define TIME_QUANTUM_USEC (100 * 1000)
01122 
01123 static void *
01124 thread_timer(void *p)
01125 {
01126     rb_global_vm_lock_t *gvl = (rb_global_vm_lock_t *)p;
01127     int result;
01128     struct timeval timeout;
01129 
01130     if (TT_DEBUG) WRITE_CONST(2, "start timer thread\n");
01131 
01132     while (system_working > 0) {
01133         fd_set rfds;
01134         int need_polling;
01135 
01136         /* timer function */
01137         ping_signal_thread_list();
01138         timer_thread_function(0);
01139         need_polling = check_signal_thread_list();
01140 
01141         if (TT_DEBUG) WRITE_CONST(2, "tick\n");
01142 
01143         /* wait */
01144         FD_ZERO(&rfds);
01145         FD_SET(timer_thread_pipe[0], &rfds);
01146 
01147         if (gvl->waiting > 0 || need_polling) {
01148             timeout.tv_sec = 0;
01149             timeout.tv_usec = TIME_QUANTUM_USEC;
01150 
01151             /* polling (TIME_QUANTUM_USEC usec) */
01152             result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, &timeout);
01153         }
01154         else {
01155             /* wait (infinite) */
01156             result = select(timer_thread_pipe[0] + 1, &rfds, 0, 0, 0);
01157         }
01158 
01159         if (result == 0) {
01160             /* maybe timeout */
01161         }
01162         else if (result > 0) {
01163             consume_communication_pipe();
01164         }
01165         else { /* result < 0 */
01166           switch (errno) {
01167             case EBADF:
01168             case EINVAL:
01169             case ENOMEM: /* from Linux man */
01170             case EFAULT: /* from FreeBSD man */
01171               rb_async_bug_errno("thread_timer: select", errno);
01172             default:
01173               /* ignore */;
01174           }
01175         }
01176     }
01177 
01178     if (TT_DEBUG) WRITE_CONST(2, "finish timer thread\n");
01179     return NULL;
01180 }
01181 
01182 static void
01183 rb_thread_create_timer_thread(void)
01184 {
01185     rb_enable_interrupt();
01186 
01187     if (!timer_thread_id) {
01188         pthread_attr_t attr;
01189         int err;
01190 
01191         pthread_attr_init(&attr);
01192 #ifdef PTHREAD_STACK_MIN
01193         if (PTHREAD_STACK_MIN < 4096 * 3) {
01194             /* Allocate the machine stack for the timer thread
01195              * at least 12KB (3 pages).  FreeBSD 8.2 AMD64 causes
01196              * machine stack overflow only with PTHREAD_STACK_MIN.
01197              */
01198             pthread_attr_setstacksize(&attr,
01199                                       4096 * 3 + (THREAD_DEBUG ? BUFSIZ : 0));
01200         }
01201         else {
01202             pthread_attr_setstacksize(&attr,
01203                                       PTHREAD_STACK_MIN + (THREAD_DEBUG ? BUFSIZ : 0));
01204         }
01205 #endif
01206 
01207         /* communication pipe with timer thread and signal handler */
01208         if (timer_thread_pipe_owner_process != getpid()) {
01209             if (timer_thread_pipe[0] != -1) {
01210                 /* close pipe of parent process */
01211                 close_communication_pipe();
01212             }
01213 
01214             err = pipe(timer_thread_pipe);
01215             if (err != 0) {
01216                 rb_bug_errno("thread_timer: Failed to create communication pipe for timer thread", errno);
01217             }
01218             rb_update_max_fd(timer_thread_pipe[0]);
01219             rb_update_max_fd(timer_thread_pipe[1]);
01220 #if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL)
01221             {
01222                 int oflags;
01223 #if defined(O_NONBLOCK)
01224                 oflags = fcntl(timer_thread_pipe[1], F_GETFL);
01225                 oflags |= O_NONBLOCK;
01226                 fcntl(timer_thread_pipe[1], F_SETFL, oflags);
01227 #endif /* defined(O_NONBLOCK) */
01228 #if defined(FD_CLOEXEC)
01229                 oflags = fcntl(timer_thread_pipe[0], F_GETFD);
01230                 fcntl(timer_thread_pipe[0], F_SETFD, oflags | FD_CLOEXEC);
01231                 oflags = fcntl(timer_thread_pipe[1], F_GETFD);
01232                 fcntl(timer_thread_pipe[1], F_SETFD, oflags | FD_CLOEXEC);
01233 #endif /* defined(FD_CLOEXEC) */
01234             }
01235 #endif /* defined(HAVE_FCNTL) && defined(F_GETFL) && defined(F_SETFL) */
01236 
01237             /* validate pipe on this process */
01238             timer_thread_pipe_owner_process = getpid();
01239         }
01240 
01241         /* create timer thread */
01242         if (timer_thread_id) {
01243             rb_bug("rb_thread_create_timer_thread: Timer thread was already created\n");
01244         }
01245         err = pthread_create(&timer_thread_id, &attr, thread_timer, &GET_VM()->gvl);
01246         if (err != 0) {
01247             fprintf(stderr, "[FATAL] Failed to create timer thread (errno: %d)\n", err);
01248             exit(EXIT_FAILURE);
01249         }
01250         pthread_attr_destroy(&attr);
01251     }
01252 
01253     rb_disable_interrupt(); /* only timer thread recieve signal */
01254 }
01255 
01256 static int
01257 native_stop_timer_thread(int close_anyway)
01258 {
01259     int stopped;
01260     stopped = --system_working <= 0;
01261 
01262     if (TT_DEBUG) fprintf(stderr, "stop timer thread\n");
01263     if (stopped) {
01264         /* join */
01265         rb_thread_wakeup_timer_thread();
01266         native_thread_join(timer_thread_id);
01267         if (TT_DEBUG) fprintf(stderr, "joined timer thread\n");
01268         timer_thread_id = 0;
01269 
01270         /* close communication pipe */
01271         if (close_anyway) {
01272             /* TODO: Uninstall all signal handlers or mask all signals.
01273              *       This pass is cleaning phase (terminate ruby process).
01274              *       To avoid such race, we skip to close communication
01275              *       pipe.  OS will close it at process termination.
01276              *       It may not good practice, but pragmatic.
01277              *       We remain it is TODO.
01278              */
01279             /* close_communication_pipe(); */
01280         }
01281     }
01282     return stopped;
01283 }
01284 
01285 static void
01286 native_reset_timer_thread(void)
01287 {
01288     if (TT_DEBUG)  fprintf(stderr, "reset timer thread\n");
01289 }
01290 
01291 #ifdef HAVE_SIGALTSTACK
01292 int
01293 ruby_stack_overflowed_p(const rb_thread_t *th, const void *addr)
01294 {
01295     void *base;
01296     size_t size;
01297     const size_t water_mark = 1024 * 1024;
01298     STACK_GROW_DIR_DETECTION;
01299 
01300     if (th) {
01301         size = th->machine_stack_maxsize;
01302         base = (char *)th->machine_stack_start - STACK_DIR_UPPER(0, size);
01303     }
01304 #ifdef STACKADDR_AVAILABLE
01305     else if (get_stack(&base, &size) == 0) {
01306         STACK_DIR_UPPER((void)(base = (char *)base + size), (void)0);
01307     }
01308 #endif
01309     else {
01310         return 0;
01311     }
01312     size /= 5;
01313     if (size > water_mark) size = water_mark;
01314     if (IS_STACK_DIR_UPPER()) {
01315         if (size > ~(size_t)base+1) size = ~(size_t)base+1;
01316         if (addr > base && addr <= (void *)((char *)base + size)) return 1;
01317     }
01318     else {
01319         if (size > (size_t)base) size = (size_t)base;
01320         if (addr > (void *)((char *)base - size) && addr <= base) return 1;
01321     }
01322     return 0;
01323 }
01324 #endif
01325 
01326 int
01327 rb_reserved_fd_p(int fd)
01328 {
01329     if (fd == timer_thread_pipe[0] ||
01330         fd == timer_thread_pipe[1]) {
01331         return 1;
01332     }
01333     else {
01334         return 0;
01335     }
01336 }
01337 
01338 #endif /* THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION */
01339