|
Ruby
1.9.3p385(2013-02-06revision39114)
|
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 ®, ®siz)); 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
1.7.6.1