Files
AnyPS5/core/libs/tests/GuestSchedParams.cpp
T
infra-rig 5090bcd175 fix(libkernel): select the registered main thread for own-pid scheduling
sched_getparam, sched_getscheduler and sched_setparam with the own pid
from a thread other than the main thread act on the main thread once it
has registered through scePthreadSelf, as FreeBSD's p1003_1b.c selects
the first thread. Before registration, after the main thread exits and
on macOS they still throw.
2026-10-30 16:35:04 -06:00

226 lines
10 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/Api.hpp"
#include "prx/libkernel/include/Api.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdio>
#include <cstdlib>
#include <thread>
#include <stdexcept>
static constexpr int GUEST_EPERM = 1;
static constexpr int GUEST_ESRCH = 3;
static constexpr int GUEST_EFAULT = 14;
static constexpr int SCHED_FIFO_POLICY = 1;
static constexpr int SCHED_RR_POLICY = 3;
static void Require(bool condition, int line) {
if (!condition) {
std::fprintf(stderr, "line %d failed, errno=%d\n", line, *__error_nid_postfix());
std::abort();
}
}
#define REQUIRE(condition) Require(condition, __LINE__)
static bool FailsWith(int result, int error) {
return result == -1 && *__error_nid_postfix() == error;
}
static void CheckCallingThread() {
int policy = -1;
KernelSchedParam expected{};
REQUIRE(pthread_getschedparam_nid_postfix(scePthreadSelf(), &policy, &expected) == 0);
REQUIRE(sched_getscheduler_nid_postfix(0) == policy);
KernelSchedParam reported{-1};
REQUIRE(sched_getparam_nid_postfix(0, &reported) == 0 && reported.sched_priority == expected.sched_priority);
for (const int priority : {256, 767, 500}) {
const KernelSchedParam set{priority};
REQUIRE(sched_setparam_nid_postfix(0, &set) == 0);
REQUIRE(sched_getparam_nid_postfix(0, &reported) == 0 && reported.sched_priority == priority);
int current = -1;
KernelSchedParam threadParam{};
REQUIRE(pthread_getschedparam_nid_postfix(scePthreadSelf(), &current, &threadParam) == 0);
REQUIRE(current == policy && threadParam.sched_priority == priority);
REQUIRE(sched_getscheduler_nid_postfix(0) == policy);
}
const KernelSchedParam roundRobin{600};
REQUIRE(pthread_setschedparam_nid_postfix(scePthreadSelf(), SCHED_RR_POLICY, &roundRobin) == 0);
REQUIRE(sched_getscheduler_nid_postfix(0) == SCHED_RR_POLICY);
const KernelSchedParam lowered{300};
REQUIRE(sched_setparam_nid_postfix(0, &lowered) == 0 && sched_getscheduler_nid_postfix(0) == SCHED_RR_POLICY);
REQUIRE(sched_getparam_nid_postfix(0, &reported) == 0 && reported.sched_priority == 300);
REQUIRE(pthread_setschedparam_nid_postfix(scePthreadSelf(), policy, &expected) == 0);
}
static void CheckTargets() {
KernelSchedParam param{-1};
const KernelSchedParam valid{500};
const int self = getpid_nid_postfix();
for (const int pid : {self + 1, -1, 99999}) {
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_getparam_nid_postfix(pid, &param), GUEST_ESRCH) && param.sched_priority == -1);
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_getscheduler_nid_postfix(pid), GUEST_ESRCH));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(pid, &valid), GUEST_ESRCH));
}
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_getparam_nid_postfix(0, nullptr), GUEST_EFAULT));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_getparam_nid_postfix(0, reinterpret_cast<KernelSchedParam*>(0x10)), GUEST_EFAULT));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_getparam_nid_postfix(self + 1, nullptr), GUEST_ESRCH));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(0, nullptr), GUEST_EFAULT));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(0, reinterpret_cast<const KernelSchedParam*>(0x10)), GUEST_EFAULT));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(self + 1, nullptr), GUEST_EFAULT));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(self + 1, reinterpret_cast<const KernelSchedParam*>(0x10)), GUEST_EFAULT));
}
static constexpr int WorkerPriority = 700;
struct Worker {
int before = -1;
int after = -1;
int policy = -1;
};
static void* APS5_VABI WorkerEntry(void* arg) {
auto& worker = *static_cast<Worker*>(arg);
KernelSchedParam param{};
REQUIRE(sched_getparam_nid_postfix(0, &param) == 0);
worker.before = param.sched_priority;
const KernelSchedParam set{WorkerPriority};
REQUIRE(sched_setparam_nid_postfix(0, &set) == 0);
REQUIRE(sched_getparam_nid_postfix(0, &param) == 0);
worker.after = param.sched_priority;
worker.policy = sched_getscheduler_nid_postfix(0);
return nullptr;
}
static void CheckOtherThread() {
constexpr int callerPriority = 400;
KernelSchedParam original{};
REQUIRE(sched_getparam_nid_postfix(0, &original) == 0);
const KernelSchedParam callerParam{callerPriority};
REQUIRE(sched_setparam_nid_postfix(0, &callerParam) == 0);
Worker worker;
Pthread thread = nullptr;
REQUIRE(scePthreadCreate(&thread, nullptr, WorkerEntry, &worker, "sched") == 0);
REQUIRE(scePthreadJoin(thread, nullptr) == 0);
REQUIRE(worker.after == WorkerPriority && (worker.policy == SCHED_FIFO_POLICY || worker.policy == SCHED_RR_POLICY));
KernelSchedParam callerAfter{};
REQUIRE(sched_getparam_nid_postfix(0, &callerAfter) == 0 && callerAfter.sched_priority == callerPriority);
REQUIRE(sched_setparam_nid_postfix(0, &original) == 0);
}
static void CheckOutOfRange() {
KernelSchedParam original{};
REQUIRE(sched_getparam_nid_postfix(0, &original) == 0);
const int policy = sched_getscheduler_nid_postfix(0);
for (const int priority : {900, 959}) {
REQUIRE(scePthreadSetprio(scePthreadSelf(), priority) == 0);
KernelSchedParam reported{-1};
REQUIRE(sched_getparam_nid_postfix(0, &reported) == 0 && reported.sched_priority == priority);
REQUIRE(sched_getscheduler_nid_postfix(0) == policy);
}
const KernelSchedParam inRange{700};
REQUIRE(sched_setparam_nid_postfix(0, &inRange) == 0);
for (const int priority : {255, 768}) {
const KernelSchedParam rejected{priority};
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(0, &rejected), GUEST_EPERM));
KernelSchedParam reported{-1};
REQUIRE(sched_getparam_nid_postfix(0, &reported) == 0 && reported.sched_priority == 700);
}
REQUIRE(sched_setparam_nid_postfix(0, &original) == 0);
}
#ifndef __APPLE__
static void* APS5_VABI UnregisteredMainWorker(void*) {
const int pid = getpid_nid_postfix();
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(pid, reinterpret_cast<const KernelSchedParam*>(0x10)), GUEST_EFAULT));
const KernelSchedParam workerParam{400};
REQUIRE(pthread_setschedparam_nid_postfix(scePthreadSelf(), SCHED_RR_POLICY, &workerParam) == 0);
KernelSchedParam param{-1};
try {
const int result = sched_getparam_nid_postfix(pid, &param);
REQUIRE(result != 0 || param.sched_priority != workerParam.sched_priority);
} catch (const std::runtime_error&) {}
try {
REQUIRE(sched_getscheduler_nid_postfix(pid) != SCHED_RR_POLICY);
} catch (const std::runtime_error&) {}
return nullptr;
}
static void CheckBeforeMainPthreadCall() {
std::thread worker([] { UnregisteredMainWorker(nullptr); });
worker.join();
Pthread guestWorker = nullptr;
REQUIRE(scePthreadCreate(&guestWorker, nullptr, UnregisteredMainWorker, nullptr, "unregistered") == 0);
REQUIRE(scePthreadJoin(guestWorker, nullptr) == 0);
}
static void CheckOwnPid() {
KernelSchedParam original{};
REQUIRE(sched_getparam_nid_postfix(0, &original) == 0);
const KernelSchedParam mainParam{600};
REQUIRE(sched_setparam_nid_postfix(0, &mainParam) == 0);
const int self = getpid_nid_postfix();
const int policy = sched_getscheduler_nid_postfix(0);
KernelSchedParam reported{-1};
REQUIRE(sched_getparam_nid_postfix(self, &reported) == 0 && reported.sched_priority == 600);
REQUIRE(sched_getscheduler_nid_postfix(self) == policy);
for (const int mainPolicy : {SCHED_FIFO_POLICY, SCHED_RR_POLICY}) {
REQUIRE(pthread_setschedparam_nid_postfix(scePthreadSelf(), mainPolicy, &mainParam) == 0);
std::thread worker([=] {
const int workerPolicy = mainPolicy == SCHED_FIFO_POLICY ? SCHED_RR_POLICY : SCHED_FIFO_POLICY;
const KernelSchedParam workerParam{400};
REQUIRE(pthread_setschedparam_nid_postfix(scePthreadSelf(), workerPolicy, &workerParam) == 0);
KernelSchedParam value{-1};
REQUIRE(sched_getparam_nid_postfix(self, &value) == 0 && value.sched_priority == 600);
REQUIRE(sched_getscheduler_nid_postfix(self) == mainPolicy);
const KernelSchedParam changed{500};
REQUIRE(sched_setparam_nid_postfix(self, &changed) == 0);
REQUIRE(sched_getparam_nid_postfix(self, &value) == 0 && value.sched_priority == 500);
REQUIRE(sched_getscheduler_nid_postfix(self) == mainPolicy);
REQUIRE(sched_getparam_nid_postfix(0, &value) == 0 && value.sched_priority == 400);
REQUIRE(sched_getscheduler_nid_postfix(0) == workerPolicy);
const KernelSchedParam rejected{768};
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(self, &rejected), GUEST_EPERM));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_getparam_nid_postfix(self, nullptr), GUEST_EFAULT));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(self, nullptr), GUEST_EFAULT));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_getparam_nid_postfix(self, reinterpret_cast<KernelSchedParam*>(0x10)), GUEST_EFAULT));
*__error_nid_postfix() = 0;
REQUIRE(FailsWith(sched_setparam_nid_postfix(self, reinterpret_cast<const KernelSchedParam*>(0x10)), GUEST_EFAULT));
});
worker.join();
REQUIRE(sched_getparam_nid_postfix(0, &reported) == 0 && reported.sched_priority == 500);
REQUIRE(sched_getscheduler_nid_postfix(0) == mainPolicy);
}
REQUIRE(pthread_setschedparam_nid_postfix(scePthreadSelf(), policy, &original) == 0);
}
#endif
int main() {
#ifndef __APPLE__
CheckBeforeMainPthreadCall();
#endif
CheckCallingThread();
CheckTargets();
CheckOtherThread();
CheckOutOfRange();
#ifndef __APPLE__
CheckOwnPid();
#endif
return 0;
}