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