diff options
author | davidxu <davidxu@FreeBSD.org> | 2008-05-30 00:02:59 +0000 |
---|---|---|
committer | davidxu <davidxu@FreeBSD.org> | 2008-05-30 00:02:59 +0000 |
commit | 8951bcd14cbf45e5917c52c16236bfa5515c6d1b (patch) | |
tree | 8b97dd790b5232097957cf65f57e0f1065e5a2b0 /lib/libthr | |
parent | 6d8aba47d87aef0990fa09d8791fef79d7a5d5ff (diff) | |
download | FreeBSD-src-8951bcd14cbf45e5917c52c16236bfa5515c6d1b.zip FreeBSD-src-8951bcd14cbf45e5917c52c16236bfa5515c6d1b.tar.gz |
Eliminate global mutex by using pthread_once's state field as
a semaphore.
Diffstat (limited to 'lib/libthr')
-rw-r--r-- | lib/libthr/thread/thr_once.c | 62 |
1 files changed, 29 insertions, 33 deletions
diff --git a/lib/libthr/thread/thr_once.c b/lib/libthr/thread/thr_once.c index bd1bed1..eb07066 100644 --- a/lib/libthr/thread/thr_once.c +++ b/lib/libthr/thread/thr_once.c @@ -38,10 +38,7 @@ __weak_reference(_pthread_once, pthread_once); #define ONCE_NEVER_DONE PTHREAD_NEEDS_INIT #define ONCE_DONE PTHREAD_DONE_INIT #define ONCE_IN_PROGRESS 0x02 -#define ONCE_MASK 0x03 - -static pthread_mutex_t _thr_once_lock = PTHREAD_MUTEX_INITIALIZER; -static pthread_cond_t _thr_once_cv = PTHREAD_COND_INITIALIZER; +#define ONCE_WAIT 0x03 /* * POSIX: @@ -55,47 +52,46 @@ once_cancel_handler(void *arg) { pthread_once_t *once_control = arg; - _pthread_mutex_lock(&_thr_once_lock); - once_control->state = ONCE_NEVER_DONE; - _pthread_mutex_unlock(&_thr_once_lock); - _pthread_cond_broadcast(&_thr_once_cv); + if (atomic_cmpset_rel_int(&once_control->state, ONCE_IN_PROGRESS, ONCE_NEVER_DONE)) + return; + atomic_store_rel_int(&once_control->state, ONCE_NEVER_DONE); + _thr_umtx_wake(&once_control->state, INT_MAX, 0); } int _pthread_once(pthread_once_t *once_control, void (*init_routine) (void)) { struct pthread *curthread; - int wakeup = 0; + int state; + + for (;;) { + state = once_control->state; + if (state == ONCE_DONE) + return (0); + if (state == ONCE_NEVER_DONE) { + if (atomic_cmpset_acq_int(&once_control->state, state, ONCE_IN_PROGRESS)) + break; + } else if (state == ONCE_IN_PROGRESS) { + if (atomic_cmpset_acq_int(&once_control->state, state, ONCE_WAIT)) + _thr_umtx_wait_uint(&once_control->state, ONCE_WAIT, NULL, 0); + } else if (state == ONCE_WAIT) { + _thr_umtx_wait_uint(&once_control->state, state, NULL, 0); + } else + return (EINVAL); + } - if (once_control->state == ONCE_DONE) + curthread = _get_curthread(); + THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control); + init_routine(); + THR_CLEANUP_POP(curthread, 0); + if (atomic_cmpset_rel_int(&once_control->state, ONCE_IN_PROGRESS, ONCE_DONE)) return (0); - _pthread_mutex_lock(&_thr_once_lock); - while (*(volatile int *)&(once_control->state) == ONCE_IN_PROGRESS) - _pthread_cond_wait(&_thr_once_cv, &_thr_once_lock); - /* - * If previous thread was canceled, then the state still - * could be ONCE_NEVER_DONE, we need to check it again. - */ - if (*(volatile int *)&(once_control->state) == ONCE_NEVER_DONE) { - once_control->state = ONCE_IN_PROGRESS; - _pthread_mutex_unlock(&_thr_once_lock); - curthread = _get_curthread(); - THR_CLEANUP_PUSH(curthread, once_cancel_handler, once_control); - init_routine(); - THR_CLEANUP_POP(curthread, 0); - _pthread_mutex_lock(&_thr_once_lock); - once_control->state = ONCE_DONE; - wakeup = 1; - } - _pthread_mutex_unlock(&_thr_once_lock); - if (wakeup) - _pthread_cond_broadcast(&_thr_once_cv); + atomic_store_rel_int(&once_control->state, ONCE_DONE); + _thr_umtx_wake(&once_control->state, INT_MAX, 0); return (0); } void _thr_once_init() { - _thr_once_lock = PTHREAD_MUTEX_INITIALIZER; - _thr_once_cv = PTHREAD_COND_INITIALIZER; } |