diff --git a/orbis-kernel/include/orbis/umtx.hpp b/orbis-kernel/include/orbis/umtx.hpp index 420350c6f..d99d5dccd 100644 --- a/orbis-kernel/include/orbis/umtx.hpp +++ b/orbis-kernel/include/orbis/umtx.hpp @@ -25,6 +25,10 @@ inline constexpr auto kUrwLockWriteWaiters = 0x40000000; inline constexpr auto kUrwLockReadWaiters = 0x20000000; inline constexpr auto kUrwLockMaxReaders = 0x1fffffff; +inline constexpr auto kCvWaitCheckUnparking = 0x01; +inline constexpr auto kCvWaitAbsTime = 0x02; +inline constexpr auto kCvWaitClockId = 0x04; + inline constexpr auto kSemNamed = 2; struct umtx { diff --git a/orbis-kernel/src/umtx.cpp b/orbis-kernel/src/umtx.cpp index 46de7796b..2c746f7cc 100644 --- a/orbis-kernel/src/umtx.cpp +++ b/orbis-kernel/src/umtx.cpp @@ -254,8 +254,51 @@ orbis::ErrorCode orbis::umtx_set_ceiling(Thread *thread, ptr m, orbis::ErrorCode orbis::umtx_cv_wait(Thread *thread, ptr cv, ptr m, std::uint64_t ut, ulong wflags) { - ORBIS_LOG_TODO(__FUNCTION__, cv, m, ut, wflags); - return ErrorCode::NOSYS; + ORBIS_LOG_NOTICE(__FUNCTION__, cv, m, ut, wflags); + const uint flags = uread(&cv->flags); + if ((wflags & kCvWaitClockId) != 0) { + ORBIS_LOG_FATAL("umtx_cv_wait: CLOCK_ID unimplemented", wflags); + return ErrorCode::NOSYS; + } + if ((wflags & kCvWaitAbsTime) != 0) { + ORBIS_LOG_FATAL("umtx_cv_wait: ABSTIME unimplemented", wflags); + return ErrorCode::NOSYS; + } + + auto [chain, key, lock] = g_context.getUmtxChain0(thread->tproc->pid, m); + auto node = chain.enqueue(key, thread); + + if (!cv->has_waiters.load(std::memory_order::relaxed)) + cv->has_waiters.store(1, std::memory_order::relaxed); + + ErrorCode result = umtx_unlock_umutex(thread, m); + if (result == ErrorCode{}) { + if (ut + 1 == 0) { + node->second.cv.wait(chain.mtx, ut); + } else { + auto start = std::chrono::steady_clock::now(); + std::uint64_t udiff = 0; + while (true) { + node->second.cv.wait(chain.mtx, ut - udiff); + if (node->second.thr != thread) + break; + udiff = (std::chrono::steady_clock::now() - start).count() / 1000; + if (udiff >= ut) { + result = ErrorCode::TIMEDOUT; + break; + } + } + } + } + + if (node->second.thr != thread) { + result = {}; + } else { + chain.erase(node); + if (chain.sleep_queue.count(key) == 0) + cv->has_waiters.store(0, std::memory_order::relaxed); + } + return result; } orbis::ErrorCode orbis::umtx_cv_signal(Thread *thread, ptr cv) {