| 1 | #include "pthread_impl.h" |
| 2 | |
| 3 | static void undo(void *control) |
| 4 | { |
| 5 | 	/* Wake all waiters, since the waiter status is lost when |
| 6 | 	 * resetting control to the initial state. */ |
| 7 | 	if (a_swap(control, 0) == 3) |
| 8 | 		__wake(control, -1, 1); |
| 9 | } |
| 10 | |
| 11 | hidden int __pthread_once_full(pthread_once_t *control, void (*init)(void)) |
| 12 | { |
| 13 | 	/* Try to enter initializing state. Four possibilities: |
| 14 | 	 * 0 - we're the first or the other cancelled; run init |
| 15 | 	 * 1 - another thread is running init; wait |
| 16 | 	 * 2 - another thread finished running init; just return |
| 17 | 	 * 3 - another thread is running init, waiters present; wait */ |
| 18 | |
| 19 | 	for (;;) switch (a_cas(control, 0, 1)) { |
| 20 | 	case 0: |
| 21 | 		pthread_cleanup_push(undo, control); |
| 22 | 		init(); |
| 23 | 		pthread_cleanup_pop(0); |
| 24 | |
| 25 | 		if (a_swap(control, 2) == 3) |
| 26 | 			__wake(control, -1, 1); |
| 27 | 		return 0; |
| 28 | 	case 1: |
| 29 | 		/* If this fails, so will __wait. */ |
| 30 | 		a_cas(control, 1, 3); |
| 31 | 	case 3: |
| 32 | 		__wait(control, 0, 3, 1); |
| 33 | 		continue; |
| 34 | 	case 2: |
| 35 | 		return 0; |
| 36 | 	} |
| 37 | } |
| 38 | |
| 39 | int __pthread_once(pthread_once_t *control, void (*init)(void)) |
| 40 | { |
| 41 | 	/* Return immediately if init finished before, but ensure that |
| 42 | 	 * effects of the init routine are visible to the caller. */ |
| 43 | 	if (*(volatile int *)control == 2) { |
| 44 | 		a_barrier(); |
| 45 | 		return 0; |
| 46 | 	} |
| 47 | 	return __pthread_once_full(control, init); |
| 48 | } |
| 49 | |
| 50 | weak_alias(__pthread_once, pthread_once); |