1 /* 2 * Copyright 2008-2011, 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 /*! Called when the thread performing the initialization function was canceled. 20 21 \param data Pointer to the \c pthread_once_t structure in question. 22 */ 23 static void 24 init_function_canceled(void* data) 25 { 26 pthread_once_t* onceControl = (pthread_once_t*)data; 27 28 // reset the control state to uninitialized 29 int32 value = atomic_set((vint32*)&onceControl->state, STATE_UNINITIALIZED); 30 31 // If someone has set a semaphore, delete it. 32 if (value >= 0) 33 delete_sem(value); 34 } 35 36 37 // #pragma mark - 38 39 40 int 41 pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void)) 42 { 43 // Algorithm: 44 // The state goes through at most four states: 45 // STATE_UNINITIALIZED: The initial uninitialized state. 46 // STATE_INITIALIZING: Set by the first thread entering the function. It 47 // will call initRoutine. 48 // semaphore/STATE_SPINNING: Set by the second thread entering the function, 49 // when the first thread is still executing initRoutine. The normal case is 50 // that the thread manages to create a semaphore. This thread (and all 51 // following threads) will block on the semaphore until the first thread is 52 // done. 53 // STATE_INITIALIZED: Set by the first thread when it returns from 54 // initRoutine. All following threads will return right away. 55 56 while (true) { 57 int32 value = atomic_test_and_set((vint32*)&onceControl->state, 58 STATE_INITIALIZING, STATE_UNINITIALIZED); 59 60 if (value == STATE_INITIALIZED) 61 return 0; 62 63 if (value == STATE_UNINITIALIZED) { 64 // we're the first -- perform the initialization 65 pthread_cleanup_push(&init_function_canceled, onceControl); 66 initRoutine(); 67 pthread_cleanup_pop(false); 68 69 value = atomic_set((vint32*)&onceControl->state, STATE_INITIALIZED); 70 71 // If someone else is waiting, we need to delete the semaphore. 72 if (value >= 0) 73 delete_sem(value); 74 75 return 0; 76 } 77 78 if (value == STATE_INITIALIZING) { 79 // someone is initializing -- we need to create a semaphore we can 80 // wait on 81 sem_id semaphore = create_sem(0, "pthread once"); 82 if (semaphore >= 0) { 83 // successfully created -- set it 84 value = atomic_test_and_set((vint32*)&onceControl->state, 85 semaphore, STATE_INITIALIZING); 86 if (value == STATE_INITIALIZING) 87 value = semaphore; 88 else 89 delete_sem(semaphore); 90 } else { 91 // Failed to create the semaphore. Can only happen when the 92 // system runs out of semaphores, but we can still handle the 93 // situation gracefully by spinning. 94 value = atomic_test_and_set((vint32*)&onceControl->state, 95 STATE_SPINNING, STATE_INITIALIZING); 96 if (value == STATE_INITIALIZING) 97 value = STATE_SPINNING; 98 } 99 } 100 101 if (value >= 0) { 102 // wait on the semaphore 103 while (acquire_sem(value) == B_INTERRUPTED); 104 105 return 0; 106 } else if (value == STATE_SPINNING) { 107 // out of semaphores -- spin 108 while (atomic_get((vint32*)&onceControl->state) == STATE_SPINNING); 109 } 110 } 111 } 112