#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(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