#include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include struct Xtime { std::int64_t sec; std::int64_t nsec; }; 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_broadcast_nid_postfix(void** handle); int APS5_VABI _Cnd_timedwait_nid_postfix(void** condition, void** mutex, const Xtime* target); } 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 timedout = 2; constexpr int plain = 0x01; int main() { void* mutex = nullptr; void* condition = nullptr; Require(_Mtx_init_nid_postfix(&mutex, plain) == success); Require(_Cnd_init_nid_postfix(&condition) == success); Require(_Mtx_lock_nid_postfix(&mutex) == success); const auto now = std::chrono::system_clock::now(); const auto nowSec = std::chrono::duration_cast(now.time_since_epoch()).count(); const auto nowNsec = std::chrono::duration_cast(now.time_since_epoch()).count() % 1000000000; Xtime pastTime{nowSec - 1, nowNsec}; Require(_Cnd_timedwait_nid_postfix(&condition, &mutex, &pastTime) == timedout); Xtime unnormalizedPastTime{nowSec - 2, nowNsec + 1000000000}; Require(_Cnd_timedwait_nid_postfix(&condition, &mutex, &unnormalizedPastTime) == timedout); Xtime negativePastTime{nowSec, nowNsec - 2000000000}; Require(_Cnd_timedwait_nid_postfix(&condition, &mutex, &negativePastTime) == timedout); const auto timeoutDeadline = std::chrono::system_clock::now() + std::chrono::milliseconds(20); const auto timeoutSec = std::chrono::duration_cast(timeoutDeadline.time_since_epoch()).count(); const auto timeoutNsec = std::chrono::duration_cast(timeoutDeadline.time_since_epoch()).count() % 1000000000; const Xtime timeoutTime{timeoutSec, timeoutNsec}; while (true) { const int result = _Cnd_timedwait_nid_postfix(&condition, &mutex, &timeoutTime); if (result == timedout) { Require(std::chrono::system_clock::now() >= timeoutDeadline); break; } Require(result == success); } const auto unnormalizedDeadline = std::chrono::system_clock::now() + std::chrono::milliseconds(20); const auto unnormalizedSec = std::chrono::duration_cast(unnormalizedDeadline.time_since_epoch()).count(); const auto unnormalizedNsec = std::chrono::duration_cast(unnormalizedDeadline.time_since_epoch()).count() % 1000000000; const Xtime unnormalizedTimeoutTime{unnormalizedSec - 1, unnormalizedNsec + 1000000000}; while (true) { const int result = _Cnd_timedwait_nid_postfix(&condition, &mutex, &unnormalizedTimeoutTime); if (result == timedout) { Require(std::chrono::system_clock::now() >= unnormalizedDeadline); break; } Require(result == success); } Xtime futureTime{nowSec + 5, nowNsec}; bool ready = false; std::thread signaler([&] { std::this_thread::sleep_for(std::chrono::milliseconds(50)); Require(_Mtx_lock_nid_postfix(&mutex) == success); ready = true; Require(_Cnd_broadcast_nid_postfix(&condition) == success); Require(_Mtx_unlock_nid_postfix(&mutex) == success); }); while (!ready) { Require(_Cnd_timedwait_nid_postfix(&condition, &mutex, &futureTime) == success); } signaler.join(); Require(_Mtx_unlock_nid_postfix(&mutex) == success); RequireThrows([&] { _Cnd_timedwait_nid_postfix(&condition, &mutex, nullptr); }); const Xtime overflowCarryTime{std::numeric_limits::max(), 1000000000}; RequireThrows([&] { _Cnd_timedwait_nid_postfix(&condition, &mutex, &overflowCarryTime); }); const Xtime overflowSecTime{std::numeric_limits::max(), 0}; RequireThrows([&] { _Cnd_timedwait_nid_postfix(&condition, &mutex, &overflowSecTime); }); _Cnd_destroy_nid_postfix(&condition); _Mtx_destroy_nid_postfix(&mutex); }