1 /* 2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2023, Haiku, Inc. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 #include <pthread.h> 8 #include <threads.h> 9 10 #include <OS.h> 11 #include <Debug.h> 12 13 14 enum { 15 STATE_UNINITIALIZED = -1, // keep in sync with PTHREAD_ONCE_INIT 16 STATE_INITIALIZING = -2, 17 STATE_SPINNING = -3, 18 STATE_INITIALIZED = -4 19 }; 20 21 #if __cplusplus >= 201103L 22 STATIC_ASSERT(((pthread_once_t)ONCE_FLAG_INIT).state == ((pthread_once_t)PTHREAD_ONCE_INIT).state); 23 STATIC_ASSERT(((pthread_once_t)PTHREAD_ONCE_INIT).state == STATE_UNINITIALIZED); 24 #endif 25 26 27 /*! Called when the thread performing the initialization function was canceled. 28 29 \param data Pointer to the \c pthread_once_t structure in question. 30 */ 31 static void 32 init_function_canceled(void* data) 33 { 34 pthread_once_t* onceControl = (pthread_once_t*)data; 35 36 // reset the control state to uninitialized 37 int32 value = atomic_get_and_set((int32*)&onceControl->state, 38 STATE_UNINITIALIZED); 39 40 // If someone has set a semaphore, delete it. 41 if (value >= 0) 42 delete_sem(value); 43 } 44 45 46 // #pragma mark - 47 48 49 int 50 pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void)) 51 { 52 // Algorithm: 53 // The state goes through at most four states: 54 // STATE_UNINITIALIZED: The initial uninitialized state. 55 // STATE_INITIALIZING: Set by the first thread entering the function. It 56 // will call initRoutine. 57 // semaphore/STATE_SPINNING: Set by the second thread entering the function, 58 // when the first thread is still executing initRoutine. The normal case is 59 // that the thread manages to create a semaphore. This thread (and all 60 // following threads) will block on the semaphore until the first thread is 61 // done. 62 // STATE_INITIALIZED: Set by the first thread when it returns from 63 // initRoutine. All following threads will return right away. 64 65 while (true) { 66 int32 value = atomic_test_and_set((int32*)&onceControl->state, 67 STATE_INITIALIZING, STATE_UNINITIALIZED); 68 69 if (value == STATE_INITIALIZED) 70 return 0; 71 72 if (value == STATE_UNINITIALIZED) { 73 // we're the first -- perform the initialization 74 pthread_cleanup_push(&init_function_canceled, onceControl); 75 initRoutine(); 76 pthread_cleanup_pop(false); 77 78 value = atomic_get_and_set((int32*)&onceControl->state, 79 STATE_INITIALIZED); 80 81 // If someone else is waiting, we need to delete the semaphore. 82 if (value >= 0) 83 delete_sem(value); 84 85 return 0; 86 } 87 88 if (value == STATE_INITIALIZING) { 89 // someone is initializing -- we need to create a semaphore we can 90 // wait on 91 sem_id semaphore = create_sem(0, "pthread once"); 92 if (semaphore >= 0) { 93 // successfully created -- set it 94 value = atomic_test_and_set((int32*)&onceControl->state, 95 semaphore, STATE_INITIALIZING); 96 if (value == STATE_INITIALIZING) 97 value = semaphore; 98 else 99 delete_sem(semaphore); 100 } else { 101 // Failed to create the semaphore. Can only happen when the 102 // system runs out of semaphores, but we can still handle the 103 // situation gracefully by spinning. 104 value = atomic_test_and_set((int32*)&onceControl->state, 105 STATE_SPINNING, STATE_INITIALIZING); 106 if (value == STATE_INITIALIZING) 107 value = STATE_SPINNING; 108 } 109 } 110 111 if (value >= 0) { 112 // wait on the semaphore 113 while (acquire_sem(value) == B_INTERRUPTED); 114 115 return 0; 116 } else if (value == STATE_SPINNING) { 117 // out of semaphores -- spin 118 while (atomic_get((int32*)&onceControl->state) == STATE_SPINNING); 119 } 120 } 121 } 122