diff --git a/zion/CMakeLists.txt b/zion/CMakeLists.txt index 9c4147a..dc562f2 100644 --- a/zion/CMakeLists.txt +++ b/zion/CMakeLists.txt @@ -11,7 +11,6 @@ add_executable(zion interrupt/interrupt.cpp interrupt/interrupt_enter.s interrupt/timer.cpp - lib/mutex.cpp lib/message_queue.cpp loader/init_loader.cpp memory/kernel_heap.cpp @@ -24,6 +23,7 @@ add_executable(zion object/endpoint.cpp object/ipc_object.cpp object/memory_object.cpp + object/mutex.cpp object/port.cpp object/process.cpp object/reply_port.cpp @@ -38,6 +38,7 @@ add_executable(zion syscall/ipc.cpp syscall/memory_object.cpp syscall/process.cpp + syscall/synchronization.cpp syscall/syscall.cpp syscall/syscall_enter.s syscall/thread.cpp diff --git a/zion/capability/capability_table.h b/zion/capability/capability_table.h index 5b59e5a..78cf85b 100644 --- a/zion/capability/capability_table.h +++ b/zion/capability/capability_table.h @@ -4,7 +4,7 @@ #include #include "capability/capability.h" -#include "lib/mutex.h" +#include "object/mutex.h" class CapabilityTable { public: @@ -25,7 +25,7 @@ class CapabilityTable { glcr::RefPtr ReleaseCapability(uint64_t id); private: - Mutex lock_{"cap table"}; + glcr::RefPtr lock_ = Mutex::Create(); // TODO: Do some randomization. uint64_t next_cap_id_ = 0x100; // FIXME: use a map data structure. diff --git a/zion/include/zcall.h b/zion/include/zcall.h index 117e569..ff8b797 100644 --- a/zion/include/zcall.h +++ b/zion/include/zcall.h @@ -57,4 +57,8 @@ SYS5(ReplyPortRecv, z_cap_t, reply_port_cap, uint64_t*, num_bytes, void*, data, SYS2(CapDuplicate, z_cap_t, cap_in, z_cap_t*, cap_out); +SYS1(MutexCreate, z_cap_t*, mutex_cap); +SYS1(MutexLock, z_cap_t, mutex_cap); +SYS1(MutexRelease, z_cap_t, mutex_cap); + SYS1(Debug, const char*, message); diff --git a/zion/include/ztypes.h b/zion/include/ztypes.h index a3c5c79..3870e4e 100644 --- a/zion/include/ztypes.h +++ b/zion/include/ztypes.h @@ -54,6 +54,11 @@ const uint64_t kZionEndpointCall = 0x65; // Capability Calls const uint64_t kZionCapDuplicate = 0x70; +// Syncronization Calls +const uint64_t kZionMutexCreate = 0x80; +const uint64_t kZionMutexLock = 0x81; +const uint64_t kZionMutexRelease = 0x82; + // Debugging Calls. const uint64_t kZionDebug = 0x1'0000; @@ -78,6 +83,10 @@ const uint64_t kZionPerm_Duplicate = 0x20; const uint64_t kZionPerm_SpawnProcess = 0x100; const uint64_t kZionPerm_SpawnThread = 0x200; +// Permissions on mutexes. +const uint64_t kZionPerm_Lock = 0x100; +const uint64_t kZionPerm_Release = 0x200; + /* ------------------------------ * Process Init Types * diff --git a/zion/lib/message_queue.cpp b/zion/lib/message_queue.cpp index 7dbf192..5588ecf 100644 --- a/zion/lib/message_queue.cpp +++ b/zion/lib/message_queue.cpp @@ -54,16 +54,16 @@ z_err_t UnboundedMessageQueue::PushBack(uint64_t num_bytes, const void* bytes, z_err_t UnboundedMessageQueue::PopFront(uint64_t* num_bytes, void* bytes, uint64_t* num_caps, z_cap_t* caps, z_cap_t* reply_cap) { - mutex_.Lock(); + mutex_->Lock(); while (pending_messages_.empty()) { auto thread = gScheduler->CurrentThread(); thread->SetState(Thread::BLOCKED); blocked_threads_.PushBack(thread); - mutex_.Unlock(); + mutex_->Release(); gScheduler->Yield(); - mutex_.Lock(); + mutex_->Lock(); } - mutex_.Unlock(); + mutex_->Release(); MutexHolder lock(mutex_); auto next_msg = pending_messages_.PeekFront(); @@ -162,16 +162,16 @@ glcr::ErrorCode SingleMessageQueue::PushBack(uint64_t num_bytes, glcr::ErrorCode SingleMessageQueue::PopFront(uint64_t* num_bytes, void* bytes, uint64_t* num_caps, z_cap_t* caps, z_cap_t* reply_port) { - mutex_.Lock(); + mutex_->Lock(); while (!has_written_) { auto thread = gScheduler->CurrentThread(); thread->SetState(Thread::BLOCKED); blocked_threads_.PushBack(thread); - mutex_.Unlock(); + mutex_->Release(); gScheduler->Yield(); - mutex_.Lock(); + mutex_->Lock(); } - mutex_.Unlock(); + mutex_->Release(); MutexHolder lock(mutex_); if (has_read_) { diff --git a/zion/lib/message_queue.h b/zion/lib/message_queue.h index bcd5d2d..3bd5733 100644 --- a/zion/lib/message_queue.h +++ b/zion/lib/message_queue.h @@ -8,7 +8,7 @@ #include "capability/capability.h" #include "include/ztypes.h" -#include "lib/mutex.h" +#include "object/mutex.h" class MessageQueue { public: @@ -23,7 +23,7 @@ class MessageQueue { virtual bool empty() = 0; protected: - Mutex mutex_{"message"}; + glcr::RefPtr mutex_ = Mutex::Create(); // FIXME: This maybe shouldn't be shared between classes since the // SingleMessageQueue should only ever have one blocked thread. glcr::IntrusiveList blocked_threads_; diff --git a/zion/lib/mutex.cpp b/zion/lib/mutex.cpp deleted file mode 100644 index 630f17c..0000000 --- a/zion/lib/mutex.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "lib/mutex.h" - -#include "debug/debug.h" -#include "scheduler/scheduler.h" - -void Mutex::Lock() { - while (__atomic_fetch_or(&lock_, 0x1, __ATOMIC_SEQ_CST) == 0x1) { - // dbgln("Lock sleep: %s", name_); - gScheduler->Preempt(); - } -} diff --git a/zion/lib/mutex.h b/zion/lib/mutex.h deleted file mode 100644 index fe86648..0000000 --- a/zion/lib/mutex.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include - -class Mutex { - public: - Mutex(const char* name) : name_(name) {} - - // FIXME: Block thread on lock rather than "preempting" - void Lock(); - void Unlock() { lock_ = false; } - - private: - const char* name_; - - uint8_t lock_ = 0; -}; - -class MutexHolder { - public: - MutexHolder(Mutex& mutex) : mutex_(mutex) { mutex_.Lock(); } - - ~MutexHolder() { mutex_.Unlock(); } - - MutexHolder(MutexHolder&) = delete; - MutexHolder(MutexHolder&&) = delete; - - private: - Mutex& mutex_; -}; diff --git a/zion/object/channel.h b/zion/object/channel.h index 8f32cd6..5a37f89 100644 --- a/zion/object/channel.h +++ b/zion/object/channel.h @@ -6,7 +6,6 @@ #include "capability/capability.h" #include "include/ztypes.h" #include "lib/message_queue.h" -#include "lib/mutex.h" #include "object/ipc_object.h" #include "object/kernel_object.h" #include "usr/zcall_internal.h" diff --git a/zion/object/endpoint.h b/zion/object/endpoint.h index a5af417..20c706a 100644 --- a/zion/object/endpoint.h +++ b/zion/object/endpoint.h @@ -5,7 +5,6 @@ #include #include "lib/message_queue.h" -#include "lib/mutex.h" #include "object/ipc_object.h" #include "object/kernel_object.h" diff --git a/zion/object/kernel_object.h b/zion/object/kernel_object.h index 7e258fa..6aca277 100644 --- a/zion/object/kernel_object.h +++ b/zion/object/kernel_object.h @@ -14,6 +14,7 @@ class KernelObject : public glcr::RefCounted { PORT = 0x6, ENDPOINT = 0x7, REPLY_PORT = 0x8, + MUTEX = 0x9, }; virtual uint64_t TypeTag() = 0; diff --git a/zion/object/mutex.cpp b/zion/object/mutex.cpp new file mode 100644 index 0000000..ff56dab --- /dev/null +++ b/zion/object/mutex.cpp @@ -0,0 +1,30 @@ +#include "object/mutex.h" + +#include "scheduler/scheduler.h" + +glcr::RefPtr Mutex::Create() { + return glcr::AdoptPtr(new Mutex()); +} + +// FIXME: We almost certainly have some race conditions +// between this and unlock where we could end up with +// a thread in the blocked_threads_ queue while noone is holding the lock. +void Mutex::Lock() { + while (__atomic_fetch_or(&lock_, 0x1, __ATOMIC_SEQ_CST) == 0x1) { + auto thread = gScheduler->CurrentThread(); + thread->SetState(Thread::BLOCKED); + blocked_threads_.PushBack(thread); + gScheduler->Yield(); + } +} + +// FIXME: Check that this thread is the one that is holding the mutex before +// releasing it. +void Mutex::Release() { + lock_ = 0; + if (blocked_threads_.size() > 0) { + auto thread = blocked_threads_.PopFront(); + thread->SetState(Thread::RUNNABLE); + gScheduler->Enqueue(thread); + } +} diff --git a/zion/object/mutex.h b/zion/object/mutex.h new file mode 100644 index 0000000..94e9f13 --- /dev/null +++ b/zion/object/mutex.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +#include "include/ztypes.h" +#include "object/kernel_object.h" +#include "object/thread.h" + +class Mutex; + +template <> +struct KernelObjectTag { + static const uint64_t type = KernelObject::MUTEX; +}; + +class Mutex : public KernelObject { + public: + uint64_t TypeTag() override { return KernelObject::MUTEX; } + static uint64_t DefaultPermissions() { + return kZionPerm_Lock | kZionPerm_Release; + } + + static glcr::RefPtr Create(); + + // Attempts to lock the mutex, suspends the current thread + // until the lock is acquired. + void Lock(); + + // Releases the mutex and activates the next thread in the + // blocked queue if it exists. + void Release(); + + private: + uint8_t lock_ = 0; + + glcr::IntrusiveList blocked_threads_; + + Mutex() {} +}; + +class MutexHolder { + public: + MutexHolder(const glcr::RefPtr& mutex) : mutex_(mutex) { + mutex_->Lock(); + } + + ~MutexHolder() { mutex_->Release(); } + + MutexHolder(MutexHolder&) = delete; + MutexHolder(MutexHolder&&) = delete; + + private: + glcr::RefPtr mutex_; +}; diff --git a/zion/object/port.h b/zion/object/port.h index 47737af..24513b8 100644 --- a/zion/object/port.h +++ b/zion/object/port.h @@ -5,7 +5,6 @@ #include "capability/capability.h" #include "lib/message_queue.h" -#include "lib/mutex.h" #include "object/ipc_object.h" #include "object/kernel_object.h" #include "object/thread.h" diff --git a/zion/object/process.h b/zion/object/process.h index d0bc3b4..a24db80 100644 --- a/zion/object/process.h +++ b/zion/object/process.h @@ -6,9 +6,9 @@ #include "capability/capability.h" #include "capability/capability_table.h" -#include "lib/mutex.h" #include "object/address_space.h" #include "object/channel.h" +#include "object/mutex.h" #include "object/port.h" // Forward decl due to cyclic dependency. @@ -66,7 +66,7 @@ class Process : public KernelObject { Process(); Process(uint64_t id) : id_(id), vmas_(AddressSpace::ForRoot()) {} - Mutex mutex_{"Process"}; + glcr::RefPtr mutex_ = Mutex::Create(); uint64_t id_; glcr::RefPtr vmas_; diff --git a/zion/object/reply_port.h b/zion/object/reply_port.h index 8dff55a..ecbc5cc 100644 --- a/zion/object/reply_port.h +++ b/zion/object/reply_port.h @@ -3,7 +3,6 @@ #include #include "lib/message_queue.h" -#include "lib/mutex.h" #include "object/ipc_object.h" #include "object/kernel_object.h" diff --git a/zion/syscall/synchronization.cpp b/zion/syscall/synchronization.cpp new file mode 100644 index 0000000..ed36879 --- /dev/null +++ b/zion/syscall/synchronization.cpp @@ -0,0 +1,31 @@ +#include "syscall/synchronization.h" + +#include "object/mutex.h" +#include "scheduler/scheduler.h" + +glcr::ErrorCode MutexCreate(ZMutexCreateReq* req) { + auto& curr_proc = gScheduler->CurrentProcess(); + *req->mutex_cap = curr_proc.AddNewCapability(Mutex::Create()); + return glcr::OK; +} + +glcr::ErrorCode MutexLock(ZMutexLockReq* req) { + auto& curr_proc = gScheduler->CurrentProcess(); + auto cap = curr_proc.GetCapability(req->mutex_cap); + RET_ERR(ValidateCapability(cap, kZionPerm_Lock)); + + auto mutex = cap->obj(); + mutex->Lock(); + return glcr::OK; +} + +glcr::ErrorCode MutexRelease(ZMutexReleaseReq* req) { + auto& curr_proc = gScheduler->CurrentProcess(); + auto cap = curr_proc.GetCapability(req->mutex_cap); + // TODO: We may not want a separate permission for releasing the mutex. + RET_ERR(ValidateCapability(cap, kZionPerm_Release)); + + auto mutex = cap->obj(); + mutex->Release(); + return glcr::OK; +} diff --git a/zion/syscall/synchronization.h b/zion/syscall/synchronization.h new file mode 100644 index 0000000..b508df2 --- /dev/null +++ b/zion/syscall/synchronization.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +#include "include/zcall.h" + +glcr::ErrorCode MutexCreate(ZMutexCreateReq* req); +glcr::ErrorCode MutexLock(ZMutexLockReq* req); +glcr::ErrorCode MutexRelease(ZMutexReleaseReq* req); diff --git a/zion/syscall/syscall.cpp b/zion/syscall/syscall.cpp index 7007cfc..ef5d249 100644 --- a/zion/syscall/syscall.cpp +++ b/zion/syscall/syscall.cpp @@ -11,6 +11,7 @@ #include "syscall/ipc.h" #include "syscall/memory_object.h" #include "syscall/process.h" +#include "syscall/synchronization.h" #include "syscall/thread.h" #define EFER 0xC0000080 @@ -80,6 +81,10 @@ extern "C" z_err_t SyscallHandler(uint64_t call_id, void* req) { CASE(ReplyPortRecv); // syscall/capability.h CASE(CapDuplicate); + // syscall/syncronization.h + CASE(MutexCreate); + CASE(MutexLock); + CASE(MutexRelease); // syscall/debug.h CASE(Debug); default: