xref: /haiku/src/system/libroot/posix/pthread/pthread_once.cpp (revision 6347519ab1e01f6d6077d36d515baeef311d0f1f)
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