#include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include #include extern "C" { int APS5_VABI _Mtx_init_nid_postfix(void** handle, int type); void APS5_VABI _Mtx_destroy_nid_postfix(void** handle); int APS5_VABI _Mtx_lock_nid_postfix(void** handle); int APS5_VABI _Mtx_unlock_nid_postfix(void** handle); int APS5_VABI _Cnd_init_nid_postfix(void** handle); void APS5_VABI _Cnd_destroy_nid_postfix(void** handle); int APS5_VABI _Cnd_wait_nid_postfix(void** condition, void** mutex); int APS5_VABI _Cnd_broadcast_nid_postfix(void** handle); } static void Require(bool value) { if (!value) std::abort(); } template static void RequireThrows(TCall call) { try { call(); } catch (const TException&) { return; } std::abort(); } constexpr int success = 0; constexpr int busy = 3; constexpr int plain = 0x01; constexpr int tryable = 0x02; constexpr int timed = 0x04; constexpr int recursive = 0x100; static void* CreateMutex(int type) { void* mutex = reinterpret_cast(std::uintptr_t{1}); Require(_Mtx_init_nid_postfix(&mutex, type) == success && mutex != nullptr); return mutex; } static void MutexBasics() { void* mutex = CreateMutex(tryable); Require(_Mtx_lock_nid_postfix(&mutex) == success); Require(_Mtx_lock_nid_postfix(&mutex) == busy); RequireThrows([&] { _Mtx_destroy_nid_postfix(&mutex); }); std::thread([&] { RequireThrows([&] { _Mtx_unlock_nid_postfix(&mutex); }); }).join(); Require(_Mtx_unlock_nid_postfix(&mutex) == success); RequireThrows([&] { _Mtx_unlock_nid_postfix(&mutex); }); void* invalidMutex = nullptr; RequireThrows([&] { _Mtx_lock_nid_postfix(&invalidMutex); }); _Mtx_destroy_nid_postfix(&mutex); void* emptyMutex = nullptr; _Mtx_destroy_nid_postfix(&emptyMutex); RequireThrows([] { _Mtx_destroy_nid_postfix(nullptr); }); void* plainMutex = CreateMutex(plain); Require(_Mtx_lock_nid_postfix(&plainMutex) == success && _Mtx_lock_nid_postfix(&plainMutex) == success); Require(_Mtx_unlock_nid_postfix(&plainMutex) == success && _Mtx_unlock_nid_postfix(&plainMutex) == success); _Mtx_destroy_nid_postfix(&plainMutex); void* timedMutex = CreateMutex(timed | tryable); Require(_Mtx_lock_nid_postfix(&timedMutex) == success && _Mtx_lock_nid_postfix(&timedMutex) == busy); Require(_Mtx_unlock_nid_postfix(&timedMutex) == success); _Mtx_destroy_nid_postfix(&timedMutex); void* untouched = reinterpret_cast(std::uintptr_t{1}); RequireThrows([&] { _Mtx_init_nid_postfix(&untouched, 0); }); RequireThrows([&] { _Mtx_init_nid_postfix(&untouched, recursive); }); RequireThrows([&] { _Mtx_init_nid_postfix(&untouched, tryable | 0x8); }); Require(untouched == reinterpret_cast(std::uintptr_t{1})); RequireThrows([] { _Mtx_init_nid_postfix(nullptr, tryable); }); RequireThrows([] { _Mtx_lock_nid_postfix(nullptr); }); RequireThrows([] { _Mtx_unlock_nid_postfix(nullptr); }); } static void RecursiveMutex() { void* mutex = CreateMutex(tryable | recursive); for (int i = 0; i < 3; ++i) Require(_Mtx_lock_nid_postfix(&mutex) == success); std::atomic acquired{false}; std::thread other([&] { Require(_Mtx_lock_nid_postfix(&mutex) == success); acquired = true; Require(_Mtx_unlock_nid_postfix(&mutex) == success); }); for (int i = 0; i < 2; ++i) Require(_Mtx_unlock_nid_postfix(&mutex) == success); std::this_thread::sleep_for(std::chrono::milliseconds(20)); Require(!acquired); Require(_Mtx_unlock_nid_postfix(&mutex) == success); other.join(); Require(acquired); _Mtx_destroy_nid_postfix(&mutex); } static void MutualExclusion() { void* mutex = CreateMutex(tryable); long counter = 0; std::array workers; for (auto& worker : workers) worker = std::thread([&] { for (int i = 0; i < 20000; ++i) { Require(_Mtx_lock_nid_postfix(&mutex) == success); ++counter; Require(_Mtx_unlock_nid_postfix(&mutex) == success); } }); for (auto& worker : workers) worker.join(); Require(counter == 80000); _Mtx_destroy_nid_postfix(&mutex); } static void ConditionBroadcast() { void* condition = reinterpret_cast(std::uintptr_t{1}); Require(_Cnd_init_nid_postfix(&condition) == success && condition != nullptr); void* mutex = CreateMutex(tryable); bool ready = false; int waiting = 0, woken = 0; std::array waiters; for (auto& waiter : waiters) waiter = std::thread([&] { Require(_Mtx_lock_nid_postfix(&mutex) == success); ++waiting; while (!ready) Require(_Cnd_wait_nid_postfix(&condition, &mutex) == success); ++woken; Require(_Mtx_lock_nid_postfix(&mutex) == busy); Require(_Mtx_unlock_nid_postfix(&mutex) == success); }); for (;;) { Require(_Mtx_lock_nid_postfix(&mutex) == success); const bool all = waiting == 2; Require(_Mtx_unlock_nid_postfix(&mutex) == success); if (all) break; std::this_thread::yield(); } Require(_Mtx_lock_nid_postfix(&mutex) == success); ready = true; Require(_Cnd_broadcast_nid_postfix(&condition) == success); Require(_Mtx_unlock_nid_postfix(&mutex) == success); for (auto& waiter : waiters) waiter.join(); Require(woken == 2); Require(_Cnd_broadcast_nid_postfix(&condition) == success); RequireThrows([&] { _Cnd_wait_nid_postfix(&condition, &mutex); }); void* recursiveMutex = CreateMutex(tryable | recursive); Require(_Mtx_lock_nid_postfix(&recursiveMutex) == success && _Mtx_lock_nid_postfix(&recursiveMutex) == success); RequireThrows([&] { _Cnd_wait_nid_postfix(&condition, &recursiveMutex); }); Require(_Mtx_unlock_nid_postfix(&recursiveMutex) == success && _Mtx_unlock_nid_postfix(&recursiveMutex) == success); _Mtx_destroy_nid_postfix(&recursiveMutex); RequireThrows([&] { _Cnd_wait_nid_postfix(&mutex, &mutex); }); void* invalidCondition = nullptr; RequireThrows([&] { _Cnd_broadcast_nid_postfix(&invalidCondition); }); RequireThrows([] { _Cnd_broadcast_nid_postfix(nullptr); }); RequireThrows([] { _Cnd_init_nid_postfix(nullptr); }); _Cnd_destroy_nid_postfix(&condition); void* emptyCondition = nullptr; _Cnd_destroy_nid_postfix(&emptyCondition); RequireThrows([] { _Cnd_destroy_nid_postfix(nullptr); }); _Mtx_destroy_nid_postfix(&mutex); } int main() { MutexBasics(); RecursiveMutex(); MutualExclusion(); ConditionBroadcast(); }