From bfffd209460eb42308470b2c977c80b03aa8d602 Mon Sep 17 00:00:00 2001 From: Ivan Chikish Date: Mon, 10 Jul 2023 13:58:53 +0300 Subject: [PATCH] [orbis-kernel] Implement _umtx_op for umutex (no PI) --- orbis-kernel/include/orbis/KernelContext.hpp | 55 +++++ orbis-kernel/include/orbis/umtx.hpp | 15 +- orbis-kernel/src/sys/sys_umtx.cpp | 132 +++++------ orbis-kernel/src/umtx.cpp | 220 ++++++++++++++++++- 4 files changed, 328 insertions(+), 94 deletions(-) diff --git a/orbis-kernel/include/orbis/KernelContext.hpp b/orbis-kernel/include/orbis/KernelContext.hpp index feaacb4fd..23ad6f28c 100644 --- a/orbis-kernel/include/orbis/KernelContext.hpp +++ b/orbis-kernel/include/orbis/KernelContext.hpp @@ -1,16 +1,44 @@ #pragma once #include "evf.hpp" #include "utils/LinkedNode.hpp" +#include "utils/SharedCV.hpp" #include "utils/SharedMutex.hpp" #include "KernelAllocator.hpp" #include "orbis/thread/types.hpp" #include +#include +#include #include #include namespace orbis { struct Process; +struct Thread; + +struct UmtxKey { + // TODO: may contain a reference to a shared memory + void *addr; + pid_t pid; + + auto operator<=>(const UmtxKey &) const = default; +}; + +struct UmtxCond { + Thread *thr; + utils::shared_cv cv; + + UmtxCond(Thread *thr) : thr(thr) {} +}; + +struct UmtxChain { + utils::shared_mutex mtx; + utils::kmultimap sleep_queue; + utils::kmultimap spare_queue; + + std::pair *enqueue(UmtxKey &key, Thread *thr); + void erase(std::pair *obj); +}; class alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) KernelContext final { public: @@ -47,6 +75,31 @@ public: return {}; } + enum { + c_golden_ratio_prime = 2654404609u, + c_umtx_chains = 512, + c_umtx_shifts = 23, + }; + + // Use getUmtxChain0 or getUmtxChain1 + std::tuple> + getUmtxChainIndexed(int i, pid_t pid, void *ptr) { + auto n = reinterpret_cast(ptr) + pid; + n = ((n * c_golden_ratio_prime) >> c_umtx_shifts) % c_umtx_chains; + std::unique_lock lock(m_umtx_chains[i][n].mtx); + return {m_umtx_chains[i][n], UmtxKey{ptr, pid}, std::move(lock)}; + } + + // Internal Umtx: Wait/Cv/Sem + auto getUmtxChain0(pid_t pid, void *ptr) { + return getUmtxChainIndexed(0, pid, ptr); + } + + // Internal Umtx: Mutex/Umtx/Rwlock + auto getUmtxChain1(pid_t pid, void *ptr) { + return getUmtxChainIndexed(1, pid, ptr); + } + private: shared_mutex m_evf_mtx; mutable pthread_mutex_t m_heap_mtx; @@ -55,6 +108,8 @@ private: utils::kmultimap m_free_heap; utils::kmultimap m_used_node; + UmtxChain m_umtx_chains[2][c_umtx_chains]{}; + mutable shared_mutex m_proc_mtx; utils::LinkedNode *m_processes = nullptr; utils::kmap> m_event_flags; diff --git a/orbis-kernel/include/orbis/umtx.hpp b/orbis-kernel/include/orbis/umtx.hpp index b8b358908..0f6f1eb66 100644 --- a/orbis-kernel/include/orbis/umtx.hpp +++ b/orbis-kernel/include/orbis/umtx.hpp @@ -61,22 +61,21 @@ struct usem { struct Thread; ErrorCode umtx_lock_umtx(Thread *thread, ptr umtx, ulong id, - timespec *ts); + std::uint64_t ut); ErrorCode umtx_unlock_umtx(Thread *thread, ptr umtx, ulong id); -ErrorCode umtx_wait(Thread *thread, ptr addr, ulong id, - timespec *timeout); +ErrorCode umtx_wait(Thread *thread, ptr addr, ulong id, std::uint64_t ut); ErrorCode umtx_wake(Thread *thread, ptr uaddr, sint n_wake); ErrorCode umtx_trylock_umutex(Thread *thread, ptr m); -ErrorCode umtx_lock_umutex(Thread *thread, ptr m, timespec *timeout); +ErrorCode umtx_lock_umutex(Thread *thread, ptr m, std::uint64_t ut); ErrorCode umtx_unlock_umutex(Thread *thread, ptr m); ErrorCode umtx_set_ceiling(Thread *thread, ptr m, std::uint32_t ceiling, ptr oldCeiling); ErrorCode umtx_cv_wait(Thread *thread, ptr cv, ptr m, - timespec *ts, ulong wflags); + std::uint64_t ut, ulong wflags); ErrorCode umtx_cv_signal(Thread *thread, ptr cv); ErrorCode umtx_cv_broadcast(Thread *thread, ptr cv); ErrorCode umtx_wait_uint(Thread *thread, ptr addr, ulong id, - timespec *timeout); + std::uint64_t ut); ErrorCode umtx_rw_rdlock(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2); ErrorCode umtx_rw_wrlock(Thread *thread, ptr obj, std::int64_t val, @@ -84,9 +83,9 @@ ErrorCode umtx_rw_wrlock(Thread *thread, ptr obj, std::int64_t val, ErrorCode umtx_rw_unlock(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2); ErrorCode umtx_wait_uint_private(Thread *thread, ptr addr, ulong id, - timespec *timeout); + std::uint64_t ut); ErrorCode umtx_wake_private(Thread *thread, ptr uaddr, sint n_wake); -ErrorCode umtx_wait_umutex(Thread *thread, ptr m, timespec *timeout); +ErrorCode umtx_wait_umutex(Thread *thread, ptr m, std::uint64_t ut); ErrorCode umtx_wake_umutex(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2); ErrorCode umtx_sem_wait(Thread *thread, ptr obj, std::int64_t val, diff --git a/orbis-kernel/src/sys/sys_umtx.cpp b/orbis-kernel/src/sys/sys_umtx.cpp index 48ab55a0e..0ea318118 100644 --- a/orbis-kernel/src/sys/sys_umtx.cpp +++ b/orbis-kernel/src/sys/sys_umtx.cpp @@ -1,6 +1,8 @@ +#include "orbis/utils/Logs.hpp" #include "sys/sysproto.hpp" #include "time.hpp" #include "umtx.hpp" +#include static orbis::ErrorCode ureadTimespec(orbis::timespec &ts, orbis::ptr addr) { @@ -13,16 +15,24 @@ static orbis::ErrorCode ureadTimespec(orbis::timespec &ts, } orbis::SysResult orbis::sys__umtx_lock(Thread *thread, ptr umtx) { - return umtx_lock_umtx(thread, umtx, thread->tid, nullptr); + ORBIS_LOG_TODO(__FUNCTION__, umtx); + if (reinterpret_cast(umtx) - 0x10000 > 0xff'fffe'ffff) + return ErrorCode::FAULT; + return umtx_lock_umtx(thread, umtx, thread->tid, -1); } orbis::SysResult orbis::sys__umtx_unlock(Thread *thread, ptr umtx) { + ORBIS_LOG_TODO(__FUNCTION__, umtx); + if (reinterpret_cast(umtx) - 0x10000 > 0xff'fffe'ffff) + return ErrorCode::FAULT; return umtx_unlock_umtx(thread, umtx, thread->tid); } orbis::SysResult orbis::sys__umtx_op(Thread *thread, ptr obj, sint op, ulong val, ptr uaddr1, ptr uaddr2) { - switch (op) { - case 0: { + ORBIS_LOG_TODO(__FUNCTION__, obj, op, val, uaddr1, uaddr2); + if (reinterpret_cast(obj) - 0x10000 > 0xff'fffe'ffff) + return ErrorCode::FAULT; + auto with_timeout = [&](auto op) -> orbis::ErrorCode { timespec *ts = nullptr; timespec timeout; if (uaddr2 != nullptr) { @@ -33,41 +43,49 @@ orbis::SysResult orbis::sys__umtx_op(Thread *thread, ptr obj, sint op, ts = &timeout; } + std::uint64_t usec = 0; + auto start = std::chrono::steady_clock::now(); - return umtx_lock_umtx(thread, (ptr)obj, val, ts); + if (!ts) { + usec = -1; + while (true) { + if (auto r = op(usec); r != ErrorCode::TIMEDOUT) + return r; + } + } else { + usec += (timeout.nsec + 999) / 1000; + usec += timeout.sec * 1000'000; + std::uint64_t udiff = 0; + while (true) { + if (auto r = op(usec - udiff); r != ErrorCode::TIMEDOUT) + return r; + udiff = (std::chrono::steady_clock::now() - start).count() / 1000; + if (udiff >= usec) + return ErrorCode::TIMEDOUT; + } + } + }; + + switch (op) { + case 0: { + return with_timeout([&](std::uint64_t ut) { + return umtx_lock_umtx(thread, (ptr)obj, val, ut); + }); } case 1: return umtx_unlock_umtx(thread, (ptr)obj, val); case 2: { - timespec *ts = nullptr; - timespec timeout; - if (uaddr2 != nullptr) { - auto result = ureadTimespec(timeout, (ptr)uaddr2); - if (result != ErrorCode{}) { - return result; - } - - ts = &timeout; - } - - return umtx_wait(thread, obj, val, ts); + return with_timeout( + [&](std::uint64_t ut) { return umtx_wait(thread, obj, val, ut); }); } case 3: return umtx_wake(thread, obj, val); case 4: return umtx_trylock_umutex(thread, (ptr)obj); case 5: { - timespec *ts = nullptr; - timespec timeout; - if (uaddr2 != nullptr) { - auto result = ureadTimespec(timeout, (ptr)uaddr2); - if (result != ErrorCode{}) { - return result; - } - - ts = &timeout; - } - return umtx_lock_umutex(thread, (ptr)obj, ts); + return with_timeout([&](std::uint64_t ut) { + return umtx_lock_umutex(thread, (ptr)obj, ut); + }); } case 6: return umtx_unlock_umutex(thread, (ptr)obj); @@ -76,18 +94,10 @@ orbis::SysResult orbis::sys__umtx_op(Thread *thread, ptr obj, sint op, (ptr)uaddr1); case 8: { - timespec *ts = nullptr; - timespec timeout; - if (uaddr2 != nullptr) { - auto result = ureadTimespec(timeout, (ptr)uaddr2); - if (result != ErrorCode{}) { - return result; - } - - ts = &timeout; - } - - return umtx_cv_wait(thread, (ptr)obj, (ptr)uaddr1, ts, val); + return with_timeout([&](std::uint64_t ut) { + return umtx_cv_wait(thread, (ptr)obj, (ptr)uaddr1, ut, + val); + }); } case 9: @@ -95,18 +105,8 @@ orbis::SysResult orbis::sys__umtx_op(Thread *thread, ptr obj, sint op, case 10: return umtx_cv_broadcast(thread, (ptr)obj); case 11: { - timespec *ts = nullptr; - timespec timeout; - if (uaddr2 != nullptr) { - auto result = ureadTimespec(timeout, (ptr)uaddr2); - if (result != ErrorCode{}) { - return result; - } - - ts = &timeout; - } - - return umtx_wait_uint(thread, obj, val, ts); + return with_timeout( + [&](std::uint64_t ut) { return umtx_wait_uint(thread, obj, val, ut); }); } case 12: return umtx_rw_rdlock(thread, obj, val, uaddr1, uaddr2); @@ -115,34 +115,16 @@ orbis::SysResult orbis::sys__umtx_op(Thread *thread, ptr obj, sint op, case 14: return umtx_rw_unlock(thread, obj, val, uaddr1, uaddr2); case 15: { - timespec *ts = nullptr; - timespec timeout; - if (uaddr2 != nullptr) { - auto result = ureadTimespec(timeout, (ptr)uaddr2); - if (result != ErrorCode{}) { - return result; - } - - ts = &timeout; - } - - return umtx_wait_uint_private(thread, obj, val, ts); + return with_timeout([&](std::uint64_t ut) { + return umtx_wait_uint_private(thread, obj, val, ut); + }); } case 16: return umtx_wake_private(thread, obj, val); case 17: { - timespec *ts = nullptr; - timespec timeout; - if (uaddr2 != nullptr) { - auto result = ureadTimespec(timeout, (ptr)uaddr2); - if (result != ErrorCode{}) { - return result; - } - - ts = &timeout; - } - - return umtx_wait_umutex(thread, (ptr)obj, ts); + return with_timeout([&](std::uint64_t ut) { + return umtx_wait_umutex(thread, (ptr)obj, ut); + }); } case 18: return umtx_wake_umutex(thread, obj, val, uaddr1, uaddr2); diff --git a/orbis-kernel/src/umtx.cpp b/orbis-kernel/src/umtx.cpp index 00728d532..8b17d51bf 100644 --- a/orbis-kernel/src/umtx.cpp +++ b/orbis-kernel/src/umtx.cpp @@ -1,122 +1,320 @@ #include "umtx.hpp" +#include "orbis/KernelContext.hpp" +#include "orbis/thread.hpp" +#include "orbis/utils/AtomicOp.hpp" +#include "orbis/utils/Logs.hpp" #include "time.hpp" +namespace orbis { +std::pair *UmtxChain::enqueue(UmtxKey &key, + Thread *thr) { + if (!spare_queue.empty()) { + auto node = spare_queue.extract(spare_queue.begin()); + node.key() = key; + node.mapped().thr = thr; + return &*sleep_queue.insert(std::move(node)); + } + return &*sleep_queue.emplace(key, thr); +} + +void UmtxChain::erase(std::pair *obj) { + for (auto [it, e] = sleep_queue.equal_range(obj->first); it != e; it++) { + if (&*it == obj) { + auto node = sleep_queue.extract(it); + node.key() = {}; + spare_queue.insert(spare_queue.begin(), std::move(node)); + return; + } + } +} +} // namespace orbis + orbis::ErrorCode orbis::umtx_lock_umtx(Thread *thread, ptr umtx, ulong id, - timespec *ts) { + std::uint64_t ut) { + ORBIS_LOG_TODO(__FUNCTION__, umtx, id, ut); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_unlock_umtx(Thread *thread, ptr umtx, ulong id) { + ORBIS_LOG_TODO(__FUNCTION__, umtx, id); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_wait(Thread *thread, ptr addr, ulong id, - timespec *timeout) { + std::uint64_t ut) { + ORBIS_LOG_TODO(__FUNCTION__, addr, id, ut); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_wake(Thread *thread, ptr uaddr, sint n_wake) { + ORBIS_LOG_TODO(__FUNCTION__, uaddr, n_wake); return ErrorCode::NOSYS; } -orbis::ErrorCode orbis::umtx_trylock_umutex(Thread *thread, ptr m) { +namespace orbis { +enum class umutex_lock_mode { + lock, + try_, + wait, +}; +template <> +void log_class_string::format(std::string &out, + const void *arg) { + switch (get_object(arg)) { + case umutex_lock_mode::lock: + out += "lock"; + break; + case umutex_lock_mode::try_: + out += "try"; + break; + case umutex_lock_mode::wait: + out += "wait"; + break; + } +} +static ErrorCode do_lock_normal(Thread *thread, ptr m, uint flags, + std::uint64_t ut, umutex_lock_mode mode) { + ORBIS_LOG_NOTICE(__FUNCTION__, m, flags, ut, mode); + ErrorCode error = {}; + while (true) { + int owner = m->owner.load(std::memory_order_acquire); + if (mode == umutex_lock_mode::wait) { + if (owner == kUmutexUnowned || owner == kUmutexContested) + return {}; + } else { + owner = kUmutexUnowned; + if (m->owner.compare_exchange_strong(owner, thread->tid)) + return {}; + if (owner == kUmutexContested) { + if (m->owner.compare_exchange_strong(owner, + thread->tid | kUmutexContested)) + return {}; + continue; + } + } + + if ((flags & kUmutexErrorCheck) != 0 && + (owner & ~kUmutexContested) == thread->tid) + return ErrorCode::DEADLK; + + if (mode == umutex_lock_mode::try_) + return ErrorCode::BUSY; + + if (error != ErrorCode{}) + return error; + + auto [chain, key, lock] = g_context.getUmtxChain1(thread->tproc->pid, m); + auto node = chain.enqueue(key, thread); + bool ok = m->owner.compare_exchange_strong(owner, owner | kUmutexContested); + if (ok && node->second.thr == thread) { + node->second.cv.wait(chain.mtx, ut); + if (node->second.thr) + error = ErrorCode::TIMEDOUT; + } + if (node->second.thr == thread) + chain.erase(node); + } + + return {}; +} +static ErrorCode do_lock_pi(Thread *thread, ptr m, uint flags, + std::uint64_t ut, umutex_lock_mode mode) { + ORBIS_LOG_TODO(__FUNCTION__, m, flags, ut, mode); return ErrorCode::NOSYS; } +static ErrorCode do_lock_pp(Thread *thread, ptr m, uint flags, + std::uint64_t ut, umutex_lock_mode mode) { + ORBIS_LOG_TODO(__FUNCTION__, m, flags, ut, mode); + return ErrorCode::NOSYS; +} +static ErrorCode do_unlock_normal(Thread *thread, ptr m, uint flags) { + ORBIS_LOG_NOTICE(__FUNCTION__, m, flags); + + int owner = m->owner.load(std::memory_order_acquire); + if ((owner & ~kUmutexContested) != thread->tid) + return ErrorCode::PERM; + + if ((owner & kUmtxContested) == 0) { + if (m->owner.compare_exchange_strong(owner, kUmutexUnowned)) + return {}; + } + + auto [chain, key, lock] = g_context.getUmtxChain1(thread->tproc->pid, m); + std::size_t count = chain.sleep_queue.count(key); + bool ok = m->owner.compare_exchange_strong( + owner, count <= 1 ? kUmutexUnowned : kUmutexContested); + auto it = chain.sleep_queue.find(key); + it->second.thr = nullptr; + it->second.cv.notify_one(chain.mtx); + chain.erase(&*it); + if (!ok) + return ErrorCode::INVAL; + return {}; +} +static ErrorCode do_unlock_pi(Thread *thread, ptr m, uint flags) { + ORBIS_LOG_TODO(__FUNCTION__, m, flags); + return ErrorCode::NOSYS; +} +static ErrorCode do_unlock_pp(Thread *thread, ptr m, uint flags) { + ORBIS_LOG_TODO(__FUNCTION__, m, flags); + return ErrorCode::NOSYS; +} + +} // namespace orbis + +orbis::ErrorCode orbis::umtx_trylock_umutex(Thread *thread, ptr m) { + ORBIS_LOG_TRACE(__FUNCTION__, m); + uint flags = uread(&m->flags); + switch (flags & (kUmutexPrioInherit | kUmutexPrioProtect)) { + case 0: + return do_lock_normal(thread, m, flags, 0, umutex_lock_mode::try_); + case kUmutexPrioInherit: + return do_lock_pi(thread, m, flags, 0, umutex_lock_mode::try_); + case kUmutexPrioProtect: + return do_lock_pp(thread, m, flags, 0, umutex_lock_mode::try_); + } + return ErrorCode::INVAL; +} orbis::ErrorCode orbis::umtx_lock_umutex(Thread *thread, ptr m, - timespec *timeout) { - return ErrorCode::NOSYS; + std::uint64_t ut) { + ORBIS_LOG_TRACE(__FUNCTION__, m, ut); + uint flags = uread(&m->flags); + switch (flags & (kUmutexPrioInherit | kUmutexPrioProtect)) { + case 0: + return do_lock_normal(thread, m, flags, ut, umutex_lock_mode::lock); + case kUmutexPrioInherit: + return do_lock_pi(thread, m, flags, ut, umutex_lock_mode::lock); + case kUmutexPrioProtect: + return do_lock_pp(thread, m, flags, ut, umutex_lock_mode::lock); + } + return ErrorCode::INVAL; } orbis::ErrorCode orbis::umtx_unlock_umutex(Thread *thread, ptr m) { - return ErrorCode::NOSYS; + ORBIS_LOG_TRACE(__FUNCTION__, m); + uint flags = uread(&m->flags); + switch (flags & (kUmutexPrioInherit | kUmutexPrioProtect)) { + case 0: + return do_unlock_normal(thread, m, flags); + case kUmutexPrioInherit: + return do_unlock_pi(thread, m, flags); + case kUmutexPrioProtect: + return do_unlock_pp(thread, m, flags); + } + return ErrorCode::INVAL; } orbis::ErrorCode orbis::umtx_set_ceiling(Thread *thread, ptr m, std::uint32_t ceiling, ptr oldCeiling) { + ORBIS_LOG_TODO(__FUNCTION__, m, ceiling, oldCeiling); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_cv_wait(Thread *thread, ptr cv, - ptr m, timespec *ts, + ptr m, std::uint64_t ut, ulong wflags) { + ORBIS_LOG_TODO(__FUNCTION__, cv, m, ut, wflags); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_cv_signal(Thread *thread, ptr cv) { + ORBIS_LOG_TODO(__FUNCTION__, cv); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_cv_broadcast(Thread *thread, ptr cv) { + ORBIS_LOG_TODO(__FUNCTION__, cv); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_wait_uint(Thread *thread, ptr addr, ulong id, - timespec *timeout) { + std::uint64_t ut) { + ORBIS_LOG_TODO(__FUNCTION__, addr, id, ut); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_rw_rdlock(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2) { + ORBIS_LOG_TODO(__FUNCTION__, obj, val, uaddr1, uaddr2); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_rw_wrlock(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2) { + ORBIS_LOG_TODO(__FUNCTION__, obj, val, uaddr1, uaddr2); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_rw_unlock(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2) { + ORBIS_LOG_TODO(__FUNCTION__, obj, val, uaddr1, uaddr2); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_wait_uint_private(Thread *thread, ptr addr, - ulong id, timespec *timeout) { + ulong id, std::uint64_t ut) { + ORBIS_LOG_TODO(__FUNCTION__, addr, id, ut); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_wake_private(Thread *thread, ptr uaddr, sint n_wake) { + ORBIS_LOG_TODO(__FUNCTION__, uaddr, n_wake); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_wait_umutex(Thread *thread, ptr m, - timespec *timeout) { - return ErrorCode::NOSYS; + std::uint64_t ut) { + ORBIS_LOG_TRACE(__FUNCTION__, m, ut); + uint flags = uread(&m->flags); + switch (flags & (kUmutexPrioInherit | kUmutexPrioProtect)) { + case 0: + return do_lock_normal(thread, m, flags, ut, umutex_lock_mode::wait); + case kUmutexPrioInherit: + return do_lock_pi(thread, m, flags, ut, umutex_lock_mode::wait); + case kUmutexPrioProtect: + return do_lock_pp(thread, m, flags, ut, umutex_lock_mode::wait); + } + return ErrorCode::INVAL; } orbis::ErrorCode orbis::umtx_wake_umutex(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2) { + ORBIS_LOG_TODO(__FUNCTION__, obj, val, uaddr1, uaddr2); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_sem_wait(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2) { + ORBIS_LOG_TODO(__FUNCTION__, obj, val, uaddr1, uaddr2); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_sem_wake(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2) { + ORBIS_LOG_TODO(__FUNCTION__, obj, val, uaddr1, uaddr2); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_nwake_private(Thread *thread, ptr uaddrs, std::int64_t count) { + ORBIS_LOG_TODO(__FUNCTION__, uaddrs, count); return ErrorCode::NOSYS; } orbis::ErrorCode orbis::umtx_wake2_umutex(Thread *thread, ptr obj, std::int64_t val, ptr uaddr1, ptr uaddr2) { + ORBIS_LOG_TODO(__FUNCTION__, obj, val, uaddr1, uaddr2); return ErrorCode::NOSYS; }