* Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
* Copyright 2023, Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <pthread.h>
#include <threads.h>
#include <OS.h>
#include <Debug.h>
enum {
STATE_UNINITIALIZED = -1,
STATE_INITIALIZING = -2,
STATE_SPINNING = -3,
STATE_INITIALIZED = -4
};
#if __cplusplus >= 201103L
STATIC_ASSERT(((pthread_once_t)ONCE_FLAG_INIT).state == ((pthread_once_t)PTHREAD_ONCE_INIT).state);
STATIC_ASSERT(((pthread_once_t)PTHREAD_ONCE_INIT).state == STATE_UNINITIALIZED);
#endif
\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;
int32 value = atomic_get_and_set((int32*)&onceControl->state,
STATE_UNINITIALIZED);
if (value >= 0)
delete_sem(value);
}
int
pthread_once(pthread_once_t* onceControl, void (*initRoutine)(void))
{
while (true) {
int32 value = atomic_test_and_set((int32*)&onceControl->state,
STATE_INITIALIZING, STATE_UNINITIALIZED);
if (value == STATE_INITIALIZED)
return 0;
if (value == STATE_UNINITIALIZED) {
pthread_cleanup_push(&init_function_canceled, onceControl);
initRoutine();
pthread_cleanup_pop(false);
value = atomic_get_and_set((int32*)&onceControl->state,
STATE_INITIALIZED);
if (value >= 0)
delete_sem(value);
return 0;
}
if (value == STATE_INITIALIZING) {
sem_id semaphore = create_sem(0, "pthread once");
if (semaphore >= 0) {
value = atomic_test_and_set((int32*)&onceControl->state,
semaphore, STATE_INITIALIZING);
if (value == STATE_INITIALIZING)
value = semaphore;
else
delete_sem(semaphore);
} else {
value = atomic_test_and_set((int32*)&onceControl->state,
STATE_SPINNING, STATE_INITIALIZING);
if (value == STATE_INITIALIZING)
value = STATE_SPINNING;
}
}
if (value >= 0) {
while (acquire_sem(value) == B_INTERRUPTED);
return 0;
} else if (value == STATE_SPINNING) {
while (atomic_get((int32*)&onceControl->state) == STATE_SPINNING);
}
}
}