rpcsx/orbis-kernel/src/evf.cpp

155 lines
4.1 KiB
C++
Raw Normal View History

2023-07-05 00:43:47 +02:00
#include "evf.hpp"
#include "error/ErrorCode.hpp"
#include "utils/Logs.hpp"
#include "utils/SharedCV.hpp"
2023-07-05 00:43:47 +02:00
#include <atomic>
orbis::ErrorCode orbis::EventFlag::wait(Thread *thread, std::uint8_t waitMode,
std::uint64_t bitPattern,
std::uint32_t *timeout) {
using namespace std::chrono;
steady_clock::time_point start{};
uint64_t elapsed = 0;
uint64_t fullTimeout = -1;
if (timeout) {
start = steady_clock::now();
fullTimeout = *timeout;
}
auto update_timeout = [&] {
if (!timeout)
return;
auto now = steady_clock::now();
elapsed = duration_cast<microseconds>(now - start).count();
if (fullTimeout > elapsed) {
*timeout = fullTimeout - elapsed;
return;
}
*timeout = 0;
};
2023-07-05 00:43:47 +02:00
thread->evfResultPattern = 0;
2023-07-23 03:44:56 +02:00
thread->evfIsCancelled = -1;
2023-07-20 14:10:38 +02:00
std::unique_lock lock(queueMtx);
int result = 0;
while (true) {
2023-07-05 00:43:47 +02:00
if (isDeleted) {
2023-07-23 03:44:56 +02:00
if (thread->evfIsCancelled == UINT64_MAX)
thread->evfResultPattern = value.load();
2023-07-05 00:43:47 +02:00
return ErrorCode::ACCES;
}
2023-07-23 03:44:56 +02:00
if (thread->evfIsCancelled == 1) {
return ErrorCode::CANCELED;
}
2023-07-23 03:44:56 +02:00
if (thread->evfIsCancelled == 0) {
break;
}
thread->evfResultPattern = 0;
thread->evfIsCancelled = -1;
2023-07-05 00:43:47 +02:00
2023-07-20 14:10:38 +02:00
auto waitingThread = WaitingThread{
.thread = thread, .bitPattern = bitPattern, .waitMode = waitMode};
2023-07-05 00:43:47 +02:00
if (auto patValue = value.load(std::memory_order::relaxed);
waitingThread.test(patValue)) {
auto resultValue = waitingThread.applyClear(patValue);
value.store(resultValue, std::memory_order::relaxed);
2023-07-23 03:44:56 +02:00
thread->evfResultPattern = patValue;
// Success
break;
2023-07-23 03:44:56 +02:00
} else if (timeout && *timeout == 0) {
thread->evfResultPattern = patValue;
return ErrorCode::TIMEDOUT;
2023-07-05 00:43:47 +02:00
}
if (attrs & kEvfAttrSingle) {
2023-07-20 15:32:47 +02:00
if (!waitingThreads.empty())
2023-07-05 00:43:47 +02:00
return ErrorCode::PERM;
} else {
if (attrs & kEvfAttrThFifo) {
} else {
// FIXME: sort waitingThreads by priority
}
}
2023-07-20 15:32:47 +02:00
waitingThreads.emplace_back(waitingThread);
2023-07-05 00:43:47 +02:00
if (timeout) {
result = thread->sync_cv.wait(queueMtx, *timeout);
update_timeout();
2023-07-23 03:44:56 +02:00
} else {
result = thread->sync_cv.wait(queueMtx);
}
2023-07-23 03:44:56 +02:00
if (thread->evfIsCancelled == UINT64_MAX) {
std::erase(waitingThreads, waitingThread);
}
2023-07-05 00:43:47 +02:00
}
// TODO: update thread state
return ErrorCode{result};
2023-07-05 00:43:47 +02:00
}
2023-07-20 14:10:38 +02:00
orbis::ErrorCode orbis::EventFlag::tryWait(Thread *thread,
std::uint8_t waitMode,
std::uint64_t bitPattern) {
2023-07-05 00:43:47 +02:00
writer_lock lock(queueMtx);
if (isDeleted) {
return ErrorCode::ACCES;
}
2023-07-06 18:16:25 +02:00
auto waitingThread =
WaitingThread{.bitPattern = bitPattern, .waitMode = waitMode};
2023-07-05 00:43:47 +02:00
if (auto patValue = value.load(std::memory_order::relaxed);
waitingThread.test(patValue)) {
auto resultValue = waitingThread.applyClear(patValue);
value.store(resultValue, std::memory_order::relaxed);
2023-07-23 03:44:56 +02:00
thread->evfResultPattern = patValue;
2023-07-05 00:43:47 +02:00
return {};
}
return ErrorCode::BUSY;
}
std::size_t orbis::EventFlag::notify(NotifyType type, std::uint64_t bits) {
2023-07-05 00:43:47 +02:00
writer_lock lock(queueMtx);
auto patValue = value.load(std::memory_order::relaxed);
if (type == NotifyType::Destroy) {
isDeleted = true;
} else if (type == NotifyType::Set) {
patValue |= bits;
}
2023-07-05 00:43:47 +02:00
auto testThread = [&](WaitingThread *thread) {
if (type == NotifyType::Set && !thread->test(patValue)) {
2023-07-05 00:43:47 +02:00
return false;
}
auto resultValue = thread->applyClear(patValue);
2023-07-23 03:50:11 +02:00
thread->thread->evfResultPattern = patValue;
thread->thread->evfIsCancelled = type == NotifyType::Cancel;
2023-07-05 00:43:47 +02:00
patValue = resultValue;
// TODO: update thread state
// release wait on waiter thread
thread->thread->sync_cv.notify_all(queueMtx);
2023-07-05 00:43:47 +02:00
return true;
};
2023-07-20 15:32:47 +02:00
std::size_t result = std::erase_if(
waitingThreads, [&](auto &thread) { return testThread(&thread); });
2023-07-05 00:43:47 +02:00
if (type == NotifyType::Cancel) {
value.store(bits, std::memory_order::relaxed);
} else {
value.store(patValue, std::memory_order::relaxed);
2023-07-05 00:43:47 +02:00
}
return result;
}