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:
DH 2024-11-13 21:53:05 +03:00
parent c19c70eb77
commit 9fbe1846c0
21 changed files with 675 additions and 236 deletions

View file

@ -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;

View file

@ -87,5 +87,8 @@ struct ProcessOps {
SysResult (*registerEhFrames)(Thread *thread);
void (*where)(Thread *);
void (*unblock)(Thread *);
void (*block)(Thread *);
};
} // namespace orbis

View file

@ -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

View file

@ -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

View file

@ -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 {};

View file

@ -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

View file

@ -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) {

View file

@ -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));

View file

@ -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;
}

View file

@ -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) {

View file

@ -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 {};
}

View file

@ -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 {};
}

View file

@ -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 {};

View file

@ -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)

View file

@ -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);
}

View file

@ -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;
}