kernel: Improve the mutex system
Some checks are pending
continuous-integration/drone/push Build is running
Some checks are pending
continuous-integration/drone/push Build is running
This commit is contained in:
parent
3e896b0f62
commit
4af337e92d
@ -20,6 +20,7 @@ set(SOURCES
|
|||||||
src/arch/Serial.cpp
|
src/arch/Serial.cpp
|
||||||
src/arch/Timer.cpp
|
src/arch/Timer.cpp
|
||||||
src/arch/PCI.cpp
|
src/arch/PCI.cpp
|
||||||
|
src/lib/Mutex.cpp
|
||||||
src/thread/Thread.cpp
|
src/thread/Thread.cpp
|
||||||
src/thread/ThreadImage.cpp
|
src/thread/ThreadImage.cpp
|
||||||
src/thread/Scheduler.cpp
|
src/thread/Scheduler.cpp
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#include "arch/x86_64/disk/ATA.h"
|
#include "arch/x86_64/disk/ATA.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "arch/CPU.h"
|
||||||
#include "arch/Serial.h"
|
#include "arch/Serial.h"
|
||||||
#include "arch/Timer.h"
|
#include "arch/Timer.h"
|
||||||
#include "arch/x86_64/IO.h"
|
#include "arch/x86_64/IO.h"
|
||||||
#include "fs/MBR.h"
|
#include "fs/MBR.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
#include <luna/Buffer.h>
|
#include <luna/Buffer.h>
|
||||||
#include <luna/CType.h>
|
#include <luna/CType.h>
|
||||||
@ -296,7 +298,7 @@ namespace ATA
|
|||||||
|
|
||||||
for (u8 drive = 0; drive < 2; drive++)
|
for (u8 drive = 0; drive < 2; drive++)
|
||||||
{
|
{
|
||||||
ScopedKMutexLock<100> lock(m_lock);
|
ScopedMutexLock lock(m_lock);
|
||||||
|
|
||||||
select(drive);
|
select(drive);
|
||||||
|
|
||||||
@ -757,7 +759,7 @@ ATADevice::ATADevice(ATA::Drive* drive) : BlockDevice(drive->block_size(), drive
|
|||||||
|
|
||||||
Result<void> ATADevice::read_block(Buffer& buf, u64 block) const
|
Result<void> ATADevice::read_block(Buffer& buf, u64 block) const
|
||||||
{
|
{
|
||||||
ScopedKMutexLock<100> lock(m_drive->channel()->lock());
|
ScopedMutexLock lock(m_drive->channel()->lock());
|
||||||
|
|
||||||
if (buf.size() != m_drive->block_size())
|
if (buf.size() != m_drive->block_size())
|
||||||
{
|
{
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
#include "arch/PCI.h"
|
#include "arch/PCI.h"
|
||||||
#include "fs/devices/BlockDevice.h"
|
#include "fs/devices/BlockDevice.h"
|
||||||
#include "fs/devices/DeviceRegistry.h"
|
#include "fs/devices/DeviceRegistry.h"
|
||||||
#include "lib/KMutex.h"
|
#include "lib/Mutex.h"
|
||||||
#include <luna/Atomic.h>
|
#include <luna/Atomic.h>
|
||||||
#include <luna/SharedPtr.h>
|
#include <luna/SharedPtr.h>
|
||||||
#include <luna/StaticString.h>
|
#include <luna/StaticString.h>
|
||||||
@ -244,7 +244,7 @@ namespace ATA
|
|||||||
return m_interrupt_line;
|
return m_interrupt_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
KMutex<100>& lock()
|
Mutex& lock()
|
||||||
{
|
{
|
||||||
return m_lock;
|
return m_lock;
|
||||||
}
|
}
|
||||||
@ -259,7 +259,7 @@ namespace ATA
|
|||||||
bool m_is_pci_native_mode;
|
bool m_is_pci_native_mode;
|
||||||
u8 m_interrupt_line;
|
u8 m_interrupt_line;
|
||||||
|
|
||||||
KMutex<100> m_lock {};
|
Mutex m_lock {};
|
||||||
|
|
||||||
Thread* m_thread { nullptr };
|
Thread* m_thread { nullptr };
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "lib/KMutex.h"
|
#include "lib/Mutex.h"
|
||||||
#include <luna/Buffer.h>
|
#include <luna/Buffer.h>
|
||||||
#include <luna/HashMap.h>
|
#include <luna/HashMap.h>
|
||||||
#include <luna/LinkedList.h>
|
#include <luna/LinkedList.h>
|
||||||
@ -33,5 +33,5 @@ class StorageCache : public LinkedListNode<StorageCache>
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
HashMap<u64, CacheEntry> m_cache_entries;
|
HashMap<u64, CacheEntry> m_cache_entries;
|
||||||
KMutex<100> m_mutex;
|
Mutex m_mutex;
|
||||||
};
|
};
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include "Log.h"
|
|
||||||
#include "arch/CPU.h"
|
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
#include "thread/Thread.h"
|
|
||||||
#include <luna/CircularQueue.h>
|
|
||||||
|
|
||||||
template <usize ConcurrentThreads> class KMutex
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void lock()
|
|
||||||
{
|
|
||||||
int expected = 0;
|
|
||||||
while (!m_lock.compare_exchange_strong(expected, 1))
|
|
||||||
{
|
|
||||||
expected = 0;
|
|
||||||
auto* current = Scheduler::current();
|
|
||||||
|
|
||||||
// We cannot be interrupted between these functions, otherwise we might never exit the loop
|
|
||||||
CPU::disable_interrupts();
|
|
||||||
bool ok = m_blocked_threads.try_push(current);
|
|
||||||
if (!ok) kernel_sleep(10);
|
|
||||||
else
|
|
||||||
kernel_wait_for_event();
|
|
||||||
CPU::enable_interrupts();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void unlock()
|
|
||||||
{
|
|
||||||
int expected = 1;
|
|
||||||
if (!m_lock.compare_exchange_strong(expected, 0))
|
|
||||||
{
|
|
||||||
kwarnln("KMutex::unlock() called on an unlocked lock with value %d", expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread* blocked;
|
|
||||||
if (m_blocked_threads.try_pop(blocked)) blocked->wake_up();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool try_lock()
|
|
||||||
{
|
|
||||||
int expected = 0;
|
|
||||||
return m_lock.compare_exchange_strong(expected, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
CircularQueue<Thread*, ConcurrentThreads> m_blocked_threads;
|
|
||||||
Atomic<int> m_lock;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <usize ConcurrentThreads> class ScopedKMutexLock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ScopedKMutexLock(KMutex<ConcurrentThreads>& lock) : m_lock(lock)
|
|
||||||
{
|
|
||||||
m_lock.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
~ScopedKMutexLock()
|
|
||||||
{
|
|
||||||
if (!m_taken_over) m_lock.unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedKMutexLock(const ScopedKMutexLock&) = delete;
|
|
||||||
ScopedKMutexLock(ScopedKMutexLock&&) = delete;
|
|
||||||
|
|
||||||
KMutex<ConcurrentThreads>& take_over()
|
|
||||||
{
|
|
||||||
m_taken_over = true;
|
|
||||||
return m_lock;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
KMutex<ConcurrentThreads>& m_lock;
|
|
||||||
bool m_taken_over { false };
|
|
||||||
};
|
|
93
kernel/src/lib/Mutex.cpp
Normal file
93
kernel/src/lib/Mutex.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#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;
|
||||||
|
}
|
44
kernel/src/lib/Mutex.h
Normal file
44
kernel/src/lib/Mutex.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "thread/Thread.h"
|
||||||
|
#include <luna/CircularQueue.h>
|
||||||
|
#include <luna/Spinlock.h>
|
||||||
|
|
||||||
|
class Mutex
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void lock();
|
||||||
|
void unlock();
|
||||||
|
bool try_lock();
|
||||||
|
|
||||||
|
private:
|
||||||
|
CircularQueue<Thread*, 32> m_blocked_threads;
|
||||||
|
Spinlock m_spinlock;
|
||||||
|
Atomic<pid_t> m_thread;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScopedMutexLock
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScopedMutexLock(Mutex& lock) : m_lock(lock)
|
||||||
|
{
|
||||||
|
m_lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
~ScopedMutexLock()
|
||||||
|
{
|
||||||
|
if (!m_taken_over) m_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedMutexLock(const ScopedMutexLock&) = delete;
|
||||||
|
ScopedMutexLock(ScopedMutexLock&&) = delete;
|
||||||
|
|
||||||
|
Mutex& take_over()
|
||||||
|
{
|
||||||
|
m_taken_over = true;
|
||||||
|
return m_lock;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Mutex& m_lock;
|
||||||
|
bool m_taken_over { false };
|
||||||
|
};
|
@ -4,6 +4,7 @@
|
|||||||
#include "fs/StorageCache.h"
|
#include "fs/StorageCache.h"
|
||||||
#include "memory/KernelVM.h"
|
#include "memory/KernelVM.h"
|
||||||
#include "memory/MemoryMap.h"
|
#include "memory/MemoryMap.h"
|
||||||
|
#include "thread/Scheduler.h"
|
||||||
#include <luna/Alignment.h>
|
#include <luna/Alignment.h>
|
||||||
#include <luna/Bitmap.h>
|
#include <luna/Bitmap.h>
|
||||||
#include <luna/Common.h>
|
#include <luna/Common.h>
|
||||||
|
Loading…
Reference in New Issue
Block a user