#include <rt/mutex.h>
#include <rt/assert.h>
#include <rt/interrupt.h>
#include <rt/syscall.h>
#include <rt/task.h>
#include <rt/trace.h>
#include <stddef.h>
static bool trylock(struct rt_mutex *mutex, uintptr_t new_holder)
{
uintptr_t e = RT_MUTEX_UNLOCKED;
if (rt_atomic_compare_exchange(&mutex->holder, &e, new_holder,
RT_ATOMIC_ACQUIRE, RT_ATOMIC_RELAXED))
{
rt_trace_mutex_lock(mutex, new_holder);
return true;
}
const uintptr_t holder = e & RT_MUTEX_HOLDER_MASK;
if (holder == new_holder)
{
rt_assert(mutex->level != RT_MUTEX_LEVEL_NONRECURSIVE,
"recursively locked a non-recursive mutex");
++mutex->level;
return true;
}
rt_trace_mutex_lock_fail(mutex, new_holder, holder);
return false;
}
static inline uintptr_t task_or_interrupt_ptr(void)
{
return rt_interrupt_is_active() ? RT_MUTEX_HOLDER_INTERRUPT
: (uintptr_t)rt_task_self();
}
bool rt_mutex_trylock(struct rt_mutex *mutex)
{
return trylock(mutex, task_or_interrupt_ptr());
}
void rt_mutex_lock(struct rt_mutex *mutex)
{
rt_assert(!rt_interrupt_is_active(), "mutex lock from an interrupt");
if (!trylock(mutex, (uintptr_t)rt_task_self()))
{
rt_syscall_mutex_lock(mutex);
}
}
bool rt_mutex_timedlock(struct rt_mutex *mutex, unsigned long ticks)
{
rt_assert(!rt_interrupt_is_active() || (ticks == 0),
"mutex timedlock from an interrupt");
return trylock(mutex, task_or_interrupt_ptr()) ||
((ticks != 0) && rt_syscall_mutex_timedlock(mutex, ticks));
}
void rt_mutex_unlock(struct rt_mutex *mutex)
{
uintptr_t holder = rt_atomic_load(&mutex->holder, RT_ATOMIC_RELAXED);
rt_assert((holder & RT_MUTEX_HOLDER_MASK) == task_or_interrupt_ptr(),
"unlock while not holding the mutex");
if (mutex->level > 0)
{
--mutex->level;
return;
}
if (((holder & RT_MUTEX_WAITED_MASK) == 0) &&
rt_atomic_compare_exchange(&mutex->holder, &holder, RT_MUTEX_UNLOCKED,
RT_ATOMIC_RELEASE, RT_ATOMIC_RELAXED))
{
rt_trace_mutex_unlock(mutex, holder);
}
else
{
rt_syscall_mutex_unlock(mutex);
}
}