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