headers/bsd: Add sys/queue.h.
[haiku.git] / src / system / libroot / posix / pthread / pthread_once.cpp
blob7ef275452b253d61caa079c10065e3d7e1735783
1 /*
2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
6 #include <pthread.h>
8 #include <OS.h>
11 enum {
12 STATE_UNINITIALIZED = -1, // keep in sync with PTHREAD_ONCE_INIT
13 STATE_INITIALIZING = -2,
14 STATE_SPINNING = -3,
15 STATE_INITIALIZED = -4
19 /*! Called when the thread performing the initialization function was canceled.
21 \param data Pointer to the \c pthread_once_t structure in question.
23 static void
24 init_function_canceled(void* data)
26 pthread_once_t* onceControl = (pthread_once_t*)data;
28 // reset the control state to uninitialized
29 int32 value = atomic_get_and_set((int32*)&onceControl->state,
30 STATE_UNINITIALIZED);
32 // If someone has set a semaphore, delete it.
33 if (value >= 0)
34 delete_sem(value);
38 // #pragma mark -
41 int
42 pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void))
44 // Algorithm:
45 // The state goes through at most four states:
46 // STATE_UNINITIALIZED: The initial uninitialized state.
47 // STATE_INITIALIZING: Set by the first thread entering the function. It
48 // will call initRoutine.
49 // semaphore/STATE_SPINNING: Set by the second thread entering the function,
50 // when the first thread is still executing initRoutine. The normal case is
51 // that the thread manages to create a semaphore. This thread (and all
52 // following threads) will block on the semaphore until the first thread is
53 // done.
54 // STATE_INITIALIZED: Set by the first thread when it returns from
55 // initRoutine. All following threads will return right away.
57 while (true) {
58 int32 value = atomic_test_and_set((int32*)&onceControl->state,
59 STATE_INITIALIZING, STATE_UNINITIALIZED);
61 if (value == STATE_INITIALIZED)
62 return 0;
64 if (value == STATE_UNINITIALIZED) {
65 // we're the first -- perform the initialization
66 pthread_cleanup_push(&init_function_canceled, onceControl);
67 initRoutine();
68 pthread_cleanup_pop(false);
70 value = atomic_get_and_set((int32*)&onceControl->state,
71 STATE_INITIALIZED);
73 // If someone else is waiting, we need to delete the semaphore.
74 if (value >= 0)
75 delete_sem(value);
77 return 0;
80 if (value == STATE_INITIALIZING) {
81 // someone is initializing -- we need to create a semaphore we can
82 // wait on
83 sem_id semaphore = create_sem(0, "pthread once");
84 if (semaphore >= 0) {
85 // successfully created -- set it
86 value = atomic_test_and_set((int32*)&onceControl->state,
87 semaphore, STATE_INITIALIZING);
88 if (value == STATE_INITIALIZING)
89 value = semaphore;
90 else
91 delete_sem(semaphore);
92 } else {
93 // Failed to create the semaphore. Can only happen when the
94 // system runs out of semaphores, but we can still handle the
95 // situation gracefully by spinning.
96 value = atomic_test_and_set((int32*)&onceControl->state,
97 STATE_SPINNING, STATE_INITIALIZING);
98 if (value == STATE_INITIALIZING)
99 value = STATE_SPINNING;
103 if (value >= 0) {
104 // wait on the semaphore
105 while (acquire_sem(value) == B_INTERRUPTED);
107 return 0;
108 } else if (value == STATE_SPINNING) {
109 // out of semaphores -- spin
110 while (atomic_get((int32*)&onceControl->state) == STATE_SPINNING);