fix(libkernel): let exception delivery interrupt timed mutex and rwlock waits on Windows

This commit is contained in:
esatturan authored and Adrià Franch committed 2026-10-11 11:20:40 +02:00
1 parent e272b92e03
commit be4ab94642
3 files changed
+90 -1

No files matched your search

+11 -1
View File
@@ -87,7 +87,17 @@ int MutexOperations::Timedlock(PthreadMutex* mutex, const KernelTimespec* abstim
if (std::chrono::duration<long double>(duration) >= std::chrono::duration<long double>(std::chrono::system_clock::duration::max()))
throw std::overflow_error("Absolute mutex timeout exceeds the host clock range");
const auto deadline = std::chrono::system_clock::time_point(std::chrono::duration_cast<std::chrono::system_clock::duration>(duration));
return acquireMutex(resolveMutex(mutex, true), [&](auto& native) { return native.try_lock_until(deadline); }, sceTimedOut, false);
return acquireMutex(resolveMutex(mutex, true), [&](auto& native) {
#ifdef _WIN32
while (!native.try_lock()) {
if (std::chrono::system_clock::now() >= deadline) return false;
TimedWait::SleepNanos(1000000);
}
return true;
#else
return native.try_lock_until(deadline);
#endif
}, sceTimedOut, false);
}
extern "C" {
@@ -30,10 +30,17 @@ bool AcquireUntil(std::uint64_t deadlineNanos, TTryLock tryLock, TTryLockFor try
const std::uint64_t now = NowNanos();
if (now >= deadlineNanos) return false;
const std::uint64_t remaining = deadlineNanos - now;
#ifdef _WIN32
if (remaining > LOCK_TAIL_NANOS) {
PollSleepUntil(now + (remaining < LOCK_POLL_NANOS ? remaining : LOCK_POLL_NANOS));
continue;
}
#else
if (remaining > LOCK_BULK_NANOS) {
if (tryLockFor((remaining - LOCK_BULK_NANOS) / 1000ULL)) return true;
continue;
}
#endif
if (remaining <= LOCK_TAIL_NANOS) SleepUntil(deadlineNanos);
else PollSleepUntil(now + LOCK_POLL_NANOS);
}
+72
View File
@@ -20,6 +20,15 @@ int APS5_VABI sceKernelCreateSema(KernelSema* sem, const char* name, uint32_t at
int APS5_VABI sceKernelDeleteSema(KernelSema sem);
int APS5_VABI sceKernelSignalSema(KernelSema sem, int count);
int APS5_VABI sceKernelWaitSema(KernelSema sem, int need, KernelUseconds* time);
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 scePthreadMutexTimedlock(PthreadMutex* mutex, KernelUseconds usec);
int APS5_VABI scePthreadMutexUnlock(PthreadMutex* mutex);
int APS5_VABI scePthreadRwlockInit(PthreadRwlock* rwlock, const PthreadRwlockattr* attr, const char* name);
int APS5_VABI scePthreadRwlockDestroy(PthreadRwlock* rwlock);
int APS5_VABI scePthreadRwlockWrlock(PthreadRwlock* rwlock);
int APS5_VABI scePthreadRwlockUnlock(PthreadRwlock* rwlock);
int APS5_VABI sceKernelSyncOnAddressWait(std::uint32_t* address, std::uint32_t expected, const KernelUseconds* timeout, const char* name);
}
@@ -316,6 +325,60 @@ static void ExpectDelivery(int before, std::thread::id thread) {
Require(handlerRsp.load() != 0 && handlerFrame.load() < handlerRsp.load());
}
static constexpr int LockRounds = 10;
static PthreadMutex guestMutex = nullptr;
static PthreadRwlock guestRwlock = nullptr;
static int LockMutex() { return scePthreadMutexLock(&guestMutex); }
static int TimedLockMutex() { return scePthreadMutexTimedlock(&guestMutex, 10000000); }
static int UnlockMutex() { return scePthreadMutexUnlock(&guestMutex); }
static int LockRwlock() { return scePthreadRwlockWrlock(&guestRwlock); }
static int UnlockRwlock() { return scePthreadRwlockUnlock(&guestRwlock); }
struct LockWorker {
int (*lock)() = nullptr;
int (*unlock)() = nullptr;
std::atomic<int> blocking{0};
std::atomic<int> acquired{0};
std::atomic<int> released{0};
std::thread::id id;
};
static void* APS5_VABI LockBlocked(void* arg) {
auto& worker = *static_cast<LockWorker*>(arg);
worker.id = std::this_thread::get_id();
for (int round = 1; round <= LockRounds; ++round) {
while (worker.released.load() != round - 1) std::this_thread::yield();
worker.blocking.store(round);
Require(worker.lock() == 0);
worker.acquired.store(round);
Require(worker.unlock() == 0);
}
return nullptr;
}
static void ExpectDeliveryWhileLockHeld(int (*lock)(), int (*unlock)(), int before) {
LockWorker worker;
worker.lock = lock;
worker.unlock = unlock;
Require(lock() == 0);
Pthread thread = nullptr;
Require(scePthreadCreate(&thread, nullptr, LockBlocked, &worker, "lock blocked") == 0);
for (int round = 1; round <= LockRounds; ++round) {
while (worker.blocking.load() != round) std::this_thread::yield();
std::this_thread::sleep_for(std::chrono::milliseconds(20));
Require(sceKernelRaiseException(thread, SIGUSR1) == 0);
ExpectDelivery(before + round - 1, worker.id);
Require(worker.acquired.load() == round - 1);
Require(unlock() == 0);
while (worker.acquired.load() != round) std::this_thread::yield();
Require(lock() == 0);
worker.released.store(round);
}
Require(unlock() == 0);
Require(scePthreadJoin(thread, nullptr) == 0);
}
int main() {
Require(sceKernelRaiseException(scePthreadSelf(), 11) == SCE_KERNEL_ERROR_EINVAL);
bool rejected = false;
@@ -462,6 +525,15 @@ int main() {
Require(std::memcmp(vectorPattern, vectorResult, VectorBytes) == 0);
}
const int beforeLocks = calls.load();
Require(scePthreadMutexInit(&guestMutex, nullptr, "raise") == 0);
ExpectDeliveryWhileLockHeld(LockMutex, UnlockMutex, beforeLocks);
ExpectDeliveryWhileLockHeld(TimedLockMutex, UnlockMutex, beforeLocks + LockRounds);
Require(scePthreadMutexDestroy(&guestMutex) == 0);
Require(scePthreadRwlockInit(&guestRwlock, nullptr, "raise") == 0);
ExpectDeliveryWhileLockHeld(LockRwlock, UnlockRwlock, beforeLocks + 2 * LockRounds);
Require(scePthreadRwlockDestroy(&guestRwlock) == 0);
Pthread finishedThread = nullptr;
Require(scePthreadCreate(&finishedThread, nullptr, Finished, nullptr, "finished") == 0);
while (!finishedReturned.load()) std::this_thread::yield();