diff --git a/core/libs/prx/libkernel/Pthread/Posix/Common.hpp b/core/libs/prx/libkernel/Pthread/Posix/Common.hpp index 3b5734120be..b9aab100b49 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Common.hpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Common.hpp @@ -2,7 +2,6 @@ #define CORE_LIBS_PRX_LIBKERNEL_PTHREAD_POSIX_COMMON_HPP #include -#include #include #include "SceTypes.hpp" @@ -22,10 +21,31 @@ inline bool RelativeMicroseconds(int clockId, const KernelTimespec* abstime, Ker if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) return false; KernelTimespec now{}; clock_gettime_nid_postfix(clockId, &now); - const auto deadline = std::chrono::seconds(abstime->tv_sec) + std::chrono::nanoseconds(abstime->tv_nsec); - const auto current = std::chrono::seconds(now.tv_sec) + std::chrono::nanoseconds(now.tv_nsec); - const auto remaining = std::chrono::duration_cast(deadline - current).count(); - *usec = remaining <= 0 ? 0 : remaining >= std::numeric_limits::max() ? std::numeric_limits::max() : static_cast(remaining); + if (abstime->tv_sec < now.tv_sec || + (abstime->tv_sec == now.tv_sec && abstime->tv_nsec <= now.tv_nsec)) { + *usec = 0; + return true; + } + + std::uint64_t seconds = static_cast(abstime->tv_sec) - static_cast(now.tv_sec); + std::uint64_t nanoseconds = 0; + if (abstime->tv_nsec >= now.tv_nsec) { + nanoseconds = static_cast(abstime->tv_nsec - now.tv_nsec); + } else { + --seconds; + nanoseconds = 1000000000ULL - static_cast(now.tv_nsec - abstime->tv_nsec); + } + + constexpr std::uint64_t microsecondsPerSecond = 1000000ULL; + constexpr std::uint64_t maxMicroseconds = std::numeric_limits::max(); + constexpr std::uint64_t maxWholeSeconds = maxMicroseconds / microsecondsPerSecond; + if (seconds > maxWholeSeconds) { + *usec = static_cast(maxMicroseconds); + return true; + } + + const std::uint64_t remaining = seconds * microsecondsPerSecond + nanoseconds / 1000ULL; + *usec = static_cast(remaining > maxMicroseconds ? maxMicroseconds : remaining); return true; } diff --git a/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp b/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp index b34390ef79f..6e563aefa08 100644 --- a/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp +++ b/core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp @@ -6,14 +6,13 @@ #include "../include/Mutex.hpp" #include "Common.hpp" #include -#include -#include #include #include namespace { constexpr int POSIX_EINVAL = 22; +constexpr int GUEST_REALTIME_CLOCK = 0; constexpr int POSIX_PRIO_PROTECT = 2; constexpr std::uintptr_t POSIX_ADAPTIVE_MUTEX_INITIALIZER = 1; @@ -61,10 +60,8 @@ int APS5_VABI pthread_mutex_timedlock_nid_postfix(PthreadMutex* mutex, const Ker if (!abstime) throw std::runtime_error("pthread_mutex_timedlock: null abstime"); if (abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000) return POSIX_EINVAL; _initializeStatic(mutex, __func__); - const auto deadline = std::chrono::seconds(abstime->tv_sec) + std::chrono::nanoseconds(abstime->tv_nsec); - const auto now = std::chrono::system_clock::now().time_since_epoch(); - const auto remaining = std::chrono::duration_cast(deadline - now).count(); - const auto usec = remaining <= 0 ? 0 : remaining >= std::numeric_limits::max() ? std::numeric_limits::max() : static_cast(remaining); + KernelUseconds usec = 0; + if (!PosixThread::RelativeMicroseconds(GUEST_REALTIME_CLOCK, abstime, &usec)) return POSIX_EINVAL; return PosixThread::ToErrno(scePthreadMutexTimedlock(mutex, usec)); } diff --git a/core/libs/tests/GuestPthreadRwlock.cpp b/core/libs/tests/GuestPthreadRwlock.cpp index 915d7d1ab1c..e9f8bd60aa8 100644 --- a/core/libs/tests/GuestPthreadRwlock.cpp +++ b/core/libs/tests/GuestPthreadRwlock.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -9,7 +10,13 @@ extern "C" { int APS5_VABI scePthreadCreate(Pthread* thread, const PthreadAttr* attr, PthreadEntry entry, void* arg, const char* name); int APS5_VABI scePthreadJoin(Pthread thread, void** retval); +int APS5_VABI scePthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr, const char* name); +int APS5_VABI scePthreadMutexDestroy(PthreadMutex* mutex); +int APS5_VABI scePthreadMutexLock(PthreadMutex* mutex); +int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex); int APS5_VABI clock_gettime_nid_postfix(int clockId, KernelTimespec* tp); +int APS5_VABI pthread_mutex_timedlock_nid_postfix(PthreadMutex* mutex, const KernelTimespec* abstime); +int APS5_VABI pthread_mutex_unlock_nid_postfix(PthreadMutex* mutex); int APS5_VABI pthread_rwlock_destroy_nid_postfix(PthreadRwlock* rwlock); int APS5_VABI pthread_rwlock_rdlock_nid_postfix(PthreadRwlock* rwlock); int APS5_VABI pthread_rwlock_wrlock_nid_postfix(PthreadRwlock* rwlock); @@ -47,6 +54,22 @@ struct Holder { Pthread thread = nullptr; }; +struct MutexHolder { + PthreadMutex* mutex; + std::atomic held{false}; + std::atomic release{false}; + Pthread thread = nullptr; +}; + +static void* APS5_VABI HoldMutex(void* arg) { + auto& holder = *static_cast(arg); + Require(scePthreadMutexLock(holder.mutex) == SCE_OK); + holder.held.store(true); + while (!holder.release.load()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); + Require(scePthreadMutexUnlock(holder.mutex) == SCE_OK); + return nullptr; +} + static void* APS5_VABI Hold(void* arg) { auto& holder = *static_cast(arg); Require((holder.write ? pthread_rwlock_wrlock_nid_postfix(holder.rwlock) : pthread_rwlock_rdlock_nid_postfix(holder.rwlock)) == 0); @@ -69,6 +92,8 @@ static void ExpectTimeout(TimedLock lock, PthreadRwlock* rwlock) { Require(std::chrono::steady_clock::now() - start >= std::chrono::milliseconds(15)); const KernelTimespec past = After(-1000); Require(lock(rwlock, &past) == GUEST_ETIMEDOUT); + const KernelTimespec subsecond = After(1); + Require(lock(rwlock, &subsecond) == GUEST_ETIMEDOUT); const KernelTimespec invalid{deadline.tv_sec, NANOS_PER_SECOND}; Require(lock(rwlock, &invalid) == GUEST_EINVAL); const KernelTimespec negative{deadline.tv_sec, -1}; @@ -123,6 +148,55 @@ int main() { Require(scePthreadJoin(writer.thread, nullptr) == SCE_OK); Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0); + Holder farFutureWriter{&rwlock, true}; + Start(farFutureWriter); + std::thread releaseFarFutureWriter([&] { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + farFutureWriter.release.store(true); + }); + const KernelTimespec farFuture{INT64_MAX, 0}; + const int farFutureResult = pthread_rwlock_timedrdlock_nid_postfix(&rwlock, &farFuture); + const int farFutureUnlockResult = farFutureResult == 0 ? pthread_rwlock_unlock_nid_postfix(&rwlock) : 0; + releaseFarFutureWriter.join(); + Require(scePthreadJoin(farFutureWriter.thread, nullptr) == SCE_OK); + if (farFutureResult != 0) { + std::fprintf(stderr, "far-future rwlock timed lock returned %d\n", farFutureResult); + std::abort(); + } + if (farFutureUnlockResult != 0) { + std::fprintf(stderr, "far-future rwlock unlock returned %d\n", farFutureUnlockResult); + std::abort(); + } + + PthreadMutex mutex = nullptr; + Require(scePthreadMutexInit(&mutex, nullptr, nullptr) == SCE_OK); + MutexHolder farFutureMutex{&mutex}; + Require(scePthreadCreate(&farFutureMutex.thread, nullptr, HoldMutex, &farFutureMutex, nullptr) == SCE_OK); + while (!farFutureMutex.held.load()) std::this_thread::yield(); + const auto mutexWaitStart = std::chrono::steady_clock::now(); + std::thread releaseFarFutureMutex([&] { + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + farFutureMutex.release.store(true); + }); + const int farFutureMutexResult = pthread_mutex_timedlock_nid_postfix(&mutex, &farFuture); + const auto mutexWaitElapsed = std::chrono::steady_clock::now() - mutexWaitStart; + const int farFutureMutexUnlockResult = farFutureMutexResult == 0 ? pthread_mutex_unlock_nid_postfix(&mutex) : 0; + releaseFarFutureMutex.join(); + Require(scePthreadJoin(farFutureMutex.thread, nullptr) == SCE_OK); + Require(scePthreadMutexDestroy(&mutex) == SCE_OK); + if (farFutureMutexResult != 0) { + std::fprintf(stderr, "far-future mutex timed lock returned %d\n", farFutureMutexResult); + std::abort(); + } + if (farFutureMutexUnlockResult != 0) { + std::fprintf(stderr, "far-future mutex unlock returned %d\n", farFutureMutexUnlockResult); + std::abort(); + } + if (mutexWaitElapsed < std::chrono::milliseconds(15)) { + std::fprintf(stderr, "far-future mutex timed lock returned too early\n"); + std::abort(); + } + bool rejected = false; try { pthread_rwlock_timedrdlock_nid_postfix(&rwlock, nullptr); } catch (const std::runtime_error&) { rejected = true; }