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