mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-21 11:26:56 +02:00
Initial commit
This commit is contained in:
@@ -0,0 +1,380 @@
|
||||
#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, µs);
|
||||
}
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user