Files
libsst/libsst-concurrency/SST_ThreadBarrier.c
2026-04-03 00:22:39 -05:00

151 lines
4.3 KiB
C

/*
SST_ThreadBarrier.c
Author: Patrick Baggett <ptbaggett@762studios.com>
Created: 12/20/2011
Purpose:
libsst-concurrency thread barrier functions
License:
This program is free software. It comes without any warranty, to
the extent permitted by applicable law. You can redistribute it
and/or modify it under the terms of the Do What The Fuck You Want
To Public License, Version 2, as published by Sam Hocevar. See
http://sam.zoy.org/wtfpl/COPYING for more details.
*/
#include <SST/SST_Concurrency.h> /* SST_WAIT_INFINITE */
#include <SST/SST_ThreadBarrier.h> /* Functions being implemented */
#include <SST/SST_Atomic.h> /* Needed for atomic operations */
#include <stdlib.h>
#define WAKEUP_UNSIGNALED (-1)
#define WAKEUP_CANCELED 0
#define WAKEUP_SUCCESS 1
typedef struct SST_ThreadBarrierImpl
{
SST_Event event;
uint32_t count;
volatile int wakeup_reason;
uint32_t threshold;
} SST_ThreadBarrierImpl;
/*************************************************************************/
SST_ThreadBarrier SST_Concurrency_CreateThreadBarrier(uint32_t threshold)
{
SST_ThreadBarrierImpl* barrier;
/* Allocate barrier */
barrier = (SST_ThreadBarrierImpl*)malloc(sizeof(SST_ThreadBarrierImpl));
if(barrier == NULL)
return NULL;
barrier->event = SST_Concurrency_CreateEvent();
if(barrier->event == NULL) /* Out of memory */
{
free(barrier);
return NULL;
}
barrier->count = 0;
barrier->wakeup_reason = WAKEUP_UNSIGNALED; /* == error condition */
barrier->threshold = threshold;
/* Ensure all memory operations are visible by the time this returns */
SST_Concurrency_MemoryBarrier();
return (SST_ThreadBarrier)barrier;
}
/*************************************************************************/
int SST_Concurrency_WaitThreadBarrier(SST_ThreadBarrier _barrier)
{
SST_ThreadBarrierImpl* barrier = (SST_ThreadBarrierImpl*)_barrier;
uint32_t newValue;
const int reason = barrier->wakeup_reason;
int retval;
/* Already canceled? */
if(reason == WAKEUP_CANCELED)
return 0;
/* Already successful? */
if(reason == WAKEUP_SUCCESS)
return (int)barrier->threshold;
/* At this point, we believe the barrier has not yet been canceled/finished. Due to
multithreading, it could have been though. */
/* Increment barrier count, see if we've exceeded the threshold */
newValue = (uint32_t)SST_Atomic_AddReturn((volatile int*)&barrier->count, 1);
if(newValue == barrier->threshold)
{
/* Yes -- but is it already canceled/finished? */
const int state = SST_Atomic_CAS(&barrier->wakeup_reason, WAKEUP_UNSIGNALED, WAKEUP_SUCCESS);
/*
'state' can only be WAKEUP_SUCCESS or WAKEUP_CANCELED at this point. Don't
(redundantly) signal the event if it was canceled.
*/
if(state == WAKEUP_SUCCESS)
SST_Concurrency_SignalEvent(barrier->event);
retval = SST_BARRIER_SINGLE;
}
else /* either newValue < threshold newValue > threshold or */
{
/* No not yet. Wait. */
SST_Concurrency_WaitEvent(barrier->event, SST_WAIT_INFINITE);
retval = 1;
}
/* Threads could have been woken up due to a cancel or due to the count being exceeded. */
if(barrier->wakeup_reason == WAKEUP_UNSIGNALED) /* error? */
return -1;
if(barrier->wakeup_reason == WAKEUP_CANCELED) /* canceled */
return 0;
return retval;
}
/*************************************************************************/
void SST_Concurrency_ResetThreadBarrier(SST_ThreadBarrier _barrier, uint32_t newThreshold)
{
SST_ThreadBarrierImpl* barrier = (SST_ThreadBarrierImpl*)_barrier;
barrier->count = 0;
barrier->threshold = newThreshold;
barrier->wakeup_reason = WAKEUP_UNSIGNALED; /* == error condition */
SST_Concurrency_ResetEvent(barrier->event);
}
/*************************************************************************/
void SST_Concurrency_CancelThreadBarrier(SST_ThreadBarrier _barrier)
{
SST_ThreadBarrierImpl* barrier = (SST_ThreadBarrierImpl*)_barrier;
barrier->wakeup_reason = WAKEUP_CANCELED; /* == barrier canceled */
SST_Concurrency_SignalEvent(barrier->event);
/* NOTE: SignalEvent() provides a memory barrier so that barrier->wakeup_reason is globally
visible before the event is signaled. */
}
/*************************************************************************/
void SST_Concurrency_DestroyThreadBarrier(SST_ThreadBarrier _barrier)
{
SST_ThreadBarrierImpl* barrier = (SST_ThreadBarrierImpl*)_barrier;
SST_Concurrency_DestroyEvent(barrier->event);
free(barrier);
}