diff --git a/zion/CMakeLists.txt b/zion/CMakeLists.txt index fa2e22e..d1c5446 100644 --- a/zion/CMakeLists.txt +++ b/zion/CMakeLists.txt @@ -31,6 +31,7 @@ add_executable(zion object/port.cpp object/process.cpp object/reply_port.cpp + object/semaphore.cpp object/thread.cpp scheduler/context_switch.s scheduler/jump_user_space.s diff --git a/zion/include/zcall.h b/zion/include/zcall.h index a610454..7823650 100644 --- a/zion/include/zcall.h +++ b/zion/include/zcall.h @@ -64,5 +64,8 @@ SYS1(CapRelease, z_cap_t, cap); SYS1(MutexCreate, z_cap_t*, mutex_cap); SYS1(MutexLock, z_cap_t, mutex_cap); SYS1(MutexRelease, z_cap_t, mutex_cap); +SYS1(SemaphoreCreate, z_cap_t*, semaphore_cap); +SYS1(SemaphoreWait, z_cap_t, semaphore_cap); +SYS1(SemaphoreSignal, z_cap_t, semaphore_cap); SYS1(Debug, const char*, message); diff --git a/zion/include/ztypes.h b/zion/include/ztypes.h index ebbb4c7..6dd9535 100644 --- a/zion/include/ztypes.h +++ b/zion/include/ztypes.h @@ -60,6 +60,9 @@ const uint64_t kZionCapRelease = 0x71; const uint64_t kZionMutexCreate = 0x80; const uint64_t kZionMutexLock = 0x81; const uint64_t kZionMutexRelease = 0x82; +const uint64_t kZionSemaphoreCreate = 0x83; +const uint64_t kZionSemaphoreWait = 0x84; +const uint64_t kZionSemaphoreSignal = 0x85; // Debugging Calls. const uint64_t kZionDebug = 0x1'0000; @@ -89,6 +92,9 @@ const uint64_t kZionPerm_SpawnThread = 0x200; // Permissions on mutexes. const uint64_t kZionPerm_Lock = 0x100; const uint64_t kZionPerm_Release = 0x200; +// Permissions on semaphores. +const uint64_t kZionPerm_Wait = 0x100; +const uint64_t kZionPerm_Signal = 0x200; const z_perm_t kZionPerm_None = 0; const z_perm_t kZionPerm_All = -1; diff --git a/zion/object/kernel_object.h b/zion/object/kernel_object.h index 6aca277..2bdd477 100644 --- a/zion/object/kernel_object.h +++ b/zion/object/kernel_object.h @@ -15,6 +15,7 @@ class KernelObject : public glcr::RefCounted { ENDPOINT = 0x7, REPLY_PORT = 0x8, MUTEX = 0x9, + SEMAPHORE = 0x10, }; virtual uint64_t TypeTag() = 0; diff --git a/zion/object/semaphore.cpp b/zion/object/semaphore.cpp new file mode 100644 index 0000000..30f78cc --- /dev/null +++ b/zion/object/semaphore.cpp @@ -0,0 +1,29 @@ +#include "object/semaphore.h" + +#include "scheduler/scheduler.h" + +glcr::RefPtr Semaphore::Create() { + return glcr::AdoptPtr(new Semaphore()); +} + +// 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 Semaphore::Signal() { + __atomic_fetch_add(&lock_, 0x1, __ATOMIC_SEQ_CST); + if (blocked_threads_.size() > 0) { + auto thread = blocked_threads_.PopFront(); + thread->SetState(Thread::RUNNABLE); + gScheduler->Enqueue(thread); + } +} + +void Semaphore::Wait() { + while (lock_ == 0) { + auto thread = gScheduler->CurrentThread(); + thread->SetState(Thread::BLOCKED); + blocked_threads_.PushBack(thread); + gScheduler->Yield(); + } + __atomic_fetch_sub(&lock_, 0x1, __ATOMIC_SEQ_CST); +} diff --git a/zion/object/semaphore.h b/zion/object/semaphore.h new file mode 100644 index 0000000..8fd0bfe --- /dev/null +++ b/zion/object/semaphore.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +#include "include/ztypes.h" +#include "object/kernel_object.h" +#include "object/thread.h" + +class Semaphore; + +template <> +struct KernelObjectTag { + static const uint64_t type = KernelObject::SEMAPHORE; +}; + +class Semaphore : public KernelObject { + public: + uint64_t TypeTag() override { return KernelObject::SEMAPHORE; } + static uint64_t DefaultPermissions() { + return kZionPerm_Wait | kZionPerm_Signal; + } + + static glcr::RefPtr Create(); + + void Wait(); + void Signal(); + + private: + uint8_t lock_ = 0; + + glcr::IntrusiveList blocked_threads_; +}; diff --git a/zion/syscall/synchronization.cpp b/zion/syscall/synchronization.cpp index ed36879..c9f1174 100644 --- a/zion/syscall/synchronization.cpp +++ b/zion/syscall/synchronization.cpp @@ -1,6 +1,7 @@ #include "syscall/synchronization.h" #include "object/mutex.h" +#include "object/semaphore.h" #include "scheduler/scheduler.h" glcr::ErrorCode MutexCreate(ZMutexCreateReq* req) { @@ -22,10 +23,35 @@ glcr::ErrorCode MutexLock(ZMutexLockReq* req) { 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; } + +glcr::ErrorCode SemaphoreCreate(ZSemaphoreCreateReq* req) { + auto& curr_proc = gScheduler->CurrentProcess(); + *req->semaphore_cap = curr_proc.AddNewCapability(Semaphore::Create()); + return glcr::OK; +} + +glcr::ErrorCode SemaphoreWait(ZSemaphoreWaitReq* req) { + auto& curr_proc = gScheduler->CurrentProcess(); + auto cap = curr_proc.GetCapability(req->semaphore_cap); + RET_ERR(ValidateCapability(cap, kZionPerm_Wait)); + + auto semaphore = cap->obj(); + semaphore->Wait(); + return glcr::OK; +} + +glcr::ErrorCode SemaphoreSignal(ZSemaphoreSignalReq* req) { + auto& curr_proc = gScheduler->CurrentProcess(); + auto cap = curr_proc.GetCapability(req->semaphore_cap); + RET_ERR(ValidateCapability(cap, kZionPerm_Signal)); + + auto semaphore = cap->obj(); + semaphore->Signal(); + return glcr::OK; +} diff --git a/zion/syscall/synchronization.h b/zion/syscall/synchronization.h index b508df2..4d1da24 100644 --- a/zion/syscall/synchronization.h +++ b/zion/syscall/synchronization.h @@ -7,3 +7,7 @@ glcr::ErrorCode MutexCreate(ZMutexCreateReq* req); glcr::ErrorCode MutexLock(ZMutexLockReq* req); glcr::ErrorCode MutexRelease(ZMutexReleaseReq* req); + +glcr::ErrorCode SemaphoreCreate(ZSemaphoreCreateReq* req); +glcr::ErrorCode SemaphoreWait(ZSemaphoreWaitReq* req); +glcr::ErrorCode SemaphoreSignal(ZSemaphoreSignalReq* req); diff --git a/zion/syscall/syscall.cpp b/zion/syscall/syscall.cpp index 89ee5b8..52b0f81 100644 --- a/zion/syscall/syscall.cpp +++ b/zion/syscall/syscall.cpp @@ -88,6 +88,9 @@ extern "C" z_err_t SyscallHandler(uint64_t call_id, void* req) { CASE(MutexCreate); CASE(MutexLock); CASE(MutexRelease); + CASE(SemaphoreCreate); + CASE(SemaphoreWait); + CASE(SemaphoreSignal); // syscall/debug.h CASE(Debug); default: