static void native_sleep(rb_thread_t *th, struct timeval *tv) { struct timespec ts; struct timeval tvn; if (tv) { gettimeofday(&tvn, NULL); ts.tv_sec = tvn.tv_sec + tv->tv_sec; ts.tv_nsec = (tvn.tv_usec + tv->tv_usec) * 1000; if (ts.tv_nsec >= PER_NANO){ ts.tv_sec += 1; ts.tv_nsec -= PER_NANO; } } thread_debug("native_sleep %ld\n", (long)(tv ? tv->tv_sec : -1)); GVL_UNLOCK_BEGIN(); { pthread_mutex_lock(&th->interrupt_lock); th->unblock.func = ubf_pthread_cond_signal; th->unblock.arg = th; if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ thread_debug("native_sleep: interrupted before sleep\n"); } else { if (tv == 0 || ts.tv_sec < tvn.tv_sec /* overflow */ ) { int r; thread_debug("native_sleep: pthread_cond_wait start\n"); r = pthread_cond_wait(&th->native_thread_data.sleep_cond, &th->interrupt_lock); if (r) rb_bug_errno("pthread_cond_wait", r); thread_debug("native_sleep: pthread_cond_wait end\n"); } else { int r; thread_debug("native_sleep: pthread_cond_timedwait start (%ld, %ld)\n", (unsigned long)ts.tv_sec, ts.tv_nsec); r = pthread_cond_timedwait(&th->native_thread_data.sleep_cond, &th->interrupt_lock, &ts); if (r && r != ETIMEDOUT) rb_bug_errno("pthread_cond_timedwait", r); thread_debug("native_sleep: pthread_cond_timedwait end (%d)\n", r); } } th->unblock.func = 0; th->unblock.arg = 0; pthread_mutex_unlock(&th->interrupt_lock); } GVL_UNLOCK_END(); thread_debug("native_sleep done\n"); }
static void native_sleep(rb_thread_t *th, struct timeval *timeout_tv) { struct timespec timeout; rb_nativethread_lock_t *lock = &th->interrupt_lock; rb_nativethread_cond_t *cond = &th->native_thread_data.sleep_cond; if (timeout_tv) { struct timespec timeout_rel; timeout_rel.tv_sec = timeout_tv->tv_sec; timeout_rel.tv_nsec = timeout_tv->tv_usec * 1000; /* Solaris cond_timedwait() return EINVAL if an argument is greater than * current_time + 100,000,000. So cut up to 100,000,000. This is * considered as a kind of spurious wakeup. The caller to native_sleep * should care about spurious wakeup. * * See also [Bug #1341] [ruby-core:29702] * http://download.oracle.com/docs/cd/E19683-01/816-0216/6m6ngupgv/index.html */ if (timeout_rel.tv_sec > 100000000) { timeout_rel.tv_sec = 100000000; timeout_rel.tv_nsec = 0; } timeout = native_cond_timeout(cond, timeout_rel); } GVL_UNLOCK_BEGIN(); { native_mutex_lock(lock); th->unblock.func = ubf_pthread_cond_signal; th->unblock.arg = th; if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ thread_debug("native_sleep: interrupted before sleep\n"); } else { if (!timeout_tv) native_cond_wait(cond, lock); else native_cond_timedwait(cond, lock, &timeout); } th->unblock.func = 0; th->unblock.arg = 0; native_mutex_unlock(lock); } GVL_UNLOCK_END(); thread_debug("native_sleep done\n"); }
static void native_sleep(rb_thread_t *th, struct timeval *timeout_tv) { struct timespec timeout; struct timeval tvn; pthread_mutex_t *lock = &th->interrupt_lock; rb_thread_cond_t *cond = &th->native_thread_data.sleep_cond; if (timeout_tv) { struct timespec timeout_rel; timeout_rel.tv_sec = timeout_tv->tv_sec; timeout_rel.tv_nsec = timeout_tv->tv_usec; timeout = native_cond_timeout(cond, timeout_rel); } GVL_UNLOCK_BEGIN(); { pthread_mutex_lock(lock); th->unblock.func = ubf_pthread_cond_signal; th->unblock.arg = th; if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ thread_debug("native_sleep: interrupted before sleep\n"); } else { if (!timeout_tv) native_cond_wait(cond, lock); else native_cond_timedwait(cond, lock, &timeout); } th->unblock.func = 0; th->unblock.arg = 0; pthread_mutex_unlock(lock); } GVL_UNLOCK_END(); thread_debug("native_sleep done\n"); }
static void native_sleep(rb_thread_t *th, struct timeval *tv) { DWORD msec; if (tv) { msec = tv->tv_sec * 1000 + tv->tv_usec / 1000; } else { msec = INFINITE; } GVL_UNLOCK_BEGIN(); { DWORD ret; native_mutex_lock(&th->interrupt_lock); th->unblock.func = ubf_handle; th->unblock.arg = th; native_mutex_unlock(&th->interrupt_lock); if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ } else { thread_debug("native_sleep start (%lu)\n", msec); ret = w32_wait_events(0, 0, msec, th); thread_debug("native_sleep done (%lu)\n", ret); } native_mutex_lock(&th->interrupt_lock); th->unblock.func = 0; th->unblock.arg = 0; native_mutex_unlock(&th->interrupt_lock); } GVL_UNLOCK_END(); }
static void native_sleep(rb_thread_t *th, struct timeval *tv) { DWORD msec; if (tv) { msec = tv->tv_sec * 1000 + tv->tv_usec / 1000; } else { msec = INFINITE; } GVL_UNLOCK_BEGIN(); { DWORD ret; int status = th->status; th->status = THREAD_STOPPED; th->unblock_function = ubf_handle; th->unblock_function_arg = th; if (RUBY_VM_INTERRUPTED(th)) { /* interrupted. return immediate */ } else { thread_debug("native_sleep start (%d)\n", (int)msec); ret = w32_wait_events(0, 0, msec, th); thread_debug("native_sleep done (%d)\n", ret); } th->unblock_function = 0; th->unblock_function_arg = 0; th->status = status; } GVL_UNLOCK_END(); RUBY_VM_CHECK_INTS(); }
/* * call-seq: * mutex.lock -> self * * Attempts to grab the lock and waits if it isn't available. * Raises +ThreadError+ if +mutex+ was locked by the current thread. */ VALUE rb_mutex_lock(VALUE self) { rb_thread_t *th = GET_THREAD(); rb_mutex_t *mutex; GetMutexPtr(self, mutex); /* When running trap handler */ if (!mutex->allow_trap && th->interrupt_mask & TRAP_INTERRUPT_MASK) { rb_raise(rb_eThreadError, "can't be called from trap context"); } if (rb_mutex_trylock(self) == Qfalse) { if (mutex->th == th) { rb_raise(rb_eThreadError, "deadlock; recursive locking"); } while (mutex->th != th) { int interrupted; enum rb_thread_status prev_status = th->status; volatile int timeout_ms = 0; struct rb_unblock_callback oldubf; set_unblock_function(th, lock_interrupt, mutex, &oldubf, FALSE); th->status = THREAD_STOPPED_FOREVER; th->locking_mutex = self; native_mutex_lock(&mutex->lock); th->vm->sleeper++; /* * Carefully! while some contended threads are in lock_func(), * vm->sleepr is unstable value. we have to avoid both deadlock * and busy loop. */ if ((vm_living_thread_num(th->vm) == th->vm->sleeper) && !patrol_thread) { timeout_ms = 100; patrol_thread = th; } GVL_UNLOCK_BEGIN(); interrupted = lock_func(th, mutex, (int)timeout_ms); native_mutex_unlock(&mutex->lock); GVL_UNLOCK_END(); if (patrol_thread == th) patrol_thread = NULL; reset_unblock_function(th, &oldubf); th->locking_mutex = Qfalse; if (mutex->th && interrupted == 2) { rb_check_deadlock(th->vm); } if (th->status == THREAD_STOPPED_FOREVER) { th->status = prev_status; } th->vm->sleeper--; if (mutex->th == th) mutex_locked(th, self); if (interrupted) { RUBY_VM_CHECK_INTS_BLOCKING(th); } } } return self; }