mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 00:22:57 -05:00
The waiter took the lock only after the holder unlocked, but under load it saw the holder's lock late and measured less than half the hold time. The holder now sets a flag just before it unlocks and the waiter requires it once its lock is taken. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
86 lines
3.8 KiB
C++
86 lines
3.8 KiB
C++
#include "SceTypes.hpp"
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <cstdlib>
|
|
#include <thread>
|
|
|
|
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 pthread_rwlock_wrlock_nid_postfix(PthreadRwlock* rwlock);
|
|
int APS5_VABI pthread_rwlock_unlock_nid_postfix(PthreadRwlock* rwlock);
|
|
int APS5_VABI pthread_rwlock_destroy_nid_postfix(PthreadRwlock* rwlock);
|
|
int APS5_VABI pthread_rwlock_timedrdlock_nid_postfix(PthreadRwlock* rwlock, const KernelTimespec* abstime);
|
|
int APS5_VABI pthread_rwlock_timedwrlock_nid_postfix(PthreadRwlock* rwlock, const KernelTimespec* abstime);
|
|
int APS5_VABI pthread_mutex_init_nid_postfix(PthreadMutex* mutex, const PthreadMutexattr* attr);
|
|
int APS5_VABI pthread_mutex_lock_nid_postfix(PthreadMutex* mutex);
|
|
int APS5_VABI pthread_mutex_unlock_nid_postfix(PthreadMutex* mutex);
|
|
int APS5_VABI pthread_mutex_destroy_nid_postfix(PthreadMutex* mutex);
|
|
int APS5_VABI pthread_mutex_timedlock_nid_postfix(PthreadMutex* mutex, const KernelTimespec* abstime);
|
|
}
|
|
|
|
static constexpr int SCE_OK = 0;
|
|
static constexpr int GUEST_EDEADLK = 11;
|
|
static constexpr auto HoldTime = std::chrono::milliseconds(50);
|
|
|
|
static void Require(bool value) { if (!value) std::abort(); }
|
|
|
|
struct Holder {
|
|
void* lock;
|
|
bool mutex;
|
|
std::atomic<bool> held{false};
|
|
std::atomic<bool> releasing{false};
|
|
Pthread thread = nullptr;
|
|
};
|
|
|
|
static void* APS5_VABI Hold(void* arg) {
|
|
auto& holder = *static_cast<Holder*>(arg);
|
|
auto* mutex = static_cast<PthreadMutex*>(holder.lock);
|
|
auto* rwlock = static_cast<PthreadRwlock*>(holder.lock);
|
|
Require((holder.mutex ? pthread_mutex_lock_nid_postfix(mutex) : pthread_rwlock_wrlock_nid_postfix(rwlock)) == 0);
|
|
holder.held.store(true);
|
|
std::this_thread::sleep_for(HoldTime);
|
|
holder.releasing.store(true);
|
|
Require((holder.mutex ? pthread_mutex_unlock_nid_postfix(mutex) : pthread_rwlock_unlock_nid_postfix(rwlock)) == 0);
|
|
return nullptr;
|
|
}
|
|
|
|
template <typename Lock>
|
|
static void RequireWaitsForHolder(void* lock, bool mutex, Lock acquire) {
|
|
Holder holder{lock, mutex};
|
|
Require(scePthreadCreate(&holder.thread, nullptr, Hold, &holder, nullptr) == SCE_OK);
|
|
while (!holder.held.load()) std::this_thread::yield();
|
|
Require(acquire() == 0);
|
|
Require(holder.releasing.load());
|
|
Require(scePthreadJoin(holder.thread, nullptr) == SCE_OK);
|
|
}
|
|
|
|
static void CheckRwlock() {
|
|
PthreadRwlock rwlock = nullptr;
|
|
Require(pthread_rwlock_timedrdlock_nid_postfix(&rwlock, nullptr) == 0 && rwlock != nullptr);
|
|
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);
|
|
Require(pthread_rwlock_timedwrlock_nid_postfix(&rwlock, nullptr) == 0);
|
|
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);
|
|
RequireWaitsForHolder(&rwlock, false, [&] { return pthread_rwlock_timedrdlock_nid_postfix(&rwlock, nullptr); });
|
|
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);
|
|
RequireWaitsForHolder(&rwlock, false, [&] { return pthread_rwlock_timedwrlock_nid_postfix(&rwlock, nullptr); });
|
|
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);
|
|
Require(pthread_rwlock_destroy_nid_postfix(&rwlock) == 0);
|
|
}
|
|
|
|
static void CheckMutex() {
|
|
PthreadMutex mutex = nullptr;
|
|
Require(pthread_mutex_init_nid_postfix(&mutex, nullptr) == 0);
|
|
Require(pthread_mutex_timedlock_nid_postfix(&mutex, nullptr) == 0);
|
|
Require(pthread_mutex_timedlock_nid_postfix(&mutex, nullptr) == GUEST_EDEADLK);
|
|
Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0);
|
|
RequireWaitsForHolder(&mutex, true, [&] { return pthread_mutex_timedlock_nid_postfix(&mutex, nullptr); });
|
|
Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0);
|
|
Require(pthread_mutex_destroy_nid_postfix(&mutex) == 0);
|
|
}
|
|
|
|
int main() {
|
|
CheckRwlock();
|
|
CheckMutex();
|
|
}
|