Files
KytyPS5/src/kernel/eventFlag.cpp
T
2026-07-16 10:21:41 +02:00

381 lines
8.9 KiB
C++

#include "kernel/eventFlag.h"
#include "common/assert.h"
#include "common/common.h"
#include "common/logging/log.h"
#include "common/stringUtils.h"
#include "common/threads.h"
#include "common/timer.h"
#include "libs/errno.h"
#include "libs/libs.h"
namespace Libs::LibKernel::EventFlag {
LIB_NAME("libkernel", "libkernel");
class KernelEventFlagPrivate {
public:
enum class Result { Ok, AlreadyWaiting, TimedOut, Canceled, Deleted };
enum class ClearMode { None, All, Bits };
enum class WaitMode { And, Or };
KernelEventFlagPrivate(const std::string& name, bool single, bool /*fifo*/, uint64_t bits)
: m_name(name), m_single_thread(single), m_bits(bits) {}
virtual ~KernelEventFlagPrivate();
KYTY_CLASS_NO_COPY(KernelEventFlagPrivate);
void Set(uint64_t bits);
void Clear(uint64_t bits);
void Cancel(uint64_t bits, int* num_waiting_threads);
Result Wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result,
uint32_t* ptr_micros);
Result Poll(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result) {
uint32_t micros = 0;
return Wait(bits, wait_mode, clear_mode, result, &micros);
}
private:
enum class Status { Set, Canceled, Deleted };
Common::Mutex m_mutex;
Common::CondVar m_cond_var;
Status m_status = Status::Set;
int m_waiting_threads = 0;
std::string m_name;
bool m_single_thread = false;
uint64_t m_bits = 0;
};
namespace {
struct EventFlagWaitMode {
KernelEventFlagPrivate::WaitMode wait = KernelEventFlagPrivate::WaitMode::And;
KernelEventFlagPrivate::ClearMode clear = KernelEventFlagPrivate::ClearMode::None;
};
EventFlagWaitMode DecodeEventFlagWaitMode(uint32_t wait_mode) {
EventFlagWaitMode mode;
switch (wait_mode & 0xfu) {
case 0x01: mode.wait = KernelEventFlagPrivate::WaitMode::And; break;
case 0x02: mode.wait = KernelEventFlagPrivate::WaitMode::Or; break;
default: EXIT("unknown mode: %u\n", wait_mode);
}
switch (wait_mode & 0xf0u) {
case 0x00: mode.clear = KernelEventFlagPrivate::ClearMode::None; break;
case 0x10: mode.clear = KernelEventFlagPrivate::ClearMode::All; break;
case 0x20: mode.clear = KernelEventFlagPrivate::ClearMode::Bits; break;
default: EXIT("unknown mode: %u\n", wait_mode);
}
return mode;
}
} // namespace
KernelEventFlagPrivate::~KernelEventFlagPrivate() {
Common::LockGuard lock(m_mutex);
while (m_status != Status::Set) {
m_mutex.Unlock();
Common::Thread::SleepMicro(10);
m_mutex.Lock();
}
m_status = Status::Deleted;
m_cond_var.SignalAll();
while (m_waiting_threads > 0) {
m_mutex.Unlock();
Common::Thread::SleepMicro(10);
m_mutex.Lock();
}
}
KernelEventFlagPrivate::Result KernelEventFlagPrivate::Wait(uint64_t bits, WaitMode wait_mode,
ClearMode clear_mode, uint64_t* result,
uint32_t* ptr_micros) {
Common::LockGuard lock(m_mutex);
uint32_t micros = 0;
bool infinitely = true;
if (ptr_micros != nullptr) {
micros = *ptr_micros;
infinitely = false;
}
uint32_t elapsed = 0;
Common::Timer t;
t.Start();
auto update_timeout = [&]() {
if (ptr_micros != nullptr) {
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
}
};
if (m_single_thread && m_waiting_threads > 0) {
return Result::AlreadyWaiting;
}
while (!((wait_mode == WaitMode::And && (m_bits & bits) == bits) ||
(wait_mode == WaitMode::Or && (m_bits & bits) != 0))) {
if ((elapsed >= micros && !infinitely)) {
if (result != nullptr) {
*result = m_bits;
}
update_timeout();
return Result::TimedOut;
}
m_waiting_threads++;
if (infinitely) {
m_cond_var.Wait(&m_mutex);
} else {
m_cond_var.WaitFor(&m_mutex, micros - elapsed);
}
m_waiting_threads--;
elapsed = static_cast<uint32_t>(t.GetTimeS() * 1000000.0);
switch (m_status) {
case Status::Canceled:
if (result != nullptr) {
*result = m_bits;
}
update_timeout();
return Result::Canceled;
case Status::Deleted:
if (result != nullptr) {
*result = m_bits;
}
update_timeout();
return Result::Deleted;
case Status::Set: break;
}
}
if (result != nullptr) {
*result = m_bits;
}
update_timeout();
switch (clear_mode) {
case ClearMode::All: m_bits = 0; break;
case ClearMode::Bits: m_bits &= ~bits; break;
case ClearMode::None: break;
}
return Result::Ok;
}
void KernelEventFlagPrivate::Set(uint64_t bits) {
Common::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
while (m_status != Status::Set) {
m_mutex.Unlock();
Common::Thread::SleepMicro(10);
m_mutex.Lock();
}
m_bits |= bits;
m_cond_var.SignalAll();
}
void KernelEventFlagPrivate::Clear(uint64_t bits) {
Common::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
while (m_status != Status::Set) {
m_mutex.Unlock();
Common::Thread::SleepMicro(10);
m_mutex.Lock();
}
m_bits &= bits;
}
void KernelEventFlagPrivate::Cancel(uint64_t bits, int* num_waiting_threads) {
Common::LockGuard lock(m_mutex);
EXIT_NOT_IMPLEMENTED(m_status == Status::Deleted);
while (m_status != Status::Set) {
m_mutex.Unlock();
Common::Thread::SleepMicro(10);
m_mutex.Lock();
}
if (num_waiting_threads != nullptr) {
*num_waiting_threads = m_waiting_threads;
}
m_status = Status::Canceled;
m_bits = bits;
m_cond_var.SignalAll();
while (m_waiting_threads > 0) {
m_mutex.Unlock();
Common::Thread::SleepMicro(10);
m_mutex.Lock();
}
m_status = Status::Set;
}
int KYTY_SYSV_ABI KernelCreateEventFlag(KernelEventFlag* ef, const char* name, uint32_t attr,
uint64_t init_pattern, const void* param) {
PRINT_NAME();
if (ef == nullptr || name == nullptr) {
return KERNEL_ERROR_EINVAL;
}
if (param != nullptr || (attr & ~0x33u) != 0) {
return KERNEL_ERROR_EINVAL;
}
bool single = true;
bool fifo = true;
switch (attr & 0x0fu) {
case 0x00:
case 0x01: fifo = true; break;
case 0x02: fifo = false; break;
default: return KERNEL_ERROR_EINVAL;
}
switch (attr & 0xf0u) {
case 0x00:
case 0x10: single = true; break;
case 0x20: single = false; break;
default: return KERNEL_ERROR_EINVAL;
}
*ef = new KernelEventFlagPrivate(std::string(name), single, fifo, init_pattern);
LOGF("\tEventFlag create: %s\n", name);
return OK;
}
int KYTY_SYSV_ABI KernelDeleteEventFlag(KernelEventFlag ef) {
PRINT_NAME();
if (ef == nullptr) {
return KERNEL_ERROR_ESRCH;
}
delete ef;
return OK;
}
int KYTY_SYSV_ABI KernelWaitEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode,
uint64_t* result_pat, KernelUseconds* timeout) {
PRINT_NAME();
if (ef == nullptr) {
return KERNEL_ERROR_ESRCH;
}
if (bit_pattern == 0) {
return KERNEL_ERROR_EINVAL;
}
const auto mode = DecodeEventFlagWaitMode(wait_mode);
auto result = ef->Wait(bit_pattern, mode.wait, mode.clear, result_pat, timeout);
int ret = OK;
switch (result) {
case KernelEventFlagPrivate::Result::Ok: ret = OK; break;
case KernelEventFlagPrivate::Result::AlreadyWaiting: ret = KERNEL_ERROR_EPERM; break;
case KernelEventFlagPrivate::Result::TimedOut: ret = KERNEL_ERROR_ETIMEDOUT; break;
case KernelEventFlagPrivate::Result::Canceled: ret = KERNEL_ERROR_ECANCELED; break;
case KernelEventFlagPrivate::Result::Deleted: ret = KERNEL_ERROR_EACCES; break;
}
return ret;
}
int KYTY_SYSV_ABI KernelPollEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode,
uint64_t* result_pat) {
PRINT_NAME();
if (ef == nullptr) {
return KERNEL_ERROR_ESRCH;
}
if (bit_pattern == 0) {
return KERNEL_ERROR_EINVAL;
}
const auto mode = DecodeEventFlagWaitMode(wait_mode);
auto result = ef->Poll(bit_pattern, mode.wait, mode.clear, result_pat);
int ret = OK;
switch (result) {
case KernelEventFlagPrivate::Result::Ok: ret = OK; break;
case KernelEventFlagPrivate::Result::AlreadyWaiting: ret = KERNEL_ERROR_EPERM; break;
case KernelEventFlagPrivate::Result::TimedOut:
case KernelEventFlagPrivate::Result::Canceled:
case KernelEventFlagPrivate::Result::Deleted: ret = KERNEL_ERROR_EBUSY; break;
}
return ret;
}
int KYTY_SYSV_ABI KernelSetEventFlag(KernelEventFlag ef, uint64_t bit_pattern) {
PRINT_NAME();
if (ef == nullptr) {
return KERNEL_ERROR_ESRCH;
}
ef->Set(bit_pattern);
return OK;
}
int KYTY_SYSV_ABI KernelClearEventFlag(KernelEventFlag ef, uint64_t bit_pattern) {
PRINT_NAME();
if (ef == nullptr) {
return KERNEL_ERROR_ESRCH;
}
ef->Clear(bit_pattern);
return OK;
}
int KYTY_SYSV_ABI KernelCancelEventFlag(KernelEventFlag ef, uint64_t set_pattern,
int* num_wait_threads) {
PRINT_NAME();
if (ef == nullptr) {
return KERNEL_ERROR_ESRCH;
}
ef->Cancel(set_pattern, num_wait_threads);
return OK;
}
} // namespace Libs::LibKernel::EventFlag