pthread_once.c 1.1 KB

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