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
10enum {
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
18status_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