AnyPS5/core/libs/tests/GuestLibcStaticImports.cpp

170 lines
5.2 KiB
C++

#include "prx/libc/include/general/VabiMacros.hpp"
#include <atomic>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <limits>
#include <stdexcept>
#include <thread>
#include <time.h>
struct Xtime {
std::int64_t sec;
std::int64_t nsec;
};
extern "C" {
float APS5_VABI nearbyintf_nid_postfix(float x);
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 _Mtx_trylock_nid_postfix(void** handle);
int APS5_VABI _Thrd_sleep_nid_postfix(const Xtime* target, Xtime* remaining);
}
namespace {
void Require(bool value) {
if (!value)
std::abort();
}
template<typename TException, typename TCall>
void RequireThrows(TCall call) {
try {
call();
} catch (const TException&) {
return;
}
std::abort();
}
constexpr int threadSuccess = 0;
constexpr int threadBusy = 3;
constexpr int mutexTryable = 0x02;
constexpr int mutexRecursive = 0x100;
constexpr std::int64_t nanosPerSecond = 1000000000LL;
void TestNearbyintf() {
const float half = nearbyintf_nid_postfix(0.5f);
Require(half == 0.0f);
Require(!std::signbit(half));
const float negHalf = nearbyintf_nid_postfix(-0.5f);
Require(negHalf == 0.0f);
Require(std::signbit(negHalf));
Require(nearbyintf_nid_postfix(1.5f) == 2.0f);
Require(nearbyintf_nid_postfix(-1.5f) == -2.0f);
Require(nearbyintf_nid_postfix(2.5f) == 2.0f);
Require(nearbyintf_nid_postfix(-2.5f) == -2.0f);
const float negZero = nearbyintf_nid_postfix(-0.0f);
Require(negZero == 0.0f);
Require(std::signbit(negZero));
const float inf = std::numeric_limits<float>::infinity();
const float posInf = nearbyintf_nid_postfix(inf);
Require(std::isinf(posInf) && posInf > 0.0f);
const float negInf = nearbyintf_nid_postfix(-inf);
Require(std::isinf(negInf) && negInf < 0.0f);
const float nan = std::numeric_limits<float>::quiet_NaN();
Require(std::isnan(nearbyintf_nid_postfix(nan)));
}
void TestMtxTrylock() {
void* mutex = nullptr;
Require(_Mtx_init_nid_postfix(&mutex, mutexTryable) == threadSuccess && mutex != nullptr);
Require(_Mtx_trylock_nid_postfix(&mutex) == threadSuccess);
Require(_Mtx_trylock_nid_postfix(&mutex) == threadBusy);
Require(_Mtx_unlock_nid_postfix(&mutex) == threadSuccess);
std::atomic<bool> held{false};
std::atomic<bool> release{false};
std::thread other([&] {
Require(_Mtx_lock_nid_postfix(&mutex) == threadSuccess);
held.store(true, std::memory_order_release);
while (!release.load(std::memory_order_acquire))
std::this_thread::yield();
Require(_Mtx_unlock_nid_postfix(&mutex) == threadSuccess);
});
while (!held.load(std::memory_order_acquire))
std::this_thread::yield();
Require(_Mtx_trylock_nid_postfix(&mutex) == threadBusy);
release.store(true, std::memory_order_release);
other.join();
Require(_Mtx_trylock_nid_postfix(&mutex) == threadSuccess);
Require(_Mtx_unlock_nid_postfix(&mutex) == threadSuccess);
_Mtx_destroy_nid_postfix(&mutex);
void* recMutex = nullptr;
Require(_Mtx_init_nid_postfix(&recMutex, mutexTryable | mutexRecursive) == threadSuccess && recMutex != nullptr);
Require(_Mtx_trylock_nid_postfix(&recMutex) == threadSuccess);
Require(_Mtx_trylock_nid_postfix(&recMutex) == threadSuccess);
Require(_Mtx_trylock_nid_postfix(&recMutex) == threadSuccess);
std::thread([&] {
Require(_Mtx_trylock_nid_postfix(&recMutex) == threadBusy);
}).join();
Require(_Mtx_unlock_nid_postfix(&recMutex) == threadSuccess);
std::thread([&] {
Require(_Mtx_trylock_nid_postfix(&recMutex) == threadBusy);
}).join();
Require(_Mtx_unlock_nid_postfix(&recMutex) == threadSuccess);
std::thread([&] {
Require(_Mtx_trylock_nid_postfix(&recMutex) == threadBusy);
}).join();
Require(_Mtx_unlock_nid_postfix(&recMutex) == threadSuccess);
std::thread([&] {
Require(_Mtx_trylock_nid_postfix(&recMutex) == threadSuccess);
Require(_Mtx_unlock_nid_postfix(&recMutex) == threadSuccess);
}).join();
_Mtx_destroy_nid_postfix(&recMutex);
}
void TestThrdSleep() {
timespec before{};
Require(clock_gettime(CLOCK_REALTIME, &before) == 0);
const std::int64_t sleepNanos = 20000000LL;
const std::int64_t targetTotal = static_cast<std::int64_t>(before.tv_sec) * nanosPerSecond + before.tv_nsec + sleepNanos;
const Xtime target{targetTotal / nanosPerSecond, targetTotal % nanosPerSecond};
Require(_Thrd_sleep_nid_postfix(&target, nullptr) == 0);
timespec after{};
Require(clock_gettime(CLOCK_REALTIME, &after) == 0);
const std::int64_t afterTotal = static_cast<std::int64_t>(after.tv_sec) * nanosPerSecond + after.tv_nsec;
Require(afterTotal >= targetTotal);
const Xtime past{before.tv_sec - 1, before.tv_nsec};
Require(_Thrd_sleep_nid_postfix(&past, nullptr) == 0);
RequireThrows<std::invalid_argument>([] {
_Thrd_sleep_nid_postfix(nullptr, nullptr);
});
}
}
int main() {
TestNearbyintf();
TestMtxTrylock();
TestThrdSleep();
return 0;
}