94 lines
2.6 KiB
C++
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;
|
|
}
|