1 /*
2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Copyright 2023, Haiku, Inc. All rights reserved.
4 * Distributed under the terms of the MIT License.
5 */
6
7 #include <pthread.h>
8 #include <threads.h>
9
10 #include <OS.h>
11 #include <Debug.h>
12
13
14 enum {
15 STATE_UNINITIALIZED = -1, // keep in sync with PTHREAD_ONCE_INIT
16 STATE_INITIALIZING = -2,
17 STATE_SPINNING = -3,
18 STATE_INITIALIZED = -4
19 };
20
21 #if __cplusplus >= 201103L
22 STATIC_ASSERT(((pthread_once_t)ONCE_FLAG_INIT).state == ((pthread_once_t)PTHREAD_ONCE_INIT).state);
23 STATIC_ASSERT(((pthread_once_t)PTHREAD_ONCE_INIT).state == STATE_UNINITIALIZED);
24 #endif
25
26
27 /*! Called when the thread performing the initialization function was canceled.
28
29 \param data Pointer to the \c pthread_once_t structure in question.
30 */
31 static void
init_function_canceled(void * data)32 init_function_canceled(void* data)
33 {
34 pthread_once_t* onceControl = (pthread_once_t*)data;
35
36 // reset the control state to uninitialized
37 int32 value = atomic_get_and_set((int32*)&onceControl->state,
38 STATE_UNINITIALIZED);
39
40 // If someone has set a semaphore, delete it.
41 if (value >= 0)
42 delete_sem(value);
43 }
44
45
46 // #pragma mark -
47
48
49 int
pthread_once(pthread_once_t * onceControl,void (* initRoutine)(void))50 pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void))
51 {
52 // Algorithm:
53 // The state goes through at most four states:
54 // STATE_UNINITIALIZED: The initial uninitialized state.
55 // STATE_INITIALIZING: Set by the first thread entering the function. It
56 // will call initRoutine.
57 // semaphore/STATE_SPINNING: Set by the second thread entering the function,
58 // when the first thread is still executing initRoutine. The normal case is
59 // that the thread manages to create a semaphore. This thread (and all
60 // following threads) will block on the semaphore until the first thread is
61 // done.
62 // STATE_INITIALIZED: Set by the first thread when it returns from
63 // initRoutine. All following threads will return right away.
64
65 while (true) {
66 int32 value = atomic_test_and_set((int32*)&onceControl->state,
67 STATE_INITIALIZING, STATE_UNINITIALIZED);
68
69 if (value == STATE_INITIALIZED)
70 return 0;
71
72 if (value == STATE_UNINITIALIZED) {
73 // we're the first -- perform the initialization
74 pthread_cleanup_push(&init_function_canceled, onceControl);
75 initRoutine();
76 pthread_cleanup_pop(false);
77
78 value = atomic_get_and_set((int32*)&onceControl->state,
79 STATE_INITIALIZED);
80
81 // If someone else is waiting, we need to delete the semaphore.
82 if (value >= 0)
83 delete_sem(value);
84
85 return 0;
86 }
87
88 if (value == STATE_INITIALIZING) {
89 // someone is initializing -- we need to create a semaphore we can
90 // wait on
91 sem_id semaphore = create_sem(0, "pthread once");
92 if (semaphore >= 0) {
93 // successfully created -- set it
94 value = atomic_test_and_set((int32*)&onceControl->state,
95 semaphore, STATE_INITIALIZING);
96 if (value == STATE_INITIALIZING)
97 value = semaphore;
98 else
99 delete_sem(semaphore);
100 } else {
101 // Failed to create the semaphore. Can only happen when the
102 // system runs out of semaphores, but we can still handle the
103 // situation gracefully by spinning.
104 value = atomic_test_and_set((int32*)&onceControl->state,
105 STATE_SPINNING, STATE_INITIALIZING);
106 if (value == STATE_INITIALIZING)
107 value = STATE_SPINNING;
108 }
109 }
110
111 if (value >= 0) {
112 // wait on the semaphore
113 while (acquire_sem(value) == B_INTERRUPTED);
114
115 return 0;
116 } else if (value == STATE_SPINNING) {
117 // out of semaphores -- spin
118 while (atomic_get((int32*)&onceControl->state) == STATE_SPINNING);
119 }
120 }
121 }
122