Proper SMP reentrant mutex implementation
Posted: Thu Mar 07, 2019 1:15 pm
So I've implemented a reentrant mutex implementation and wanted to hear some opinions whether if I have covered all the edge-cases.
It's multiprocessor safe and modification is protected from preemption (hence the interrupt disabling meanwhile).
taskingGetLocal() returns a processor-local structure - as long as locksHeld variable is > 0, no tasks are scheduled to avoid kernel deadlocking.
What are your best practices for mutual exclusion?
It's multiprocessor safe and modification is protected from preemption (hence the interrupt disabling meanwhile).
taskingGetLocal() returns a processor-local structure - as long as locksHeld variable is > 0, no tasks are scheduled to avoid kernel deadlocking.
Code: Select all
#define G_MUTEX_INITIALIZED 0xFEED
struct g_mutex
{
volatile int initialized = 0;
volatile int lock = 0;
int depth = 0;
uint32_t owner = -1;
};
Code: Select all
void mutexAcquire(g_mutex* mutex, bool smp)
{
if(mutex->initialized != G_MUTEX_INITIALIZED)
mutexErrorUninitialized(mutex);
while(!mutexTryAcquire(mutex, smp)) {
asm("pause");
}
}
bool mutexTryAcquire(g_mutex* mutex, bool smp)
{
if(mutex->initialized != G_MUTEX_INITIALIZED)
mutexErrorUninitialized(mutex);
// Disable scheduling
bool enableInt = interruptsAreEnabled();
if(enableInt) interruptsDisable();
// Lock editing
while(!__sync_bool_compare_and_swap(&mutex->lock, 0, 1))
asm("pause");
// Update mutex
bool success = false;
if(mutex->depth == 0)
{
mutex->owner = processorGetCurrentId();
mutex->depth = 1;
if(smp) __sync_fetch_and_add(&taskingGetLocal()->locksHeld, 1);
success = true;
} else if(mutex->owner == processorGetCurrentId())
{
mutex->depth++;
success = true;
}
// Allow editing again
mutex->lock = 0;
// Enable interrupts
if(enableInt) interruptsEnable();
return success;
}
void mutexRelease(g_mutex* mutex, bool smp)
{
if(mutex->initialized != G_MUTEX_INITIALIZED)
mutexErrorUninitialized(mutex);
// Disable interrupts
bool enableInt = interruptsAreEnabled();
if(enableInt) interruptsDisable();
// Lock editing
while(!__sync_bool_compare_and_swap(&mutex->lock, 0, 1))
asm("pause");
// Update mutex
if(mutex->depth > 0 && --mutex->depth == 0)
{
mutex->depth = 0;
mutex->owner = -1;
if(smp) __sync_fetch_and_sub(&taskingGetLocal()->locksHeld, 1);
}
// Allow editing again
mutex->lock = 0;
// Enable interrupts
if(enableInt) interruptsEnable();
}
volatile int mutexInitializerLock = 0;
void mutexInitialize(g_mutex* mutex)
{
while(!__sync_bool_compare_and_swap(&mutexInitializerLock, 0, 1))
asm("pause");
if(mutex->initialized != G_MUTEX_INITIALIZED) {
mutex->initialized = G_MUTEX_INITIALIZED;
mutex->lock = 0;
mutex->depth = 0;
mutex->owner = -1;
}
mutexInitializerLock = 0;
}