xref: /haiku/src/system/libroot/posix/pthread/pthread_once.cpp (revision b671e9bbdbd10268a042b4f4cc4317ccd03d105e)
1 /*
2  * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include <pthread.h>
7 
8 #include <OS.h>
9 
10 
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
16 };
17 
18 
19 int
20 pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void))
21 {
22 	// Algorithm:
23 	// The state goes through at most four states:
24 	// STATE_UNINITIALIZED: The initial uninitialized state.
25 	// STATE_INITIALIZING: Set by the first thread entering the function. It
26 	// will call initRoutine.
27 	// semaphore/STATE_SPINNING: Set by the second thread entering the function,
28 	// when the first thread is still executing initRoutine. The normal case is
29 	// that the thread manages to create a semaphore. This thread (and all
30 	// following threads) will block on the semaphore until the first thread is
31 	// done.
32 	// STATE_INITIALIZED: Set by the first thread when it returns from
33 	// initRoutine. All following threads will return right away.
34 
35 	int32 value = atomic_test_and_set((vint32*)&onceControl->state,
36 		STATE_INITIALIZING, STATE_UNINITIALIZED);
37 
38 	if (value == STATE_INITIALIZED)
39 		return 0;
40 
41 	if (value == STATE_UNINITIALIZED) {
42 		// we're the first -- perform the initialization
43 		initRoutine();
44 
45 		value = atomic_set((vint32*)&onceControl->state, STATE_INITIALIZED);
46 
47 		// If someone else is waiting, we need to delete the semaphore.
48 		if (value >= 0)
49 			delete_sem(value);
50 
51 		return 0;
52 	}
53 
54 	if (value == STATE_INITIALIZING) {
55 		// someone is initializing -- we need to create a semaphore we can wait
56 		// on
57 		sem_id semaphore = create_sem(0, "pthread once");
58 		if (semaphore >= 0) {
59 			// successfully created -- set it
60 			value = atomic_test_and_set((vint32*)&onceControl->state,
61 				semaphore, STATE_INITIALIZING);
62 			if (value == STATE_INITIALIZING)
63 				value = semaphore;
64 			else
65 				delete_sem(semaphore);
66 		} else {
67 			// Failed to create the semaphore. Can only happen when the system
68 			// runs out of semaphores, but we can still handle the situation
69 			// gracefully by spinning.
70 			value = atomic_test_and_set((vint32*)&onceControl->state,
71 				STATE_SPINNING, STATE_INITIALIZING);
72 			if (value == STATE_INITIALIZING)
73 				value = STATE_SPINNING;
74 		}
75 	}
76 
77 	if (value >= 0) {
78 		// wait on the semaphore
79 		while (acquire_sem(value) == B_INTERRUPTED);
80 
81 		return 0;
82 	} else if (value == STATE_SPINNING) {
83 		// out of semaphores -- spin
84 		while (atomic_get((vint32*)&onceControl->state) == STATE_SPINNING);
85 	}
86 
87 	return 0;
88 }
89