Skip to content

Commit

Permalink
posix: optimize epicsThreadOnce()
Browse files Browse the repository at this point in the history
Use atomic ops to short circuit when already initialized
  • Loading branch information
mdavidsaver committed Jan 10, 2023
1 parent 4da03c4 commit 77463b0
Showing 1 changed file with 18 additions and 9 deletions.
27 changes: 18 additions & 9 deletions modules/libcom/src/osi/os/posix/osdThread.c
Expand Up @@ -523,33 +523,42 @@ LIBCOM_API unsigned int epicsStdCall epicsThreadGetStackSize (epicsThreadStackSi

LIBCOM_API void epicsStdCall epicsThreadOnce(epicsThreadOnceId *id, void (*func)(void *), void *arg)
{
static struct epicsThreadOSD threadOnceComplete;
#define EPICS_THREAD_ONCE_DONE &threadOnceComplete
static const struct epicsThreadOSD threadOnceComplete;
#define EPICS_THREAD_ONCE_DONE (void*)&threadOnceComplete
int status;
void *prev, *self;

epicsThreadInit();
if(epicsAtomicGetPtrT((void**)id) == EPICS_THREAD_ONCE_DONE) {
return; /* fast path. global init already done. No need to lock */
}

self = epicsThreadGetIdSelf();
status = mutexLock(&onceLock);
if(status) {
fprintf(stderr,"epicsThreadOnce: pthread_mutex_lock returned %s.\n",
strerror(status));
exit(-1);
}

if (*id != EPICS_THREAD_ONCE_DONE) {
if (*id == EPICS_THREAD_ONCE_INIT) { /* first call */
*id = epicsThreadGetIdSelf(); /* mark active */
/* slow path, check again and attempt to begin */
prev = epicsAtomicCmpAndSwapPtrT((void**)id, EPICS_THREAD_ONCE_INIT, self);

if (prev != EPICS_THREAD_ONCE_DONE) {
if (prev == EPICS_THREAD_ONCE_INIT) { /* first call, already marked active */
status = pthread_mutex_unlock(&onceLock);
checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce");
func(arg);
status = mutexLock(&onceLock);
checkStatusQuit(status,"pthread_mutex_lock", "epicsThreadOnce");
*id = EPICS_THREAD_ONCE_DONE; /* mark done */
} else if (*id == epicsThreadGetIdSelf()) {
epicsAtomicSetPtrT((void**)id, EPICS_THREAD_ONCE_DONE); /* mark done */

} else if (prev == self) {
status = pthread_mutex_unlock(&onceLock);
checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce");
cantProceed("Recursive epicsThreadOnce() initialization\n");

} else
while (*id != EPICS_THREAD_ONCE_DONE) {
while ((prev = epicsAtomicGetPtrT((void**)id)) != EPICS_THREAD_ONCE_DONE) {
/* Another thread is in the above func(arg) call. */
status = pthread_mutex_unlock(&onceLock);
checkStatusQuit(status,"pthread_mutex_unlock", "epicsThreadOnce");
Expand Down

0 comments on commit 77463b0

Please sign in to comment.