In the pthread_once(), if the initializer has already run, then the

calling thread is supposed to see accesses issued by the initializer.
This means that the read of the once_control->state variable should
have an acquire semantic.  Use atomic_thread_fence_acq() when the
value read is ONCE_DONE, instead of straightforward atomic_load_acq(),
to only put a barrier when needed (*).

On the other hand, the updates of the once_control->state with the
intermediate progress state do not need to synchronize with other
state accesses, remove _acq suffix.

Reviewed by:	alc (previous version)
Suggested by:	alc (*)
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
This commit is contained in:
Konstantin Belousov 2015-09-08 08:41:07 +00:00
parent 599acbbcd8
commit 3e7e67c08d
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=287556

View File

@ -68,13 +68,15 @@ _pthread_once(pthread_once_t *once_control, void (*init_routine) (void))
for (;;) {
state = once_control->state;
if (state == ONCE_DONE)
if (state == ONCE_DONE) {
atomic_thread_fence_acq();
return (0);
}
if (state == ONCE_NEVER_DONE) {
if (atomic_cmpset_acq_int(&once_control->state, state, ONCE_IN_PROGRESS))
if (atomic_cmpset_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))
if (atomic_cmpset_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);