Author: bonefish Date: 2011-06-09 01:06:55 +0200 (Thu, 09 Jun 2011) New Revision: 42064 Changeset: https://dev.haiku-os.org/changeset/42064 Modified: haiku/branches/developer/bonefish/signals/src/system/libroot/posix/pthread/pthread_once.cpp Log: pthread_once(): The initialization function may be a cancellation point. So push a cleanup function that resets the control state before executing it. The whole function must loop now for that to work. Modified: haiku/branches/developer/bonefish/signals/src/system/libroot/posix/pthread/pthread_once.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/libroot/posix/pthread/pthread_once.cpp 2011-06-08 22:25:04 UTC (rev 42063) +++ haiku/branches/developer/bonefish/signals/src/system/libroot/posix/pthread/pthread_once.cpp 2011-06-08 23:06:55 UTC (rev 42064) @@ -1,5 +1,5 @@ /* - * Copyright 2008, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@xxxxxxx * Distributed under the terms of the MIT License. */ @@ -16,6 +16,27 @@ }; +/*! Called when the thread performing the initialization function was canceled. + + \param data Pointer to the \c pthread_once_t structure in question. +*/ +static void +init_function_canceled(void* data) +{ + pthread_once_t* onceControl = (pthread_once_t*)data; + + // reset the control state to uninitialized + int32 value = atomic_set((vint32*)&onceControl->state, STATE_UNINITIALIZED); + + // If someone has set a semaphore, delete it. + if (value >= 0) + delete_sem(value); +} + + +// #pragma mark - + + int pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void)) { @@ -32,57 +53,59 @@ // STATE_INITIALIZED: Set by the first thread when it returns from // initRoutine. All following threads will return right away. - int32 value = atomic_test_and_set((vint32*)&onceControl->state, - STATE_INITIALIZING, STATE_UNINITIALIZED); + while (true) { + int32 value = atomic_test_and_set((vint32*)&onceControl->state, + STATE_INITIALIZING, STATE_UNINITIALIZED); - if (value == STATE_INITIALIZED) - return 0; + if (value == STATE_INITIALIZED) + return 0; - if (value == STATE_UNINITIALIZED) { - // we're the first -- perform the initialization - initRoutine(); + if (value == STATE_UNINITIALIZED) { + // we're the first -- perform the initialization + pthread_cleanup_push(&init_function_canceled, onceControl); + initRoutine(); + pthread_cleanup_pop(false); - value = atomic_set((vint32*)&onceControl->state, STATE_INITIALIZED); + value = atomic_set((vint32*)&onceControl->state, STATE_INITIALIZED); - // If someone else is waiting, we need to delete the semaphore. - if (value >= 0) - delete_sem(value); + // If someone else is waiting, we need to delete the semaphore. + if (value >= 0) + delete_sem(value); - return 0; - } + return 0; + } - if (value == STATE_INITIALIZING) { - // someone is initializing -- we need to create a semaphore we can wait - // on - sem_id semaphore = create_sem(0, "pthread once"); - if (semaphore >= 0) { - // successfully created -- set it - value = atomic_test_and_set((vint32*)&onceControl->state, - semaphore, STATE_INITIALIZING); - if (value == STATE_INITIALIZING) - value = semaphore; - else - delete_sem(semaphore); - } else { - // Failed to create the semaphore. Can only happen when the system - // runs out of semaphores, but we can still handle the situation - // gracefully by spinning. - value = atomic_test_and_set((vint32*)&onceControl->state, - STATE_SPINNING, STATE_INITIALIZING); - if (value == STATE_INITIALIZING) - value = STATE_SPINNING; + if (value == STATE_INITIALIZING) { + // someone is initializing -- we need to create a semaphore we can + // wait on + sem_id semaphore = create_sem(0, "pthread once"); + if (semaphore >= 0) { + // successfully created -- set it + value = atomic_test_and_set((vint32*)&onceControl->state, + semaphore, STATE_INITIALIZING); + if (value == STATE_INITIALIZING) + value = semaphore; + else + delete_sem(semaphore); + } else { + // Failed to create the semaphore. Can only happen when the + // system runs out of semaphores, but we can still handle the + // situation gracefully by spinning. + value = atomic_test_and_set((vint32*)&onceControl->state, + STATE_SPINNING, STATE_INITIALIZING); + if (value == STATE_INITIALIZING) + value = STATE_SPINNING; + } } - } - if (value >= 0) { - // wait on the semaphore - while (acquire_sem(value) == B_INTERRUPTED); + if (value >= 0) { + // wait on the semaphore + while (acquire_sem(value) == B_INTERRUPTED); - return 0; - } else if (value == STATE_SPINNING) { - // out of semaphores -- spin - while (atomic_get((vint32*)&onceControl->state) == STATE_SPINNING); + return 0; + } else if (value == STATE_SPINNING) { + // out of semaphores -- spin + while (atomic_get((vint32*)&onceControl->state) == STATE_SPINNING); + } } - - return 0; }