00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047 #include "eval_intern.h"
00048 #include "gc.h"
00049
00050 #ifndef USE_NATIVE_THREAD_PRIORITY
00051 #define USE_NATIVE_THREAD_PRIORITY 0
00052 #define RUBY_THREAD_PRIORITY_MAX 3
00053 #define RUBY_THREAD_PRIORITY_MIN -3
00054 #endif
00055
00056 #ifndef THREAD_DEBUG
00057 #define THREAD_DEBUG 0
00058 #endif
00059
00060 VALUE rb_cMutex;
00061 VALUE rb_cBarrier;
00062
00063 static void sleep_timeval(rb_thread_t *th, struct timeval time);
00064 static void sleep_wait_for_interrupt(rb_thread_t *th, double sleepsec);
00065 static void sleep_forever(rb_thread_t *th, int nodeadlock);
00066 static double timeofday(void);
00067 struct timeval rb_time_interval(VALUE);
00068 static int rb_threadptr_dead(rb_thread_t *th);
00069
00070 static void rb_check_deadlock(rb_vm_t *vm);
00071
00072 int rb_signal_buff_size(void);
00073 void rb_signal_exec(rb_thread_t *th, int sig);
00074 void rb_disable_interrupt(void);
00075 void rb_thread_stop_timer_thread(void);
00076
00077 static const VALUE eKillSignal = INT2FIX(0);
00078 static const VALUE eTerminateSignal = INT2FIX(1);
00079 static volatile int system_working = 1;
00080
00081 inline static void
00082 st_delete_wrap(st_table *table, st_data_t key)
00083 {
00084 st_delete(table, &key, 0);
00085 }
00086
00087
00088
00089 #define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
00090
00091 struct rb_blocking_region_buffer {
00092 enum rb_thread_status prev_status;
00093 struct rb_unblock_callback oldubf;
00094 };
00095
00096 static void set_unblock_function(rb_thread_t *th, rb_unblock_function_t *func, void *arg,
00097 struct rb_unblock_callback *old);
00098 static void reset_unblock_function(rb_thread_t *th, const struct rb_unblock_callback *old);
00099
00100 static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region);
00101
00102 #define RB_GC_SAVE_MACHINE_CONTEXT(th) \
00103 do { \
00104 rb_gc_save_machine_context(th); \
00105 SET_MACHINE_STACK_END(&(th)->machine_stack_end); \
00106 } while (0)
00107
00108 #define GVL_UNLOCK_BEGIN() do { \
00109 rb_thread_t *_th_stored = GET_THREAD(); \
00110 RB_GC_SAVE_MACHINE_CONTEXT(_th_stored); \
00111 native_mutex_unlock(&_th_stored->vm->global_vm_lock)
00112
00113 #define GVL_UNLOCK_END() \
00114 native_mutex_lock(&_th_stored->vm->global_vm_lock); \
00115 rb_thread_set_current(_th_stored); \
00116 } while(0)
00117
00118 #define BLOCKING_REGION_CORE(exec) do { \
00119 GVL_UNLOCK_BEGIN(); {\
00120 exec; \
00121 } \
00122 GVL_UNLOCK_END(); \
00123 } while(0);
00124
00125 #define blocking_region_begin(th, region, func, arg) \
00126 do { \
00127 (region)->prev_status = (th)->status; \
00128 set_unblock_function((th), (func), (arg), &(region)->oldubf); \
00129 (th)->blocking_region_buffer = (region); \
00130 (th)->status = THREAD_STOPPED; \
00131 thread_debug("enter blocking region (%p)\n", (void *)(th)); \
00132 RB_GC_SAVE_MACHINE_CONTEXT(th); \
00133 native_mutex_unlock(&(th)->vm->global_vm_lock); \
00134 } while (0)
00135
00136 #define BLOCKING_REGION(exec, ubf, ubfarg) do { \
00137 rb_thread_t *__th = GET_THREAD(); \
00138 struct rb_blocking_region_buffer __region; \
00139 blocking_region_begin(__th, &__region, ubf, ubfarg); \
00140 exec; \
00141 blocking_region_end(__th, &__region); \
00142 RUBY_VM_CHECK_INTS(); \
00143 } while(0)
00144
00145 #if THREAD_DEBUG
00146 #ifdef HAVE_VA_ARGS_MACRO
00147 void rb_thread_debug(const char *file, int line, const char *fmt, ...);
00148 #define thread_debug(fmt, ...) rb_thread_debug(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
00149 #define POSITION_FORMAT "%s:%d:"
00150 #define POSITION_ARGS ,file, line
00151 #else
00152 void rb_thread_debug(const char *fmt, ...);
00153 #define thread_debug rb_thread_debug
00154 #define POSITION_FORMAT
00155 #define POSITION_ARGS
00156 #endif
00157
00158 # if THREAD_DEBUG < 0
00159 static int rb_thread_debug_enabled;
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169 static VALUE
00170 rb_thread_s_debug(void)
00171 {
00172 return INT2NUM(rb_thread_debug_enabled);
00173 }
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183 static VALUE
00184 rb_thread_s_debug_set(VALUE self, VALUE val)
00185 {
00186 rb_thread_debug_enabled = RTEST(val) ? NUM2INT(val) : 0;
00187 return val;
00188 }
00189 # else
00190 # define rb_thread_debug_enabled THREAD_DEBUG
00191 # endif
00192 #else
00193 #define thread_debug if(0)printf
00194 #endif
00195
00196 #ifndef __ia64
00197 #define thread_start_func_2(th, st, rst) thread_start_func_2(th, st)
00198 #endif
00199 NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start,
00200 VALUE *register_stack_start));
00201 static void timer_thread_function(void *);
00202
00203 #if defined(_WIN32)
00204 #include "thread_win32.c"
00205
00206 #define DEBUG_OUT() \
00207 WaitForSingleObject(&debug_mutex, INFINITE); \
00208 printf(POSITION_FORMAT"%p - %s" POSITION_ARGS, GetCurrentThreadId(), buf); \
00209 fflush(stdout); \
00210 ReleaseMutex(&debug_mutex);
00211
00212 #elif defined(HAVE_PTHREAD_H)
00213 #include "thread_pthread.c"
00214
00215 #define DEBUG_OUT() \
00216 pthread_mutex_lock(&debug_mutex); \
00217 printf(POSITION_FORMAT"%#"PRIxVALUE" - %s" POSITION_ARGS, (VALUE)pthread_self(), buf); \
00218 fflush(stdout); \
00219 pthread_mutex_unlock(&debug_mutex);
00220
00221 #else
00222 #error "unsupported thread type"
00223 #endif
00224
00225 #if THREAD_DEBUG
00226 static int debug_mutex_initialized = 1;
00227 static rb_thread_lock_t debug_mutex;
00228
00229 void
00230 rb_thread_debug(
00231 #ifdef HAVE_VA_ARGS_MACRO
00232 const char *file, int line,
00233 #endif
00234 const char *fmt, ...)
00235 {
00236 va_list args;
00237 char buf[BUFSIZ];
00238
00239 if (!rb_thread_debug_enabled) return;
00240
00241 if (debug_mutex_initialized == 1) {
00242 debug_mutex_initialized = 0;
00243 native_mutex_initialize(&debug_mutex);
00244 }
00245
00246 va_start(args, fmt);
00247 vsnprintf(buf, BUFSIZ, fmt, args);
00248 va_end(args);
00249
00250 DEBUG_OUT();
00251 }
00252 #endif
00253
00254 void
00255 rb_thread_lock_unlock(rb_thread_lock_t *lock)
00256 {
00257 native_mutex_unlock(lock);
00258 }
00259
00260 void
00261 rb_thread_lock_destroy(rb_thread_lock_t *lock)
00262 {
00263 native_mutex_destroy(lock);
00264 }
00265
00266 static void
00267 set_unblock_function(rb_thread_t *th, rb_unblock_function_t *func, void *arg,
00268 struct rb_unblock_callback *old)
00269 {
00270 check_ints:
00271 RUBY_VM_CHECK_INTS();
00272 native_mutex_lock(&th->interrupt_lock);
00273 if (th->interrupt_flag) {
00274 native_mutex_unlock(&th->interrupt_lock);
00275 goto check_ints;
00276 }
00277 else {
00278 if (old) *old = th->unblock;
00279 th->unblock.func = func;
00280 th->unblock.arg = arg;
00281 }
00282 native_mutex_unlock(&th->interrupt_lock);
00283 }
00284
00285 static void
00286 reset_unblock_function(rb_thread_t *th, const struct rb_unblock_callback *old)
00287 {
00288 native_mutex_lock(&th->interrupt_lock);
00289 th->unblock = *old;
00290 native_mutex_unlock(&th->interrupt_lock);
00291 }
00292
00293 void
00294 rb_threadptr_interrupt(rb_thread_t *th)
00295 {
00296 native_mutex_lock(&th->interrupt_lock);
00297 RUBY_VM_SET_INTERRUPT(th);
00298 if (th->unblock.func) {
00299 (th->unblock.func)(th->unblock.arg);
00300 }
00301 else {
00302
00303 }
00304 native_mutex_unlock(&th->interrupt_lock);
00305 }
00306
00307
00308 static int
00309 terminate_i(st_data_t key, st_data_t val, rb_thread_t *main_thread)
00310 {
00311 VALUE thval = key;
00312 rb_thread_t *th;
00313 GetThreadPtr(thval, th);
00314
00315 if (th != main_thread) {
00316 thread_debug("terminate_i: %p\n", (void *)th);
00317 rb_threadptr_interrupt(th);
00318 th->thrown_errinfo = eTerminateSignal;
00319 th->status = THREAD_TO_KILL;
00320 }
00321 else {
00322 thread_debug("terminate_i: main thread (%p)\n", (void *)th);
00323 }
00324 return ST_CONTINUE;
00325 }
00326
00327 typedef struct rb_mutex_struct
00328 {
00329 rb_thread_lock_t lock;
00330 rb_thread_cond_t cond;
00331 struct rb_thread_struct volatile *th;
00332 volatile int cond_waiting, cond_notified;
00333 struct rb_mutex_struct *next_mutex;
00334 } mutex_t;
00335
00336 static void rb_mutex_unlock_all(mutex_t *mutex, rb_thread_t *th);
00337 static void rb_mutex_abandon_all(mutex_t *mutexes);
00338
00339 void
00340 rb_thread_terminate_all(void)
00341 {
00342 rb_thread_t *th = GET_THREAD();
00343 rb_vm_t *vm = th->vm;
00344
00345 if (vm->main_thread != th) {
00346 rb_bug("rb_thread_terminate_all: called by child thread (%p, %p)",
00347 (void *)vm->main_thread, (void *)th);
00348 }
00349
00350
00351 if (th->keeping_mutexes) {
00352 rb_mutex_unlock_all(th->keeping_mutexes, GET_THREAD());
00353 }
00354
00355 thread_debug("rb_thread_terminate_all (main thread: %p)\n", (void *)th);
00356 st_foreach(vm->living_threads, terminate_i, (st_data_t)th);
00357
00358 while (!rb_thread_alone()) {
00359 PUSH_TAG();
00360 if (EXEC_TAG() == 0) {
00361 rb_thread_schedule();
00362 }
00363 else {
00364
00365 }
00366 POP_TAG();
00367 }
00368 rb_thread_stop_timer_thread();
00369 }
00370
00371 static void
00372 thread_unlock_all_locking_mutexes(rb_thread_t *th)
00373 {
00374 if (th->keeping_mutexes) {
00375 rb_mutex_unlock_all(th->keeping_mutexes, th);
00376 th->keeping_mutexes = NULL;
00377 }
00378 }
00379
00380 static void
00381 thread_cleanup_func_before_exec(void *th_ptr)
00382 {
00383 rb_thread_t *th = th_ptr;
00384 th->status = THREAD_KILLED;
00385 th->machine_stack_start = th->machine_stack_end = 0;
00386 #ifdef __ia64
00387 th->machine_register_stack_start = th->machine_register_stack_end = 0;
00388 #endif
00389 }
00390
00391 static void
00392 thread_cleanup_func(void *th_ptr)
00393 {
00394 rb_thread_t *th = th_ptr;
00395
00396 th->locking_mutex = Qfalse;
00397 thread_cleanup_func_before_exec(th_ptr);
00398 native_thread_destroy(th);
00399 }
00400
00401 extern void ruby_error_print(void);
00402 static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
00403 void rb_thread_recycle_stack_release(VALUE *);
00404
00405 void
00406 ruby_thread_init_stack(rb_thread_t *th)
00407 {
00408 native_thread_init_stack(th);
00409 }
00410
00411 static int
00412 thread_start_func_2(rb_thread_t *th, VALUE *stack_start, VALUE *register_stack_start)
00413 {
00414 int state;
00415 VALUE args = th->first_args;
00416 rb_proc_t *proc;
00417 rb_thread_t *join_th;
00418 rb_thread_t *main_th;
00419 VALUE errinfo = Qnil;
00420 # ifdef USE_SIGALTSTACK
00421 void rb_register_sigaltstack(rb_thread_t *th);
00422
00423 rb_register_sigaltstack(th);
00424 # endif
00425
00426 ruby_thread_set_native(th);
00427
00428 th->machine_stack_start = stack_start;
00429 #ifdef __ia64
00430 th->machine_register_stack_start = register_stack_start;
00431 #endif
00432 thread_debug("thread start: %p\n", (void *)th);
00433
00434 native_mutex_lock(&th->vm->global_vm_lock);
00435 {
00436 thread_debug("thread start (get lock): %p\n", (void *)th);
00437 rb_thread_set_current(th);
00438
00439 TH_PUSH_TAG(th);
00440 if ((state = EXEC_TAG()) == 0) {
00441 SAVE_ROOT_JMPBUF(th, {
00442 if (!th->first_func) {
00443 GetProcPtr(th->first_proc, proc);
00444 th->errinfo = Qnil;
00445 th->local_lfp = proc->block.lfp;
00446 th->local_svar = Qnil;
00447 th->value = rb_vm_invoke_proc(th, proc, proc->block.self,
00448 (int)RARRAY_LEN(args), RARRAY_PTR(args), 0);
00449 }
00450 else {
00451 th->value = (*th->first_func)((void *)args);
00452 }
00453 });
00454 }
00455 else {
00456 errinfo = th->errinfo;
00457 if (NIL_P(errinfo)) errinfo = rb_errinfo();
00458 if (state == TAG_FATAL) {
00459
00460 }
00461 else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
00462 if (th->safe_level >= 4) {
00463 th->errinfo = rb_exc_new3(rb_eSecurityError,
00464 rb_sprintf("Insecure exit at level %d", th->safe_level));
00465 errinfo = Qnil;
00466 }
00467 }
00468 else if (th->safe_level < 4 &&
00469 (th->vm->thread_abort_on_exception ||
00470 th->abort_on_exception || RTEST(ruby_debug))) {
00471
00472 }
00473 else {
00474 errinfo = Qnil;
00475 }
00476 th->value = Qnil;
00477 }
00478
00479 th->status = THREAD_KILLED;
00480 thread_debug("thread end: %p\n", (void *)th);
00481
00482 main_th = th->vm->main_thread;
00483 if (th != main_th) {
00484 if (TYPE(errinfo) == T_OBJECT) {
00485
00486 rb_threadptr_raise(main_th, 1, &errinfo);
00487 }
00488 }
00489 TH_POP_TAG();
00490
00491
00492 if (th->locking_mutex != Qfalse) {
00493 rb_bug("thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
00494 (void *)th, th->locking_mutex);
00495 }
00496
00497
00498 if (th != main_th) {
00499 st_delete_wrap(th->vm->living_threads, th->self);
00500 }
00501
00502
00503 join_th = th->join_list_head;
00504 while (join_th) {
00505 if (join_th == main_th) errinfo = Qnil;
00506 rb_threadptr_interrupt(join_th);
00507 switch (join_th->status) {
00508 case THREAD_STOPPED: case THREAD_STOPPED_FOREVER:
00509 join_th->status = THREAD_RUNNABLE;
00510 default: break;
00511 }
00512 join_th = join_th->join_list_next;
00513 }
00514
00515 if (!th->root_fiber) {
00516 rb_thread_recycle_stack_release(th->stack);
00517 th->stack = 0;
00518 }
00519 }
00520 thread_unlock_all_locking_mutexes(th);
00521 if (th != main_th) rb_check_deadlock(th->vm);
00522 if (th->vm->main_thread == th) {
00523 ruby_cleanup(state);
00524 }
00525 else {
00526 thread_cleanup_func(th);
00527 native_mutex_unlock(&th->vm->global_vm_lock);
00528 }
00529
00530 return 0;
00531 }
00532
00533 static VALUE
00534 thread_create_core(VALUE thval, VALUE args, VALUE (*fn)(ANYARGS))
00535 {
00536 rb_thread_t *th;
00537 int err;
00538
00539 if (OBJ_FROZEN(GET_THREAD()->thgroup)) {
00540 rb_raise(rb_eThreadError,
00541 "can't start a new thread (frozen ThreadGroup)");
00542 }
00543 GetThreadPtr(thval, th);
00544
00545
00546 th->first_func = fn;
00547 th->first_proc = fn ? Qfalse : rb_block_proc();
00548 th->first_args = args;
00549
00550 th->priority = GET_THREAD()->priority;
00551 th->thgroup = GET_THREAD()->thgroup;
00552
00553 native_mutex_initialize(&th->interrupt_lock);
00554 if (GET_VM()->event_hooks != NULL)
00555 th->event_flags |= RUBY_EVENT_VM;
00556
00557
00558 st_insert(th->vm->living_threads, thval, (st_data_t) th->thread_id);
00559 err = native_thread_create(th);
00560 if (err) {
00561 st_delete_wrap(th->vm->living_threads, th->self);
00562 th->status = THREAD_KILLED;
00563 rb_raise(rb_eThreadError, "can't create Thread (%d)", err);
00564 }
00565 return thval;
00566 }
00567
00568
00569 static VALUE
00570 thread_s_new(int argc, VALUE *argv, VALUE klass)
00571 {
00572 rb_thread_t *th;
00573 VALUE thread = rb_thread_alloc(klass);
00574 rb_obj_call_init(thread, argc, argv);
00575 GetThreadPtr(thread, th);
00576 if (!th->first_args) {
00577 rb_raise(rb_eThreadError, "uninitialized thread - check `%s#initialize'",
00578 rb_class2name(klass));
00579 }
00580 return thread;
00581 }
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593 static VALUE
00594 thread_start(VALUE klass, VALUE args)
00595 {
00596 return thread_create_core(rb_thread_alloc(klass), args, 0);
00597 }
00598
00599
00600 static VALUE
00601 thread_initialize(VALUE thread, VALUE args)
00602 {
00603 rb_thread_t *th;
00604 if (!rb_block_given_p()) {
00605 rb_raise(rb_eThreadError, "must be called with a block");
00606 }
00607 GetThreadPtr(thread, th);
00608 if (th->first_args) {
00609 VALUE rb_proc_location(VALUE self);
00610 VALUE proc = th->first_proc, line, loc;
00611 const char *file;
00612 if (!proc || !RTEST(loc = rb_proc_location(proc))) {
00613 rb_raise(rb_eThreadError, "already initialized thread");
00614 }
00615 file = RSTRING_PTR(RARRAY_PTR(loc)[0]);
00616 if (NIL_P(line = RARRAY_PTR(loc)[1])) {
00617 rb_raise(rb_eThreadError, "already initialized thread - %s",
00618 file);
00619 }
00620 rb_raise(rb_eThreadError, "already initialized thread - %s:%d",
00621 file, NUM2INT(line));
00622 }
00623 return thread_create_core(thread, args, 0);
00624 }
00625
00626 VALUE
00627 rb_thread_create(VALUE (*fn)(ANYARGS), void *arg)
00628 {
00629 return thread_create_core(rb_thread_alloc(rb_cThread), (VALUE)arg, fn);
00630 }
00631
00632
00633
00634 #define DELAY_INFTY 1E30
00635
00636 struct join_arg {
00637 rb_thread_t *target, *waiting;
00638 double limit;
00639 int forever;
00640 };
00641
00642 static VALUE
00643 remove_from_join_list(VALUE arg)
00644 {
00645 struct join_arg *p = (struct join_arg *)arg;
00646 rb_thread_t *target_th = p->target, *th = p->waiting;
00647
00648 if (target_th->status != THREAD_KILLED) {
00649 rb_thread_t **pth = &target_th->join_list_head;
00650
00651 while (*pth) {
00652 if (*pth == th) {
00653 *pth = th->join_list_next;
00654 break;
00655 }
00656 pth = &(*pth)->join_list_next;
00657 }
00658 }
00659
00660 return Qnil;
00661 }
00662
00663 static VALUE
00664 thread_join_sleep(VALUE arg)
00665 {
00666 struct join_arg *p = (struct join_arg *)arg;
00667 rb_thread_t *target_th = p->target, *th = p->waiting;
00668 double now, limit = p->limit;
00669
00670 while (target_th->status != THREAD_KILLED) {
00671 if (p->forever) {
00672 sleep_forever(th, 1);
00673 }
00674 else {
00675 now = timeofday();
00676 if (now > limit) {
00677 thread_debug("thread_join: timeout (thid: %p)\n",
00678 (void *)target_th->thread_id);
00679 return Qfalse;
00680 }
00681 sleep_wait_for_interrupt(th, limit - now);
00682 }
00683 thread_debug("thread_join: interrupted (thid: %p)\n",
00684 (void *)target_th->thread_id);
00685 }
00686 return Qtrue;
00687 }
00688
00689 static VALUE
00690 thread_join(rb_thread_t *target_th, double delay)
00691 {
00692 rb_thread_t *th = GET_THREAD();
00693 struct join_arg arg;
00694
00695 arg.target = target_th;
00696 arg.waiting = th;
00697 arg.limit = timeofday() + delay;
00698 arg.forever = delay == DELAY_INFTY;
00699
00700 thread_debug("thread_join (thid: %p)\n", (void *)target_th->thread_id);
00701
00702 if (target_th->status != THREAD_KILLED) {
00703 th->join_list_next = target_th->join_list_head;
00704 target_th->join_list_head = th;
00705 if (!rb_ensure(thread_join_sleep, (VALUE)&arg,
00706 remove_from_join_list, (VALUE)&arg)) {
00707 return Qnil;
00708 }
00709 }
00710
00711 thread_debug("thread_join: success (thid: %p)\n",
00712 (void *)target_th->thread_id);
00713
00714 if (target_th->errinfo != Qnil) {
00715 VALUE err = target_th->errinfo;
00716
00717 if (FIXNUM_P(err)) {
00718
00719 }
00720 else if (TYPE(target_th->errinfo) == T_NODE) {
00721 rb_exc_raise(rb_vm_make_jump_tag_but_local_jump(
00722 GET_THROWOBJ_STATE(err), GET_THROWOBJ_VAL(err)));
00723 }
00724 else {
00725
00726 rb_exc_raise(err);
00727 }
00728 }
00729 return target_th->self;
00730 }
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772 static VALUE
00773 thread_join_m(int argc, VALUE *argv, VALUE self)
00774 {
00775 rb_thread_t *target_th;
00776 double delay = DELAY_INFTY;
00777 VALUE limit;
00778
00779 GetThreadPtr(self, target_th);
00780
00781 rb_scan_args(argc, argv, "01", &limit);
00782 if (!NIL_P(limit)) {
00783 delay = rb_num2dbl(limit);
00784 }
00785
00786 return thread_join(target_th, delay);
00787 }
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800 static VALUE
00801 thread_value(VALUE self)
00802 {
00803 rb_thread_t *th;
00804 GetThreadPtr(self, th);
00805 thread_join(th, DELAY_INFTY);
00806 return th->value;
00807 }
00808
00809
00810
00811
00812
00813 static struct timeval
00814 double2timeval(double d)
00815 {
00816 struct timeval time;
00817
00818 time.tv_sec = (int)d;
00819 time.tv_usec = (int)((d - (int)d) * 1e6);
00820 if (time.tv_usec < 0) {
00821 time.tv_usec += (int)1e6;
00822 time.tv_sec -= 1;
00823 }
00824 return time;
00825 }
00826
00827 static void
00828 sleep_forever(rb_thread_t *th, int deadlockable)
00829 {
00830 enum rb_thread_status prev_status = th->status;
00831
00832 th->status = deadlockable ? THREAD_STOPPED_FOREVER : THREAD_STOPPED;
00833 do {
00834 if (deadlockable) {
00835 th->vm->sleeper++;
00836 rb_check_deadlock(th->vm);
00837 }
00838 native_sleep(th, 0);
00839 if (deadlockable) {
00840 th->vm->sleeper--;
00841 }
00842 RUBY_VM_CHECK_INTS();
00843 } while (th->status == THREAD_STOPPED_FOREVER);
00844 th->status = prev_status;
00845 }
00846
00847 static void
00848 getclockofday(struct timeval *tp)
00849 {
00850 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
00851 struct timespec ts;
00852
00853 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
00854 tp->tv_sec = ts.tv_sec;
00855 tp->tv_usec = ts.tv_nsec / 1000;
00856 } else
00857 #endif
00858 {
00859 gettimeofday(tp, NULL);
00860 }
00861 }
00862
00863 static void
00864 sleep_timeval(rb_thread_t *th, struct timeval tv)
00865 {
00866 struct timeval to, tvn;
00867 enum rb_thread_status prev_status = th->status;
00868
00869 getclockofday(&to);
00870 to.tv_sec += tv.tv_sec;
00871 if ((to.tv_usec += tv.tv_usec) >= 1000000) {
00872 to.tv_sec++;
00873 to.tv_usec -= 1000000;
00874 }
00875
00876 th->status = THREAD_STOPPED;
00877 do {
00878 native_sleep(th, &tv);
00879 RUBY_VM_CHECK_INTS();
00880 getclockofday(&tvn);
00881 if (to.tv_sec < tvn.tv_sec) break;
00882 if (to.tv_sec == tvn.tv_sec && to.tv_usec <= tvn.tv_usec) break;
00883 thread_debug("sleep_timeval: %ld.%.6ld > %ld.%.6ld\n",
00884 (long)to.tv_sec, (long)to.tv_usec,
00885 (long)tvn.tv_sec, (long)tvn.tv_usec);
00886 tv.tv_sec = to.tv_sec - tvn.tv_sec;
00887 if ((tv.tv_usec = to.tv_usec - tvn.tv_usec) < 0) {
00888 --tv.tv_sec;
00889 tv.tv_usec += 1000000;
00890 }
00891 } while (th->status == THREAD_STOPPED);
00892 th->status = prev_status;
00893 }
00894
00895 void
00896 rb_thread_sleep_forever(void)
00897 {
00898 thread_debug("rb_thread_sleep_forever\n");
00899 sleep_forever(GET_THREAD(), 0);
00900 }
00901
00902 static void
00903 rb_thread_sleep_deadly(void)
00904 {
00905 thread_debug("rb_thread_sleep_deadly\n");
00906 sleep_forever(GET_THREAD(), 1);
00907 }
00908
00909 static double
00910 timeofday(void)
00911 {
00912 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
00913 struct timespec tp;
00914
00915 if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) {
00916 return (double)tp.tv_sec + (double)tp.tv_nsec * 1e-9;
00917 } else
00918 #endif
00919 {
00920 struct timeval tv;
00921 gettimeofday(&tv, NULL);
00922 return (double)tv.tv_sec + (double)tv.tv_usec * 1e-6;
00923 }
00924 }
00925
00926 static void
00927 sleep_wait_for_interrupt(rb_thread_t *th, double sleepsec)
00928 {
00929 sleep_timeval(th, double2timeval(sleepsec));
00930 }
00931
00932 static void
00933 sleep_for_polling(rb_thread_t *th)
00934 {
00935 struct timeval time;
00936 time.tv_sec = 0;
00937 time.tv_usec = 100 * 1000;
00938 sleep_timeval(th, time);
00939 }
00940
00941 void
00942 rb_thread_wait_for(struct timeval time)
00943 {
00944 rb_thread_t *th = GET_THREAD();
00945 sleep_timeval(th, time);
00946 }
00947
00948 void
00949 rb_thread_polling(void)
00950 {
00951 RUBY_VM_CHECK_INTS();
00952 if (!rb_thread_alone()) {
00953 rb_thread_t *th = GET_THREAD();
00954 sleep_for_polling(th);
00955 }
00956 }
00957
00958
00959
00960
00961
00962
00963
00964
00965 void
00966 rb_thread_check_ints(void)
00967 {
00968 RUBY_VM_CHECK_INTS();
00969 }
00970
00971
00972
00973
00974
00975 int
00976 rb_thread_check_trap_pending(void)
00977 {
00978 return GET_THREAD()->exec_signal != 0;
00979 }
00980
00981
00982 int
00983 rb_thread_interrupted(VALUE thval)
00984 {
00985 rb_thread_t *th;
00986 GetThreadPtr(thval, th);
00987 return RUBY_VM_INTERRUPTED(th);
00988 }
00989
00990 struct timeval rb_time_timeval(VALUE);
00991
00992 void
00993 rb_thread_sleep(int sec)
00994 {
00995 rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
00996 }
00997
00998 static void rb_threadptr_execute_interrupts_rec(rb_thread_t *, int);
00999
01000 static void
01001 rb_thread_schedule_rec(int sched_depth)
01002 {
01003 thread_debug("rb_thread_schedule\n");
01004 if (!rb_thread_alone()) {
01005 rb_thread_t *th = GET_THREAD();
01006
01007 thread_debug("rb_thread_schedule/switch start\n");
01008
01009 RB_GC_SAVE_MACHINE_CONTEXT(th);
01010 native_mutex_unlock(&th->vm->global_vm_lock);
01011 {
01012 native_thread_yield();
01013 }
01014 native_mutex_lock(&th->vm->global_vm_lock);
01015
01016 rb_thread_set_current(th);
01017 thread_debug("rb_thread_schedule/switch done\n");
01018
01019 if (!sched_depth && UNLIKELY(GET_THREAD()->interrupt_flag)) {
01020 rb_threadptr_execute_interrupts_rec(GET_THREAD(), sched_depth+1);
01021 }
01022 }
01023 }
01024
01025 void
01026 rb_thread_schedule(void)
01027 {
01028 rb_thread_schedule_rec(0);
01029 }
01030
01031
01032
01033 static inline void
01034 blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region)
01035 {
01036 native_mutex_lock(&th->vm->global_vm_lock);
01037 rb_thread_set_current(th);
01038 thread_debug("leave blocking region (%p)\n", (void *)th);
01039 remove_signal_thread_list(th);
01040 th->blocking_region_buffer = 0;
01041 reset_unblock_function(th, ®ion->oldubf);
01042 if (th->status == THREAD_STOPPED) {
01043 th->status = region->prev_status;
01044 }
01045 }
01046
01047 struct rb_blocking_region_buffer *
01048 rb_thread_blocking_region_begin(void)
01049 {
01050 rb_thread_t *th = GET_THREAD();
01051 struct rb_blocking_region_buffer *region = ALLOC(struct rb_blocking_region_buffer);
01052 blocking_region_begin(th, region, ubf_select, th);
01053 return region;
01054 }
01055
01056 void
01057 rb_thread_blocking_region_end(struct rb_blocking_region_buffer *region)
01058 {
01059 int saved_errno = errno;
01060 rb_thread_t *th = GET_THREAD();
01061 blocking_region_end(th, region);
01062 xfree(region);
01063 RUBY_VM_CHECK_INTS();
01064 errno = saved_errno;
01065 }
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103 VALUE
01104 rb_thread_blocking_region(
01105 rb_blocking_function_t *func, void *data1,
01106 rb_unblock_function_t *ubf, void *data2)
01107 {
01108 VALUE val;
01109 rb_thread_t *th = GET_THREAD();
01110 int saved_errno = 0;
01111
01112 if (ubf == RUBY_UBF_IO || ubf == RUBY_UBF_PROCESS) {
01113 ubf = ubf_select;
01114 data2 = th;
01115 }
01116
01117 BLOCKING_REGION({
01118 val = func(data1);
01119 saved_errno = errno;
01120 }, ubf, data2);
01121 errno = saved_errno;
01122
01123 return val;
01124 }
01125
01126
01127
01128 VALUE
01129 rb_thread_call_without_gvl(
01130 rb_blocking_function_t *func, void *data1,
01131 rb_unblock_function_t *ubf, void *data2)
01132 {
01133 return rb_thread_blocking_region(func, data1, ubf, data2);
01134 }
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168 void *
01169 rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
01170 {
01171 rb_thread_t *th = ruby_thread_from_native();
01172 struct rb_blocking_region_buffer *brb;
01173 struct rb_unblock_callback prev_unblock;
01174 void *r;
01175
01176 if (th == 0) {
01177
01178
01179
01180
01181
01182 fprintf(stderr, "[BUG] rb_thread_call_with_gvl() is called by non-ruby thread\n");
01183 exit(1);
01184 }
01185
01186 brb = (struct rb_blocking_region_buffer *)th->blocking_region_buffer;
01187 prev_unblock = th->unblock;
01188
01189 if (brb == 0) {
01190 rb_bug("rb_thread_call_with_gvl: called by a thread which has GVL.");
01191 }
01192
01193 blocking_region_end(th, brb);
01194
01195 r = (*func)(data1);
01196
01197 blocking_region_begin(th, brb, prev_unblock.func, prev_unblock.arg);
01198 return r;
01199 }
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210 int
01211 ruby_thread_has_gvl_p(void)
01212 {
01213 rb_thread_t *th = ruby_thread_from_native();
01214
01215 if (th && th->blocking_region_buffer == 0) {
01216 return 1;
01217 }
01218 else {
01219 return 0;
01220 }
01221 }
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243 static VALUE
01244 thread_s_pass(VALUE klass)
01245 {
01246 rb_thread_schedule();
01247 return Qnil;
01248 }
01249
01250
01251
01252
01253
01254 static void
01255 rb_threadptr_execute_interrupts_rec(rb_thread_t *th, int sched_depth)
01256 {
01257 if (GET_VM()->main_thread == th) {
01258 while (rb_signal_buff_size() && !th->exec_signal) native_thread_yield();
01259 }
01260
01261 if (th->raised_flag) return;
01262
01263 while (th->interrupt_flag) {
01264 enum rb_thread_status status = th->status;
01265 int timer_interrupt = th->interrupt_flag & 0x01;
01266 int finalizer_interrupt = th->interrupt_flag & 0x04;
01267
01268 th->status = THREAD_RUNNABLE;
01269 th->interrupt_flag = 0;
01270
01271
01272 if (th->exec_signal) {
01273 int sig = th->exec_signal;
01274 th->exec_signal = 0;
01275 rb_signal_exec(th, sig);
01276 }
01277
01278
01279 if (th->thrown_errinfo) {
01280 VALUE err = th->thrown_errinfo;
01281 th->thrown_errinfo = 0;
01282 thread_debug("rb_thread_execute_interrupts: %ld\n", err);
01283
01284 if (err == eKillSignal || err == eTerminateSignal) {
01285 th->errinfo = INT2FIX(TAG_FATAL);
01286 TH_JUMP_TAG(th, TAG_FATAL);
01287 }
01288 else {
01289 rb_exc_raise(err);
01290 }
01291 }
01292 th->status = status;
01293
01294 if (finalizer_interrupt) {
01295 rb_gc_finalize_deferred();
01296 }
01297
01298 if (!sched_depth && timer_interrupt) {
01299 sched_depth++;
01300 EXEC_EVENT_HOOK(th, RUBY_EVENT_SWITCH, th->cfp->self, 0, 0);
01301
01302 if (th->slice > 0) {
01303 th->slice--;
01304 }
01305 else {
01306 reschedule:
01307 rb_thread_schedule_rec(sched_depth+1);
01308 if (th->slice < 0) {
01309 th->slice++;
01310 goto reschedule;
01311 }
01312 else {
01313 th->slice = th->priority;
01314 }
01315 }
01316 }
01317 }
01318 }
01319
01320 void
01321 rb_threadptr_execute_interrupts(rb_thread_t *th)
01322 {
01323 rb_threadptr_execute_interrupts_rec(th, 0);
01324 }
01325
01326 void
01327 rb_gc_mark_threads(void)
01328 {
01329
01330 }
01331
01332
01333
01334 static void
01335 rb_threadptr_ready(rb_thread_t *th)
01336 {
01337 rb_threadptr_interrupt(th);
01338 }
01339
01340 static VALUE
01341 rb_threadptr_raise(rb_thread_t *th, int argc, VALUE *argv)
01342 {
01343 VALUE exc;
01344
01345 again:
01346 if (rb_threadptr_dead(th)) {
01347 return Qnil;
01348 }
01349
01350 if (th->thrown_errinfo != 0 || th->raised_flag) {
01351 rb_thread_schedule();
01352 goto again;
01353 }
01354
01355 exc = rb_make_exception(argc, argv);
01356 th->thrown_errinfo = exc;
01357 rb_threadptr_ready(th);
01358 return Qnil;
01359 }
01360
01361 void
01362 rb_threadptr_signal_raise(rb_thread_t *th, int sig)
01363 {
01364 VALUE argv[2];
01365
01366 argv[0] = rb_eSignal;
01367 argv[1] = INT2FIX(sig);
01368 rb_threadptr_raise(th->vm->main_thread, 2, argv);
01369 }
01370
01371 void
01372 rb_threadptr_signal_exit(rb_thread_t *th)
01373 {
01374 VALUE argv[2];
01375
01376 argv[0] = rb_eSystemExit;
01377 argv[1] = rb_str_new2("exit");
01378 rb_threadptr_raise(th->vm->main_thread, 2, argv);
01379 }
01380
01381 #if defined(POSIX_SIGNAL) && defined(SIGSEGV) && defined(HAVE_SIGALTSTACK)
01382 #define USE_SIGALTSTACK
01383 #endif
01384
01385 void
01386 ruby_thread_stack_overflow(rb_thread_t *th)
01387 {
01388 th->raised_flag = 0;
01389 #ifdef USE_SIGALTSTACK
01390 th->raised_flag = 0;
01391 rb_exc_raise(sysstack_error);
01392 #else
01393 th->errinfo = sysstack_error;
01394 TH_JUMP_TAG(th, TAG_RAISE);
01395 #endif
01396 }
01397
01398 int
01399 rb_threadptr_set_raised(rb_thread_t *th)
01400 {
01401 if (th->raised_flag & RAISED_EXCEPTION) {
01402 return 1;
01403 }
01404 th->raised_flag |= RAISED_EXCEPTION;
01405 return 0;
01406 }
01407
01408 int
01409 rb_threadptr_reset_raised(rb_thread_t *th)
01410 {
01411 if (!(th->raised_flag & RAISED_EXCEPTION)) {
01412 return 0;
01413 }
01414 th->raised_flag &= ~RAISED_EXCEPTION;
01415 return 1;
01416 }
01417
01418 void
01419 rb_thread_fd_close(int fd)
01420 {
01421
01422 }
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439
01440
01441
01442
01443
01444
01445 static VALUE
01446 thread_raise_m(int argc, VALUE *argv, VALUE self)
01447 {
01448 rb_thread_t *th;
01449 GetThreadPtr(self, th);
01450 rb_threadptr_raise(th, argc, argv);
01451 return Qnil;
01452 }
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467 VALUE
01468 rb_thread_kill(VALUE thread)
01469 {
01470 rb_thread_t *th;
01471
01472 GetThreadPtr(thread, th);
01473
01474 if (th != GET_THREAD() && th->safe_level < 4) {
01475 rb_secure(4);
01476 }
01477 if (th->status == THREAD_TO_KILL || th->status == THREAD_KILLED) {
01478 return thread;
01479 }
01480 if (th == th->vm->main_thread) {
01481 rb_exit(EXIT_SUCCESS);
01482 }
01483
01484 thread_debug("rb_thread_kill: %p (%p)\n", (void *)th, (void *)th->thread_id);
01485
01486 rb_threadptr_interrupt(th);
01487 th->thrown_errinfo = eKillSignal;
01488 th->status = THREAD_TO_KILL;
01489
01490 return thread;
01491 }
01492
01493
01494
01495
01496
01497
01498
01499
01500
01501
01502
01503
01504
01505
01506
01507
01508 static VALUE
01509 rb_thread_s_kill(VALUE obj, VALUE th)
01510 {
01511 return rb_thread_kill(th);
01512 }
01513
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523
01524
01525 static VALUE
01526 rb_thread_exit(void)
01527 {
01528 return rb_thread_kill(GET_THREAD()->self);
01529 }
01530
01531
01532
01533
01534
01535
01536
01537
01538
01539
01540
01541
01542
01543
01544
01545
01546
01547 VALUE
01548 rb_thread_wakeup(VALUE thread)
01549 {
01550 rb_thread_t *th;
01551 GetThreadPtr(thread, th);
01552
01553 if (th->status == THREAD_KILLED) {
01554 rb_raise(rb_eThreadError, "killed thread");
01555 }
01556 rb_threadptr_ready(th);
01557 if (th->status != THREAD_TO_KILL) {
01558 th->status = THREAD_RUNNABLE;
01559 }
01560 return thread;
01561 }
01562
01563
01564
01565
01566
01567
01568
01569
01570
01571
01572
01573
01574
01575
01576
01577
01578
01579
01580
01581
01582
01583 VALUE
01584 rb_thread_run(VALUE thread)
01585 {
01586 rb_thread_wakeup(thread);
01587 rb_thread_schedule();
01588 return thread;
01589 }
01590
01591
01592
01593
01594
01595
01596
01597
01598
01599
01600
01601
01602
01603
01604
01605
01606
01607
01608
01609
01610 VALUE
01611 rb_thread_stop(void)
01612 {
01613 if (rb_thread_alone()) {
01614 rb_raise(rb_eThreadError,
01615 "stopping only thread\n\tnote: use sleep to stop forever");
01616 }
01617 rb_thread_sleep_deadly();
01618 return Qnil;
01619 }
01620
01621 static int
01622 thread_list_i(st_data_t key, st_data_t val, void *data)
01623 {
01624 VALUE ary = (VALUE)data;
01625 rb_thread_t *th;
01626 GetThreadPtr((VALUE)key, th);
01627
01628 switch (th->status) {
01629 case THREAD_RUNNABLE:
01630 case THREAD_STOPPED:
01631 case THREAD_STOPPED_FOREVER:
01632 case THREAD_TO_KILL:
01633 rb_ary_push(ary, th->self);
01634 default:
01635 break;
01636 }
01637 return ST_CONTINUE;
01638 }
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649
01650
01651
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661
01662 VALUE
01663 rb_thread_list(void)
01664 {
01665 VALUE ary = rb_ary_new();
01666 st_foreach(GET_THREAD()->vm->living_threads, thread_list_i, ary);
01667 return ary;
01668 }
01669
01670 VALUE
01671 rb_thread_current(void)
01672 {
01673 return GET_THREAD()->self;
01674 }
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684
01685 static VALUE
01686 thread_s_current(VALUE klass)
01687 {
01688 return rb_thread_current();
01689 }
01690
01691 VALUE
01692 rb_thread_main(void)
01693 {
01694 return GET_THREAD()->vm->main_thread->self;
01695 }
01696
01697
01698
01699
01700
01701
01702
01703
01704 static VALUE
01705 rb_thread_s_main(VALUE klass)
01706 {
01707 return rb_thread_main();
01708 }
01709
01710
01711
01712
01713
01714
01715
01716
01717
01718
01719
01720
01721
01722
01723 static VALUE
01724 rb_thread_s_abort_exc(void)
01725 {
01726 return GET_THREAD()->vm->thread_abort_on_exception ? Qtrue : Qfalse;
01727 }
01728
01729
01730
01731
01732
01733
01734
01735
01736
01737
01738
01739
01740
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754 static VALUE
01755 rb_thread_s_abort_exc_set(VALUE self, VALUE val)
01756 {
01757 rb_secure(4);
01758 GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
01759 return val;
01760 }
01761
01762
01763
01764
01765
01766
01767
01768
01769
01770
01771
01772 static VALUE
01773 rb_thread_abort_exc(VALUE thread)
01774 {
01775 rb_thread_t *th;
01776 GetThreadPtr(thread, th);
01777 return th->abort_on_exception ? Qtrue : Qfalse;
01778 }
01779
01780
01781
01782
01783
01784
01785
01786
01787
01788
01789
01790 static VALUE
01791 rb_thread_abort_exc_set(VALUE thread, VALUE val)
01792 {
01793 rb_thread_t *th;
01794 rb_secure(4);
01795
01796 GetThreadPtr(thread, th);
01797 th->abort_on_exception = RTEST(val);
01798 return val;
01799 }
01800
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812 VALUE
01813 rb_thread_group(VALUE thread)
01814 {
01815 rb_thread_t *th;
01816 VALUE group;
01817 GetThreadPtr(thread, th);
01818 group = th->thgroup;
01819
01820 if (!group) {
01821 group = Qnil;
01822 }
01823 return group;
01824 }
01825
01826 static const char *
01827 thread_status_name(enum rb_thread_status status)
01828 {
01829 switch (status) {
01830 case THREAD_RUNNABLE:
01831 return "run";
01832 case THREAD_STOPPED:
01833 case THREAD_STOPPED_FOREVER:
01834 return "sleep";
01835 case THREAD_TO_KILL:
01836 return "aborting";
01837 case THREAD_KILLED:
01838 return "dead";
01839 default:
01840 return "unknown";
01841 }
01842 }
01843
01844 static int
01845 rb_threadptr_dead(rb_thread_t *th)
01846 {
01847 return th->status == THREAD_KILLED;
01848 }
01849
01850
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873 static VALUE
01874 rb_thread_status(VALUE thread)
01875 {
01876 rb_thread_t *th;
01877 GetThreadPtr(thread, th);
01878
01879 if (rb_threadptr_dead(th)) {
01880 if (!NIL_P(th->errinfo) && !FIXNUM_P(th->errinfo)
01881 ) {
01882 return Qnil;
01883 }
01884 return Qfalse;
01885 }
01886 return rb_str_new2(thread_status_name(th->status));
01887 }
01888
01889
01890
01891
01892
01893
01894
01895
01896
01897
01898
01899
01900
01901
01902 static VALUE
01903 rb_thread_alive_p(VALUE thread)
01904 {
01905 rb_thread_t *th;
01906 GetThreadPtr(thread, th);
01907
01908 if (rb_threadptr_dead(th))
01909 return Qfalse;
01910 return Qtrue;
01911 }
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921
01922
01923
01924
01925 static VALUE
01926 rb_thread_stop_p(VALUE thread)
01927 {
01928 rb_thread_t *th;
01929 GetThreadPtr(thread, th);
01930
01931 if (rb_threadptr_dead(th))
01932 return Qtrue;
01933 if (th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER)
01934 return Qtrue;
01935 return Qfalse;
01936 }
01937
01938
01939
01940
01941
01942
01943
01944
01945
01946
01947
01948
01949
01950 static VALUE
01951 rb_thread_safe_level(VALUE thread)
01952 {
01953 rb_thread_t *th;
01954 GetThreadPtr(thread, th);
01955
01956 return INT2NUM(th->safe_level);
01957 }
01958
01959
01960
01961
01962
01963
01964
01965
01966 static VALUE
01967 rb_thread_inspect(VALUE thread)
01968 {
01969 const char *cname = rb_obj_classname(thread);
01970 rb_thread_t *th;
01971 const char *status;
01972 VALUE str;
01973
01974 GetThreadPtr(thread, th);
01975 status = thread_status_name(th->status);
01976 str = rb_sprintf("#<%s:%p %s>", cname, (void *)thread, status);
01977 OBJ_INFECT(str, thread);
01978
01979 return str;
01980 }
01981
01982 VALUE
01983 rb_thread_local_aref(VALUE thread, ID id)
01984 {
01985 rb_thread_t *th;
01986 VALUE val;
01987
01988 GetThreadPtr(thread, th);
01989 if (rb_safe_level() >= 4 && th != GET_THREAD()) {
01990 rb_raise(rb_eSecurityError, "Insecure: thread locals");
01991 }
01992 if (!th->local_storage) {
01993 return Qnil;
01994 }
01995 if (st_lookup(th->local_storage, id, &val)) {
01996 return val;
01997 }
01998 return Qnil;
01999 }
02000
02001
02002
02003
02004
02005
02006
02007
02008
02009
02010
02011
02012
02013
02014
02015
02016
02017
02018
02019
02020
02021
02022 static VALUE
02023 rb_thread_aref(VALUE thread, VALUE id)
02024 {
02025 return rb_thread_local_aref(thread, rb_to_id(id));
02026 }
02027
02028 VALUE
02029 rb_thread_local_aset(VALUE thread, ID id, VALUE val)
02030 {
02031 rb_thread_t *th;
02032 GetThreadPtr(thread, th);
02033
02034 if (rb_safe_level() >= 4 && th != GET_THREAD()) {
02035 rb_raise(rb_eSecurityError, "Insecure: can't modify thread locals");
02036 }
02037 if (OBJ_FROZEN(thread)) {
02038 rb_error_frozen("thread locals");
02039 }
02040 if (!th->local_storage) {
02041 th->local_storage = st_init_numtable();
02042 }
02043 if (NIL_P(val)) {
02044 st_delete_wrap(th->local_storage, id);
02045 return Qnil;
02046 }
02047 st_insert(th->local_storage, id, val);
02048 return val;
02049 }
02050
02051
02052
02053
02054
02055
02056
02057
02058
02059 static VALUE
02060 rb_thread_aset(VALUE self, VALUE id, VALUE val)
02061 {
02062 return rb_thread_local_aset(self, rb_to_id(id), val);
02063 }
02064
02065
02066
02067
02068
02069
02070
02071
02072
02073
02074
02075
02076
02077
02078 static VALUE
02079 rb_thread_key_p(VALUE self, VALUE key)
02080 {
02081 rb_thread_t *th;
02082 ID id = rb_to_id(key);
02083
02084 GetThreadPtr(self, th);
02085
02086 if (!th->local_storage) {
02087 return Qfalse;
02088 }
02089 if (st_lookup(th->local_storage, id, 0)) {
02090 return Qtrue;
02091 }
02092 return Qfalse;
02093 }
02094
02095 static int
02096 thread_keys_i(ID key, VALUE value, VALUE ary)
02097 {
02098 rb_ary_push(ary, ID2SYM(key));
02099 return ST_CONTINUE;
02100 }
02101
02102 static int
02103 vm_living_thread_num(rb_vm_t *vm)
02104 {
02105 return vm->living_threads->num_entries;
02106 }
02107
02108 int
02109 rb_thread_alone(void)
02110 {
02111 int num = 1;
02112 if (GET_THREAD()->vm->living_threads) {
02113 num = vm_living_thread_num(GET_THREAD()->vm);
02114 thread_debug("rb_thread_alone: %d\n", num);
02115 }
02116 return num == 1;
02117 }
02118
02119
02120
02121
02122
02123
02124
02125
02126
02127
02128
02129
02130
02131
02132
02133 static VALUE
02134 rb_thread_keys(VALUE self)
02135 {
02136 rb_thread_t *th;
02137 VALUE ary = rb_ary_new();
02138 GetThreadPtr(self, th);
02139
02140 if (th->local_storage) {
02141 st_foreach(th->local_storage, thread_keys_i, ary);
02142 }
02143 return ary;
02144 }
02145
02146
02147
02148
02149
02150
02151
02152
02153
02154
02155
02156
02157
02158
02159
02160
02161 static VALUE
02162 rb_thread_priority(VALUE thread)
02163 {
02164 rb_thread_t *th;
02165 GetThreadPtr(thread, th);
02166 return INT2NUM(th->priority);
02167 }
02168
02169
02170
02171
02172
02173
02174
02175
02176
02177
02178
02179
02180
02181
02182
02183
02184
02185
02186
02187
02188
02189
02190
02191
02192
02193
02194
02195
02196 static VALUE
02197 rb_thread_priority_set(VALUE thread, VALUE prio)
02198 {
02199 rb_thread_t *th;
02200 int priority;
02201 GetThreadPtr(thread, th);
02202
02203 rb_secure(4);
02204
02205 #if USE_NATIVE_THREAD_PRIORITY
02206 th->priority = NUM2INT(prio);
02207 native_thread_apply_priority(th);
02208 #else
02209 priority = NUM2INT(prio);
02210 if (priority > RUBY_THREAD_PRIORITY_MAX) {
02211 priority = RUBY_THREAD_PRIORITY_MAX;
02212 }
02213 else if (priority < RUBY_THREAD_PRIORITY_MIN) {
02214 priority = RUBY_THREAD_PRIORITY_MIN;
02215 }
02216 th->priority = priority;
02217 th->slice = priority;
02218 #endif
02219 return INT2NUM(th->priority);
02220 }
02221
02222
02223
02224 #if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
02225
02226
02227
02228
02229
02230
02231
02232
02233
02234
02235
02236
02237
02238
02239
02240
02241
02242
02243
02244
02245
02246
02247
02248
02249
02250
02251
02252
02253 void
02254 rb_fd_init(volatile rb_fdset_t *fds)
02255 {
02256 fds->maxfd = 0;
02257 fds->fdset = ALLOC(fd_set);
02258 FD_ZERO(fds->fdset);
02259 }
02260
02261 void
02262 rb_fd_term(rb_fdset_t *fds)
02263 {
02264 if (fds->fdset) xfree(fds->fdset);
02265 fds->maxfd = 0;
02266 fds->fdset = 0;
02267 }
02268
02269 void
02270 rb_fd_zero(rb_fdset_t *fds)
02271 {
02272 if (fds->fdset) {
02273 MEMZERO(fds->fdset, fd_mask, howmany(fds->maxfd, NFDBITS));
02274 FD_ZERO(fds->fdset);
02275 }
02276 }
02277
02278 static void
02279 rb_fd_resize(int n, rb_fdset_t *fds)
02280 {
02281 size_t m = howmany(n + 1, NFDBITS) * sizeof(fd_mask);
02282 size_t o = howmany(fds->maxfd, NFDBITS) * sizeof(fd_mask);
02283
02284 if (m < sizeof(fd_set)) m = sizeof(fd_set);
02285 if (o < sizeof(fd_set)) o = sizeof(fd_set);
02286
02287 if (m > o) {
02288 fds->fdset = xrealloc(fds->fdset, m);
02289 memset((char *)fds->fdset + o, 0, m - o);
02290 }
02291 if (n >= fds->maxfd) fds->maxfd = n + 1;
02292 }
02293
02294 void
02295 rb_fd_set(int n, rb_fdset_t *fds)
02296 {
02297 rb_fd_resize(n, fds);
02298 FD_SET(n, fds->fdset);
02299 }
02300
02301 void
02302 rb_fd_clr(int n, rb_fdset_t *fds)
02303 {
02304 if (n >= fds->maxfd) return;
02305 FD_CLR(n, fds->fdset);
02306 }
02307
02308 int
02309 rb_fd_isset(int n, const rb_fdset_t *fds)
02310 {
02311 if (n >= fds->maxfd) return 0;
02312 return FD_ISSET(n, fds->fdset) != 0;
02313 }
02314
02315 void
02316 rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
02317 {
02318 size_t size = howmany(max, NFDBITS) * sizeof(fd_mask);
02319
02320 if (size < sizeof(fd_set)) size = sizeof(fd_set);
02321 dst->maxfd = max;
02322 dst->fdset = xrealloc(dst->fdset, size);
02323 memcpy(dst->fdset, src, size);
02324 }
02325
02326 int
02327 rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
02328 {
02329 fd_set *r = NULL, *w = NULL, *e = NULL;
02330 if (readfds) {
02331 rb_fd_resize(n - 1, readfds);
02332 r = rb_fd_ptr(readfds);
02333 }
02334 if (writefds) {
02335 rb_fd_resize(n - 1, writefds);
02336 w = rb_fd_ptr(writefds);
02337 }
02338 if (exceptfds) {
02339 rb_fd_resize(n - 1, exceptfds);
02340 e = rb_fd_ptr(exceptfds);
02341 }
02342 return select(n, r, w, e, timeout);
02343 }
02344
02345 #undef FD_ZERO
02346 #undef FD_SET
02347 #undef FD_CLR
02348 #undef FD_ISSET
02349
02350 #define FD_ZERO(f) rb_fd_zero(f)
02351 #define FD_SET(i, f) rb_fd_set(i, f)
02352 #define FD_CLR(i, f) rb_fd_clr(i, f)
02353 #define FD_ISSET(i, f) rb_fd_isset(i, f)
02354
02355 #elif defined(_WIN32)
02356
02357 void
02358 rb_fd_init(volatile rb_fdset_t *set)
02359 {
02360 set->capa = FD_SETSIZE;
02361 set->fdset = ALLOC(fd_set);
02362 FD_ZERO(set->fdset);
02363 }
02364
02365 void
02366 rb_fd_term(rb_fdset_t *set)
02367 {
02368 xfree(set->fdset);
02369 set->fdset = NULL;
02370 set->capa = 0;
02371 }
02372
02373 void
02374 rb_fd_set(int fd, rb_fdset_t *set)
02375 {
02376 unsigned int i;
02377 SOCKET s = rb_w32_get_osfhandle(fd);
02378
02379 for (i = 0; i < set->fdset->fd_count; i++) {
02380 if (set->fdset->fd_array[i] == s) {
02381 return;
02382 }
02383 }
02384 if (set->fdset->fd_count >= (unsigned)set->capa) {
02385 set->capa = (set->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
02386 set->fdset = xrealloc(set->fdset, sizeof(unsigned int) + sizeof(SOCKET) * set->capa);
02387 }
02388 set->fdset->fd_array[set->fdset->fd_count++] = s;
02389 }
02390
02391 #undef FD_ZERO
02392 #undef FD_SET
02393 #undef FD_CLR
02394 #undef FD_ISSET
02395
02396 #define FD_ZERO(f) rb_fd_zero(f)
02397 #define FD_SET(i, f) rb_fd_set(i, f)
02398 #define FD_CLR(i, f) rb_fd_clr(i, f)
02399 #define FD_ISSET(i, f) rb_fd_isset(i, f)
02400
02401 #endif
02402
02403 #if defined(__CYGWIN__) || defined(_WIN32)
02404 static long
02405 cmp_tv(const struct timeval *a, const struct timeval *b)
02406 {
02407 long d = (a->tv_sec - b->tv_sec);
02408 return (d != 0) ? d : (a->tv_usec - b->tv_usec);
02409 }
02410
02411 static int
02412 subtract_tv(struct timeval *rest, const struct timeval *wait)
02413 {
02414 if (rest->tv_sec < wait->tv_sec) {
02415 return 0;
02416 }
02417 while (rest->tv_usec < wait->tv_usec) {
02418 if (rest->tv_sec <= wait->tv_sec) {
02419 return 0;
02420 }
02421 rest->tv_sec -= 1;
02422 rest->tv_usec += 1000 * 1000;
02423 }
02424 rest->tv_sec -= wait->tv_sec;
02425 rest->tv_usec -= wait->tv_usec;
02426 return rest->tv_sec != 0 || rest->tv_usec != 0;
02427 }
02428 #endif
02429
02430 static int
02431 do_select(int n, fd_set *read, fd_set *write, fd_set *except,
02432 struct timeval *timeout)
02433 {
02434 int result, lerrno;
02435 fd_set UNINITIALIZED_VAR(orig_read);
02436 fd_set UNINITIALIZED_VAR(orig_write);
02437 fd_set UNINITIALIZED_VAR(orig_except);
02438
02439 #ifndef linux
02440 double limit = 0;
02441 struct timeval wait_rest;
02442 # if defined(__CYGWIN__) || defined(_WIN32)
02443 struct timeval start_time;
02444 # endif
02445
02446 if (timeout) {
02447 # if defined(__CYGWIN__) || defined(_WIN32)
02448 gettimeofday(&start_time, NULL);
02449 limit = (double)start_time.tv_sec + (double)start_time.tv_usec*1e-6;
02450 # else
02451 limit = timeofday();
02452 # endif
02453 limit += (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6;
02454 wait_rest = *timeout;
02455 timeout = &wait_rest;
02456 }
02457 #endif
02458
02459 if (read) orig_read = *read;
02460 if (write) orig_write = *write;
02461 if (except) orig_except = *except;
02462
02463 retry:
02464 lerrno = 0;
02465
02466 #if defined(__CYGWIN__) || defined(_WIN32)
02467 {
02468 int finish = 0;
02469
02470 struct timeval wait_100ms, *wait;
02471 wait_100ms.tv_sec = 0;
02472 wait_100ms.tv_usec = 100 * 1000;
02473
02474 do {
02475 wait = (timeout == 0 || cmp_tv(&wait_100ms, timeout) < 0) ? &wait_100ms : timeout;
02476 BLOCKING_REGION({
02477 do {
02478 result = select(n, read, write, except, wait);
02479 if (result < 0) lerrno = errno;
02480 if (result != 0) break;
02481
02482 if (read) *read = orig_read;
02483 if (write) *write = orig_write;
02484 if (except) *except = orig_except;
02485 if (timeout) {
02486 struct timeval elapsed;
02487 gettimeofday(&elapsed, NULL);
02488 subtract_tv(&elapsed, &start_time);
02489 gettimeofday(&start_time, NULL);
02490 if (!subtract_tv(timeout, &elapsed)) {
02491 finish = 1;
02492 break;
02493 }
02494 if (cmp_tv(&wait_100ms, timeout) > 0) wait = timeout;
02495 }
02496 } while (__th->interrupt_flag == 0);
02497 }, 0, 0);
02498 } while (result == 0 && !finish);
02499 }
02500 #else
02501 BLOCKING_REGION({
02502 result = select(n, read, write, except, timeout);
02503 if (result < 0) lerrno = errno;
02504 }, ubf_select, GET_THREAD());
02505 #endif
02506
02507 errno = lerrno;
02508
02509 if (result < 0) {
02510 switch (errno) {
02511 case EINTR:
02512 #ifdef ERESTART
02513 case ERESTART:
02514 #endif
02515 if (read) *read = orig_read;
02516 if (write) *write = orig_write;
02517 if (except) *except = orig_except;
02518 #ifndef linux
02519 if (timeout) {
02520 double d = limit - timeofday();
02521
02522 wait_rest.tv_sec = (unsigned int)d;
02523 wait_rest.tv_usec = (int)((d-(double)wait_rest.tv_sec)*1e6);
02524 if (wait_rest.tv_sec < 0) wait_rest.tv_sec = 0;
02525 if (wait_rest.tv_usec < 0) wait_rest.tv_usec = 0;
02526 }
02527 #endif
02528 goto retry;
02529 default:
02530 break;
02531 }
02532 }
02533 return result;
02534 }
02535
02536 static void
02537 rb_thread_wait_fd_rw(int fd, int read)
02538 {
02539 int result = 0;
02540 thread_debug("rb_thread_wait_fd_rw(%d, %s)\n", fd, read ? "read" : "write");
02541
02542 if (fd < 0) {
02543 rb_raise(rb_eIOError, "closed stream");
02544 }
02545 if (rb_thread_alone()) return;
02546 while (result <= 0) {
02547 rb_fdset_t set;
02548 rb_fd_init(&set);
02549 FD_SET(fd, &set);
02550
02551 if (read) {
02552 result = do_select(fd + 1, rb_fd_ptr(&set), 0, 0, 0);
02553 }
02554 else {
02555 result = do_select(fd + 1, 0, rb_fd_ptr(&set), 0, 0);
02556 }
02557
02558 rb_fd_term(&set);
02559
02560 if (result < 0) {
02561 rb_sys_fail(0);
02562 }
02563 }
02564
02565 thread_debug("rb_thread_wait_fd_rw(%d, %s): done\n", fd, read ? "read" : "write");
02566 }
02567
02568 void
02569 rb_thread_wait_fd(int fd)
02570 {
02571 rb_thread_wait_fd_rw(fd, 1);
02572 }
02573
02574 int
02575 rb_thread_fd_writable(int fd)
02576 {
02577 rb_thread_wait_fd_rw(fd, 0);
02578 return TRUE;
02579 }
02580
02581 int
02582 rb_thread_select(int max, fd_set * read, fd_set * write, fd_set * except,
02583 struct timeval *timeout)
02584 {
02585 if (!read && !write && !except) {
02586 if (!timeout) {
02587 rb_thread_sleep_forever();
02588 return 0;
02589 }
02590 rb_thread_wait_for(*timeout);
02591 return 0;
02592 }
02593 else {
02594 return do_select(max, read, write, except, timeout);
02595 }
02596 }
02597
02598
02599 int
02600 rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
02601 struct timeval *timeout)
02602 {
02603 fd_set *r = NULL, *w = NULL, *e = NULL;
02604
02605 if (!read && !write && !except) {
02606 if (!timeout) {
02607 rb_thread_sleep_forever();
02608 return 0;
02609 }
02610 rb_thread_wait_for(*timeout);
02611 return 0;
02612 }
02613
02614 if (read) {
02615 rb_fd_resize(max - 1, read);
02616 r = rb_fd_ptr(read);
02617 }
02618 if (write) {
02619 rb_fd_resize(max - 1, write);
02620 w = rb_fd_ptr(write);
02621 }
02622 if (except) {
02623 rb_fd_resize(max - 1, except);
02624 e = rb_fd_ptr(except);
02625 }
02626 return do_select(max, r, w, e, timeout);
02627 }
02628
02629
02630
02631
02632
02633
02634 #ifdef USE_CONSERVATIVE_STACK_END
02635 void
02636 rb_gc_set_stack_end(VALUE **stack_end_p)
02637 {
02638 VALUE stack_end;
02639 *stack_end_p = &stack_end;
02640 }
02641 #endif
02642
02643 void
02644 rb_gc_save_machine_context(rb_thread_t *th)
02645 {
02646 FLUSH_REGISTER_WINDOWS;
02647 #ifdef __ia64
02648 th->machine_register_stack_end = rb_ia64_bsp();
02649 #endif
02650 setjmp(th->machine_regs);
02651 }
02652
02653
02654
02655
02656
02657 int rb_get_next_signal(void);
02658
02659 void
02660 rb_threadptr_check_signal(rb_thread_t *mth)
02661 {
02662 int sig;
02663
02664
02665
02666 if (!mth->exec_signal && (sig = rb_get_next_signal()) > 0) {
02667 enum rb_thread_status prev_status = mth->status;
02668 thread_debug("main_thread: %s, sig: %d\n",
02669 thread_status_name(prev_status), sig);
02670 mth->exec_signal = sig;
02671 if (mth->status != THREAD_KILLED) mth->status = THREAD_RUNNABLE;
02672 rb_threadptr_interrupt(mth);
02673 mth->status = prev_status;
02674 }
02675 }
02676
02677 static void
02678 timer_thread_function(void *arg)
02679 {
02680 rb_vm_t *vm = GET_VM();
02681
02682
02683 RUBY_VM_SET_TIMER_INTERRUPT(vm->running_thread);
02684
02685
02686 rb_threadptr_check_signal(vm->main_thread);
02687
02688 #if 0
02689
02690 if (vm->prove_profile.enable) {
02691 rb_thread_t *th = vm->running_thread;
02692
02693 if (vm->during_gc) {
02694
02695 }
02696 }
02697 #endif
02698 }
02699
02700 void
02701 rb_thread_stop_timer_thread(void)
02702 {
02703 if (timer_thread_id && native_stop_timer_thread()) {
02704 native_reset_timer_thread();
02705 }
02706 }
02707
02708 void
02709 rb_thread_reset_timer_thread(void)
02710 {
02711 native_reset_timer_thread();
02712 }
02713
02714 void
02715 rb_thread_start_timer_thread(void)
02716 {
02717 system_working = 1;
02718 rb_thread_create_timer_thread();
02719 }
02720
02721 static int
02722 clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
02723 {
02724 int i;
02725 VALUE lines = (VALUE)val;
02726
02727 for (i = 0; i < RARRAY_LEN(lines); i++) {
02728 if (RARRAY_PTR(lines)[i] != Qnil) {
02729 RARRAY_PTR(lines)[i] = INT2FIX(0);
02730 }
02731 }
02732 return ST_CONTINUE;
02733 }
02734
02735 static void
02736 clear_coverage(void)
02737 {
02738 extern VALUE rb_get_coverages(void);
02739 VALUE coverages = rb_get_coverages();
02740 if (RTEST(coverages)) {
02741 st_foreach(RHASH_TBL(coverages), clear_coverage_i, 0);
02742 }
02743 }
02744
02745 static void
02746 rb_thread_atfork_internal(int (*atfork)(st_data_t, st_data_t, st_data_t))
02747 {
02748 rb_thread_t *th = GET_THREAD();
02749 rb_vm_t *vm = th->vm;
02750 VALUE thval = th->self;
02751 vm->main_thread = th;
02752
02753 native_mutex_reinitialize_atfork(&th->vm->global_vm_lock);
02754 st_foreach(vm->living_threads, atfork, (st_data_t)th);
02755 st_clear(vm->living_threads);
02756 st_insert(vm->living_threads, thval, (st_data_t)th->thread_id);
02757 vm->sleeper = 0;
02758 clear_coverage();
02759 }
02760
02761 static int
02762 terminate_atfork_i(st_data_t key, st_data_t val, st_data_t current_th)
02763 {
02764 VALUE thval = key;
02765 rb_thread_t *th;
02766 GetThreadPtr(thval, th);
02767
02768 if (th != (rb_thread_t *)current_th) {
02769 if (th->keeping_mutexes) {
02770 rb_mutex_abandon_all(th->keeping_mutexes);
02771 }
02772 th->keeping_mutexes = NULL;
02773 thread_cleanup_func(th);
02774 }
02775 return ST_CONTINUE;
02776 }
02777
02778 void
02779 rb_thread_atfork(void)
02780 {
02781 rb_thread_atfork_internal(terminate_atfork_i);
02782 GET_THREAD()->join_list_head = 0;
02783 rb_reset_random_seed();
02784 }
02785
02786 static int
02787 terminate_atfork_before_exec_i(st_data_t key, st_data_t val, st_data_t current_th)
02788 {
02789 VALUE thval = key;
02790 rb_thread_t *th;
02791 GetThreadPtr(thval, th);
02792
02793 if (th != (rb_thread_t *)current_th) {
02794 thread_cleanup_func_before_exec(th);
02795 }
02796 return ST_CONTINUE;
02797 }
02798
02799 void
02800 rb_thread_atfork_before_exec(void)
02801 {
02802 rb_thread_atfork_internal(terminate_atfork_before_exec_i);
02803 }
02804
02805 struct thgroup {
02806 int enclosed;
02807 VALUE group;
02808 };
02809
02810 static size_t
02811 thgroup_memsize(const void *ptr)
02812 {
02813 return ptr ? sizeof(struct thgroup) : 0;
02814 }
02815
02816 static const rb_data_type_t thgroup_data_type = {
02817 "thgroup",
02818 NULL, RUBY_TYPED_DEFAULT_FREE, thgroup_memsize,
02819 };
02820
02821
02822
02823
02824
02825
02826
02827
02828
02829
02830
02831
02832
02833 static VALUE
02834 thgroup_s_alloc(VALUE klass)
02835 {
02836 VALUE group;
02837 struct thgroup *data;
02838
02839 group = TypedData_Make_Struct(klass, struct thgroup, &thgroup_data_type, data);
02840 data->enclosed = 0;
02841 data->group = group;
02842
02843 return group;
02844 }
02845
02846 struct thgroup_list_params {
02847 VALUE ary;
02848 VALUE group;
02849 };
02850
02851 static int
02852 thgroup_list_i(st_data_t key, st_data_t val, st_data_t data)
02853 {
02854 VALUE thread = (VALUE)key;
02855 VALUE ary = ((struct thgroup_list_params *)data)->ary;
02856 VALUE group = ((struct thgroup_list_params *)data)->group;
02857 rb_thread_t *th;
02858 GetThreadPtr(thread, th);
02859
02860 if (th->thgroup == group) {
02861 rb_ary_push(ary, thread);
02862 }
02863 return ST_CONTINUE;
02864 }
02865
02866
02867
02868
02869
02870
02871
02872
02873
02874
02875
02876 static VALUE
02877 thgroup_list(VALUE group)
02878 {
02879 VALUE ary = rb_ary_new();
02880 struct thgroup_list_params param;
02881
02882 param.ary = ary;
02883 param.group = group;
02884 st_foreach(GET_THREAD()->vm->living_threads, thgroup_list_i, (st_data_t) & param);
02885 return ary;
02886 }
02887
02888
02889
02890
02891
02892
02893
02894
02895
02896
02897
02898
02899
02900
02901
02902
02903
02904
02905
02906
02907 static VALUE
02908 thgroup_enclose(VALUE group)
02909 {
02910 struct thgroup *data;
02911
02912 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
02913 data->enclosed = 1;
02914
02915 return group;
02916 }
02917
02918
02919
02920
02921
02922
02923
02924
02925
02926
02927 static VALUE
02928 thgroup_enclosed_p(VALUE group)
02929 {
02930 struct thgroup *data;
02931
02932 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
02933 if (data->enclosed)
02934 return Qtrue;
02935 return Qfalse;
02936 }
02937
02938
02939
02940
02941
02942
02943
02944
02945
02946
02947
02948
02949
02950
02951
02952
02953
02954
02955
02956
02957
02958
02959
02960
02961
02962
02963
02964
02965 static VALUE
02966 thgroup_add(VALUE group, VALUE thread)
02967 {
02968 rb_thread_t *th;
02969 struct thgroup *data;
02970
02971 rb_secure(4);
02972 GetThreadPtr(thread, th);
02973
02974 if (OBJ_FROZEN(group)) {
02975 rb_raise(rb_eThreadError, "can't move to the frozen thread group");
02976 }
02977 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
02978 if (data->enclosed) {
02979 rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
02980 }
02981
02982 if (!th->thgroup) {
02983 return Qnil;
02984 }
02985
02986 if (OBJ_FROZEN(th->thgroup)) {
02987 rb_raise(rb_eThreadError, "can't move from the frozen thread group");
02988 }
02989 TypedData_Get_Struct(th->thgroup, struct thgroup, &thgroup_data_type, data);
02990 if (data->enclosed) {
02991 rb_raise(rb_eThreadError,
02992 "can't move from the enclosed thread group");
02993 }
02994
02995 th->thgroup = group;
02996 return group;
02997 }
02998
02999
03000
03001
03002
03003
03004
03005
03006
03007
03008
03009
03010
03011
03012
03013
03014
03015
03016
03017
03018
03019
03020
03021
03022
03023
03024
03025 #define GetMutexPtr(obj, tobj) \
03026 TypedData_Get_Struct(obj, mutex_t, &mutex_data_type, tobj)
03027
03028 static const char *mutex_unlock(mutex_t *mutex, rb_thread_t volatile *th);
03029
03030 #define mutex_mark NULL
03031
03032 static void
03033 mutex_free(void *ptr)
03034 {
03035 if (ptr) {
03036 mutex_t *mutex = ptr;
03037 if (mutex->th) {
03038
03039 const char *err = mutex_unlock(mutex, mutex->th);
03040 if (err) rb_bug("%s", err);
03041 }
03042 native_mutex_destroy(&mutex->lock);
03043 native_cond_destroy(&mutex->cond);
03044 }
03045 ruby_xfree(ptr);
03046 }
03047
03048 static size_t
03049 mutex_memsize(const void *ptr)
03050 {
03051 return ptr ? sizeof(mutex_t) : 0;
03052 }
03053
03054 static const rb_data_type_t mutex_data_type = {
03055 "mutex",
03056 mutex_mark, mutex_free, mutex_memsize,
03057 };
03058
03059 static VALUE
03060 mutex_alloc(VALUE klass)
03061 {
03062 VALUE volatile obj;
03063 mutex_t *mutex;
03064
03065 obj = TypedData_Make_Struct(klass, mutex_t, &mutex_data_type, mutex);
03066 native_mutex_initialize(&mutex->lock);
03067 native_cond_initialize(&mutex->cond);
03068 return obj;
03069 }
03070
03071
03072
03073
03074
03075
03076
03077 static VALUE
03078 mutex_initialize(VALUE self)
03079 {
03080 return self;
03081 }
03082
03083 VALUE
03084 rb_mutex_new(void)
03085 {
03086 return mutex_alloc(rb_cMutex);
03087 }
03088
03089
03090
03091
03092
03093
03094
03095 VALUE
03096 rb_mutex_locked_p(VALUE self)
03097 {
03098 mutex_t *mutex;
03099 GetMutexPtr(self, mutex);
03100 return mutex->th ? Qtrue : Qfalse;
03101 }
03102
03103 static void
03104 mutex_locked(rb_thread_t *th, VALUE self)
03105 {
03106 mutex_t *mutex;
03107 GetMutexPtr(self, mutex);
03108
03109 if (th->keeping_mutexes) {
03110 mutex->next_mutex = th->keeping_mutexes;
03111 }
03112 th->keeping_mutexes = mutex;
03113 }
03114
03115
03116
03117
03118
03119
03120
03121
03122 VALUE
03123 rb_mutex_trylock(VALUE self)
03124 {
03125 mutex_t *mutex;
03126 VALUE locked = Qfalse;
03127 GetMutexPtr(self, mutex);
03128
03129 native_mutex_lock(&mutex->lock);
03130 if (mutex->th == 0) {
03131 mutex->th = GET_THREAD();
03132 locked = Qtrue;
03133
03134 mutex_locked(GET_THREAD(), self);
03135 }
03136 native_mutex_unlock(&mutex->lock);
03137
03138 return locked;
03139 }
03140
03141 static int
03142 lock_func(rb_thread_t *th, mutex_t *mutex, int last_thread)
03143 {
03144 int interrupted = 0;
03145 #if 0
03146 native_thread_yield();
03147 #endif
03148
03149 native_mutex_lock(&mutex->lock);
03150 th->transition_for_lock = 0;
03151 while (mutex->th || (mutex->th = th, 0)) {
03152 if (last_thread) {
03153 interrupted = 2;
03154 break;
03155 }
03156
03157 mutex->cond_waiting++;
03158 native_cond_wait(&mutex->cond, &mutex->lock);
03159 mutex->cond_notified--;
03160
03161 if (RUBY_VM_INTERRUPTED(th)) {
03162 interrupted = 1;
03163 break;
03164 }
03165 }
03166 th->transition_for_lock = 1;
03167 native_mutex_unlock(&mutex->lock);
03168
03169 if (interrupted == 2) native_thread_yield();
03170 #if 0
03171 native_thread_yield();
03172 #endif
03173
03174 return interrupted;
03175 }
03176
03177 static void
03178 lock_interrupt(void *ptr)
03179 {
03180 mutex_t *mutex = (mutex_t *)ptr;
03181 native_mutex_lock(&mutex->lock);
03182 if (mutex->cond_waiting > 0) {
03183 native_cond_broadcast(&mutex->cond);
03184 mutex->cond_notified = mutex->cond_waiting;
03185 mutex->cond_waiting = 0;
03186 }
03187 native_mutex_unlock(&mutex->lock);
03188 }
03189
03190
03191
03192
03193
03194
03195
03196
03197 VALUE
03198 rb_mutex_lock(VALUE self)
03199 {
03200
03201 if (rb_mutex_trylock(self) == Qfalse) {
03202 mutex_t *mutex;
03203 rb_thread_t *th = GET_THREAD();
03204 GetMutexPtr(self, mutex);
03205
03206 if (mutex->th == GET_THREAD()) {
03207 rb_raise(rb_eThreadError, "deadlock; recursive locking");
03208 }
03209
03210 while (mutex->th != th) {
03211 int interrupted;
03212 enum rb_thread_status prev_status = th->status;
03213 int last_thread = 0;
03214 struct rb_unblock_callback oldubf;
03215
03216 set_unblock_function(th, lock_interrupt, mutex, &oldubf);
03217 th->status = THREAD_STOPPED_FOREVER;
03218 th->vm->sleeper++;
03219 th->locking_mutex = self;
03220 if (vm_living_thread_num(th->vm) == th->vm->sleeper) {
03221 last_thread = 1;
03222 }
03223
03224 th->transition_for_lock = 1;
03225 BLOCKING_REGION_CORE({
03226 interrupted = lock_func(th, mutex, last_thread);
03227 });
03228 th->transition_for_lock = 0;
03229 remove_signal_thread_list(th);
03230 reset_unblock_function(th, &oldubf);
03231
03232 th->locking_mutex = Qfalse;
03233 if (mutex->th && interrupted == 2) {
03234 rb_check_deadlock(th->vm);
03235 }
03236 if (th->status == THREAD_STOPPED_FOREVER) {
03237 th->status = prev_status;
03238 }
03239 th->vm->sleeper--;
03240
03241 if (mutex->th == th) mutex_locked(th, self);
03242
03243 if (interrupted) {
03244 RUBY_VM_CHECK_INTS();
03245 }
03246 }
03247 }
03248 return self;
03249 }
03250
03251 static const char *
03252 mutex_unlock(mutex_t *mutex, rb_thread_t volatile *th)
03253 {
03254 const char *err = NULL;
03255 mutex_t *th_mutex;
03256
03257 native_mutex_lock(&mutex->lock);
03258
03259 if (mutex->th == 0) {
03260 err = "Attempt to unlock a mutex which is not locked";
03261 }
03262 else if (mutex->th != th) {
03263 err = "Attempt to unlock a mutex which is locked by another thread";
03264 }
03265 else {
03266 mutex->th = 0;
03267 if (mutex->cond_waiting > 0) {
03268
03269 native_cond_signal(&mutex->cond);
03270 mutex->cond_waiting--;
03271 mutex->cond_notified++;
03272 }
03273 }
03274
03275 native_mutex_unlock(&mutex->lock);
03276
03277 if (!err) {
03278 th_mutex = th->keeping_mutexes;
03279 if (th_mutex == mutex) {
03280 th->keeping_mutexes = mutex->next_mutex;
03281 }
03282 else {
03283 while (1) {
03284 mutex_t *tmp_mutex;
03285 tmp_mutex = th_mutex->next_mutex;
03286 if (tmp_mutex == mutex) {
03287 th_mutex->next_mutex = tmp_mutex->next_mutex;
03288 break;
03289 }
03290 th_mutex = tmp_mutex;
03291 }
03292 }
03293 mutex->next_mutex = NULL;
03294 }
03295
03296 return err;
03297 }
03298
03299
03300
03301
03302
03303
03304
03305
03306 VALUE
03307 rb_mutex_unlock(VALUE self)
03308 {
03309 const char *err;
03310 mutex_t *mutex;
03311 GetMutexPtr(self, mutex);
03312
03313 err = mutex_unlock(mutex, GET_THREAD());
03314 if (err) rb_raise(rb_eThreadError, "%s", err);
03315
03316 return self;
03317 }
03318
03319 static void
03320 rb_mutex_unlock_all(mutex_t *mutexes, rb_thread_t *th)
03321 {
03322 const char *err;
03323 mutex_t *mutex;
03324
03325 while (mutexes) {
03326 mutex = mutexes;
03327
03328
03329 mutexes = mutex->next_mutex;
03330 err = mutex_unlock(mutex, th);
03331 if (err) rb_bug("invalid keeping_mutexes: %s", err);
03332 }
03333 }
03334
03335 static void
03336 rb_mutex_abandon_all(mutex_t *mutexes)
03337 {
03338 mutex_t *mutex;
03339
03340 while (mutexes) {
03341 mutex = mutexes;
03342 mutexes = mutex->next_mutex;
03343 mutex->th = 0;
03344 mutex->next_mutex = 0;
03345 }
03346 }
03347
03348 static VALUE
03349 rb_mutex_sleep_forever(VALUE time)
03350 {
03351 rb_thread_sleep_deadly();
03352 return Qnil;
03353 }
03354
03355 static VALUE
03356 rb_mutex_wait_for(VALUE time)
03357 {
03358 const struct timeval *t = (struct timeval *)time;
03359 rb_thread_wait_for(*t);
03360 return Qnil;
03361 }
03362
03363 VALUE
03364 rb_mutex_sleep(VALUE self, VALUE timeout)
03365 {
03366 time_t beg, end;
03367 struct timeval t;
03368
03369 if (!NIL_P(timeout)) {
03370 t = rb_time_interval(timeout);
03371 }
03372 rb_mutex_unlock(self);
03373 beg = time(0);
03374 if (NIL_P(timeout)) {
03375 rb_ensure(rb_mutex_sleep_forever, Qnil, rb_mutex_lock, self);
03376 }
03377 else {
03378 rb_ensure(rb_mutex_wait_for, (VALUE)&t, rb_mutex_lock, self);
03379 }
03380 end = time(0) - beg;
03381 return INT2FIX(end);
03382 }
03383
03384
03385
03386
03387
03388
03389
03390
03391
03392 static VALUE
03393 mutex_sleep(int argc, VALUE *argv, VALUE self)
03394 {
03395 VALUE timeout;
03396
03397 rb_scan_args(argc, argv, "01", &timeout);
03398 return rb_mutex_sleep(self, timeout);
03399 }
03400
03401
03402
03403
03404
03405
03406
03407
03408
03409 VALUE
03410 rb_mutex_synchronize(VALUE mutex, VALUE (*func)(VALUE arg), VALUE arg)
03411 {
03412 rb_mutex_lock(mutex);
03413 return rb_ensure(func, arg, rb_mutex_unlock, mutex);
03414 }
03415
03416
03417
03418
03419 static void
03420 barrier_mark(void *ptr)
03421 {
03422 rb_gc_mark((VALUE)ptr);
03423 }
03424
03425 static const rb_data_type_t barrier_data_type = {
03426 "barrier",
03427 barrier_mark, 0, 0,
03428 };
03429
03430 static VALUE
03431 barrier_alloc(VALUE klass)
03432 {
03433 return TypedData_Wrap_Struct(klass, &barrier_data_type, (void *)mutex_alloc(0));
03434 }
03435
03436 #define GetBarrierPtr(obj) (VALUE)rb_check_typeddata(obj, &barrier_data_type)
03437
03438 VALUE
03439 rb_barrier_new(void)
03440 {
03441 VALUE barrier = barrier_alloc(rb_cBarrier);
03442 rb_mutex_lock((VALUE)DATA_PTR(barrier));
03443 return barrier;
03444 }
03445
03446 VALUE
03447 rb_barrier_wait(VALUE self)
03448 {
03449 VALUE mutex = GetBarrierPtr(self);
03450 mutex_t *m;
03451
03452 if (!mutex) return Qfalse;
03453 GetMutexPtr(mutex, m);
03454 if (m->th == GET_THREAD()) return Qfalse;
03455 rb_mutex_lock(mutex);
03456 if (DATA_PTR(self)) return Qtrue;
03457 rb_mutex_unlock(mutex);
03458 return Qfalse;
03459 }
03460
03461 VALUE
03462 rb_barrier_release(VALUE self)
03463 {
03464 return rb_mutex_unlock(GetBarrierPtr(self));
03465 }
03466
03467 VALUE
03468 rb_barrier_destroy(VALUE self)
03469 {
03470 VALUE mutex = GetBarrierPtr(self);
03471 DATA_PTR(self) = 0;
03472 return rb_mutex_unlock(mutex);
03473 }
03474
03475
03476 static ID recursive_key;
03477
03478
03479
03480
03481
03482
03483
03484 static VALUE
03485 recursive_list_access(void)
03486 {
03487 volatile VALUE hash = rb_thread_local_aref(rb_thread_current(), recursive_key);
03488 VALUE sym = ID2SYM(rb_frame_this_func());
03489 VALUE list;
03490 if (NIL_P(hash) || TYPE(hash) != T_HASH) {
03491 hash = rb_hash_new();
03492 OBJ_UNTRUST(hash);
03493 rb_thread_local_aset(rb_thread_current(), recursive_key, hash);
03494 list = Qnil;
03495 }
03496 else {
03497 list = rb_hash_aref(hash, sym);
03498 }
03499 if (NIL_P(list) || TYPE(list) != T_HASH) {
03500 list = rb_hash_new();
03501 OBJ_UNTRUST(list);
03502 rb_hash_aset(hash, sym, list);
03503 }
03504 return list;
03505 }
03506
03507
03508
03509
03510
03511
03512
03513 static VALUE
03514 recursive_check(VALUE list, VALUE obj_id, VALUE paired_obj_id)
03515 {
03516 VALUE pair_list = rb_hash_lookup2(list, obj_id, Qundef);
03517 if (pair_list == Qundef)
03518 return Qfalse;
03519 if (paired_obj_id) {
03520 if (TYPE(pair_list) != T_HASH) {
03521 if (pair_list != paired_obj_id)
03522 return Qfalse;
03523 }
03524 else {
03525 if (NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
03526 return Qfalse;
03527 }
03528 }
03529 return Qtrue;
03530 }
03531
03532
03533
03534
03535
03536
03537
03538
03539
03540
03541 static void
03542 recursive_push(VALUE list, VALUE obj, VALUE paired_obj)
03543 {
03544 VALUE pair_list;
03545
03546 if (!paired_obj) {
03547 rb_hash_aset(list, obj, Qtrue);
03548 }
03549 else if ((pair_list = rb_hash_lookup2(list, obj, Qundef)) == Qundef) {
03550 rb_hash_aset(list, obj, paired_obj);
03551 }
03552 else {
03553 if (TYPE(pair_list) != T_HASH){
03554 VALUE other_paired_obj = pair_list;
03555 pair_list = rb_hash_new();
03556 OBJ_UNTRUST(pair_list);
03557 rb_hash_aset(pair_list, other_paired_obj, Qtrue);
03558 rb_hash_aset(list, obj, pair_list);
03559 }
03560 rb_hash_aset(pair_list, paired_obj, Qtrue);
03561 }
03562 }
03563
03564
03565
03566
03567
03568
03569
03570
03571
03572 static void
03573 recursive_pop(VALUE list, VALUE obj, VALUE paired_obj)
03574 {
03575 if (paired_obj) {
03576 VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
03577 if (pair_list == Qundef) {
03578 VALUE symname = rb_inspect(ID2SYM(rb_frame_this_func()));
03579 VALUE thrname = rb_inspect(rb_thread_current());
03580 rb_raise(rb_eTypeError, "invalid inspect_tbl pair_list for %s in %s",
03581 StringValuePtr(symname), StringValuePtr(thrname));
03582 }
03583 if (TYPE(pair_list) == T_HASH) {
03584 rb_hash_delete(pair_list, paired_obj);
03585 if (!RHASH_EMPTY_P(pair_list)) {
03586 return;
03587 }
03588 }
03589 }
03590 rb_hash_delete(list, obj);
03591 }
03592
03593 struct exec_recursive_params {
03594 VALUE (*func) (VALUE, VALUE, int);
03595 VALUE list;
03596 VALUE obj;
03597 VALUE objid;
03598 VALUE pairid;
03599 VALUE arg;
03600 };
03601
03602 static VALUE
03603 exec_recursive_i(VALUE tag, struct exec_recursive_params *p)
03604 {
03605 VALUE result = Qundef;
03606 int state;
03607
03608 recursive_push(p->list, p->objid, p->pairid);
03609 PUSH_TAG();
03610 if ((state = EXEC_TAG()) == 0) {
03611 result = (*p->func)(p->obj, p->arg, FALSE);
03612 }
03613 POP_TAG();
03614 recursive_pop(p->list, p->objid, p->pairid);
03615 if (state)
03616 JUMP_TAG(state);
03617 return result;
03618 }
03619
03620
03621
03622
03623
03624
03625
03626
03627
03628
03629
03630
03631 static VALUE
03632 exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE arg, int outer)
03633 {
03634 struct exec_recursive_params p;
03635 int outermost;
03636 p.list = recursive_list_access();
03637 p.objid = rb_obj_id(obj);
03638 outermost = outer && !recursive_check(p.list, ID2SYM(recursive_key), 0);
03639
03640 if (recursive_check(p.list, p.objid, pairid)) {
03641 if (outer && !outermost) {
03642 rb_throw_obj(p.list, p.list);
03643 }
03644 return (*func)(obj, arg, TRUE);
03645 }
03646 else {
03647 VALUE result = Qundef;
03648 p.func = func;
03649 p.obj = obj;
03650 p.pairid = pairid;
03651 p.arg = arg;
03652
03653 if (outermost) {
03654 recursive_push(p.list, ID2SYM(recursive_key), 0);
03655 result = rb_catch_obj(p.list, exec_recursive_i, (VALUE)&p);
03656 recursive_pop(p.list, ID2SYM(recursive_key), 0);
03657 if (result == p.list) {
03658 result = (*func)(obj, arg, TRUE);
03659 }
03660 }
03661 else {
03662 result = exec_recursive_i(0, &p);
03663 }
03664 return result;
03665 }
03666 }
03667
03668
03669
03670
03671
03672
03673 VALUE
03674 rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
03675 {
03676 return exec_recursive(func, obj, 0, arg, 0);
03677 }
03678
03679
03680
03681
03682
03683
03684 VALUE
03685 rb_exec_recursive_paired(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
03686 {
03687 return exec_recursive(func, obj, rb_obj_id(paired_obj), arg, 0);
03688 }
03689
03690
03691
03692
03693
03694
03695
03696 VALUE
03697 rb_exec_recursive_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
03698 {
03699 return exec_recursive(func, obj, 0, arg, 1);
03700 }
03701
03702
03703
03704 static rb_event_hook_t *
03705 alloc_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
03706 {
03707 rb_event_hook_t *hook = ALLOC(rb_event_hook_t);
03708 hook->func = func;
03709 hook->flag = events;
03710 hook->data = data;
03711 return hook;
03712 }
03713
03714 static void
03715 thread_reset_event_flags(rb_thread_t *th)
03716 {
03717 rb_event_hook_t *hook = th->event_hooks;
03718 rb_event_flag_t flag = th->event_flags & RUBY_EVENT_VM;
03719
03720 while (hook) {
03721 flag |= hook->flag;
03722 hook = hook->next;
03723 }
03724 th->event_flags = flag;
03725 }
03726
03727 static void
03728 rb_threadptr_add_event_hook(rb_thread_t *th,
03729 rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
03730 {
03731 rb_event_hook_t *hook = alloc_event_hook(func, events, data);
03732 hook->next = th->event_hooks;
03733 th->event_hooks = hook;
03734 thread_reset_event_flags(th);
03735 }
03736
03737 static rb_thread_t *
03738 thval2thread_t(VALUE thval)
03739 {
03740 rb_thread_t *th;
03741 GetThreadPtr(thval, th);
03742 return th;
03743 }
03744
03745 void
03746 rb_thread_add_event_hook(VALUE thval,
03747 rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
03748 {
03749 rb_threadptr_add_event_hook(thval2thread_t(thval), func, events, data);
03750 }
03751
03752 static int
03753 set_threads_event_flags_i(st_data_t key, st_data_t val, st_data_t flag)
03754 {
03755 VALUE thval = key;
03756 rb_thread_t *th;
03757 GetThreadPtr(thval, th);
03758
03759 if (flag) {
03760 th->event_flags |= RUBY_EVENT_VM;
03761 }
03762 else {
03763 th->event_flags &= (~RUBY_EVENT_VM);
03764 }
03765 return ST_CONTINUE;
03766 }
03767
03768 static void
03769 set_threads_event_flags(int flag)
03770 {
03771 st_foreach(GET_VM()->living_threads, set_threads_event_flags_i, (st_data_t) flag);
03772 }
03773
03774 static inline void
03775 exec_event_hooks(const rb_event_hook_t *hook, rb_event_flag_t flag, VALUE self, ID id, VALUE klass)
03776 {
03777 for (; hook; hook = hook->next) {
03778 if (flag & hook->flag) {
03779 (*hook->func)(flag, hook->data, self, id, klass);
03780 }
03781 }
03782 }
03783
03784 void
03785 rb_threadptr_exec_event_hooks(rb_thread_t *th, rb_event_flag_t flag, VALUE self, ID id, VALUE klass)
03786 {
03787 const VALUE errinfo = th->errinfo;
03788 const rb_event_flag_t wait_event = th->event_flags;
03789
03790 if (self == rb_mRubyVMFrozenCore) return;
03791 if (wait_event & flag) {
03792 exec_event_hooks(th->event_hooks, flag, self, id, klass);
03793 }
03794 if (wait_event & RUBY_EVENT_VM) {
03795 if (th->vm->event_hooks == NULL) {
03796 th->event_flags &= (~RUBY_EVENT_VM);
03797 }
03798 else {
03799 exec_event_hooks(th->vm->event_hooks, flag, self, id, klass);
03800 }
03801 }
03802 th->errinfo = errinfo;
03803 }
03804
03805 void
03806 rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)
03807 {
03808 rb_event_hook_t *hook = alloc_event_hook(func, events, data);
03809 rb_vm_t *vm = GET_VM();
03810
03811 hook->next = vm->event_hooks;
03812 vm->event_hooks = hook;
03813
03814 set_threads_event_flags(1);
03815 }
03816
03817 static int
03818 remove_event_hook(rb_event_hook_t **root, rb_event_hook_func_t func)
03819 {
03820 rb_event_hook_t *prev = NULL, *hook = *root, *next;
03821
03822 while (hook) {
03823 next = hook->next;
03824 if (func == 0 || hook->func == func) {
03825 if (prev) {
03826 prev->next = hook->next;
03827 }
03828 else {
03829 *root = hook->next;
03830 }
03831 xfree(hook);
03832 }
03833 else {
03834 prev = hook;
03835 }
03836 hook = next;
03837 }
03838 return -1;
03839 }
03840
03841 static int
03842 rb_threadptr_revmove_event_hook(rb_thread_t *th, rb_event_hook_func_t func)
03843 {
03844 int ret = remove_event_hook(&th->event_hooks, func);
03845 thread_reset_event_flags(th);
03846 return ret;
03847 }
03848
03849 int
03850 rb_thread_remove_event_hook(VALUE thval, rb_event_hook_func_t func)
03851 {
03852 return rb_threadptr_revmove_event_hook(thval2thread_t(thval), func);
03853 }
03854
03855 int
03856 rb_remove_event_hook(rb_event_hook_func_t func)
03857 {
03858 rb_vm_t *vm = GET_VM();
03859 rb_event_hook_t *hook = vm->event_hooks;
03860 int ret = remove_event_hook(&vm->event_hooks, func);
03861
03862 if (hook != NULL && vm->event_hooks == NULL) {
03863 set_threads_event_flags(0);
03864 }
03865
03866 return ret;
03867 }
03868
03869 static int
03870 clear_trace_func_i(st_data_t key, st_data_t val, st_data_t flag)
03871 {
03872 rb_thread_t *th;
03873 GetThreadPtr((VALUE)key, th);
03874 rb_threadptr_revmove_event_hook(th, 0);
03875 return ST_CONTINUE;
03876 }
03877
03878 void
03879 rb_clear_trace_func(void)
03880 {
03881 st_foreach(GET_VM()->living_threads, clear_trace_func_i, (st_data_t) 0);
03882 rb_remove_event_hook(0);
03883 }
03884
03885 static void call_trace_func(rb_event_flag_t, VALUE data, VALUE self, ID id, VALUE klass);
03886
03887
03888
03889
03890
03891
03892
03893
03894
03895
03896
03897
03898
03899
03900
03901
03902
03903
03904
03905
03906
03907
03908
03909
03910
03911
03912
03913
03914
03915
03916
03917
03918
03919
03920
03921
03922
03923
03924
03925
03926
03927
03928
03929
03930 static VALUE
03931 set_trace_func(VALUE obj, VALUE trace)
03932 {
03933 rb_remove_event_hook(call_trace_func);
03934
03935 if (NIL_P(trace)) {
03936 return Qnil;
03937 }
03938
03939 if (!rb_obj_is_proc(trace)) {
03940 rb_raise(rb_eTypeError, "trace_func needs to be Proc");
03941 }
03942
03943 rb_add_event_hook(call_trace_func, RUBY_EVENT_ALL, trace);
03944 return trace;
03945 }
03946
03947 static void
03948 thread_add_trace_func(rb_thread_t *th, VALUE trace)
03949 {
03950 if (!rb_obj_is_proc(trace)) {
03951 rb_raise(rb_eTypeError, "trace_func needs to be Proc");
03952 }
03953
03954 rb_threadptr_add_event_hook(th, call_trace_func, RUBY_EVENT_ALL, trace);
03955 }
03956
03957
03958
03959
03960
03961
03962
03963
03964
03965 static VALUE
03966 thread_add_trace_func_m(VALUE obj, VALUE trace)
03967 {
03968 rb_thread_t *th;
03969 GetThreadPtr(obj, th);
03970 thread_add_trace_func(th, trace);
03971 return trace;
03972 }
03973
03974
03975
03976
03977
03978
03979
03980
03981
03982
03983
03984 static VALUE
03985 thread_set_trace_func_m(VALUE obj, VALUE trace)
03986 {
03987 rb_thread_t *th;
03988 GetThreadPtr(obj, th);
03989 rb_threadptr_revmove_event_hook(th, call_trace_func);
03990
03991 if (NIL_P(trace)) {
03992 return Qnil;
03993 }
03994 thread_add_trace_func(th, trace);
03995 return trace;
03996 }
03997
03998 static const char *
03999 get_event_name(rb_event_flag_t event)
04000 {
04001 switch (event) {
04002 case RUBY_EVENT_LINE:
04003 return "line";
04004 case RUBY_EVENT_CLASS:
04005 return "class";
04006 case RUBY_EVENT_END:
04007 return "end";
04008 case RUBY_EVENT_CALL:
04009 return "call";
04010 case RUBY_EVENT_RETURN:
04011 return "return";
04012 case RUBY_EVENT_C_CALL:
04013 return "c-call";
04014 case RUBY_EVENT_C_RETURN:
04015 return "c-return";
04016 case RUBY_EVENT_RAISE:
04017 return "raise";
04018 default:
04019 return "unknown";
04020 }
04021 }
04022
04023 VALUE ruby_suppress_tracing(VALUE (*func)(VALUE, int), VALUE arg, int always);
04024
04025 struct call_trace_func_args {
04026 rb_event_flag_t event;
04027 VALUE proc;
04028 VALUE self;
04029 ID id;
04030 VALUE klass;
04031 };
04032
04033 static VALUE
04034 call_trace_proc(VALUE args, int tracing)
04035 {
04036 struct call_trace_func_args *p = (struct call_trace_func_args *)args;
04037 const char *srcfile = rb_sourcefile();
04038 VALUE eventname = rb_str_new2(get_event_name(p->event));
04039 VALUE filename = srcfile ? rb_str_new2(srcfile) : Qnil;
04040 VALUE argv[6];
04041 int line = rb_sourceline();
04042 ID id = 0;
04043 VALUE klass = 0;
04044
04045 if (p->event == RUBY_EVENT_C_CALL ||
04046 p->event == RUBY_EVENT_C_RETURN) {
04047 id = p->id;
04048 klass = p->klass;
04049 }
04050 else {
04051 rb_thread_method_id_and_class(GET_THREAD(), &id, &klass);
04052 }
04053 if (id == ID_ALLOCATOR)
04054 return Qnil;
04055 if (klass) {
04056 if (TYPE(klass) == T_ICLASS) {
04057 klass = RBASIC(klass)->klass;
04058 }
04059 else if (FL_TEST(klass, FL_SINGLETON)) {
04060 klass = rb_iv_get(klass, "__attached__");
04061 }
04062 }
04063
04064 argv[0] = eventname;
04065 argv[1] = filename;
04066 argv[2] = INT2FIX(line);
04067 argv[3] = id ? ID2SYM(id) : Qnil;
04068 argv[4] = (p->self && srcfile) ? rb_binding_new() : Qnil;
04069 argv[5] = klass ? klass : Qnil;
04070
04071 return rb_proc_call_with_block(p->proc, 6, argv, Qnil);
04072 }
04073
04074 static void
04075 call_trace_func(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
04076 {
04077 struct call_trace_func_args args;
04078
04079 args.event = event;
04080 args.proc = proc;
04081 args.self = self;
04082 args.id = id;
04083 args.klass = klass;
04084 ruby_suppress_tracing(call_trace_proc, (VALUE)&args, FALSE);
04085 }
04086
04087 VALUE
04088 ruby_suppress_tracing(VALUE (*func)(VALUE, int), VALUE arg, int always)
04089 {
04090 rb_thread_t *th = GET_THREAD();
04091 int state, tracing;
04092 volatile int raised;
04093 volatile int outer_state;
04094 VALUE result = Qnil;
04095
04096 if ((tracing = th->tracing) != 0 && !always) {
04097 return Qnil;
04098 }
04099 else {
04100 th->tracing = 1;
04101 }
04102
04103 raised = rb_threadptr_reset_raised(th);
04104 outer_state = th->state;
04105 th->state = 0;
04106
04107 PUSH_TAG();
04108 if ((state = EXEC_TAG()) == 0) {
04109 result = (*func)(arg, tracing);
04110 }
04111
04112 if (raised) {
04113 rb_threadptr_set_raised(th);
04114 }
04115 POP_TAG();
04116
04117 th->tracing = tracing;
04118 if (state) {
04119 JUMP_TAG(state);
04120 }
04121 th->state = outer_state;
04122
04123 return result;
04124 }
04125
04126 VALUE rb_thread_backtrace(VALUE thval);
04127
04128
04129
04130
04131
04132
04133
04134
04135 static VALUE
04136 rb_thread_backtrace_m(VALUE thval)
04137 {
04138 return rb_thread_backtrace(thval);
04139 }
04140
04141
04142
04143
04144
04145
04146
04147
04148
04149
04150
04151
04152
04153
04154
04155
04156
04157
04158
04159
04160
04161
04162
04163
04164 void
04165 Init_Thread(void)
04166 {
04167 #undef rb_intern
04168 #define rb_intern(str) rb_intern_const(str)
04169
04170 VALUE cThGroup;
04171
04172 rb_define_singleton_method(rb_cThread, "new", thread_s_new, -1);
04173 rb_define_singleton_method(rb_cThread, "start", thread_start, -2);
04174 rb_define_singleton_method(rb_cThread, "fork", thread_start, -2);
04175 rb_define_singleton_method(rb_cThread, "main", rb_thread_s_main, 0);
04176 rb_define_singleton_method(rb_cThread, "current", thread_s_current, 0);
04177 rb_define_singleton_method(rb_cThread, "stop", rb_thread_stop, 0);
04178 rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
04179 rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
04180 rb_define_singleton_method(rb_cThread, "pass", thread_s_pass, 0);
04181 rb_define_singleton_method(rb_cThread, "list", rb_thread_list, 0);
04182 rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
04183 rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
04184 #if THREAD_DEBUG < 0
04185 rb_define_singleton_method(rb_cThread, "DEBUG", rb_thread_s_debug, 0);
04186 rb_define_singleton_method(rb_cThread, "DEBUG=", rb_thread_s_debug_set, 1);
04187 #endif
04188
04189 rb_define_method(rb_cThread, "initialize", thread_initialize, -2);
04190 rb_define_method(rb_cThread, "raise", thread_raise_m, -1);
04191 rb_define_method(rb_cThread, "join", thread_join_m, -1);
04192 rb_define_method(rb_cThread, "value", thread_value, 0);
04193 rb_define_method(rb_cThread, "kill", rb_thread_kill, 0);
04194 rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0);
04195 rb_define_method(rb_cThread, "exit", rb_thread_kill, 0);
04196 rb_define_method(rb_cThread, "run", rb_thread_run, 0);
04197 rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 0);
04198 rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
04199 rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
04200 rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
04201 rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
04202 rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
04203 rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
04204 rb_define_method(rb_cThread, "status", rb_thread_status, 0);
04205 rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
04206 rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
04207 rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
04208 rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
04209 rb_define_method(rb_cThread, "safe_level", rb_thread_safe_level, 0);
04210 rb_define_method(rb_cThread, "group", rb_thread_group, 0);
04211 rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, 0);
04212
04213 rb_define_method(rb_cThread, "inspect", rb_thread_inspect, 0);
04214
04215 cThGroup = rb_define_class("ThreadGroup", rb_cObject);
04216 rb_define_alloc_func(cThGroup, thgroup_s_alloc);
04217 rb_define_method(cThGroup, "list", thgroup_list, 0);
04218 rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
04219 rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
04220 rb_define_method(cThGroup, "add", thgroup_add, 1);
04221
04222 {
04223 rb_thread_t *th = GET_THREAD();
04224 th->thgroup = th->vm->thgroup_default = rb_obj_alloc(cThGroup);
04225 rb_define_const(cThGroup, "Default", th->thgroup);
04226 }
04227
04228 rb_cMutex = rb_define_class("Mutex", rb_cObject);
04229 rb_define_alloc_func(rb_cMutex, mutex_alloc);
04230 rb_define_method(rb_cMutex, "initialize", mutex_initialize, 0);
04231 rb_define_method(rb_cMutex, "locked?", rb_mutex_locked_p, 0);
04232 rb_define_method(rb_cMutex, "try_lock", rb_mutex_trylock, 0);
04233 rb_define_method(rb_cMutex, "lock", rb_mutex_lock, 0);
04234 rb_define_method(rb_cMutex, "unlock", rb_mutex_unlock, 0);
04235 rb_define_method(rb_cMutex, "sleep", mutex_sleep, -1);
04236
04237 recursive_key = rb_intern("__recursive_key__");
04238 rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
04239
04240
04241 rb_define_global_function("set_trace_func", set_trace_func, 1);
04242 rb_define_method(rb_cThread, "set_trace_func", thread_set_trace_func_m, 1);
04243 rb_define_method(rb_cThread, "add_trace_func", thread_add_trace_func_m, 1);
04244
04245
04246 {
04247
04248 {
04249
04250 rb_thread_lock_t *lp = &GET_THREAD()->vm->global_vm_lock;
04251 native_mutex_initialize(lp);
04252 native_mutex_lock(lp);
04253 native_mutex_initialize(&GET_THREAD()->interrupt_lock);
04254 }
04255 }
04256
04257 rb_thread_create_timer_thread();
04258
04259 (void)native_mutex_trylock;
04260 (void)ruby_thread_set_native;
04261 }
04262
04263 int
04264 ruby_native_thread_p(void)
04265 {
04266 rb_thread_t *th = ruby_thread_from_native();
04267
04268 return th != 0;
04269 }
04270
04271 static int
04272 check_deadlock_i(st_data_t key, st_data_t val, int *found)
04273 {
04274 VALUE thval = key;
04275 rb_thread_t *th;
04276 GetThreadPtr(thval, th);
04277
04278 if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th) || th->transition_for_lock) {
04279 *found = 1;
04280 }
04281 else if (th->locking_mutex) {
04282 mutex_t *mutex;
04283 GetMutexPtr(th->locking_mutex, mutex);
04284
04285 native_mutex_lock(&mutex->lock);
04286 if (mutex->th == th || (!mutex->th && mutex->cond_notified)) {
04287 *found = 1;
04288 }
04289 native_mutex_unlock(&mutex->lock);
04290 }
04291
04292 return (*found) ? ST_STOP : ST_CONTINUE;
04293 }
04294
04295 #if 0
04296 static int
04297 debug_i(st_data_t key, st_data_t val, int *found)
04298 {
04299 VALUE thval = key;
04300 rb_thread_t *th;
04301 GetThreadPtr(thval, th);
04302
04303 printf("th:%p %d %d %d", th, th->status, th->interrupt_flag, th->transition_for_lock);
04304 if (th->locking_mutex) {
04305 mutex_t *mutex;
04306 GetMutexPtr(th->locking_mutex, mutex);
04307
04308 native_mutex_lock(&mutex->lock);
04309 printf(" %p %d\n", mutex->th, mutex->cond_notified);
04310 native_mutex_unlock(&mutex->lock);
04311 }
04312 else puts("");
04313
04314 return ST_CONTINUE;
04315 }
04316 #endif
04317
04318 static void
04319 rb_check_deadlock(rb_vm_t *vm)
04320 {
04321 int found = 0;
04322
04323 if (vm_living_thread_num(vm) > vm->sleeper) return;
04324 if (vm_living_thread_num(vm) < vm->sleeper) rb_bug("sleeper must not be more than vm_living_thread_num(vm)");
04325
04326 st_foreach(vm->living_threads, check_deadlock_i, (st_data_t)&found);
04327
04328 if (!found) {
04329 VALUE argv[2];
04330 argv[0] = rb_eFatal;
04331 argv[1] = rb_str_new2("deadlock detected");
04332 #if 0
04333 printf("%d %d %p %p\n", vm->living_threads->num_entries, vm->sleeper, GET_THREAD(), vm->main_thread);
04334 st_foreach(vm->living_threads, debug_i, (st_data_t)0);
04335 #endif
04336 vm->sleeper--;
04337 rb_threadptr_raise(vm->main_thread, 2, argv);
04338 }
04339 }
04340
04341 static void
04342 update_coverage(rb_event_flag_t event, VALUE proc, VALUE self, ID id, VALUE klass)
04343 {
04344 VALUE coverage = GET_THREAD()->cfp->iseq->coverage;
04345 if (coverage && RBASIC(coverage)->klass == 0) {
04346 long line = rb_sourceline() - 1;
04347 long count;
04348 if (RARRAY_PTR(coverage)[line] == Qnil) {
04349 rb_bug("bug");
04350 }
04351 count = FIX2LONG(RARRAY_PTR(coverage)[line]) + 1;
04352 if (POSFIXABLE(count)) {
04353 RARRAY_PTR(coverage)[line] = LONG2FIX(count);
04354 }
04355 }
04356 }
04357
04358 VALUE
04359 rb_get_coverages(void)
04360 {
04361 return GET_VM()->coverages;
04362 }
04363
04364 void
04365 rb_set_coverages(VALUE coverages)
04366 {
04367 GET_VM()->coverages = coverages;
04368 rb_add_event_hook(update_coverage, RUBY_EVENT_COVERAGE, Qnil);
04369 }
04370
04371 void
04372 rb_reset_coverages(void)
04373 {
04374 GET_VM()->coverages = Qfalse;
04375 rb_remove_event_hook(update_coverage);
04376 }
04377