Luna/kernel/src/lib/Mutex.cpp
apio 4af337e92d
Some checks are pending
continuous-integration/drone/push Build is running
kernel: Improve the mutex system
2023-09-20 07:05:33 +02:00

94 lines
2.6 KiB
C++

#include "lib/Mutex.h"
#include "Log.h"
#include "arch/CPU.h"
#include "thread/Scheduler.h"
void Mutex::lock()
{
auto* current = Scheduler::current();
const pid_t desired = current->id;
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
while (true)
{
// Make sure only one thread is touching the mutex at the same time.
m_spinlock.lock();
pid_t expected = 0;
if (!m_thread.compare_exchange_strong(expected, desired))
{
if (expected == desired)
{
kerrorln("DEADLOCK! KMutex::lock() recursively called by the same thread (%d)", current->id);
fail("Mutex deadlock detected");
}
bool ok = m_blocked_threads.try_push(current);
m_spinlock.unlock();
if (!ok) kernel_sleep(10);
else
kernel_wait_for_event();
}
else
{
m_spinlock.unlock();
break;
}
}
};
void Mutex::unlock()
{
auto* current = Scheduler::current();
pid_t expected = current->id;
check(expected > 0); // Why the hell would the idle thread be touching a mutex?
m_spinlock.lock();
if (!m_thread.compare_exchange_strong(expected, 0))
{
kerrorln("KMutex::unlock() called on a lock already locked by another thread (%d, current is %d)", expected,
current->id);
fail("Mutex unlock by different thread");
}
Thread* blocked;
if (m_blocked_threads.try_pop(blocked))
{
while (blocked->state == ThreadState::Runnable)
{
// Rarely, we could switch to here between m_spinlock.unlock() and kernel_wait_for_event() above.
// Let the thread go to sleep before waking it up again.
kernel_yield();
}
blocked->wake_up();
}
m_spinlock.unlock();
}
bool Mutex::try_lock()
{
auto* current = Scheduler::current();
const pid_t desired = current->id;
check(desired > 0); // Why the hell would the idle thread be touching a mutex?
// Make sure only one thread is touching the mutex at the same time.
m_spinlock.lock();
pid_t expected = 0;
bool ok = m_thread.compare_exchange_strong(expected, desired);
if (expected == desired)
{
kwarnln("Deadlock avoided! KMutex::try_lock() failed because it was already locked by the same thread "
"(%d), this is not supposed to happen",
current->id);
CPU::print_stack_trace();
}
m_spinlock.unlock();
return ok;
}