diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/thread/pthread_once.c | 22 | 
1 files changed, 13 insertions, 9 deletions
diff --git a/src/thread/pthread_once.c b/src/thread/pthread_once.c index b7388b93..7c47385c 100644 --- a/src/thread/pthread_once.c +++ b/src/thread/pthread_once.c @@ -2,14 +2,14 @@  static void undo(void *control)  { -	a_store(control, 0); -	__wake(control, 1, 1); +	/* Wake all waiters, since the waiter status is lost when +	 * resetting control to the initial state. */ +	if (a_swap(control, 0) == 3) +		__wake(control, -1, 1);  }  int __pthread_once(pthread_once_t *control, void (*init)(void))  { -	static int waiters; -  	/* Return immediately if init finished before, but ensure that  	 * effects of the init routine are visible to the caller. */  	if (*control == 2) { @@ -17,10 +17,11 @@ int __pthread_once(pthread_once_t *control, void (*init)(void))  		return 0;  	} -	/* Try to enter initializing state. Three possibilities: +	/* Try to enter initializing state. Four possibilities:  	 *  0 - we're the first or the other cancelled; run init  	 *  1 - another thread is running init; wait -	 *  2 - another thread finished running init; just return */ +	 *  2 - another thread finished running init; just return +	 *  3 - another thread is running init, waiters present; wait */  	for (;;) switch (a_cas(control, 0, 1)) {  	case 0: @@ -28,11 +29,14 @@ int __pthread_once(pthread_once_t *control, void (*init)(void))  		init();  		pthread_cleanup_pop(0); -		a_store(control, 2); -		if (waiters) __wake(control, -1, 1); +		if (a_swap(control, 2) == 3) +			__wake(control, -1, 1);  		return 0;  	case 1: -		__wait(control, &waiters, 1, 1); +		/* If this fails, so will __wait. */ +		a_cas(control, 1, 3); +	case 3: +		__wait(control, 0, 3, 1);  		continue;  	case 2:  		return 0;  | 
