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