static VALUE rb_mutex_lock(VALUE self, SEL sel) { rb_vm_thread_t *current = GetThreadPtr(rb_vm_current_thread()); rb_vm_mutex_t *m = GetMutexPtr(self); rb_vm_thread_status_t prev_status; if (m->thread == current) { rb_raise(rb_eThreadError, "deadlock; recursive locking"); } prev_status = current->status; if (current->status == THREAD_ALIVE) { current->status = THREAD_SLEEP; } current->wait_for_mutex_lock = true; pthread_assert(pthread_mutex_lock(&m->mutex)); current->wait_for_mutex_lock = false; current->status = prev_status; m->thread = current; if (current->mutexes == Qnil) { GC_WB(¤t->mutexes, rb_ary_new()); OBJ_UNTRUST(current->mutexes); } rb_ary_push(current->mutexes, self); return self; }
static VALUE thread_initialize(VALUE thread, SEL sel, int argc, const VALUE *argv) { if (!rb_block_given_p()) { rb_raise(rb_eThreadError, "must be called with a block"); } rb_vm_block_t *b = rb_vm_current_block(); assert(b != NULL); rb_vm_thread_t *t = GetThreadPtr(thread); rb_vm_thread_pre_init(t, b, argc, argv, rb_vm_create_vm()); // The thread's group is always the parent's one. rb_thgroup_add(GetThreadPtr(rb_vm_current_thread())->group, thread); // Retain the Thread object to avoid a potential GC, the corresponding // release is done in rb_vm_thread_run(). rb_objc_retain((void *)thread); if (pthread_create(&t->thread, NULL, (void *(*)(void *))rb_vm_thread_run, (void *)thread) != 0) { rb_sys_fail("pthread_create() failed"); } return thread; }
static VALUE rb_mutex_trylock(VALUE self, SEL sel) { if (pthread_mutex_trylock(&GetMutexPtr(self)->mutex) == 0) { GetMutexPtr(self)->thread = GetThreadPtr(rb_vm_current_thread()); return Qtrue; } return Qfalse; }
static VALUE thread_initialize(VALUE thread, SEL sel, int argc, const VALUE *argv) { if (!rb_block_given_p()) { rb_raise(rb_eThreadError, "must be called with a block"); } rb_vm_block_t *b = rb_vm_current_block(); assert(b != NULL); rb_vm_thread_t *t = GetThreadPtr(thread); if (t->thread != 0) { rb_raise(rb_eThreadError, "already initialized thread"); } rb_vm_thread_pre_init(t, b, argc, argv, rb_vm_create_vm()); // The thread's group is always the parent's one. // The parent group might be nil (ex. if created from GCD). VALUE group = GetThreadPtr(rb_vm_current_thread())->group; if (group != Qnil) { thgroup_add_m(group, thread, false); } // Retain the Thread object to avoid a potential GC, the corresponding // release is done in rb_vm_thread_run(). GC_RETAIN(thread); // Prepare attributes for the thread. pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // Register the thread to the core. We are doing this before actually // running it because the current thread might perform a method poking at // the current registered threads (such as Kernel#sleep) right after that. rb_vm_register_thread(thread); // Launch it. if (pthread_create(&t->thread, &attr, (void *(*)(void *))rb_vm_thread_run, (void *)thread) != 0) { rb_sys_fail("pthread_create() failed"); } pthread_attr_destroy(&attr); return thread; }
static VALUE rb_mutex_trylock(VALUE self, SEL sel) { rb_vm_mutex_t *m = GetMutexPtr(self); if (pthread_mutex_trylock(&m->mutex) == 0) { rb_vm_thread_t *current = GetThreadPtr(rb_vm_current_thread()); m->thread = current; if (current->mutexes == Qnil) { GC_WB(¤t->mutexes, rb_ary_new()); OBJ_UNTRUST(current->mutexes); } rb_ary_push(current->mutexes, self); return Qtrue; } return Qfalse; }
static bool rb_mutex_can_unlock(rb_vm_mutex_t *m, bool raise) { if (m->thread == NULL) { if (!raise) { return false; } rb_raise(rb_eThreadError, "Attempt to unlock a mutex which is not locked"); } else if (m->thread != GetThreadPtr(rb_vm_current_thread())) { if (!raise) { return false; } rb_raise(rb_eThreadError, "Attempt to unlock a mutex which is locked by another thread"); } return true; }
static VALUE rb_mutex_lock(VALUE self, SEL sel) { rb_vm_thread_t *current = GetThreadPtr(rb_vm_current_thread()); rb_vm_mutex_t *m = GetMutexPtr(self); if (m->thread == current) { rb_raise(rb_eThreadError, "deadlock; recursive locking"); } current->status = THREAD_SLEEP; pthread_assert(pthread_mutex_lock(&m->mutex)); current->status = THREAD_ALIVE; m->thread = current; if (current->mutexes == Qnil) { GC_WB(¤t->mutexes, rb_ary_new()); } rb_ary_push(current->mutexes, self); return self; }
static VALUE thread_s_current(VALUE klass, SEL sel) { return rb_vm_current_thread(); }
static VALUE rb_thread_exit(void) { return rb_thread_kill(rb_vm_current_thread(), 0); }
static int do_select(int n, fd_set *read, fd_set *write, fd_set *except, struct timeval *timeout) { int result, lerrno; fd_set orig_read, orig_write, orig_except; bzero(&orig_read, sizeof(fd_set)); bzero(&orig_write, sizeof(fd_set)); bzero(&orig_except, sizeof(fd_set)); #ifndef linux double limit = 0; struct timeval wait_rest; if (timeout) { limit = timeofday() + (double)timeout->tv_sec+(double)timeout->tv_usec*1e-6; wait_rest = *timeout; timeout = &wait_rest; } #endif if (read) orig_read = *read; if (write) orig_write = *write; if (except) orig_except = *except; retry: lerrno = 0; rb_vm_thread_t *thread = GetThreadPtr(rb_vm_current_thread()); rb_vm_thread_status_t prev_status = thread->status; thread->status = THREAD_SLEEP; //BLOCKING_REGION({ result = select(n, read, write, except, timeout); if (result < 0) lerrno = errno; //}, ubf_select, GET_THREAD()); thread->status = prev_status; errno = lerrno; if (result < 0) { switch (errno) { case EINTR: #ifdef ERESTART case ERESTART: #endif if (read) *read = orig_read; if (write) *write = orig_write; if (except) *except = orig_except; #ifndef linux if (timeout) { double d = limit - timeofday(); wait_rest.tv_sec = (unsigned int)d; wait_rest.tv_usec = (long)((d-(double)wait_rest.tv_sec)*1e6); if (wait_rest.tv_sec < 0) wait_rest.tv_sec = 0; if (wait_rest.tv_usec < 0) wait_rest.tv_usec = 0; } #endif goto retry; default: break; } } return result; }