From 439444d72bd2e59c410adaf130385b4eb900002a Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Mon, 17 Jul 2023 19:53:48 +0300 Subject: [PATCH] [orbis-kernel] Initial osem semaphore implementation --- orbis-kernel/include/orbis/KernelContext.hpp | 26 ++++ orbis-kernel/include/orbis/osem.hpp | 45 ++++++ orbis-kernel/include/orbis/sys/sysproto.hpp | 18 ++- orbis-kernel/include/orbis/thread/Process.hpp | 2 + orbis-kernel/src/sys/sys_sce.cpp | 140 ++++++++++++++++-- 5 files changed, 209 insertions(+), 22 deletions(-) create mode 100644 orbis-kernel/include/orbis/osem.hpp diff --git a/orbis-kernel/include/orbis/KernelContext.hpp b/orbis-kernel/include/orbis/KernelContext.hpp index 6ce34f964..250467ecf 100644 --- a/orbis-kernel/include/orbis/KernelContext.hpp +++ b/orbis-kernel/include/orbis/KernelContext.hpp @@ -1,5 +1,6 @@ #pragma once #include "evf.hpp" +#include "osem.hpp" #include "utils/LinkedNode.hpp" #include "utils/SharedCV.hpp" #include "utils/SharedMutex.hpp" @@ -81,6 +82,28 @@ public: return {}; } + std::pair createSemaphore(utils::kstring name, + std::uint32_t attrs, + std::int32_t initCount, + std::int32_t maxCount) { + std::lock_guard lock(m_sem_mtx); + auto [it, inserted] = m_semaphores.try_emplace(std::move(name), nullptr); + if (inserted) { + it->second = knew(attrs, initCount, maxCount); + } + + return {it->second.get(), inserted}; + } + + Ref findSemaphore(std::string_view name) { + std::lock_guard lock(m_sem_mtx); + if (auto it = m_semaphores.find(name); it != m_semaphores.end()) { + return it->second; + } + + return {}; + } + enum { c_golden_ratio_prime = 2654404609u, c_umtx_chains = 512, @@ -122,6 +145,9 @@ private: shared_mutex m_evf_mtx; utils::kmap> m_event_flags; + + shared_mutex m_sem_mtx; + utils::kmap> m_semaphores; }; extern KernelContext &g_context; diff --git a/orbis-kernel/include/orbis/osem.hpp b/orbis-kernel/include/orbis/osem.hpp new file mode 100644 index 000000000..ab68af84b --- /dev/null +++ b/orbis-kernel/include/orbis/osem.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "KernelAllocator.hpp" +#include "thread/Thread.hpp" +#include "utils/SharedCV.hpp" +#include "utils/SharedMutex.hpp" +#include +#include + +namespace orbis { +enum { + kSemaAttrThFifo = 1, + kSemaAttrThPrio = 2, + kSemaAttrShared = 256, +}; + +struct Semaphore final { + char name[32]; + + bool isDeleted = false; + std::uint8_t attrs; + std::atomic references{0}; + std::atomic value; + const sint maxValue; + utils::shared_mutex mtx; + utils::shared_cv cond; + + Semaphore(uint attrs, sint value, sint max) + : attrs(attrs), value(value), maxValue(max) {} + + void destroy() { + std::lock_guard lock(mtx); + isDeleted = true; + cond.notify_all(mtx); + } + + void incRef() { references.fetch_add(1, std::memory_order::relaxed); } + + void decRef() { + if (references.fetch_sub(1, std::memory_order::relaxed) == 1) { + kdelete(this); + } + } +}; +} // namespace orbis \ No newline at end of file diff --git a/orbis-kernel/include/orbis/sys/sysproto.hpp b/orbis-kernel/include/orbis/sys/sysproto.hpp index 9e9bdd99f..66bacced2 100644 --- a/orbis-kernel/include/orbis/sys/sysproto.hpp +++ b/orbis-kernel/include/orbis/sys/sysproto.hpp @@ -635,14 +635,16 @@ SysResult sys_evf_cancel(Thread *thread, sint id, uint64_t value, ptr pNumWaitThreads); SysResult sys_query_memory_protection(Thread *thread /* TODO */); SysResult sys_batch_map(Thread *thread /* TODO */); -SysResult sys_osem_create(Thread *thread /* TODO */); -SysResult sys_osem_delete(Thread *thread /* TODO */); -SysResult sys_osem_open(Thread *thread /* TODO */); -SysResult sys_osem_close(Thread *thread /* TODO */); -SysResult sys_osem_wait(Thread *thread /* TODO */); -SysResult sys_osem_trywait(Thread *thread /* TODO */); -SysResult sys_osem_post(Thread *thread /* TODO */); -SysResult sys_osem_cancel(Thread *thread /* TODO */); +SysResult sys_osem_create(Thread *thread, ptr name, uint attrs, + sint initCount, sint maxCount); +SysResult sys_osem_delete(Thread *thread, sint id); +SysResult sys_osem_open(Thread *thread, ptr name); +SysResult sys_osem_close(Thread *thread, sint id); +SysResult sys_osem_wait(Thread *thread, sint id, sint need, ptr pTimeout); +SysResult sys_osem_trywait(Thread *thread, sint id, sint need); +SysResult sys_osem_post(Thread *thread, sint id, sint count); +SysResult sys_osem_cancel(Thread *thread, sint id, sint set, + ptr pNumWaitThreads); SysResult sys_namedobj_create(Thread *thread, ptr name, ptr object, uint16_t type); SysResult sys_namedobj_delete(Thread *thread, uint id, uint16_t type); diff --git a/orbis-kernel/include/orbis/thread/Process.hpp b/orbis-kernel/include/orbis/thread/Process.hpp index a8fe26cbe..fdfdd601a 100644 --- a/orbis-kernel/include/orbis/thread/Process.hpp +++ b/orbis-kernel/include/orbis/thread/Process.hpp @@ -2,6 +2,7 @@ #include "orbis-config.hpp" #include "../evf.hpp" +#include "../osem.hpp" #include "../thread/Thread.hpp" #include "../thread/types.hpp" #include "ProcessState.hpp" @@ -57,6 +58,7 @@ struct Process final { std::uint64_t lastTlsOffset = 0; utils::RcIdMap evfMap; + utils::RcIdMap semMap; utils::RcIdMap modulesMap; utils::OwningIdMap threadsMap; utils::RcIdMap fileDescriptors; diff --git a/orbis-kernel/src/sys/sys_sce.cpp b/orbis-kernel/src/sys/sys_sce.cpp index ede47ae42..3be7aad18 100644 --- a/orbis-kernel/src/sys/sys_sce.cpp +++ b/orbis-kernel/src/sys/sys_sce.cpp @@ -5,6 +5,7 @@ #include "module/ModuleInfo.hpp" #include "module/ModuleInfoEx.hpp" #include "orbis/time.hpp" +#include "osem.hpp" #include "sys/sysproto.hpp" #include "utils/Logs.hpp" #include @@ -283,28 +284,139 @@ orbis::SysResult orbis::sys_query_memory_protection(Thread *thread /* TODO */) { orbis::SysResult orbis::sys_batch_map(Thread *thread /* TODO */) { return ErrorCode::NOSYS; } -orbis::SysResult orbis::sys_osem_create(Thread *thread /* TODO */) { +orbis::SysResult orbis::sys_osem_create(Thread *thread, + ptr name, uint attrs, + sint initCount, sint maxCount) { + ORBIS_LOG_WARNING(__FUNCTION__, name, attrs, initCount, maxCount); + if (name == nullptr) { + return ErrorCode::INVAL; + } + + if (attrs & ~(kSemaAttrThPrio | kSemaAttrThFifo | kSemaAttrShared)) { + return ErrorCode::INVAL; + } + + switch (attrs & (kSemaAttrThPrio | kSemaAttrThFifo)) { + case kSemaAttrThPrio | kSemaAttrThFifo: + return ErrorCode::INVAL; + case 0: + attrs |= kSemaAttrThFifo; + break; + + default: + break; + } + + if (maxCount <= 0 || initCount < 0 || maxCount < initCount) + return ErrorCode::INVAL; + + char _name[32]; + if (auto result = ureadString(_name, sizeof(_name), (const char *)name); + result != ErrorCode{}) { + return result; + } + + Semaphore *sem; + if (attrs & kSemaAttrShared) { + auto [insertedSem, ok] = thread->tproc->context->createSemaphore( + _name, attrs, initCount, maxCount); + + if (!ok) { + return ErrorCode::EXIST; // FIXME: verify + } + + sem = insertedSem; + } else { + sem = knew(attrs, initCount, maxCount); + } + + thread->retval[0] = thread->tproc->semMap.insert(sem); return {}; } -orbis::SysResult orbis::sys_osem_delete(Thread *thread /* TODO */) { - return ErrorCode::NOSYS; +orbis::SysResult orbis::sys_osem_delete(Thread *thread, sint id) { + ORBIS_LOG_WARNING(__FUNCTION__, id); + Ref sem = thread->tproc->semMap.get(id); + if (sem == nullptr) { + return ErrorCode::SRCH; + } + + thread->tproc->semMap.destroy(id); + return {}; } -orbis::SysResult orbis::sys_osem_open(Thread *thread /* TODO */) { - return ErrorCode::NOSYS; +orbis::SysResult orbis::sys_osem_open(Thread *thread, + ptr name) { + ORBIS_LOG_WARNING(__FUNCTION__, name); + char _name[32]; + if (auto result = ureadString(_name, sizeof(_name), (const char *)name); + result != ErrorCode{}) { + return result; + } + + auto sem = thread->tproc->context->findSemaphore(_name); + if (sem == nullptr) { + return ErrorCode::SRCH; + } + + thread->retval[0] = thread->tproc->semMap.insert(sem); + return {}; } -orbis::SysResult orbis::sys_osem_close(Thread *thread /* TODO */) { - return ErrorCode::NOSYS; +orbis::SysResult orbis::sys_osem_close(Thread *thread, sint id) { + ORBIS_LOG_WARNING(__FUNCTION__, id); + if (!thread->tproc->semMap.close(id)) { + return ErrorCode::SRCH; + } + + return {}; } -orbis::SysResult orbis::sys_osem_wait(Thread *thread /* TODO */) { - return ErrorCode::NOSYS; +orbis::SysResult orbis::sys_osem_wait(Thread *thread, sint id, sint need, + ptr pTimeout) { + ORBIS_LOG_NOTICE(__FUNCTION__, thread, id, need, pTimeout); + Ref sem = thread->tproc->semMap.get(id); + if (pTimeout) + ORBIS_LOG_FATAL("sys_osem_wait timeout is not implemented!"); + if (need < 1 || need > sem->maxValue) + return ErrorCode::INVAL; + + std::lock_guard lock(sem->mtx); + while (true) { + if (sem->isDeleted) + return ErrorCode::ACCES; + if (sem->value >= need) { + sem->value -= need; + break; + } + sem->cond.wait(sem->mtx); + } + return {}; } -orbis::SysResult orbis::sys_osem_trywait(Thread *thread /* TODO */) { - return ErrorCode::NOSYS; +orbis::SysResult orbis::sys_osem_trywait(Thread *thread, sint id, sint need) { + ORBIS_LOG_NOTICE(__FUNCTION__, thread, id, need); + Ref sem = thread->tproc->semMap.get(id); + if (need < 1 || need > sem->maxValue) + return ErrorCode::INVAL; + + std::lock_guard lock(sem->mtx); + if (sem->isDeleted || sem->value < need) + return ErrorCode::BUSY; + sem->value -= need; + return {}; } -orbis::SysResult orbis::sys_osem_post(Thread *thread /* TODO */) { - return ErrorCode::NOSYS; +orbis::SysResult orbis::sys_osem_post(Thread *thread, sint id, sint count) { + ORBIS_LOG_NOTICE(__FUNCTION__, thread, id, count); + Ref sem = thread->tproc->semMap.get(id); + if (count < 1 || count > sem->maxValue - sem->value) + return ErrorCode::INVAL; + + std::lock_guard lock(sem->mtx); + if (sem->isDeleted) + return {}; + sem->value += count; + sem->cond.notify_all(sem->mtx); + return {}; } -orbis::SysResult orbis::sys_osem_cancel(Thread *thread /* TODO */) { +orbis::SysResult orbis::sys_osem_cancel(Thread *thread, sint id, sint set, + ptr pNumWaitThreads) { + ORBIS_LOG_TODO(__FUNCTION__, thread, id, set, pNumWaitThreads); return ErrorCode::NOSYS; } orbis::SysResult orbis::sys_namedobj_create(Thread *thread,