mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-04-04 22:19:02 +00:00
orbis-kernel: unblock signals only on wait operations
implement sys_cpuset_getaffinity, sys_cpuset_setaffinity, sys_rtprio_thread fix hang on sys_select simplify sys_thr_*_ucontext
This commit is contained in:
parent
c19c70eb77
commit
9fbe1846c0
21 changed files with 675 additions and 236 deletions
|
|
@ -8,6 +8,7 @@
|
|||
#include "../thread/Thread.hpp"
|
||||
#include "../thread/types.hpp"
|
||||
#include "ProcessState.hpp"
|
||||
#include "cpuset.hpp"
|
||||
#include "orbis/AppInfo.hpp"
|
||||
#include "orbis/AuthInfo.hpp"
|
||||
#include "orbis/file.hpp"
|
||||
|
|
@ -64,6 +65,7 @@ struct Process final {
|
|||
AuthInfo authInfo{};
|
||||
kstring cwd;
|
||||
kstring root = "/";
|
||||
cpuset affinity{(1 << 7) - 1};
|
||||
sint memoryContainer{1};
|
||||
sint budgetId{1};
|
||||
bool isInSandbox = false;
|
||||
|
|
|
|||
|
|
@ -87,5 +87,8 @@ struct ProcessOps {
|
|||
SysResult (*registerEhFrames)(Thread *thread);
|
||||
|
||||
void (*where)(Thread *);
|
||||
|
||||
void (*unblock)(Thread *);
|
||||
void (*block)(Thread *);
|
||||
};
|
||||
} // namespace orbis
|
||||
|
|
|
|||
|
|
@ -1,53 +1,76 @@
|
|||
#pragma once
|
||||
|
||||
#include "ThreadState.hpp"
|
||||
#include "cpuset.hpp"
|
||||
#include "orbis-config.hpp"
|
||||
#include "types.hpp"
|
||||
|
||||
#include "../KernelAllocator.hpp"
|
||||
#include "../ucontext.hpp"
|
||||
#include "../utils/SharedAtomic.hpp"
|
||||
#include "../utils/SharedCV.hpp"
|
||||
#include "../utils/SharedMutex.hpp"
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
|
||||
namespace orbis {
|
||||
struct Process;
|
||||
|
||||
static constexpr std::uint32_t kThreadSuspendFlag = 1 << 31;
|
||||
struct Thread {
|
||||
utils::shared_mutex mtx;
|
||||
Process *tproc = nullptr;
|
||||
uint64_t retval[2]{};
|
||||
void *context{};
|
||||
kvector<void *> altStack;
|
||||
ptr<void> stackStart;
|
||||
ptr<void> stackEnd;
|
||||
uint64_t fsBase{};
|
||||
uint64_t gsBase{};
|
||||
char name[32]{};
|
||||
|
||||
cpuset affinity{~0u};
|
||||
SigSet sigMask = {0x7fff'ffff, ~0u, ~0u, ~0u};
|
||||
rtprio prio{
|
||||
.type = 2,
|
||||
.prio = 10,
|
||||
};
|
||||
utils::shared_mutex suspend_mtx;
|
||||
utils::shared_cv suspend_cv;
|
||||
kdeque<int> signalQueue;
|
||||
kvector<UContext> sigReturns;
|
||||
std::atomic<unsigned> suspended{0};
|
||||
kvector<SigInfo> blockedSignals;
|
||||
kvector<SigInfo> queuedSignals;
|
||||
shared_atomic32 suspendFlags{0};
|
||||
|
||||
std::int64_t hostTid = -1;
|
||||
lwpid_t tid = -1;
|
||||
unsigned unblocked = 0;
|
||||
ThreadState state = ThreadState::INACTIVE;
|
||||
std::thread handle;
|
||||
std::thread::native_handle_type nativeHandle;
|
||||
|
||||
// Used to wake up thread in sleep queue
|
||||
utils::shared_cv sync_cv;
|
||||
uint64_t evfResultPattern;
|
||||
uint64_t evfIsCancelled;
|
||||
|
||||
[[nodiscard]] std::thread::native_handle_type getNativeHandle() {
|
||||
if (handle.joinable()) {
|
||||
return handle.native_handle();
|
||||
}
|
||||
|
||||
return nativeHandle;
|
||||
}
|
||||
|
||||
// Print backtrace
|
||||
void where();
|
||||
|
||||
void unblock();
|
||||
void block();
|
||||
|
||||
void suspend();
|
||||
void resume();
|
||||
void sendSignal(int signo);
|
||||
void notifyUnblockedSignal(int signo);
|
||||
|
||||
// FIXME: implement thread destruction
|
||||
void incRef() {}
|
||||
|
|
@ -55,4 +78,31 @@ struct Thread {
|
|||
};
|
||||
|
||||
extern thread_local Thread *g_currentThread;
|
||||
|
||||
struct scoped_unblock {
|
||||
scoped_unblock();
|
||||
~scoped_unblock();
|
||||
|
||||
scoped_unblock(const scoped_unblock &) = delete;
|
||||
};
|
||||
|
||||
class scoped_unblock_now {
|
||||
bool unblocked = false;
|
||||
|
||||
public:
|
||||
scoped_unblock_now() {
|
||||
if (g_currentThread && g_currentThread->context) {
|
||||
g_currentThread->unblock();
|
||||
unblocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
~scoped_unblock_now() {
|
||||
if (unblocked) {
|
||||
g_currentThread->block();
|
||||
}
|
||||
}
|
||||
|
||||
scoped_unblock_now(const scoped_unblock_now &) = delete;
|
||||
};
|
||||
} // namespace orbis
|
||||
|
|
|
|||
|
|
@ -3,10 +3,7 @@
|
|||
#include "orbis-config.hpp"
|
||||
|
||||
namespace orbis {
|
||||
static constexpr auto NCPUBITS = sizeof(slong) * 8;
|
||||
static constexpr auto NCPUWORDS = 128 / NCPUBITS;
|
||||
|
||||
struct cpuset {
|
||||
slong bits[NCPUWORDS];
|
||||
uint bits;
|
||||
};
|
||||
} // namespace orbis
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ static constexpr auto kRelaxSpinCount = 12;
|
|||
static constexpr auto kSpinCount = 16;
|
||||
|
||||
inline namespace utils {
|
||||
inline thread_local void (*g_scopedUnblock)(bool) = nullptr;
|
||||
|
||||
bool try_spin_wait(auto &&pred) {
|
||||
for (std::size_t i = 0; i < kSpinCount; ++i) {
|
||||
if (pred()) {
|
||||
|
|
@ -58,8 +60,8 @@ struct shared_atomic32 : std::atomic<std::uint32_t> {
|
|||
using atomic::operator=;
|
||||
|
||||
template <typename Clock, typename Dur>
|
||||
[[nodiscard]] std::errc wait(std::uint32_t oldValue,
|
||||
std::chrono::time_point<Clock, Dur> timeout) {
|
||||
std::errc wait(std::uint32_t oldValue,
|
||||
std::chrono::time_point<Clock, Dur> timeout) {
|
||||
if (try_spin_wait(
|
||||
[&] { return load(std::memory_order::acquire) != oldValue; })) {
|
||||
return {};
|
||||
|
|
@ -76,12 +78,12 @@ struct shared_atomic32 : std::atomic<std::uint32_t> {
|
|||
std::chrono::duration_cast<std::chrono::microseconds>(timeout - now));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::errc wait(std::uint32_t oldValue,
|
||||
std::chrono::microseconds usec_timeout) {
|
||||
std::errc wait(std::uint32_t oldValue,
|
||||
std::chrono::microseconds usec_timeout) {
|
||||
return wait_impl(oldValue, usec_timeout);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::errc wait(std::uint32_t oldValue) {
|
||||
std::errc wait(std::uint32_t oldValue) {
|
||||
if (try_spin_wait(
|
||||
[&] { return load(std::memory_order::acquire) != oldValue; })) {
|
||||
return {};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
#include "orbis/thread/Process.hpp"
|
||||
#include "orbis/thread/ProcessOps.hpp"
|
||||
#include "orbis/utils/Logs.hpp"
|
||||
#include "utils/SharedAtomic.hpp"
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <csignal>
|
||||
|
|
@ -314,16 +315,42 @@ void log_class_string<kstring>::format(std::string &out, const void *arg) {
|
|||
} // namespace logs
|
||||
|
||||
void Thread::suspend() { sendSignal(-1); }
|
||||
|
||||
void Thread::resume() { sendSignal(-2); }
|
||||
|
||||
void Thread::sendSignal(int signo) {
|
||||
std::lock_guard lock(mtx);
|
||||
signalQueue.push_back(signo);
|
||||
if (::tgkill(tproc->hostPid, hostTid, SIGUSR1) < 0) {
|
||||
perror("tgkill");
|
||||
if (pthread_sigqueue(getNativeHandle(), SIGUSR1, {.sival_int = signo})) {
|
||||
perror("pthread_sigqueue");
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::notifyUnblockedSignal(int signo) {
|
||||
for (std::size_t i = 0; i < blockedSignals.size();) {
|
||||
if (blockedSignals[i].signo != signo) {
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
queuedSignals.push_back(blockedSignals[i]);
|
||||
blockedSignals.erase(blockedSignals.begin() + i);
|
||||
}
|
||||
}
|
||||
|
||||
void Thread::where() { tproc->ops->where(this); }
|
||||
|
||||
void Thread::unblock() { tproc->ops->unblock(this); }
|
||||
void Thread::block() { tproc->ops->block(this); }
|
||||
|
||||
scoped_unblock::scoped_unblock() {
|
||||
if (g_currentThread && g_currentThread->context) {
|
||||
g_scopedUnblock = [](bool unblock) {
|
||||
if (unblock) {
|
||||
g_currentThread->unblock();
|
||||
} else {
|
||||
g_currentThread->block();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
scoped_unblock::~scoped_unblock() { g_scopedUnblock = nullptr; }
|
||||
} // namespace orbis
|
||||
|
|
|
|||
|
|
@ -77,11 +77,14 @@ orbis::ErrorCode orbis::EventFlag::wait(Thread *thread, std::uint8_t waitMode,
|
|||
|
||||
waitingThreads.emplace_back(waitingThread);
|
||||
|
||||
if (timeout) {
|
||||
result = toErrorCode(thread->sync_cv.wait(queueMtx, *timeout));
|
||||
update_timeout();
|
||||
} else {
|
||||
result = toErrorCode(thread->sync_cv.wait(queueMtx));
|
||||
{
|
||||
orbis::scoped_unblock unblock;
|
||||
if (timeout) {
|
||||
result = toErrorCode(thread->sync_cv.wait(queueMtx, *timeout));
|
||||
update_timeout();
|
||||
} else {
|
||||
result = toErrorCode(thread->sync_cv.wait(queueMtx));
|
||||
}
|
||||
}
|
||||
|
||||
if (thread->evfIsCancelled == UINT64_MAX) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#include "ipmi.hpp"
|
||||
#include "KernelContext.hpp"
|
||||
#include "thread/Process.hpp"
|
||||
#include "thread/Thread.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
#include <chrono>
|
||||
#include <span>
|
||||
|
|
@ -243,6 +244,7 @@ orbis::SysResult orbis::sysIpmiServerReceivePacket(Thread *thread,
|
|||
{
|
||||
std::lock_guard lock(server->mutex);
|
||||
while (server->packets.empty()) {
|
||||
orbis::scoped_unblock unblock;
|
||||
server->receiveCv.wait(server->mutex);
|
||||
}
|
||||
|
||||
|
|
@ -621,6 +623,7 @@ orbis::SysResult orbis::sysIpmiClientTryGetResult(Thread *thread,
|
|||
return uwrite(result, 0u);
|
||||
}
|
||||
|
||||
orbis::scoped_unblock unblock;
|
||||
client->asyncResponseCv.wait(client->mutex);
|
||||
}
|
||||
|
||||
|
|
@ -684,7 +687,11 @@ orbis::SysResult orbis::sysIpmiClientGetMessage(Thread *thread,
|
|||
|
||||
auto waitTime = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
timeoutPoint - now);
|
||||
queue.messageCv.wait(client->mutex, waitTime.count());
|
||||
|
||||
{
|
||||
orbis::scoped_unblock unblock;
|
||||
queue.messageCv.wait(client->mutex, waitTime.count());
|
||||
}
|
||||
|
||||
if (!queue.messages.empty()) {
|
||||
now = clock::now();
|
||||
|
|
@ -704,6 +711,7 @@ orbis::SysResult orbis::sysIpmiClientGetMessage(Thread *thread,
|
|||
}
|
||||
} else {
|
||||
while (queue.messages.empty()) {
|
||||
orbis::scoped_unblock unblock;
|
||||
queue.messageCv.wait(client->mutex);
|
||||
}
|
||||
}
|
||||
|
|
@ -966,7 +974,10 @@ orbis::sysIpmiClientInvokeSyncMethod(Thread *thread, ptr<uint> result, uint kid,
|
|||
IpmiSession::SyncResponse response;
|
||||
|
||||
while (true) {
|
||||
session->responseCv.wait(session->mutex);
|
||||
{
|
||||
orbis::scoped_unblock unblock;
|
||||
session->responseCv.wait(session->mutex);
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (auto it = session->syncResponses.begin();
|
||||
|
|
@ -1131,10 +1142,12 @@ orbis::SysResult orbis::sysIpmiClientConnect(Thread *thread, ptr<uint> result,
|
|||
}
|
||||
|
||||
while (client->session == nullptr && !client->connectionStatus) {
|
||||
orbis::scoped_unblock unblock;
|
||||
client->sessionCv.wait(client->mutex);
|
||||
}
|
||||
|
||||
while (!client->connectionStatus) {
|
||||
orbis::scoped_unblock unblock;
|
||||
client->connectCv.wait(client->mutex);
|
||||
}
|
||||
|
||||
|
|
@ -1287,8 +1300,13 @@ orbis::SysResult orbis::sysIpmiClientWaitEventFlag(Thread *thread,
|
|||
}
|
||||
|
||||
auto &evf = client->eventFlags[_params.index];
|
||||
auto waitResult = evf.wait(thread, _params.mode, _params.patternSet,
|
||||
_params.pTimeout != 0 ? &resultTimeout : nullptr);
|
||||
ErrorCode waitResult;
|
||||
|
||||
{
|
||||
orbis::scoped_unblock unblock;
|
||||
waitResult = evf.wait(thread, _params.mode, _params.patternSet,
|
||||
_params.pTimeout != 0 ? &resultTimeout : nullptr);
|
||||
}
|
||||
|
||||
if (_params.pPatternSet != nullptr) {
|
||||
ORBIS_RET_ON_ERROR(uwrite(_params.pPatternSet, thread->evfResultPattern));
|
||||
|
|
|
|||
|
|
@ -1,4 +1,50 @@
|
|||
#include "KernelContext.hpp"
|
||||
#include "sys/sysproto.hpp"
|
||||
#include "thread/Process.hpp"
|
||||
#include "thread/Thread.hpp"
|
||||
#include "thread/cpuset.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
|
||||
#include <bit>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
enum class CpuLevel {
|
||||
Root = 1,
|
||||
CpuSet = 2,
|
||||
Which = 3,
|
||||
};
|
||||
|
||||
enum class CpuWhich {
|
||||
Tid = 1,
|
||||
Pid = 2,
|
||||
CpuSet = 3,
|
||||
Irq = 4,
|
||||
Jail = 5,
|
||||
};
|
||||
|
||||
static cpu_set_t toHostCpuSet(orbis::cpuset cpuSet) {
|
||||
const int procCount = get_nprocs();
|
||||
cpu_set_t result{};
|
||||
|
||||
for (unsigned cpu = std::countr_zero(cpuSet.bits);
|
||||
cpu < sizeof(cpuSet.bits) * 8;
|
||||
cpu = std::countr_zero(cpuSet.bits >> (cpu + 1)) + cpu + 1) {
|
||||
unsigned hostCpu = cpu;
|
||||
if (procCount < 8) {
|
||||
hostCpu = cpu % procCount;
|
||||
} else if (procCount >= 8 * 2) {
|
||||
hostCpu = cpu * 2;
|
||||
}
|
||||
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, cpu, hostCpu);
|
||||
CPU_SET(hostCpu, &result);
|
||||
}
|
||||
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, procCount, result.__bits[0], cpuSet.bits);
|
||||
return result;
|
||||
}
|
||||
|
||||
orbis::SysResult orbis::sys_cpuset(Thread *thread, ptr<cpusetid_t> setid) {
|
||||
return ErrorCode::NOSYS;
|
||||
|
|
@ -16,11 +62,133 @@ orbis::SysResult orbis::sys_cpuset_getaffinity(Thread *thread, cpulevel_t level,
|
|||
cpuwhich_t which, id_t id,
|
||||
size_t cpusetsize,
|
||||
ptr<cpuset> mask) {
|
||||
return {};
|
||||
if (cpusetsize != sizeof(cpuset)) {
|
||||
return ErrorCode::INVAL;
|
||||
}
|
||||
|
||||
std::lock_guard lock(thread->mtx);
|
||||
std::lock_guard lockProc(thread->tproc->mtx);
|
||||
|
||||
switch (CpuLevel{level}) {
|
||||
case CpuLevel::Root:
|
||||
case CpuLevel::CpuSet:
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, level, which, id, cpusetsize);
|
||||
return ErrorCode::INVAL;
|
||||
|
||||
case CpuLevel::Which:
|
||||
switch (CpuWhich(which)) {
|
||||
case CpuWhich::Tid: {
|
||||
Thread *whichThread = nullptr;
|
||||
if (id == ~id_t(0) || thread->tid == id) {
|
||||
whichThread = thread;
|
||||
} else {
|
||||
whichThread = thread->tproc->threadsMap.get(id - thread->tproc->pid);
|
||||
if (whichThread == nullptr) {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, "thread not found", level, which, id,
|
||||
cpusetsize);
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
}
|
||||
|
||||
return uwrite(mask, whichThread->affinity);
|
||||
}
|
||||
case CpuWhich::Pid: {
|
||||
Process *whichProcess = nullptr;
|
||||
if (id == ~id_t(0) || id == thread->tproc->pid) {
|
||||
whichProcess = thread->tproc;
|
||||
} else {
|
||||
whichProcess = g_context.findProcessById(id);
|
||||
|
||||
if (whichProcess == nullptr) {
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
}
|
||||
|
||||
return uwrite(mask, whichProcess->affinity);
|
||||
}
|
||||
case CpuWhich::CpuSet:
|
||||
case CpuWhich::Irq:
|
||||
case CpuWhich::Jail:
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, level, which, id, cpusetsize);
|
||||
return ErrorCode::INVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ErrorCode::INVAL;
|
||||
}
|
||||
orbis::SysResult orbis::sys_cpuset_setaffinity(Thread *thread, cpulevel_t level,
|
||||
cpuwhich_t which, id_t id,
|
||||
size_t cpusetsize,
|
||||
ptr<const cpuset> mask) {
|
||||
return {};
|
||||
std::lock_guard lock(thread->mtx);
|
||||
std::lock_guard lockProc(thread->tproc->mtx);
|
||||
switch (CpuLevel{level}) {
|
||||
case CpuLevel::Root:
|
||||
case CpuLevel::CpuSet:
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, level, which, id, cpusetsize);
|
||||
return ErrorCode::INVAL;
|
||||
|
||||
case CpuLevel::Which:
|
||||
switch (CpuWhich(which)) {
|
||||
case CpuWhich::Tid: {
|
||||
Thread *whichThread = nullptr;
|
||||
if (id == ~id_t(0) || thread->tid == id) {
|
||||
whichThread = thread;
|
||||
} else {
|
||||
whichThread = thread->tproc->threadsMap.get(id - thread->tproc->pid);
|
||||
if (whichThread == nullptr) {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, "thread not found", level, which, id,
|
||||
cpusetsize);
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
}
|
||||
|
||||
ORBIS_RET_ON_ERROR(uread(whichThread->affinity, mask));
|
||||
auto threadHandle = whichThread->getNativeHandle();
|
||||
auto hostCpuSet = toHostCpuSet(whichThread->affinity);
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, threadHandle, thread->tid, id);
|
||||
if (pthread_setaffinity_np(threadHandle, sizeof(hostCpuSet), &hostCpuSet)) {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__,
|
||||
"failed to set affinity mask for host thread",
|
||||
whichThread->hostTid, whichThread->affinity.bits);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
case CpuWhich::Pid: {
|
||||
Process *whichProcess = nullptr;
|
||||
if (id == ~id_t(0) || id == thread->tproc->pid) {
|
||||
whichProcess = thread->tproc;
|
||||
} else {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, "process not found", level, which, id,
|
||||
cpusetsize);
|
||||
whichProcess = g_context.findProcessById(id);
|
||||
|
||||
if (whichProcess == nullptr) {
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
}
|
||||
|
||||
ORBIS_RET_ON_ERROR(uread(whichProcess->affinity, mask));
|
||||
auto hostCpuSet = toHostCpuSet(whichProcess->affinity);
|
||||
|
||||
if (sched_setaffinity(whichProcess->hostPid, sizeof(hostCpuSet),
|
||||
&hostCpuSet)) {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__,
|
||||
"failed to set affinity mask for host process",
|
||||
whichProcess->hostPid, whichProcess->affinity.bits);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
case CpuWhich::CpuSet:
|
||||
case CpuWhich::Irq:
|
||||
case CpuWhich::Jail:
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, level, which, id, cpusetsize);
|
||||
return ErrorCode::INVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return ErrorCode::INVAL;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -450,7 +450,15 @@ orbis::SysResult orbis::sys_select(Thread *thread, sint nd,
|
|||
ptr<struct fd_set_t> out,
|
||||
ptr<struct fd_set_t> ex,
|
||||
ptr<struct timeval> tv) {
|
||||
return ErrorCode::NOSYS;
|
||||
if (tv == nullptr) {
|
||||
orbis::scoped_unblock_now unblock;
|
||||
std::this_thread::sleep_for(std::chrono::days(1));
|
||||
} else {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(tv->tv_sec));
|
||||
return orbis::ErrorCode::TIMEDOUT;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
orbis::SysResult orbis::sys_poll(Thread *thread, ptr<struct pollfd> fds,
|
||||
uint nfds, sint timeout) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "sys/sysproto.hpp"
|
||||
#include "thread/Process.hpp"
|
||||
#include "thread/Thread.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
#include <sched.h>
|
||||
|
||||
namespace orbis {
|
||||
struct rlimit {
|
||||
|
|
@ -21,9 +23,54 @@ orbis::SysResult orbis::sys_rtprio_thread(Thread *thread, sint function,
|
|||
ptr<struct rtprio> rtp) {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, function, lwpid, rtp->prio, rtp->type);
|
||||
thread->where();
|
||||
|
||||
Thread *targetThread;
|
||||
if (lwpid == thread->tid || lwpid == -1) {
|
||||
targetThread = thread;
|
||||
} else {
|
||||
targetThread = thread->tproc->threadsMap.get(lwpid - thread->tproc->pid);
|
||||
if (targetThread == nullptr) {
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
}
|
||||
if (function == 0) {
|
||||
rtp->type = 2;
|
||||
rtp->prio = 10;
|
||||
return orbis::uwrite(rtp, targetThread->prio);
|
||||
} else if (function == 1) {
|
||||
ORBIS_RET_ON_ERROR(orbis::uread(targetThread->prio, rtp));
|
||||
|
||||
int hostPolicy = SCHED_RR;
|
||||
auto prioMin = sched_get_priority_min(hostPolicy);
|
||||
auto prioMax = sched_get_priority_max(hostPolicy);
|
||||
auto hostPriority =
|
||||
(targetThread->prio.prio * (prioMax - prioMin + 1)) / 1000 - prioMin;
|
||||
::sched_param hostParam{};
|
||||
hostParam.sched_priority = hostPriority;
|
||||
if (pthread_setschedparam(targetThread->getNativeHandle(), hostPolicy,
|
||||
&hostParam)) {
|
||||
|
||||
auto normPrio = targetThread->prio.prio / 1000.f;
|
||||
hostParam.sched_priority = 0;
|
||||
|
||||
if (normPrio < 0.3f) {
|
||||
hostPolicy = SCHED_BATCH;
|
||||
} else if (normPrio < 0.7f) {
|
||||
hostPolicy = SCHED_OTHER;
|
||||
} else {
|
||||
hostPolicy = SCHED_IDLE;
|
||||
}
|
||||
|
||||
if (pthread_setschedparam(targetThread->getNativeHandle(),
|
||||
hostPolicy, &hostParam)) {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, "failed to set host priority",
|
||||
hostPriority, targetThread->prio.prio,
|
||||
targetThread->prio.type, errno,
|
||||
targetThread->getNativeHandle(), prioMin, prioMax, errno);
|
||||
}
|
||||
} else {
|
||||
ORBIS_LOG_ERROR(__FUNCTION__, "set host priority", hostPriority,
|
||||
targetThread->tid, targetThread->prio.prio,
|
||||
targetThread->prio.type, prioMin, prioMax);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@
|
|||
#include "uio.hpp"
|
||||
#include "utils/Logs.hpp"
|
||||
#include <fcntl.h>
|
||||
#include <ranges>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <thread>
|
||||
|
||||
orbis::SysResult orbis::sys_netcontrol(Thread *thread, sint fd, uint op,
|
||||
ptr<void> buf, uint nbuf) {
|
||||
|
|
@ -576,6 +576,8 @@ orbis::SysResult orbis::sys_osem_wait(Thread *thread, sint id, sint need,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
orbis::scoped_unblock unblock;
|
||||
sem->cond.wait(sem->mtx, ut);
|
||||
}
|
||||
|
||||
|
|
@ -1158,10 +1160,17 @@ orbis::SysResult orbis::sys_get_resident_fmem_count(Thread *thread, pid_t pid) {
|
|||
orbis::SysResult orbis::sys_thr_get_name(Thread *thread, lwpid_t lwpid,
|
||||
char *buf, size_t buflen) {
|
||||
Thread *searchThread;
|
||||
if (thread->tid == lwpid) {
|
||||
if (thread->tid == lwpid || lwpid == -1) {
|
||||
searchThread = thread;
|
||||
} else {
|
||||
searchThread = thread->tproc->threadsMap.get(lwpid);
|
||||
searchThread = thread->tproc->threadsMap.get(lwpid - thread->tproc->pid);
|
||||
|
||||
if (searchThread == nullptr) {
|
||||
if (auto process = g_context.findProcessById(lwpid)) {
|
||||
searchThread = process->threadsMap.get(lwpid - process->pid);
|
||||
}
|
||||
}
|
||||
|
||||
if (searchThread == nullptr) {
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
|
|
@ -1169,11 +1178,13 @@ orbis::SysResult orbis::sys_thr_get_name(Thread *thread, lwpid_t lwpid,
|
|||
|
||||
auto namelen = std::strlen(searchThread->name);
|
||||
|
||||
if (namelen >= buflen) {
|
||||
return ErrorCode::INVAL;
|
||||
auto writeLen = std::min(namelen + 1, buflen);
|
||||
if (writeLen > 0) {
|
||||
ORBIS_RET_ON_ERROR(uwriteRaw(buf, searchThread->name, writeLen - 1));
|
||||
buf[writeLen] = 0;
|
||||
}
|
||||
|
||||
ORBIS_RET_ON_ERROR(uwriteRaw(buf, searchThread->name, namelen));
|
||||
thread->retval[0] = writeLen > 0 ? writeLen : namelen + 1;
|
||||
return {};
|
||||
}
|
||||
orbis::SysResult orbis::sys_set_gpo(Thread *thread /* TODO */) {
|
||||
|
|
@ -1318,78 +1329,77 @@ orbis::SysResult orbis::sys_resume_internal_hdd(Thread *thread /* TODO */) {
|
|||
return ErrorCode::NOSYS;
|
||||
}
|
||||
orbis::SysResult orbis::sys_thr_suspend_ucontext(Thread *thread, lwpid_t tid) {
|
||||
auto t = tid == thread->tid ? thread
|
||||
: thread->tproc->threadsMap.get(tid % 10000 - 1);
|
||||
auto t = tid == thread->tid
|
||||
? thread
|
||||
: thread->tproc->threadsMap.get(tid - thread->tproc->pid);
|
||||
if (t == nullptr) {
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
unsigned prevSuspend = 0;
|
||||
if (t->suspended.compare_exchange_strong(prevSuspend, 1)) {
|
||||
t->suspended.fetch_sub(1);
|
||||
t->suspend();
|
||||
ORBIS_LOG_NOTICE(__FUNCTION__, tid);
|
||||
|
||||
while (t->suspended == 0) {
|
||||
std::this_thread::yield();
|
||||
}
|
||||
auto prevValue = t->suspendFlags.fetch_add(1, std::memory_order::relaxed);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (t->suspended.compare_exchange_strong(prevSuspend, prevSuspend + 1)) {
|
||||
break;
|
||||
}
|
||||
while ((prevValue & kThreadSuspendFlag) == 0) {
|
||||
t->suspend();
|
||||
t->suspendFlags.wait(prevValue);
|
||||
prevValue = t->suspendFlags.load(std::memory_order::relaxed);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
orbis::SysResult orbis::sys_thr_resume_ucontext(Thread *thread, lwpid_t tid) {
|
||||
auto t = tid == thread->tid ? thread
|
||||
: thread->tproc->threadsMap.get(tid % 10000 - 1);
|
||||
auto t = tid == thread->tid
|
||||
? thread
|
||||
: thread->tproc->threadsMap.get(tid - thread->tproc->pid);
|
||||
if (t == nullptr) {
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
unsigned prevSuspend = t->suspended.load();
|
||||
if (t->suspended == prevSuspend) {
|
||||
t->resume();
|
||||
break;
|
||||
}
|
||||
ORBIS_LOG_NOTICE(__FUNCTION__, tid);
|
||||
|
||||
if (prevSuspend == 0) {
|
||||
auto result = t->suspendFlags.op([](unsigned &value) -> ErrorCode {
|
||||
if ((value & kThreadSuspendFlag) == 0) {
|
||||
return ErrorCode::INVAL;
|
||||
}
|
||||
|
||||
if (t->suspended.compare_exchange_strong(prevSuspend, prevSuspend - 1)) {
|
||||
break;
|
||||
if ((value & ~kThreadSuspendFlag) == 0) {
|
||||
return ErrorCode::INVAL;
|
||||
}
|
||||
|
||||
--value;
|
||||
return {};
|
||||
});
|
||||
|
||||
if (result == ErrorCode{}) {
|
||||
t->suspendFlags.notify_all();
|
||||
}
|
||||
return {};
|
||||
|
||||
return result;
|
||||
}
|
||||
orbis::SysResult orbis::sys_thr_get_ucontext(Thread *thread, lwpid_t tid,
|
||||
ptr<UContext> context) {
|
||||
|
||||
auto t = tid == thread->tid ? thread
|
||||
: thread->tproc->threadsMap.get(tid % 10000 - 1);
|
||||
auto t = tid == thread->tid
|
||||
? thread
|
||||
: thread->tproc->threadsMap.get(tid - thread->tproc->pid);
|
||||
if (t == nullptr) {
|
||||
return ErrorCode::SRCH;
|
||||
}
|
||||
|
||||
std::lock_guard lock(t->mtx);
|
||||
|
||||
if (t->suspended == 0) {
|
||||
if ((t->suspendFlags.load() & kThreadSuspendFlag) == 0) {
|
||||
return ErrorCode::INVAL;
|
||||
}
|
||||
|
||||
for (auto it = t->sigReturns.rbegin(); it != t->sigReturns.rend(); ++it) {
|
||||
auto &savedContext = *it;
|
||||
if (savedContext.mcontext.rip < 0x100'0000'0000) {
|
||||
for (auto &savedContext : std::ranges::reverse_view(t->sigReturns)) {
|
||||
if (savedContext.mcontext.rip < orbis::kMaxAddress) {
|
||||
return uwrite(context, savedContext);
|
||||
}
|
||||
}
|
||||
ORBIS_LOG_FATAL(__FUNCTION__, tid, "not found guest context");
|
||||
std::abort();
|
||||
*context = {};
|
||||
return {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,8 @@ orbis::SysResult orbis::sys_sigaction(Thread *thread, sint sig,
|
|||
|
||||
orbis::SysResult orbis::sys_sigprocmask(Thread *thread, sint how,
|
||||
ptr<SigSet> set, ptr<SigSet> oset) {
|
||||
std::lock_guard lock(thread->mtx);
|
||||
|
||||
if (oset) {
|
||||
ORBIS_RET_ON_ERROR(uwrite(oset, thread->sigMask));
|
||||
}
|
||||
|
|
@ -40,20 +42,23 @@ orbis::SysResult orbis::sys_sigprocmask(Thread *thread, sint how,
|
|||
SigSet _set;
|
||||
ORBIS_RET_ON_ERROR(uread(_set, set));
|
||||
|
||||
auto newSigMask = thread->sigMask;
|
||||
auto oldSigMask = newSigMask;
|
||||
|
||||
switch (how) {
|
||||
case 1: // block
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
thread->sigMask.bits[i] |= _set.bits[i];
|
||||
newSigMask.bits[i] |= _set.bits[i];
|
||||
}
|
||||
break;
|
||||
|
||||
case 2: // unblock
|
||||
for (std::size_t i = 0; i < 4; ++i) {
|
||||
thread->sigMask.bits[i] &= ~_set.bits[i];
|
||||
newSigMask.bits[i] &= ~_set.bits[i];
|
||||
}
|
||||
break;
|
||||
case 3: // set
|
||||
thread->sigMask = _set;
|
||||
newSigMask = _set;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -62,8 +67,20 @@ orbis::SysResult orbis::sys_sigprocmask(Thread *thread, sint how,
|
|||
return ErrorCode::INVAL;
|
||||
}
|
||||
|
||||
thread->sigMask.clear(kSigKill);
|
||||
thread->sigMask.clear(kSigStop);
|
||||
newSigMask.clear(kSigKill);
|
||||
newSigMask.clear(kSigStop);
|
||||
|
||||
thread->sigMask = newSigMask;
|
||||
|
||||
for (std::size_t word = 0; word < std::size(newSigMask.bits); ++word) {
|
||||
auto unblockedBits = ~oldSigMask.bits[word] & newSigMask.bits[word];
|
||||
std::uint32_t offset = word * 32 + 1;
|
||||
|
||||
for (std::uint32_t i = std::countr_zero(unblockedBits); i < 32;
|
||||
i += std::countr_zero(unblockedBits >> (i + 1)) + 1) {
|
||||
thread->notifyUnblockedSignal(offset + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ orbis::ErrorCode orbis::umtx_wait(Thread *thread, ptr<void> addr, ulong id,
|
|||
if (val == id) {
|
||||
if (ut + 1 == 0) {
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
result = orbis::toErrorCode(node->second.cv.wait(chain.mtx));
|
||||
if (result != ErrorCode{} || node->second.thr != thread)
|
||||
break;
|
||||
|
|
@ -99,6 +100,7 @@ orbis::ErrorCode orbis::umtx_wait(Thread *thread, ptr<void> addr, ulong id,
|
|||
auto start = std::chrono::steady_clock::now();
|
||||
std::uint64_t udiff = 0;
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
result =
|
||||
orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff));
|
||||
if (node->second.thr != thread)
|
||||
|
|
@ -191,6 +193,7 @@ static ErrorCode do_lock_normal(Thread *thread, ptr<umutex> m, uint flags,
|
|||
auto [chain, key, lock] = g_context.getUmtxChain1(thread, flags, m);
|
||||
auto node = chain.enqueue(key, thread);
|
||||
if (m->owner.compare_exchange_strong(owner, owner | kUmutexContested)) {
|
||||
orbis::scoped_unblock unblock;
|
||||
error = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut));
|
||||
if (error == ErrorCode{} && node->second.thr == thread) {
|
||||
error = ErrorCode::TIMEDOUT;
|
||||
|
|
@ -350,6 +353,7 @@ orbis::ErrorCode orbis::umtx_cv_wait(Thread *thread, ptr<ucond> cv,
|
|||
if (result == ErrorCode{}) {
|
||||
if (ut + 1 == 0) {
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut));
|
||||
if (result != ErrorCode{} || node->second.thr != thread) {
|
||||
break;
|
||||
|
|
@ -359,6 +363,7 @@ orbis::ErrorCode orbis::umtx_cv_wait(Thread *thread, ptr<ucond> cv,
|
|||
auto start = std::chrono::steady_clock::now();
|
||||
std::uint64_t udiff = 0;
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
result =
|
||||
orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff));
|
||||
if (node->second.thr != thread) {
|
||||
|
|
@ -455,6 +460,7 @@ orbis::ErrorCode orbis::umtx_rw_rdlock(Thread *thread, ptr<urwlock> rwlock,
|
|||
|
||||
if (ut + 1 == 0) {
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut));
|
||||
if (result != ErrorCode{} || node->second.thr != thread) {
|
||||
break;
|
||||
|
|
@ -464,6 +470,7 @@ orbis::ErrorCode orbis::umtx_rw_rdlock(Thread *thread, ptr<urwlock> rwlock,
|
|||
auto start = std::chrono::steady_clock::now();
|
||||
std::uint64_t udiff = 0;
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
result =
|
||||
orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff));
|
||||
if (node->second.thr != thread)
|
||||
|
|
@ -556,6 +563,7 @@ orbis::ErrorCode orbis::umtx_rw_wrlock(Thread *thread, ptr<urwlock> rwlock,
|
|||
|
||||
if (ut + 1 == 0) {
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
error = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut));
|
||||
if (error != ErrorCode{} || node->second.thr != thread) {
|
||||
break;
|
||||
|
|
@ -565,6 +573,7 @@ orbis::ErrorCode orbis::umtx_rw_wrlock(Thread *thread, ptr<urwlock> rwlock,
|
|||
auto start = std::chrono::steady_clock::now();
|
||||
std::uint64_t udiff = 0;
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
error =
|
||||
orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff));
|
||||
if (node->second.thr != thread)
|
||||
|
|
@ -729,6 +738,7 @@ orbis::ErrorCode orbis::umtx_sem_wait(Thread *thread, ptr<usem> sem,
|
|||
if (!sem->count) {
|
||||
if (ut + 1 == 0) {
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
result = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut));
|
||||
if (result != ErrorCode{} || node->second.thr != thread)
|
||||
break;
|
||||
|
|
@ -737,6 +747,7 @@ orbis::ErrorCode orbis::umtx_sem_wait(Thread *thread, ptr<usem> sem,
|
|||
auto start = std::chrono::steady_clock::now();
|
||||
std::uint64_t udiff = 0;
|
||||
while (true) {
|
||||
orbis::scoped_unblock unblock;
|
||||
result =
|
||||
orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut - udiff));
|
||||
if (node->second.thr != thread)
|
||||
|
|
|
|||
|
|
@ -15,9 +15,20 @@ std::errc shared_atomic32::wait_impl(std::uint32_t oldValue,
|
|||
timeout.tv_sec = (usec_timeout_count / 1000'000);
|
||||
}
|
||||
|
||||
bool unblock = (!useTimeout || usec_timeout.count() > 1000) &&
|
||||
g_scopedUnblock != nullptr;
|
||||
|
||||
if (unblock) {
|
||||
g_scopedUnblock(true);
|
||||
}
|
||||
|
||||
int result = syscall(SYS_futex, this, FUTEX_WAIT, oldValue,
|
||||
useTimeout ? &timeout : nullptr);
|
||||
|
||||
if (unblock) {
|
||||
g_scopedUnblock(false);
|
||||
}
|
||||
|
||||
if (result < 0) {
|
||||
return static_cast<std::errc>(errno);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,17 +17,23 @@ std::errc shared_cv::impl_wait(shared_mutex &mutex, unsigned _val,
|
|||
|
||||
std::errc result = {};
|
||||
|
||||
bool useTimeout = usec_timeout != static_cast<std::uint64_t>(-1);
|
||||
|
||||
while (true) {
|
||||
result = m_value.wait(_val, usec_timeout == static_cast<std::uint64_t>(-1)
|
||||
? std::chrono::microseconds::max()
|
||||
: std::chrono::microseconds(usec_timeout));
|
||||
result =
|
||||
m_value.wait(_val, useTimeout ? std::chrono::microseconds(usec_timeout)
|
||||
: std::chrono::microseconds::max());
|
||||
bool spurious = result == std::errc::resource_unavailable_try_again ||
|
||||
result == std::errc::interrupted;
|
||||
|
||||
// Cleanup
|
||||
const auto old = m_value.fetch_op([&](unsigned &value) {
|
||||
// Remove waiter if no signals
|
||||
if (!(value & ~c_waiter_mask) &&
|
||||
result != std::errc::resource_unavailable_try_again) {
|
||||
value -= 1;
|
||||
if ((value & ~c_waiter_mask) == 0) {
|
||||
|
||||
if (useTimeout || !spurious) {
|
||||
value -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to remove signal
|
||||
|
|
@ -60,7 +66,7 @@ std::errc shared_cv::impl_wait(shared_mutex &mutex, unsigned _val,
|
|||
#endif
|
||||
|
||||
// Possibly spurious wakeup
|
||||
if (result != std::errc::resource_unavailable_try_again) {
|
||||
if (useTimeout || !spurious) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue