2023-07-05 00:43:47 +02:00
|
|
|
#include "evf.hpp"
|
|
|
|
|
#include "error/ErrorCode.hpp"
|
|
|
|
|
#include "utils/Logs.hpp"
|
2023-07-18 14:55:13 +02:00
|
|
|
#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) {
|
2023-07-18 15:32:53 +02:00
|
|
|
using namespace std::chrono;
|
|
|
|
|
|
|
|
|
|
steady_clock::time_point start{};
|
|
|
|
|
uint64_t elapsed = 0;
|
2023-07-18 18:13:11 +02:00
|
|
|
uint64_t fullTimeout = -1;
|
|
|
|
|
if (timeout) {
|
|
|
|
|
start = steady_clock::now();
|
|
|
|
|
fullTimeout = *timeout;
|
|
|
|
|
}
|
2023-07-18 14:55:13 +02:00
|
|
|
|
|
|
|
|
auto update_timeout = [&] {
|
|
|
|
|
if (!timeout)
|
|
|
|
|
return;
|
2023-07-18 15:32:53 +02:00
|
|
|
auto now = steady_clock::now();
|
|
|
|
|
elapsed = duration_cast<microseconds>(now - start).count();
|
2023-07-18 18:13:11 +02:00
|
|
|
if (fullTimeout > elapsed) {
|
|
|
|
|
*timeout = fullTimeout - elapsed;
|
2023-07-18 14:55:13 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
*timeout = 0;
|
|
|
|
|
};
|
2023-07-05 00:43:47 +02:00
|
|
|
|
2023-07-20 14:10:38 +02:00
|
|
|
// Use retval to pass information between threads
|
|
|
|
|
thread->retval[0] = 0; // resultPattern
|
|
|
|
|
thread->retval[1] = 0; // isCanceled
|
|
|
|
|
|
2023-07-18 14:55:13 +02:00
|
|
|
std::unique_lock lock(queueMtx);
|
|
|
|
|
while (true) {
|
2023-07-05 00:43:47 +02:00
|
|
|
if (isDeleted) {
|
|
|
|
|
return ErrorCode::ACCES;
|
|
|
|
|
}
|
2023-07-20 14:10:38 +02:00
|
|
|
if (thread->retval[1]) {
|
2023-07-18 14:55:13 +02:00
|
|
|
return ErrorCode::CANCELED;
|
|
|
|
|
}
|
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-20 14:10:38 +02:00
|
|
|
thread->retval[0] = resultValue;
|
2023-07-18 14:55:13 +02:00
|
|
|
// Success
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (timeout && *timeout == 0) {
|
|
|
|
|
return ErrorCode::TIMEDOUT;
|
2023-07-05 00:43:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::size_t position;
|
|
|
|
|
|
|
|
|
|
if (attrs & kEvfAttrSingle) {
|
|
|
|
|
if (waitingThreadsCount.fetch_add(1, std::memory_order::relaxed) != 0) {
|
|
|
|
|
waitingThreadsCount.store(1, std::memory_order::relaxed);
|
|
|
|
|
return ErrorCode::PERM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
position = 0;
|
|
|
|
|
} else {
|
|
|
|
|
if (attrs & kEvfAttrThFifo) {
|
|
|
|
|
position = waitingThreadsCount.fetch_add(1, std::memory_order::relaxed);
|
|
|
|
|
} else {
|
|
|
|
|
// FIXME: sort waitingThreads by priority
|
|
|
|
|
position = waitingThreadsCount.fetch_add(1, std::memory_order::relaxed);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (position >= std::size(waitingThreads)) {
|
|
|
|
|
std::abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
waitingThreads[position] = waitingThread;
|
|
|
|
|
|
2023-07-18 15:32:53 +02:00
|
|
|
if (timeout) {
|
2023-07-20 14:10:38 +02:00
|
|
|
thread->sync_cv.wait(queueMtx, *timeout);
|
2023-07-18 15:32:53 +02:00
|
|
|
update_timeout();
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 14:10:38 +02:00
|
|
|
thread->sync_cv.wait(queueMtx);
|
2023-07-05 00:43:47 +02:00
|
|
|
}
|
|
|
|
|
|
2023-07-18 14:55:13 +02:00
|
|
|
// TODO: update thread state
|
2023-07-05 00:43:47 +02:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
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-20 14:10:38 +02:00
|
|
|
thread->retval[0] = resultValue;
|
2023-07-05 00:43:47 +02:00
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ErrorCode::BUSY;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-05 21:23:42 +02:00
|
|
|
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 count = waitingThreadsCount.load(std::memory_order::relaxed);
|
|
|
|
|
auto patValue = value.load(std::memory_order::relaxed);
|
|
|
|
|
|
2023-07-05 21:23:42 +02:00
|
|
|
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) {
|
2023-07-08 01:21:10 +02:00
|
|
|
if (type == NotifyType::Set && !thread->test(patValue)) {
|
2023-07-05 00:43:47 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto resultValue = thread->applyClear(patValue);
|
|
|
|
|
patValue = resultValue;
|
2023-07-20 14:10:38 +02:00
|
|
|
thread->thread->retval[0] = resultValue;
|
2023-07-05 21:23:42 +02:00
|
|
|
if (type == NotifyType::Cancel) {
|
2023-07-20 14:10:38 +02:00
|
|
|
thread->thread->retval[1] = true;
|
2023-07-05 00:43:47 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: update thread state
|
2023-07-18 14:55:13 +02:00
|
|
|
// release wait on waiter thread
|
2023-07-20 14:10:38 +02:00
|
|
|
thread->thread->sync_cv.notify_one(queueMtx);
|
2023-07-05 00:43:47 +02:00
|
|
|
|
|
|
|
|
waitingThreadsCount.fetch_sub(1, std::memory_order::relaxed);
|
2023-07-06 18:16:25 +02:00
|
|
|
std::memmove(thread, thread + 1,
|
|
|
|
|
(waitingThreads + count - (thread + 1)) *
|
|
|
|
|
sizeof(*waitingThreads));
|
2023-07-05 00:43:47 +02:00
|
|
|
--count;
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::size_t result = 0;
|
|
|
|
|
if (attrs & kEvfAttrThFifo) {
|
|
|
|
|
for (std::size_t i = count; i > 0;) {
|
|
|
|
|
if (!testThread(waitingThreads + i - 1)) {
|
|
|
|
|
--i;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++result;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for (std::size_t i = 0; i < count;) {
|
|
|
|
|
if (!testThread(waitingThreads + i)) {
|
|
|
|
|
++i;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-05 21:23:42 +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;
|
|
|
|
|
}
|