diff options
author | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2010-11-27 20:15:59 +0000 |
---|---|---|
committer | ko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2010-11-27 20:15:59 +0000 |
commit | 450463d5fbc7098666c1405b5ea1ece4c8dd04a5 (patch) | |
tree | 028db9106314e277c7bd6329c97ce13fef9e9094 /thread_pthread.c | |
parent | ac0f5a53b6d567b319df73878bd08d70cbbdc37c (diff) |
* thread.c, vm_core.h: make gvl_acquire/release/init/destruct
APIs to modularize GVL implementation.
* thread_pthread.c, thread_pthread.h: Two GVL implementations.
(1) Simple locking GVL which is same as existing GVL.
(2) Wake-up queued threads. The wake-up order is simple FIFO.
(We can make several queues to support exact priorities, however
this causes some issues such as priority inversion and so on.)
This impl. prevents spin-loop (*1) caused on SMP environemnts.
*1: Only one Ruby thread acqures GVL again and again.
Bug #2359 [ruby-core:26694]
* thread_win32.c, thread_win32.h: Using simple lock
not by CRITICAL_SECTION but by Mutex.
Bug #3890 [ruby-dev:42315]
* vm.c (ruby_vm_destruct): ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@29956 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'thread_pthread.c')
-rw-r--r-- | thread_pthread.c | 166 |
1 files changed, 161 insertions, 5 deletions
diff --git a/thread_pthread.c b/thread_pthread.c index e835bf81cd..2207fde0e3 100644 --- a/thread_pthread.c +++ b/thread_pthread.c @@ -29,10 +29,158 @@ static void native_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); static void native_cond_initialize(pthread_cond_t *cond); static void native_cond_destroy(pthread_cond_t *cond); +static void native_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)); + +#define native_mutex_reinitialize_atfork(lock) (\ + native_mutex_unlock(lock), \ + native_mutex_initialize(lock), \ + native_mutex_lock(lock)) + +#define GVL_SIMPLE_LOCK 0 +#define GVL_DEBUG 0 + +static void +gvl_show_waiting_threads(rb_vm_t *vm) +{ + rb_thread_t *th = vm->gvl.waiting_threads; + int i = 0; + while (th) { + fprintf(stderr, "waiting (%d): %p\n", i++, th); + th = th->native_thread_data.gvl_next; + } +} + +static void +gvl_waiting_push(rb_vm_t *vm, rb_thread_t *th) +{ + th->native_thread_data.gvl_next = 0; + + if (vm->gvl.waiting_threads) { + vm->gvl.waiting_last_thread->native_thread_data.gvl_next = th; + vm->gvl.waiting_last_thread = th; + } + else { + vm->gvl.waiting_threads = th; + vm->gvl.waiting_last_thread = th; + } + th = vm->gvl.waiting_threads; + vm->gvl.waiting++; +} + +static void +gvl_waiting_shift(rb_vm_t *vm, rb_thread_t *th) +{ + vm->gvl.waiting_threads = vm->gvl.waiting_threads->native_thread_data.gvl_next; + vm->gvl.waiting--; +} + +static void +gvl_acquire(rb_vm_t *vm, rb_thread_t *th) +{ +#if GVL_SIMPLE_LOCK + native_mutex_lock(&vm->gvl.lock); +#else + native_mutex_lock(&vm->gvl.lock); + if (vm->gvl.waiting > 0 || vm->gvl.acquired != 0) { + if (GVL_DEBUG) fprintf(stderr, "gvl acquire (%p): sleep\n", th); + gvl_waiting_push(vm, th); + if (GVL_DEBUG) gvl_show_waiting_threads(vm); + + while (vm->gvl.acquired != 0 || vm->gvl.waiting_threads != th) { + native_cond_wait(&th->native_thread_data.gvl_cond, &vm->gvl.lock); + } + gvl_waiting_shift(vm, th); + } + else { + /* do nothing */ + } + vm->gvl.acquired = 1; + native_mutex_unlock(&vm->gvl.lock); +#endif + if (GVL_DEBUG) gvl_show_waiting_threads(vm); + if (GVL_DEBUG) fprintf(stderr, "gvl acquire (%p): acquire\n", th); +} + +static void +gvl_release(rb_vm_t *vm) +{ +#if GVL_SIMPLE_LOCK + native_mutex_unlock(&vm->gvl.lock); +#else + native_mutex_lock(&vm->gvl.lock); + if (vm->gvl.waiting > 0) { + rb_thread_t *th = vm->gvl.waiting_threads; + if (GVL_DEBUG) fprintf(stderr, "gvl release (%p): wakeup: %p\n", GET_THREAD(), th); + native_cond_signal(&th->native_thread_data.gvl_cond); + } + else { + if (GVL_DEBUG) fprintf(stderr, "gvl release (%p): wakeup: %p\n", GET_THREAD(), 0); + /* do nothing */ + } + vm->gvl.acquired = 0; + native_mutex_unlock(&vm->gvl.lock); +#endif +} + +static void +gvl_atfork(rb_vm_t *vm) +{ +#if GVL_SIMPLE_LOCK + native_mutex_reinitialize_atfork(&vm->gvl.lock); +#else + /* do nothing */ +#endif +} + +static void gvl_init(rb_vm_t *vm); + +static void +gvl_atfork_child(void) +{ + gvl_init(GET_VM()); +} + +static void +gvl_init(rb_vm_t *vm) +{ + int r; + if (GVL_DEBUG) fprintf(stderr, "gvl init\n"); + native_mutex_initialize(&vm->gvl.lock); + native_atfork(0, 0, gvl_atfork_child); + + vm->gvl.waiting_threads = 0; + vm->gvl.waiting_last_thread = 0; + vm->gvl.waiting = 0; + vm->gvl.acquired = 0; +} + +static void +gvl_destroy(rb_vm_t *vm) +{ + if (GVL_DEBUG) fprintf(stderr, "gvl destroy\n"); + native_mutex_destroy(&vm->gvl.lock); +} + +static void +mutex_debug(const char *msg, pthread_mutex_t *lock) +{ + if (0) { + int r; + static pthread_mutex_t dbglock = PTHREAD_MUTEX_INITIALIZER; + + if ((r = pthread_mutex_lock(&dbglock)) != 0) {exit(1);} + fprintf(stdout, "%s: %p\n", msg, lock); + if ((r = pthread_mutex_unlock(&dbglock)) != 0) {exit(1);} + } +} + +#define NATIVE_MUTEX_LOCK_DEBUG 1 + static void native_mutex_lock(pthread_mutex_t *lock) { int r; + mutex_debug("lock", lock); if ((r = pthread_mutex_lock(lock)) != 0) { rb_bug_errno("pthread_mutex_lock", r); } @@ -42,6 +190,7 @@ static void native_mutex_unlock(pthread_mutex_t *lock) { int r; + mutex_debug("unlock", lock); if ((r = pthread_mutex_unlock(lock)) != 0) { rb_bug_errno("pthread_mutex_unlock", r); } @@ -51,6 +200,7 @@ static inline int native_mutex_trylock(pthread_mutex_t *lock) { int r; + mutex_debug("trylock", lock); if ((r = pthread_mutex_trylock(lock)) != 0) { if (r == EBUSY) { return EBUSY; @@ -66,20 +216,17 @@ static void native_mutex_initialize(pthread_mutex_t *lock) { int r = pthread_mutex_init(lock, 0); + mutex_debug("init", lock); if (r != 0) { rb_bug_errno("pthread_mutex_init", r); } } -#define native_mutex_reinitialize_atfork(lock) (\ - native_mutex_unlock(lock), \ - native_mutex_initialize(lock), \ - native_mutex_lock(lock)) - static void native_mutex_destroy(pthread_mutex_t *lock) { int r = pthread_mutex_destroy(lock); + mutex_debug("destroy", lock); if (r != 0) { rb_bug_errno("pthread_mutex_destroy", r); } @@ -127,6 +274,14 @@ native_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, struct times return pthread_cond_timedwait(cond, mutex, ts); } +static void +native_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void)) +{ + int r = pthread_atfork(prepare, parent, child); + if (r != 0) { + rb_bug_errno("native_atfork", r); + } +} #define native_cleanup_push pthread_cleanup_push #define native_cleanup_pop pthread_cleanup_pop @@ -171,6 +326,7 @@ Init_native_thread(void) pthread_key_create(&ruby_native_thread_key, NULL); th->thread_id = pthread_self(); native_cond_initialize(&th->native_thread_data.sleep_cond); + native_cond_initialize(&th->native_thread_data.gvl_cond); ruby_thread_set_native(th); native_mutex_initialize(&signal_thread_list_lock); posix_signal(SIGVTALRM, null_func); |