mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-20 19:07:01 +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
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTFLAG_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTFLAG_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "kernel/pthread.h"
|
||||
|
||||
namespace Libs::LibKernel::EventFlag {
|
||||
|
||||
class KernelEventFlagPrivate;
|
||||
|
||||
using KernelEventFlag = KernelEventFlagPrivate*;
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateEventFlag(KernelEventFlag* ef, const char* name, uint32_t attr,
|
||||
uint64_t init_pattern, const void* param);
|
||||
int KYTY_SYSV_ABI KernelDeleteEventFlag(KernelEventFlag ef);
|
||||
int KYTY_SYSV_ABI KernelWaitEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode,
|
||||
uint64_t* result_pat, KernelUseconds* timeout);
|
||||
int KYTY_SYSV_ABI KernelPollEventFlag(KernelEventFlag ef, uint64_t bit_pattern, uint32_t wait_mode,
|
||||
uint64_t* result_pat);
|
||||
int KYTY_SYSV_ABI KernelSetEventFlag(KernelEventFlag ef, uint64_t bit_pattern);
|
||||
int KYTY_SYSV_ABI KernelClearEventFlag(KernelEventFlag ef, uint64_t bit_pattern);
|
||||
int KYTY_SYSV_ABI KernelCancelEventFlag(KernelEventFlag ef, uint64_t set_pattern,
|
||||
int* num_wait_threads);
|
||||
|
||||
} // namespace Libs::LibKernel::EventFlag
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTFLAG_H_ */
|
||||
@@ -0,0 +1,621 @@
|
||||
#include "kernel/eventQueue.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"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <fmt/format.h>
|
||||
#include <list>
|
||||
|
||||
namespace Libs::LibKernel::EventQueue {
|
||||
|
||||
LIB_NAME("libkernel", "libkernel");
|
||||
|
||||
constexpr uint16_t EV_ADD = 0x01;
|
||||
constexpr uint16_t EV_ONESHOT = 0x10;
|
||||
constexpr uint16_t EV_CLEAR = 0x20;
|
||||
constexpr uint16_t EV_ERROR = 0x4000;
|
||||
|
||||
static uint64_t MonotonicTimeNs() {
|
||||
return static_cast<uint64_t>(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::steady_clock::now().time_since_epoch())
|
||||
.count());
|
||||
}
|
||||
|
||||
static std::list<KernelEqueue> g_equeues;
|
||||
static Common::Mutex g_equeues_mutex;
|
||||
|
||||
class KernelEqueuePrivate {
|
||||
public:
|
||||
KernelEqueuePrivate() = default;
|
||||
virtual ~KernelEqueuePrivate();
|
||||
|
||||
KYTY_CLASS_NO_COPY(KernelEqueuePrivate);
|
||||
|
||||
[[nodiscard]] const std::string& GetName() const { return m_name; }
|
||||
void SetName(const std::string& m_name) { this->m_name = m_name; }
|
||||
|
||||
void AddEvent(const KernelEqueueEvent& event);
|
||||
bool TriggerEvent(uintptr_t ident, int16_t filter, void* trigger_data);
|
||||
bool DeleteEvent(uintptr_t ident, int16_t filter);
|
||||
|
||||
int GetTriggeredEvents(KernelEvent* ev, int num);
|
||||
int WaitForEvents(KernelEvent* ev, int num, uint32_t micros);
|
||||
|
||||
private:
|
||||
void TriggerExpiredTimers(uint64_t now_ns);
|
||||
bool GetNextTimerWaitMicros(uint64_t now_ns, uint32_t* wait_micros) const;
|
||||
|
||||
std::list<KernelEqueueEvent> m_events;
|
||||
Common::Mutex m_mutex;
|
||||
Common::CondVar m_cond_var;
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
KernelEqueuePrivate::~KernelEqueuePrivate() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
for (auto& event: m_events) {
|
||||
if (event.filter.delete_event_func != nullptr) {
|
||||
event.filter.delete_event_func(this, &event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int KernelEqueuePrivate::GetTriggeredEvents(KernelEvent* ev, int num) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_IF(num < 1);
|
||||
|
||||
TriggerExpiredTimers(MonotonicTimeNs());
|
||||
|
||||
int ret = 0;
|
||||
|
||||
for (auto it = m_events.begin(); it != m_events.end();) {
|
||||
auto& event = *it;
|
||||
bool erase = false;
|
||||
while (event.triggered) {
|
||||
ev[ret++] = event.event;
|
||||
if ((event.event.flags & EV_ONESHOT) != 0) {
|
||||
erase = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (event.filter.reset_func != nullptr) {
|
||||
event.filter.reset_func(&event);
|
||||
} else if ((event.event.flags & EV_CLEAR) != 0) {
|
||||
event.triggered = false;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
}
|
||||
|
||||
if (!event.pending_events.empty()) {
|
||||
event.event = event.pending_events.front();
|
||||
event.pending_events.pop_front();
|
||||
event.triggered = true;
|
||||
}
|
||||
|
||||
if (ret >= num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
it = (erase ? m_events.erase(it) : std::next(it));
|
||||
if (ret >= num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void KernelEqueuePrivate::TriggerExpiredTimers(uint64_t now_ns) {
|
||||
for (auto& event: m_events) {
|
||||
if (!event.triggered && event.deadline_ns != 0 && event.deadline_ns <= now_ns) {
|
||||
event.triggered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KernelEqueuePrivate::GetNextTimerWaitMicros(uint64_t now_ns, uint32_t* wait_micros) const {
|
||||
EXIT_IF(wait_micros == nullptr);
|
||||
|
||||
uint64_t nearest_deadline = UINT64_MAX;
|
||||
for (const auto& event: m_events) {
|
||||
if (!event.triggered && event.deadline_ns != 0) {
|
||||
nearest_deadline = std::min(nearest_deadline, event.deadline_ns);
|
||||
}
|
||||
}
|
||||
if (nearest_deadline == UINT64_MAX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto remaining_ns = nearest_deadline > now_ns ? nearest_deadline - now_ns : 0;
|
||||
const auto rounded_us = std::max<uint64_t>(1, (remaining_ns + 999u) / 1000u);
|
||||
*wait_micros = static_cast<uint32_t>(std::min<uint64_t>(rounded_us, UINT32_MAX));
|
||||
return true;
|
||||
}
|
||||
|
||||
int KernelEqueuePrivate::WaitForEvents(KernelEvent* ev, int num, uint32_t micros) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
EXIT_IF(num < 1);
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
Common::Timer t;
|
||||
t.Start();
|
||||
|
||||
for (;;) {
|
||||
int ret = GetTriggeredEvents(ev, num);
|
||||
|
||||
if (ret > 0 || (elapsed >= micros && micros != 0)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t timer_wait = 0;
|
||||
const bool has_timer = GetNextTimerWaitMicros(MonotonicTimeNs(), &timer_wait);
|
||||
if (micros == 0 && !has_timer) {
|
||||
m_cond_var.Wait(&m_mutex);
|
||||
} else {
|
||||
const auto external_wait = micros != 0 ? micros - elapsed : UINT32_MAX;
|
||||
m_cond_var.WaitFor(&m_mutex,
|
||||
has_timer ? std::min(external_wait, timer_wait) : external_wait);
|
||||
}
|
||||
|
||||
elapsed = static_cast<uint32_t>(t.GetTimeS() * 1000000.0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KernelEqueuePrivate::AddEvent(const KernelEqueueEvent& event) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
auto it = std::find_if(m_events.begin(), m_events.end(),
|
||||
[ident = event.event.ident, filter = event.event.filter](const auto& e) {
|
||||
return e.event.ident == ident && e.event.filter == filter;
|
||||
});
|
||||
if (it != m_events.end()) {
|
||||
*it = event;
|
||||
} else {
|
||||
m_events.push_back(event);
|
||||
}
|
||||
|
||||
m_cond_var.Signal();
|
||||
}
|
||||
|
||||
bool KernelEqueuePrivate::TriggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
auto it = std::find_if(m_events.begin(), m_events.end(), [ident, filter](const auto& e) {
|
||||
return e.event.ident == ident && e.event.filter == filter;
|
||||
});
|
||||
if (it != m_events.end()) {
|
||||
auto& event = *it;
|
||||
|
||||
if (event.filter.trigger_func != nullptr) {
|
||||
event.filter.trigger_func(&event, trigger_data);
|
||||
} else {
|
||||
event.triggered = true;
|
||||
}
|
||||
|
||||
m_cond_var.Signal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void UserEventTriggerFunc(KernelEqueueEvent* event, void* trigger_data) {
|
||||
EXIT_IF(event == nullptr);
|
||||
event->triggered = true;
|
||||
event->event.data = reinterpret_cast<intptr_t>(trigger_data);
|
||||
event->event.udata = trigger_data;
|
||||
}
|
||||
|
||||
static void AmprEventTriggerFunc(KernelEqueueEvent* event, void* trigger_data) {
|
||||
EXIT_IF(event == nullptr);
|
||||
auto triggered_event = event->event;
|
||||
triggered_event.data = static_cast<intptr_t>(reinterpret_cast<uintptr_t>(trigger_data));
|
||||
if (event->triggered) {
|
||||
event->pending_events.push_back(triggered_event);
|
||||
} else {
|
||||
event->event = triggered_event;
|
||||
event->triggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void UserEventResetFunc(KernelEqueueEvent* event) {
|
||||
EXIT_IF(event == nullptr);
|
||||
if ((event->event.flags & EV_CLEAR) != 0) {
|
||||
event->triggered = false;
|
||||
event->event.fflags = 0;
|
||||
event->event.data = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool KernelEqueuePrivate::DeleteEvent(uintptr_t ident, int16_t filter) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
auto it = std::find_if(m_events.begin(), m_events.end(), [ident, filter](const auto& e) {
|
||||
return e.event.ident == ident && e.event.filter == filter;
|
||||
});
|
||||
if (it != m_events.end()) {
|
||||
auto& event = *it;
|
||||
|
||||
if (event.filter.delete_event_func != nullptr) {
|
||||
event.filter.delete_event_func(this, &event);
|
||||
}
|
||||
|
||||
m_events.erase(it);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateEqueue(KernelEqueue* eq, const char* name) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (eq == nullptr || name == nullptr) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
*eq = new KernelEqueuePrivate;
|
||||
|
||||
(*eq)->SetName(std::string(name));
|
||||
|
||||
{
|
||||
Common::LockGuard lock(g_equeues_mutex);
|
||||
g_equeues.push_back(*eq);
|
||||
}
|
||||
|
||||
LOGF("\tEqueue create: %s\n", name);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddEvent(KernelEqueue eq, const KernelEqueueEvent& event) {
|
||||
if (eq == nullptr) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
eq->AddEvent(event);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelTriggerEvent(KernelEqueue eq, uintptr_t ident, int16_t filter,
|
||||
void* trigger_data) {
|
||||
if (eq == nullptr) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!eq->TriggerEvent(ident, filter, trigger_data)) {
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteEvent(KernelEqueue eq, uintptr_t ident, int16_t filter) {
|
||||
if (eq == nullptr) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (!eq->DeleteEvent(ident, filter)) {
|
||||
return KERNEL_ERROR_ENOENT;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteEqueue(KernelEqueue eq) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (eq == nullptr) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
LOGF("\tEqueue delete: %s\n", eq->GetName().c_str());
|
||||
|
||||
{
|
||||
Common::LockGuard lock(g_equeues_mutex);
|
||||
g_equeues.remove(eq);
|
||||
}
|
||||
|
||||
delete eq;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelWaitEqueue(KernelEqueue eq, KernelEvent* ev, int num, int* out,
|
||||
const KernelUseconds* timo) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (eq == nullptr) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
if (ev == nullptr) {
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
|
||||
if (num < 1) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(out == nullptr);
|
||||
|
||||
LOGF("\tEqueue wait: %s, caller = 0x%016" PRIx64 ", eq = 0x%016" PRIx64 ", ev = 0x%016" PRIx64
|
||||
", num = %d, timo = %s, thread_id = %d\n",
|
||||
eq->GetName().c_str(), reinterpret_cast<uint64_t>(__builtin_return_address(0)),
|
||||
reinterpret_cast<uint64_t>(eq), reinterpret_cast<uint64_t>(ev), num,
|
||||
(timo == nullptr ? "inf" : fmt::format("{}", *timo).c_str()),
|
||||
Common::Thread::GetThreadIdUnique());
|
||||
|
||||
if (timo == nullptr) {
|
||||
*out = eq->WaitForEvents(ev, num, 0);
|
||||
}
|
||||
|
||||
if (timo != nullptr) {
|
||||
if (*timo == 0) {
|
||||
*out = eq->GetTriggeredEvents(ev, num);
|
||||
} else {
|
||||
*out = eq->WaitForEvents(ev, num, *timo);
|
||||
}
|
||||
}
|
||||
|
||||
if (*out == 0) {
|
||||
LOGF("\tEqueue wait timedout: %s\n", eq->GetName().c_str());
|
||||
return KERNEL_ERROR_ETIMEDOUT;
|
||||
}
|
||||
|
||||
LOGF("\tEqueue wait received %u events: ident = 0x%016" PRIx64
|
||||
", filter = %d, flags = 0x%04" PRIx16 ", fflags = 0x%08" PRIx32 ", data = 0x%016" PRIx64
|
||||
", udata = 0x%016" PRIx64 "\n",
|
||||
*out, static_cast<uint64_t>(ev[0].ident), ev[0].filter, ev[0].flags, ev[0].fflags,
|
||||
static_cast<uint64_t>(ev[0].data), reinterpret_cast<uint64_t>(ev[0].udata));
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddUserEvent(KernelEqueue eq, int id) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t user event add: eq = 0x%016" PRIx64 ", id = %d\n", reinterpret_cast<uint64_t>(eq), id);
|
||||
|
||||
if (eq == nullptr) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
KernelEqueueEvent event {};
|
||||
event.event.ident = static_cast<uintptr_t>(id);
|
||||
event.event.filter = KERNEL_EVFILT_USER;
|
||||
event.event.flags = EV_ADD;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
event.event.udata = nullptr;
|
||||
event.filter.trigger_func = UserEventTriggerFunc;
|
||||
event.filter.reset_func = UserEventResetFunc;
|
||||
|
||||
eq->AddEvent(event);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddUserEventEdge(KernelEqueue eq, int id) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t user event edge add: eq = 0x%016" PRIx64 ", id = %d\n", reinterpret_cast<uint64_t>(eq),
|
||||
id);
|
||||
|
||||
if (eq == nullptr) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
|
||||
KernelEqueueEvent event {};
|
||||
event.event.ident = static_cast<uintptr_t>(id);
|
||||
event.event.filter = KERNEL_EVFILT_USER;
|
||||
event.event.flags = EV_ADD | EV_CLEAR;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
event.event.udata = nullptr;
|
||||
event.filter.trigger_func = UserEventTriggerFunc;
|
||||
event.filter.reset_func = UserEventResetFunc;
|
||||
|
||||
eq->AddEvent(event);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelTriggerUserEvent(KernelEqueue eq, int id, void* udata) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t user event trigger: eq = 0x%016" PRIx64 ", id = %d, udata = 0x%016" PRIx64 "\n",
|
||||
reinterpret_cast<uint64_t>(eq), id, reinterpret_cast<uint64_t>(udata));
|
||||
|
||||
return KernelTriggerEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_USER, udata);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelTriggerUserEventForAll(int id, void* udata) {
|
||||
int triggered = 0;
|
||||
|
||||
Common::LockGuard lock(g_equeues_mutex);
|
||||
|
||||
for (auto* eq: g_equeues) {
|
||||
if (eq != nullptr &&
|
||||
eq->TriggerEvent(static_cast<uintptr_t>(id), KERNEL_EVFILT_USER, udata)) {
|
||||
triggered++;
|
||||
}
|
||||
}
|
||||
|
||||
return (triggered > 0 ? OK : KERNEL_ERROR_ENOENT);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteUserEvent(KernelEqueue eq, int id) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t user event delete: eq = 0x%016" PRIx64 ", id = %d\n", reinterpret_cast<uint64_t>(eq),
|
||||
id);
|
||||
|
||||
return KernelDeleteEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_USER);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTimespec* ts,
|
||||
void* udata) {
|
||||
if (eq == nullptr) {
|
||||
return KERNEL_ERROR_EBADF;
|
||||
}
|
||||
if (ts == nullptr) {
|
||||
return KERNEL_ERROR_EFAULT;
|
||||
}
|
||||
if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000ll ||
|
||||
static_cast<uint64_t>(ts->tv_sec) >
|
||||
(UINT64_MAX - static_cast<uint64_t>(ts->tv_nsec)) / 1000000000ull) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
const auto delay_ns =
|
||||
static_cast<uint64_t>(ts->tv_sec) * 1000000000ull + static_cast<uint64_t>(ts->tv_nsec);
|
||||
const auto now_ns = MonotonicTimeNs();
|
||||
|
||||
KernelEqueueEvent event {};
|
||||
event.deadline_ns = delay_ns <= UINT64_MAX - now_ns ? now_ns + delay_ns : UINT64_MAX;
|
||||
event.event.ident = static_cast<uintptr_t>(id);
|
||||
event.event.filter = KERNEL_EVFILT_HRTIMER;
|
||||
event.event.flags = EV_ADD | EV_ONESHOT;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
event.event.udata = udata;
|
||||
eq->AddEvent(event);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteHRTimerEvent(KernelEqueue eq, int id) {
|
||||
return KernelDeleteEvent(eq, static_cast<uintptr_t>(id), KERNEL_EVFILT_HRTIMER);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddAmprEvent(KernelEqueue eq, int id, void* udata) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t AMPR event add: eq = 0x%016" PRIx64 ", id = %d, udata = 0x%016" PRIx64 "\n",
|
||||
reinterpret_cast<uint64_t>(eq), id, reinterpret_cast<uint64_t>(udata));
|
||||
|
||||
if (eq != nullptr) {
|
||||
KernelEqueueEvent event {};
|
||||
event.event.ident = static_cast<uintptr_t>(id);
|
||||
event.event.filter = KERNEL_EVFILT_USER;
|
||||
event.event.flags = EV_ADD | EV_CLEAR;
|
||||
event.event.fflags = 0;
|
||||
event.event.data = 0;
|
||||
event.event.udata = udata;
|
||||
event.filter.trigger_func = AmprEventTriggerFunc;
|
||||
event.filter.reset_func = UserEventResetFunc;
|
||||
eq->AddEvent(event);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddAmprSystemEvent(KernelEqueue eq, int id, void* udata) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t AMPR system event add: eq = 0x%016" PRIx64 ", id = %d, udata = 0x%016" PRIx64 "\n",
|
||||
reinterpret_cast<uint64_t>(eq), id, reinterpret_cast<uint64_t>(udata));
|
||||
|
||||
return KernelAddAmprEvent(eq, id, udata);
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteAmprEvent(KernelEqueue eq, int id) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t AMPR event delete: eq = 0x%016" PRIx64 ", id = %d\n", reinterpret_cast<uint64_t>(eq),
|
||||
id);
|
||||
|
||||
if (eq != nullptr) {
|
||||
eq->DeleteEvent(static_cast<uintptr_t>(id), KERNEL_EVFILT_USER);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteAmprSystemEvent(KernelEqueue eq, int id) {
|
||||
PRINT_NAME();
|
||||
|
||||
LOGF("\t AMPR system event delete: eq = 0x%016" PRIx64 ", id = %d\n",
|
||||
reinterpret_cast<uint64_t>(eq), id);
|
||||
|
||||
return KernelDeleteAmprEvent(eq, id);
|
||||
}
|
||||
|
||||
intptr_t KYTY_SYSV_ABI KernelGetEventData(const KernelEvent* ev) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr) {
|
||||
return ev->data;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
intptr_t KYTY_SYSV_ABI KernelGetEventFflags(const KernelEvent* ev) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr) {
|
||||
return ev->fflags;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelGetEventFilter(const KernelEvent* ev) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr) {
|
||||
return ev->filter;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uintptr_t KYTY_SYSV_ABI KernelGetEventId(const KernelEvent* ev) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr) {
|
||||
return ev->ident;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* KYTY_SYSV_ABI KernelGetEventUserData(const KernelEvent* ev) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr) {
|
||||
return ev->udata;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelGetEventError(const KernelEvent* ev) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (ev != nullptr && (ev->flags & EV_ERROR) != 0) {
|
||||
return static_cast<int>(ev->data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Libs::LibKernel::EventQueue
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTQUEUE_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTQUEUE_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "kernel/pthread.h"
|
||||
|
||||
#include <deque>
|
||||
|
||||
namespace Libs::LibKernel::EventQueue {
|
||||
|
||||
constexpr int16_t KERNEL_EVFILT_TIMER = -7;
|
||||
constexpr int16_t KERNEL_EVFILT_READ = -1;
|
||||
constexpr int16_t KERNEL_EVFILT_WRITE = -2;
|
||||
constexpr int16_t KERNEL_EVFILT_USER = -11;
|
||||
constexpr int16_t KERNEL_EVFILT_FILE = -4;
|
||||
constexpr int16_t KERNEL_EVFILT_GRAPHICS = -14;
|
||||
constexpr int16_t KERNEL_EVFILT_VIDEO_OUT = -13;
|
||||
constexpr int16_t KERNEL_EVFILT_HRTIMER = -15;
|
||||
|
||||
class KernelEqueuePrivate;
|
||||
struct KernelEqueueEvent;
|
||||
|
||||
using KernelEqueue = KernelEqueuePrivate*;
|
||||
|
||||
using trigger_func_t = void (*)(KernelEqueueEvent* event, void* trigger_data);
|
||||
using reset_func_t = void (*)(KernelEqueueEvent* event);
|
||||
using delete_func_t = void (*)(KernelEqueue eq, KernelEqueueEvent* event);
|
||||
|
||||
struct KernelEvent {
|
||||
uintptr_t ident = 0;
|
||||
int16_t filter = 0;
|
||||
uint16_t flags = 0;
|
||||
uint32_t fflags = 0;
|
||||
intptr_t data = 0;
|
||||
void* udata = nullptr;
|
||||
};
|
||||
|
||||
struct KernelFilter {
|
||||
void* data = nullptr;
|
||||
trigger_func_t trigger_func = nullptr;
|
||||
reset_func_t reset_func = nullptr;
|
||||
delete_func_t delete_event_func = nullptr;
|
||||
};
|
||||
|
||||
struct KernelEqueueEvent {
|
||||
bool triggered = false;
|
||||
uint64_t deadline_ns = 0;
|
||||
KernelEvent event;
|
||||
KernelFilter filter;
|
||||
std::deque<KernelEvent> pending_events;
|
||||
};
|
||||
|
||||
int KYTY_SYSV_ABI KernelAddEvent(KernelEqueue eq, const KernelEqueueEvent& event);
|
||||
int KYTY_SYSV_ABI KernelTriggerEvent(KernelEqueue eq, uintptr_t ident, int16_t filter,
|
||||
void* trigger_data);
|
||||
int KYTY_SYSV_ABI KernelDeleteEvent(KernelEqueue eq, uintptr_t ident, int16_t filter);
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateEqueue(KernelEqueue* eq, const char* name);
|
||||
int KYTY_SYSV_ABI KernelDeleteEqueue(KernelEqueue eq);
|
||||
int KYTY_SYSV_ABI KernelWaitEqueue(KernelEqueue eq, KernelEvent* ev, int num, int* out,
|
||||
const KernelUseconds* timo);
|
||||
int KYTY_SYSV_ABI KernelAddUserEvent(KernelEqueue eq, int id);
|
||||
int KYTY_SYSV_ABI KernelAddUserEventEdge(KernelEqueue eq, int id);
|
||||
int KYTY_SYSV_ABI KernelTriggerUserEvent(KernelEqueue eq, int id, void* udata);
|
||||
int KYTY_SYSV_ABI KernelTriggerUserEventForAll(int id, void* udata);
|
||||
int KYTY_SYSV_ABI KernelDeleteUserEvent(KernelEqueue eq, int id);
|
||||
int KYTY_SYSV_ABI KernelAddHRTimerEvent(KernelEqueue eq, int id, const KernelTimespec* ts,
|
||||
void* udata);
|
||||
int KYTY_SYSV_ABI KernelDeleteHRTimerEvent(KernelEqueue eq, int id);
|
||||
int KYTY_SYSV_ABI KernelAddAmprEvent(KernelEqueue eq, int id, void* udata);
|
||||
int KYTY_SYSV_ABI KernelAddAmprSystemEvent(KernelEqueue eq, int id, void* udata);
|
||||
int KYTY_SYSV_ABI KernelDeleteAmprEvent(KernelEqueue eq, int id);
|
||||
int KYTY_SYSV_ABI KernelDeleteAmprSystemEvent(KernelEqueue eq, int id);
|
||||
|
||||
intptr_t KYTY_SYSV_ABI KernelGetEventData(const KernelEvent* ev);
|
||||
intptr_t KYTY_SYSV_ABI KernelGetEventFflags(const KernelEvent* ev);
|
||||
int KYTY_SYSV_ABI KernelGetEventFilter(const KernelEvent* ev);
|
||||
uintptr_t KYTY_SYSV_ABI KernelGetEventId(const KernelEvent* ev);
|
||||
void* KYTY_SYSV_ABI KernelGetEventUserData(const KernelEvent* ev);
|
||||
int KYTY_SYSV_ABI KernelGetEventError(const KernelEvent* ev);
|
||||
|
||||
} // namespace Libs::LibKernel::EventQueue
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_EVENTQUEUE_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_FILESYSTEM_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_FILESYSTEM_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "kernel/pthread.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace Libs::LibKernel::FileSystem {
|
||||
|
||||
struct FileStat {
|
||||
uint32_t st_dev;
|
||||
uint32_t st_ino;
|
||||
uint16_t st_mode;
|
||||
uint16_t st_nlink;
|
||||
uint32_t st_uid;
|
||||
uint32_t st_gid;
|
||||
uint32_t st_rdev;
|
||||
KernelTimespec st_atim;
|
||||
KernelTimespec st_mtim;
|
||||
KernelTimespec st_ctim;
|
||||
int64_t st_size;
|
||||
int64_t st_blocks;
|
||||
uint32_t st_blksize;
|
||||
uint32_t st_flags;
|
||||
uint32_t st_gen;
|
||||
int32_t st_lspare;
|
||||
KernelTimespec st_birthtim;
|
||||
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(KernelTimespec)));
|
||||
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(KernelTimespec)));
|
||||
};
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(FileSystem);
|
||||
|
||||
void Mount(const std::filesystem::path& folder, const std::string& point);
|
||||
void Umount(const std::string& folder_or_point);
|
||||
std::filesystem::path GetRealFilename(const std::string& mounted_file_name);
|
||||
|
||||
int KYTY_SYSV_ABI KernelOpen(const char* path, int flags, uint16_t mode);
|
||||
int KYTY_SYSV_ABI KernelClose(int d);
|
||||
int64_t KYTY_SYSV_ABI KernelRead(int d, void* buf, size_t nbytes);
|
||||
int64_t KYTY_SYSV_ABI KernelPread(int d, void* buf, size_t nbytes, int64_t offset);
|
||||
int64_t KYTY_SYSV_ABI KernelWrite(int d, const void* buf, size_t nbytes);
|
||||
int64_t KYTY_SYSV_ABI KernelPwrite(int d, const void* buf, size_t nbytes, int64_t offset);
|
||||
int64_t KYTY_SYSV_ABI KernelLseek(int d, int64_t offset, int whence);
|
||||
int KYTY_SYSV_ABI KernelStat(const char* path, FileStat* sb);
|
||||
int KYTY_SYSV_ABI KernelFstat(int d, FileStat* sb);
|
||||
int KYTY_SYSV_ABI KernelUnlink(const char* path);
|
||||
int KYTY_SYSV_ABI KernelRename(const char* from, const char* to);
|
||||
int KYTY_SYSV_ABI KernelGetdirentries(int fd, char* buf, int nbytes, int64_t* basep);
|
||||
int KYTY_SYSV_ABI KernelGetdents(int fd, char* buf, int nbytes);
|
||||
int KYTY_SYSV_ABI KernelMkdir(const char* path, uint16_t mode);
|
||||
int KYTY_SYSV_ABI KernelRmdir(const char* path);
|
||||
int KYTY_SYSV_ABI KernelCheckReachability(const char* path);
|
||||
|
||||
} // namespace Libs::LibKernel::FileSystem
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_FILESYSTEM_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,164 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_MEMORY_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_MEMORY_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
#include "common/virtualMemory.h"
|
||||
|
||||
namespace Libs::LibKernel::Memory {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Memory);
|
||||
|
||||
using callback_func_t = void (*)(uintptr_t addr, size_t size);
|
||||
|
||||
constexpr uint32_t KERNEL_MAXIMUM_NAME_LENGTH = 32;
|
||||
|
||||
struct VirtualQueryInfo {
|
||||
uintptr_t start;
|
||||
uintptr_t end;
|
||||
uint64_t offset;
|
||||
int32_t protection;
|
||||
int32_t memory_type;
|
||||
uint32_t is_flexible : 1;
|
||||
uint32_t is_direct : 1;
|
||||
uint32_t is_stack : 1;
|
||||
uint32_t is_pooled : 1;
|
||||
uint32_t is_committed : 1;
|
||||
uint32_t is_gpu_prt : 1;
|
||||
uint32_t amm_usage : 1;
|
||||
uint32_t reserved : 1;
|
||||
char name[KERNEL_MAXIMUM_NAME_LENGTH];
|
||||
uint8_t gpu_mask_id;
|
||||
uint8_t reserved2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VirtualQueryInfo) == 72, "VirtualQueryInfo struct size is incorrect");
|
||||
|
||||
struct KernelBatchMapEntry {
|
||||
void* start;
|
||||
uint64_t offset;
|
||||
uint64_t length;
|
||||
char protection;
|
||||
char type;
|
||||
int16_t reserved;
|
||||
int32_t operation;
|
||||
};
|
||||
|
||||
static_assert(sizeof(KernelBatchMapEntry) == 32, "KernelBatchMapEntry struct size is incorrect");
|
||||
|
||||
struct KernelMemoryPoolBatchEntry {
|
||||
uint32_t op;
|
||||
uint32_t flags;
|
||||
union {
|
||||
struct {
|
||||
void* addr;
|
||||
uint64_t len;
|
||||
uint8_t prot;
|
||||
uint8_t type;
|
||||
} commit;
|
||||
struct {
|
||||
void* addr;
|
||||
uint64_t len;
|
||||
} decommit;
|
||||
struct {
|
||||
void* addr;
|
||||
uint64_t len;
|
||||
uint8_t prot;
|
||||
} protect;
|
||||
struct {
|
||||
void* addr;
|
||||
uint64_t len;
|
||||
uint8_t prot;
|
||||
uint8_t type;
|
||||
} type_protect;
|
||||
struct {
|
||||
void* dst;
|
||||
void* src;
|
||||
uint64_t len;
|
||||
} move;
|
||||
uintptr_t padding[3];
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(KernelMemoryPoolBatchEntry) == 32,
|
||||
"KernelMemoryPoolBatchEntry struct size is incorrect");
|
||||
|
||||
struct KernelMemoryPoolBlockStats {
|
||||
int32_t available_flushed_blocks;
|
||||
int32_t available_cached_blocks;
|
||||
int32_t allocated_flushed_blocks;
|
||||
int32_t allocated_cached_blocks;
|
||||
};
|
||||
|
||||
static_assert(sizeof(KernelMemoryPoolBlockStats) == 16,
|
||||
"KernelMemoryPoolBlockStats struct size is incorrect");
|
||||
|
||||
void RegisterCallbacks(callback_func_t alloc_func, callback_func_t free_func);
|
||||
void SetFlexibleMemorySize(uint64_t size);
|
||||
bool TryWriteBacking(uint64_t vaddr, const void* data, uint64_t size);
|
||||
bool TryReadBacking(uint64_t vaddr, void* data, uint64_t size);
|
||||
void WriteBacking(uint64_t vaddr, const void* data, uint64_t size) noexcept;
|
||||
|
||||
int KYTY_SYSV_ABI KernelMapNamedFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags,
|
||||
const char* name);
|
||||
int KYTY_SYSV_ABI KernelMapFlexibleMemory(void** addr_in_out, size_t len, int prot, int flags);
|
||||
int KYTY_SYSV_ABI KernelSetVirtualRangeName(const void* addr, uint64_t len, const char* name);
|
||||
int KYTY_SYSV_ABI KernelMunmap(uint64_t vaddr, size_t len);
|
||||
size_t KYTY_SYSV_ABI KernelGetDirectMemorySize();
|
||||
int KYTY_SYSV_ABI KernelAvailableDirectMemorySize(int64_t search_start, int64_t search_end,
|
||||
size_t alignment, int64_t* phys_addr_out,
|
||||
size_t* size_out);
|
||||
int KYTY_SYSV_ABI KernelGetPageTableStats(int* cpu_total, int* cpu_available, int* gpu_total,
|
||||
int* gpu_available);
|
||||
int KYTY_SYSV_ABI KernelAllocateDirectMemory(int64_t search_start, int64_t search_end, size_t len,
|
||||
size_t alignment, int memory_type,
|
||||
int64_t* phys_addr_out);
|
||||
int KYTY_SYSV_ABI KernelAllocateMainDirectMemory(size_t len, size_t alignment, int memory_type,
|
||||
int64_t* phys_addr_out);
|
||||
int KYTY_SYSV_ABI KernelCheckedReleaseDirectMemory(int64_t start, size_t len);
|
||||
int KYTY_SYSV_ABI KernelReleaseDirectMemory(int64_t start, size_t len);
|
||||
int KYTY_SYSV_ABI KernelMapDirectMemory(void** addr, size_t len, int prot, int flags,
|
||||
int64_t direct_memory_start, size_t alignment);
|
||||
int KYTY_SYSV_ABI KernelMapDirectMemory2(void** addr, size_t len, int type, int prot, int flags,
|
||||
int64_t direct_memory_start, size_t alignment);
|
||||
int KYTY_SYSV_ABI KernelMapNamedDirectMemory(void** addr, size_t len, int prot, int flags,
|
||||
int64_t direct_memory_start, size_t alignment,
|
||||
const char* name);
|
||||
int KYTY_SYSV_ABI KernelSetPrtAperture(int index, void* addr, size_t len);
|
||||
int KYTY_SYSV_ABI KernelGetPrtAperture(int index, void** addr, size_t* len);
|
||||
int KYTY_SYSV_ABI KernelIsAddressSanitizerEnabled();
|
||||
int KYTY_SYSV_ABI KernelQueryMemoryProtection(void* addr, void** start, void** end, int* prot);
|
||||
int KYTY_SYSV_ABI KernelDirectMemoryQuery(int64_t offset, int flags, void* info, size_t info_size);
|
||||
int KYTY_SYSV_ABI KernelVirtualQuery(const void* addr, int flags, VirtualQueryInfo* info,
|
||||
uint64_t info_size);
|
||||
int KYTY_SYSV_ABI KernelIsStack(void* addr, void** start, void** end);
|
||||
int KYTY_SYSV_ABI KernelReserveVirtualRange(void** addr, size_t len, int flags, size_t alignment);
|
||||
bool KernelHandleReservedRangeAccessViolation(uint64_t vaddr);
|
||||
int KYTY_SYSV_ABI KernelAvailableFlexibleMemorySize(size_t* size);
|
||||
int KYTY_SYSV_ABI KernelConfiguredFlexibleMemorySize(uint64_t* size);
|
||||
int KYTY_SYSV_ABI KernelMprotect(const void* addr, size_t len, int prot);
|
||||
int KYTY_SYSV_ABI KernelMtypeprotect(const void* addr, size_t len, int type, int prot);
|
||||
int KYTY_SYSV_ABI KernelBatchMap(KernelBatchMapEntry* entries, int num_entries,
|
||||
int* num_entries_out);
|
||||
int KYTY_SYSV_ABI KernelBatchMap2(KernelBatchMapEntry* entries, int num_entries,
|
||||
int* num_entries_out, int flags);
|
||||
int KYTY_SYSV_ABI KernelMemoryPoolExpand(int64_t search_start, int64_t search_end, size_t len,
|
||||
size_t alignment, int64_t* phys_addr_out);
|
||||
int KYTY_SYSV_ABI KernelMemoryPoolReserve(void* addr_in, size_t len, size_t alignment, int flags,
|
||||
void** addr_out);
|
||||
int KYTY_SYSV_ABI KernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags);
|
||||
int KYTY_SYSV_ABI KernelMemoryPoolDecommit(void* addr, size_t len, int flags);
|
||||
int KYTY_SYSV_ABI KernelMemoryPoolBatch(const KernelMemoryPoolBatchEntry* entries, int num_entries,
|
||||
int* num_entries_out, int flags);
|
||||
int KYTY_SYSV_ABI KernelMemoryPoolGetBlockStats(KernelMemoryPoolBlockStats* output,
|
||||
size_t output_size);
|
||||
|
||||
void RegisterProgramMemory(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode,
|
||||
const char* name);
|
||||
void UpdateProgramMemoryProtection(uint64_t vaddr, uint64_t size, Common::VirtualMemory::Mode mode);
|
||||
void UnregisterProgramMemory(uint64_t vaddr, uint64_t size);
|
||||
|
||||
} // namespace Libs::LibKernel::Memory
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_MEMORY_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,283 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_PTHREAD_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_PTHREAD_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "common/subsystems.h"
|
||||
|
||||
// IWYU pragma: no_include <pthread.h>
|
||||
|
||||
extern "C" {
|
||||
struct sched_param;
|
||||
}
|
||||
|
||||
namespace Loader {
|
||||
struct Program;
|
||||
} // namespace Loader
|
||||
|
||||
namespace Libs {
|
||||
|
||||
namespace LibKernel {
|
||||
|
||||
KYTY_SUBSYSTEM_DEFINE(Pthread);
|
||||
|
||||
struct PthreadAttrPrivate;
|
||||
struct PthreadPrivate;
|
||||
struct PthreadMutexPrivate;
|
||||
struct PthreadMutexattrPrivate;
|
||||
struct PthreadRwlockPrivate;
|
||||
struct PthreadRwlockattrPrivate;
|
||||
struct PthreadCondattrPrivate;
|
||||
struct PthreadCondPrivate;
|
||||
|
||||
struct KernelTimespec {
|
||||
int64_t tv_sec;
|
||||
int64_t tv_nsec;
|
||||
};
|
||||
|
||||
struct KernelTimeval {
|
||||
int64_t tv_sec;
|
||||
int64_t tv_usec;
|
||||
};
|
||||
|
||||
struct KernelTimezone {
|
||||
int32_t tz_minuteswest;
|
||||
int32_t tz_dsttime;
|
||||
};
|
||||
|
||||
struct KernelTimesec {
|
||||
int64_t t;
|
||||
uint32_t west_sec;
|
||||
uint32_t dst_sec;
|
||||
};
|
||||
|
||||
using PthreadAttr = PthreadAttrPrivate*;
|
||||
using Pthread = PthreadPrivate*;
|
||||
using KernelCpumask = uint64_t;
|
||||
using PthreadMutex = PthreadMutexPrivate*;
|
||||
using PthreadMutexattr = PthreadMutexattrPrivate*;
|
||||
using KernelSchedParam = struct sched_param;
|
||||
using PthreadRwlock = PthreadRwlockPrivate*;
|
||||
using PthreadRwlockattr = PthreadRwlockattrPrivate*;
|
||||
using KernelUseconds = unsigned int;
|
||||
using PthreadCondattr = PthreadCondattrPrivate*;
|
||||
using PthreadCond = PthreadCondPrivate*;
|
||||
using KernelClockid = int32_t;
|
||||
using PthreadKey = int;
|
||||
|
||||
using pthread_entry_func_t = KYTY_SYSV_ABI void* (*)(void*);
|
||||
using thread_dtors_func_t = KYTY_SYSV_ABI void (*)();
|
||||
using pthread_key_destructor_func_t = KYTY_SYSV_ABI void (*)(void*);
|
||||
|
||||
void PthreadInitSelfForMainThread();
|
||||
void* PthreadCreateMainGuestStack();
|
||||
void PthreadDeleteStaticObjects(Loader::Program* program);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadMutexattrInit(PthreadMutexattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadMutexattrDestroy(PthreadMutexattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadMutexattrSettype(PthreadMutexattr* attr, int type);
|
||||
int KYTY_SYSV_ABI PthreadMutexattrSetprotocol(PthreadMutexattr* attr, int protocol);
|
||||
int KYTY_SYSV_ABI PthreadMutexInit(PthreadMutex* mutex, const PthreadMutexattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadMutexDestroy(PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI PthreadMutexLock(PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI PthreadMutexTrylock(PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI PthreadMutexTimedlock(PthreadMutex* mutex, KernelUseconds usec);
|
||||
int KYTY_SYSV_ABI PthreadMutexUnlock(PthreadMutex* mutex);
|
||||
|
||||
Pthread KYTY_SYSV_ABI PthreadSelf();
|
||||
Pthread PthreadSelfOrNull();
|
||||
Pthread PthreadSwapSelfForSignal(Pthread thread);
|
||||
int KYTY_SYSV_ABI PthreadCreate(Pthread* thread, const PthreadAttr* attr,
|
||||
pthread_entry_func_t entry, void* arg, const char* name);
|
||||
int KYTY_SYSV_ABI PthreadDetach(Pthread thread);
|
||||
int KYTY_SYSV_ABI PthreadJoin(Pthread thread, void** value);
|
||||
int KYTY_SYSV_ABI PthreadCancel(Pthread thread);
|
||||
int KYTY_SYSV_ABI PthreadSetcancelstate(int state, int* old_state);
|
||||
int KYTY_SYSV_ABI PthreadSetcanceltype(int type, int* old_type);
|
||||
int KYTY_SYSV_ABI PthreadGetprio(Pthread thread, int* prio);
|
||||
int KYTY_SYSV_ABI PthreadSetprio(Pthread thread, int prio);
|
||||
void KYTY_SYSV_ABI PthreadTestcancel();
|
||||
int KYTY_SYSV_ABI PthreadSetaffinity(Pthread thread, KernelCpumask mask);
|
||||
void KYTY_SYSV_ABI PthreadExit(void* value);
|
||||
int KYTY_SYSV_ABI PthreadEqual(Pthread thread1, Pthread thread2);
|
||||
int KYTY_SYSV_ABI PthreadGetname(Pthread thread, char* name);
|
||||
int KYTY_SYSV_ABI PthreadRename(Pthread thread, const char* name);
|
||||
void KYTY_SYSV_ABI PthreadYield();
|
||||
int KYTY_SYSV_ABI PthreadGetthreadid();
|
||||
int KYTY_SYSV_ABI PthreadGetaffinity(Pthread thread, KernelCpumask* mask);
|
||||
int PthreadGetUniqueId(Pthread thread);
|
||||
uint64_t PthreadGetHostThreadId(Pthread thread);
|
||||
void PthreadWakeForSignal(Pthread thread);
|
||||
void PthreadQueuePendingSignal(Pthread thread, int signum);
|
||||
bool PthreadHasPendingSignal(Pthread thread, int signum);
|
||||
bool PthreadTakePendingSignal(Pthread thread, int signum);
|
||||
bool PthreadGetGuestStack(Pthread thread, uint64_t* stack_addr, uint64_t* stack_size);
|
||||
int PthreadGetPriorityForKernel(Pthread thread);
|
||||
int PthreadGetCurrentPriorityForKernel();
|
||||
|
||||
int KYTY_SYSV_ABI KernelUsleep(KernelUseconds microseconds);
|
||||
unsigned int KYTY_SYSV_ABI KernelSleep(unsigned int seconds);
|
||||
int KYTY_SYSV_ABI KernelNanosleep(const KernelTimespec* rqtp, KernelTimespec* rmtp);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadKeyCreate(PthreadKey* key, pthread_key_destructor_func_t destructor);
|
||||
int KYTY_SYSV_ABI PthreadKeyDelete(PthreadKey key);
|
||||
int KYTY_SYSV_ABI PthreadSetspecific(PthreadKey key, void* value);
|
||||
void* KYTY_SYSV_ABI PthreadGetspecific(PthreadKey key);
|
||||
|
||||
void KYTY_SYSV_ABI KernelSetThreadDtors(thread_dtors_func_t dtors);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadAttrInit(PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI PthreadAttrDestroy(PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI PthreadAttrGet(Pthread thread, PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetaffinity(const PthreadAttr* attr, KernelCpumask* mask);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetdetachstate(const PthreadAttr* attr, int* state);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetguardsize(const PthreadAttr* attr, size_t* guard_size);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetinheritsched(const PthreadAttr* attr, int* inherit_sched);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetschedparam(const PthreadAttr* attr, KernelSchedParam* param);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetschedpolicy(const PthreadAttr* attr, int* policy);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetsolosched(const PthreadAttr* attr, int* solosched);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetstack(const PthreadAttr* __restrict attr,
|
||||
void** __restrict stack_addr, size_t* __restrict stack_size);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetstackaddr(const PthreadAttr* attr, void** stack_addr);
|
||||
int KYTY_SYSV_ABI PthreadAttrGetstacksize(const PthreadAttr* attr, size_t* stack_size);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetaffinity(PthreadAttr* attr, KernelCpumask mask);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetdetachstate(PthreadAttr* attr, int state);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetguardsize(PthreadAttr* attr, size_t guard_size);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetinheritsched(PthreadAttr* attr, int inherit_sched);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetschedparam(PthreadAttr* attr, const KernelSchedParam* param);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetschedpolicy(PthreadAttr* attr, int policy);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetsolosched(PthreadAttr* attr, int solosched);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetstack(PthreadAttr* attr, void* addr, size_t size);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetstackaddr(PthreadAttr* attr, void* addr);
|
||||
int KYTY_SYSV_ABI PthreadAttrSetstacksize(PthreadAttr* attr, size_t stack_size);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadRwlockDestroy(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockInit(PthreadRwlock* rwlock, const PthreadRwlockattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadRwlockRdlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockTimedrdlock(PthreadRwlock* rwlock, KernelUseconds usec);
|
||||
int KYTY_SYSV_ABI PthreadRwlockTimedwrlock(PthreadRwlock* rwlock, KernelUseconds usec);
|
||||
int KYTY_SYSV_ABI PthreadRwlockTryrdlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockTrywrlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockUnlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockWrlock(PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI PthreadRwlockattrDestroy(PthreadRwlockattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadRwlockattrInit(PthreadRwlockattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadRwlockattrGettype(PthreadRwlockattr* attr, int* type);
|
||||
int KYTY_SYSV_ABI PthreadRwlockattrSettype(PthreadRwlockattr* attr, int type);
|
||||
|
||||
int KYTY_SYSV_ABI PthreadCondattrDestroy(PthreadCondattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadCondattrInit(PthreadCondattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadCondattrSetclock(PthreadCondattr* attr, KernelClockid clock_id);
|
||||
int KYTY_SYSV_ABI PthreadCondBroadcast(PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI PthreadCondDestroy(PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI PthreadCondInit(PthreadCond* cond, const PthreadCondattr* attr);
|
||||
int KYTY_SYSV_ABI PthreadCondSignal(PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI PthreadCondSignalto(PthreadCond* cond, Pthread thread);
|
||||
int KYTY_SYSV_ABI PthreadCondTimedwait(PthreadCond* cond, PthreadMutex* mutex, KernelUseconds usec);
|
||||
int KYTY_SYSV_ABI PthreadCondTimedwaitAbs(PthreadCond* cond, PthreadMutex* mutex,
|
||||
const KernelTimespec* abstime);
|
||||
int KYTY_SYSV_ABI PthreadCondWait(PthreadCond* cond, PthreadMutex* mutex);
|
||||
|
||||
int KYTY_SYSV_ABI KernelClockGetres(KernelClockid clock_id, KernelTimespec* tp);
|
||||
int KYTY_SYSV_ABI KernelClockGettime(KernelClockid clock_id, KernelTimespec* tp);
|
||||
int KYTY_SYSV_ABI KernelGettimeofday(KernelTimeval* tp);
|
||||
int KYTY_SYSV_ABI KernelGettimezone(KernelTimezone* tz);
|
||||
int KYTY_SYSV_ABI KernelConvertLocaltimeToUtc(int64_t local_time, int64_t reserved,
|
||||
int64_t* utc_time, KernelTimezone* timezone,
|
||||
int32_t* dst_seconds);
|
||||
int KYTY_SYSV_ABI KernelConvertUtcToLocaltime(int64_t utc_time, int64_t* local_time,
|
||||
KernelTimesec* st, uint64_t* dst_sec);
|
||||
uint64_t KYTY_SYSV_ABI KernelGetTscFrequency();
|
||||
uint64_t KYTY_SYSV_ABI KernelReadTsc();
|
||||
uint64_t KYTY_SYSV_ABI KernelGetProcessTime();
|
||||
uint64_t KYTY_SYSV_ABI KernelGetProcessTimeCounter();
|
||||
uint64_t KYTY_SYSV_ABI KernelGetProcessTimeCounterFrequency();
|
||||
|
||||
} // namespace LibKernel
|
||||
|
||||
namespace Posix {
|
||||
|
||||
int KYTY_SYSV_ABI pthread_create(LibKernel::Pthread* thread, const LibKernel::PthreadAttr* attr,
|
||||
LibKernel::pthread_entry_func_t entry, void* arg);
|
||||
int KYTY_SYSV_ABI pthread_create_name_np(LibKernel::Pthread* thread,
|
||||
const LibKernel::PthreadAttr* attr,
|
||||
LibKernel::pthread_entry_func_t entry, void* arg,
|
||||
const char* name);
|
||||
int KYTY_SYSV_ABI pthread_detach(LibKernel::Pthread thread);
|
||||
void KYTY_SYSV_ABI pthread_exit(void* value);
|
||||
LibKernel::Pthread KYTY_SYSV_ABI pthread_self();
|
||||
int KYTY_SYSV_ABI pthread_rename_np(LibKernel::Pthread thread, const char* name);
|
||||
int KYTY_SYSV_ABI pthread_setcancelstate(int state, int* old_state);
|
||||
int KYTY_SYSV_ABI pthread_setprio(LibKernel::Pthread thread, int prio);
|
||||
int KYTY_SYSV_ABI pthread_getschedparam(LibKernel::Pthread thread, int* policy,
|
||||
LibKernel::KernelSchedParam* param);
|
||||
int KYTY_SYSV_ABI pthread_setschedparam(LibKernel::Pthread thread, int policy,
|
||||
const LibKernel::KernelSchedParam* param);
|
||||
void KYTY_SYSV_ABI pthread_yield();
|
||||
int KYTY_SYSV_ABI sched_get_priority_max(int policy);
|
||||
int KYTY_SYSV_ABI sched_get_priority_min(int policy);
|
||||
int KYTY_SYSV_ABI pthread_join(LibKernel::Pthread thread, void** value);
|
||||
int KYTY_SYSV_ABI pthread_attr_init(LibKernel::PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI pthread_attr_destroy(LibKernel::PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI pthread_attr_get_np(LibKernel::Pthread thread, LibKernel::PthreadAttr* attr);
|
||||
int KYTY_SYSV_ABI pthread_attr_getdetachstate(const LibKernel::PthreadAttr* attr, int* state);
|
||||
int KYTY_SYSV_ABI pthread_attr_getguardsize(const LibKernel::PthreadAttr* attr, size_t* guard_size);
|
||||
int KYTY_SYSV_ABI pthread_attr_getinheritsched(const LibKernel::PthreadAttr* attr,
|
||||
int* inherit_sched);
|
||||
int KYTY_SYSV_ABI pthread_attr_getschedparam(const LibKernel::PthreadAttr* attr,
|
||||
LibKernel::KernelSchedParam* param);
|
||||
int KYTY_SYSV_ABI pthread_attr_getschedpolicy(const LibKernel::PthreadAttr* attr, int* policy);
|
||||
int KYTY_SYSV_ABI pthread_attr_getstack(const LibKernel::PthreadAttr* __restrict attr,
|
||||
void** __restrict stack_addr,
|
||||
size_t* __restrict stack_size);
|
||||
int KYTY_SYSV_ABI pthread_attr_getstacksize(const LibKernel::PthreadAttr* attr, size_t* stack_size);
|
||||
int KYTY_SYSV_ABI pthread_attr_setdetachstate(LibKernel::PthreadAttr* attr, int state);
|
||||
int KYTY_SYSV_ABI pthread_attr_setguardsize(LibKernel::PthreadAttr* attr, size_t guard_size);
|
||||
int KYTY_SYSV_ABI pthread_attr_setinheritsched(LibKernel::PthreadAttr* attr, int inherit_sched);
|
||||
int KYTY_SYSV_ABI pthread_attr_setschedparam(LibKernel::PthreadAttr* attr,
|
||||
const LibKernel::KernelSchedParam* param);
|
||||
int KYTY_SYSV_ABI pthread_attr_setschedpolicy(LibKernel::PthreadAttr* attr, int policy);
|
||||
int KYTY_SYSV_ABI pthread_attr_setstacksize(LibKernel::PthreadAttr* attr, size_t stack_size);
|
||||
int KYTY_SYSV_ABI pthread_cond_broadcast(LibKernel::PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI pthread_cond_signal(LibKernel::PthreadCond* cond);
|
||||
int KYTY_SYSV_ABI pthread_cond_init(LibKernel::PthreadCond* cond,
|
||||
const LibKernel::PthreadCondattr* attr);
|
||||
int KYTY_SYSV_ABI pthread_condattr_init(LibKernel::PthreadCondattr* attr);
|
||||
int KYTY_SYSV_ABI pthread_condattr_destroy(LibKernel::PthreadCondattr* attr);
|
||||
int KYTY_SYSV_ABI pthread_condattr_setclock(LibKernel::PthreadCondattr* attr,
|
||||
LibKernel::KernelClockid clock_id);
|
||||
int KYTY_SYSV_ABI pthread_cond_wait(LibKernel::PthreadCond* cond, LibKernel::PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI pthread_cond_timedwait(LibKernel::PthreadCond* cond,
|
||||
LibKernel::PthreadMutex* mutex,
|
||||
const LibKernel::KernelTimespec* abstime);
|
||||
int KYTY_SYSV_ABI pthread_once(void* once_control, void(KYTY_SYSV_ABI* init_routine)());
|
||||
int KYTY_SYSV_ABI pthread_mutex_lock(LibKernel::PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI pthread_mutex_trylock(LibKernel::PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI pthread_mutex_timedlock(LibKernel::PthreadMutex* mutex,
|
||||
const LibKernel::KernelTimespec* abstime);
|
||||
int KYTY_SYSV_ABI pthread_mutex_unlock(LibKernel::PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI pthread_rwlock_rdlock(LibKernel::PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI pthread_rwlock_unlock(LibKernel::PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI pthread_rwlock_wrlock(LibKernel::PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI pthread_rwlock_destroy(LibKernel::PthreadRwlock* rwlock);
|
||||
int KYTY_SYSV_ABI pthread_key_create(LibKernel::PthreadKey* key,
|
||||
LibKernel::pthread_key_destructor_func_t destructor);
|
||||
int KYTY_SYSV_ABI pthread_key_delete(LibKernel::PthreadKey key);
|
||||
int KYTY_SYSV_ABI pthread_setspecific(LibKernel::PthreadKey key, void* value);
|
||||
void* KYTY_SYSV_ABI pthread_getspecific(LibKernel::PthreadKey key);
|
||||
int KYTY_SYSV_ABI pthread_mutex_destroy(LibKernel::PthreadMutex* mutex);
|
||||
int KYTY_SYSV_ABI pthread_mutex_init(LibKernel::PthreadMutex* mutex,
|
||||
const LibKernel::PthreadMutexattr* attr);
|
||||
int KYTY_SYSV_ABI pthread_mutexattr_init(LibKernel::PthreadMutexattr* attr);
|
||||
int KYTY_SYSV_ABI pthread_mutexattr_settype(LibKernel::PthreadMutexattr* attr, int type);
|
||||
int KYTY_SYSV_ABI pthread_mutexattr_setprotocol(LibKernel::PthreadMutexattr* attr, int protocol);
|
||||
int KYTY_SYSV_ABI pthread_mutexattr_destroy(LibKernel::PthreadMutexattr* attr);
|
||||
int KYTY_SYSV_ABI pthread_getstack(const LibKernel::PthreadAttr* __restrict attr,
|
||||
void** __restrict stack_addr, size_t* __restrict stack_size);
|
||||
int64_t KYTY_SYSV_ABI lseek(int d, int64_t offset, int whence);
|
||||
|
||||
} // namespace Posix
|
||||
|
||||
} // namespace Libs
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_PTHREAD_H_ */
|
||||
@@ -0,0 +1,745 @@
|
||||
#include "kernel/semaphore.h"
|
||||
|
||||
#include "common/assert.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"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace Libs::LibKernel {
|
||||
void KernelDispatchPendingSignalForCurrentThread();
|
||||
} // namespace Libs::LibKernel
|
||||
|
||||
namespace Libs::LibKernel::Semaphore {
|
||||
|
||||
LIB_NAME("libkernel", "libkernel");
|
||||
|
||||
constexpr uint32_t SIGNAL_APC_POLL_MICROS = 10000;
|
||||
|
||||
class KernelSemaPrivate {
|
||||
public:
|
||||
enum class Result { Ok, TimedOut, Canceled, Deleted, InvalCount };
|
||||
|
||||
KernelSemaPrivate(const std::string& name, bool fifo, int init_count, int max_count)
|
||||
: m_name(name), m_fifo_order(fifo), m_count(init_count), m_init_count(init_count),
|
||||
m_max_count(max_count) {};
|
||||
virtual ~KernelSemaPrivate();
|
||||
|
||||
KYTY_CLASS_NO_COPY(KernelSemaPrivate);
|
||||
|
||||
Result Cancel(int set_count, int* num_waiting_threads);
|
||||
Result Signal(int signal_count);
|
||||
Result Wait(int need_count, uint32_t* ptr_micros);
|
||||
|
||||
Result Poll(int need_count) {
|
||||
uint32_t micros = 0;
|
||||
return Wait(need_count, µs);
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& GetName() const { return m_name; }
|
||||
|
||||
private:
|
||||
enum class Status { Set, Deleted };
|
||||
|
||||
struct WaitingThread {
|
||||
int id = 0;
|
||||
int priority = 700;
|
||||
int need_count = 0;
|
||||
Result result = Result::TimedOut;
|
||||
bool ready = false;
|
||||
};
|
||||
|
||||
void AddWaiter(WaitingThread* waiter);
|
||||
void RemoveWaiter(WaitingThread* waiter);
|
||||
void WakeWaiters();
|
||||
|
||||
Common::Mutex m_mutex;
|
||||
Common::CondVar m_cond_var;
|
||||
std::string m_name;
|
||||
Status m_status = Status::Set;
|
||||
std::vector<WaitingThread*> m_waiting_threads;
|
||||
bool m_fifo_order;
|
||||
int m_count;
|
||||
int m_init_count;
|
||||
int m_max_count;
|
||||
};
|
||||
|
||||
KernelSemaPrivate::~KernelSemaPrivate() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
while (m_status != Status::Set) {
|
||||
m_mutex.Unlock();
|
||||
Common::Thread::SleepMicro(10);
|
||||
m_mutex.Lock();
|
||||
}
|
||||
|
||||
m_status = Status::Deleted;
|
||||
|
||||
for (auto* waiter: m_waiting_threads) {
|
||||
if (!waiter->ready) {
|
||||
waiter->result = Result::Deleted;
|
||||
waiter->ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_cond_var.SignalAll();
|
||||
|
||||
while (!m_waiting_threads.empty()) {
|
||||
m_mutex.Unlock();
|
||||
Common::Thread::SleepMicro(10);
|
||||
m_mutex.Lock();
|
||||
}
|
||||
}
|
||||
|
||||
KernelSemaPrivate::Result KernelSemaPrivate::Cancel(int set_count, int* num_waiting_threads) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (set_count > m_max_count) {
|
||||
return Result::InvalCount;
|
||||
}
|
||||
|
||||
if (m_status == Status::Deleted) {
|
||||
return Result::Deleted;
|
||||
}
|
||||
|
||||
if (num_waiting_threads != nullptr) {
|
||||
*num_waiting_threads = static_cast<int>(
|
||||
std::count_if(m_waiting_threads.begin(), m_waiting_threads.end(),
|
||||
[](const WaitingThread* waiter) { return !waiter->ready; }));
|
||||
}
|
||||
|
||||
m_count = (set_count < 0 ? m_init_count : set_count);
|
||||
|
||||
for (auto* waiter: m_waiting_threads) {
|
||||
if (!waiter->ready) {
|
||||
waiter->result = Result::Canceled;
|
||||
waiter->ready = true;
|
||||
}
|
||||
}
|
||||
|
||||
m_cond_var.SignalAll();
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
KernelSemaPrivate::Result KernelSemaPrivate::Signal(int signal_count) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (m_status == Status::Deleted) {
|
||||
return Result::Deleted;
|
||||
}
|
||||
|
||||
if (m_count + signal_count > m_max_count) {
|
||||
return Result::InvalCount;
|
||||
}
|
||||
|
||||
m_count += signal_count;
|
||||
|
||||
WakeWaiters();
|
||||
|
||||
m_cond_var.SignalAll();
|
||||
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
void KernelSemaPrivate::AddWaiter(WaitingThread* waiter) {
|
||||
if (m_fifo_order) {
|
||||
m_waiting_threads.push_back(waiter);
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = m_waiting_threads.begin();
|
||||
while (it != m_waiting_threads.end() && (*it)->priority <= waiter->priority) {
|
||||
++it;
|
||||
}
|
||||
m_waiting_threads.insert(it, waiter);
|
||||
}
|
||||
|
||||
void KernelSemaPrivate::RemoveWaiter(WaitingThread* waiter) {
|
||||
auto it = std::find(m_waiting_threads.begin(), m_waiting_threads.end(), waiter);
|
||||
if (it != m_waiting_threads.end()) {
|
||||
m_waiting_threads.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void KernelSemaPrivate::WakeWaiters() {
|
||||
for (auto* waiter: m_waiting_threads) {
|
||||
if (!waiter->ready && waiter->need_count <= m_count) {
|
||||
m_count -= waiter->need_count;
|
||||
waiter->result = Result::Ok;
|
||||
waiter->ready = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KernelSemaPrivate::Result KernelSemaPrivate::Wait(int need_count, uint32_t* ptr_micros) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (need_count < 1 || need_count > m_max_count) {
|
||||
return Result::InvalCount;
|
||||
}
|
||||
|
||||
if (m_status == Status::Deleted) {
|
||||
return Result::Deleted;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
int id = Common::Thread::GetThreadIdUnique();
|
||||
|
||||
if (m_count >= need_count) {
|
||||
m_count -= need_count;
|
||||
if (ptr_micros != nullptr) {
|
||||
*ptr_micros = micros;
|
||||
}
|
||||
return Result::Ok;
|
||||
}
|
||||
|
||||
if (!infinitely && micros == 0) {
|
||||
return Result::TimedOut;
|
||||
}
|
||||
|
||||
WaitingThread waiter {};
|
||||
waiter.id = id;
|
||||
waiter.priority = (m_fifo_order ? 0 : Libs::LibKernel::PthreadGetCurrentPriorityForKernel());
|
||||
waiter.need_count = need_count;
|
||||
AddWaiter(&waiter);
|
||||
|
||||
while (!waiter.ready) {
|
||||
if ((elapsed >= micros && !infinitely)) {
|
||||
RemoveWaiter(&waiter);
|
||||
*ptr_micros = 0;
|
||||
return Result::TimedOut;
|
||||
}
|
||||
|
||||
if (infinitely) {
|
||||
m_cond_var.WaitFor(&m_mutex, SIGNAL_APC_POLL_MICROS);
|
||||
} else {
|
||||
m_cond_var.WaitFor(&m_mutex, micros - elapsed);
|
||||
}
|
||||
|
||||
m_mutex.Unlock();
|
||||
LibKernel::KernelDispatchPendingSignalForCurrentThread();
|
||||
m_mutex.Lock();
|
||||
|
||||
elapsed = static_cast<uint32_t>(t.GetTimeS() * 1000000.0);
|
||||
}
|
||||
|
||||
RemoveWaiter(&waiter);
|
||||
|
||||
if (ptr_micros != nullptr) {
|
||||
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
|
||||
}
|
||||
|
||||
return waiter.result;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateSema(KernelSema* sem, const char* name, uint32_t attr, int init,
|
||||
int max, void* opt) {
|
||||
PRINT_NAME();
|
||||
|
||||
EXIT_NOT_IMPLEMENTED(sem == nullptr);
|
||||
|
||||
if (name == nullptr || attr > 2 || init < 0 || max <= 0 || init > max) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
bool fifo = false;
|
||||
|
||||
switch (attr) {
|
||||
case 0x01: fifo = true; break;
|
||||
case 0x02:
|
||||
default: fifo = false; break;
|
||||
}
|
||||
|
||||
*sem = new KernelSemaPrivate(std::string(name), fifo, init, max);
|
||||
|
||||
LOGF("\t Semaphore create: %s, ptr=0x%016" PRIx64 ", init=%d, max=%d\n", name,
|
||||
reinterpret_cast<uint64_t>(*sem), init, max);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelDeleteSema(KernelSema sem) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (sem == nullptr) {
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
delete sem;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelWaitSema(KernelSema sem, int need, KernelUseconds* time) {
|
||||
if (sem == nullptr) {
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
auto result = sem->Wait(need, time);
|
||||
|
||||
int ret = OK;
|
||||
|
||||
switch (result) {
|
||||
case KernelSemaPrivate::Result::Ok: ret = OK; break;
|
||||
case KernelSemaPrivate::Result::InvalCount: ret = KERNEL_ERROR_EINVAL; break;
|
||||
case KernelSemaPrivate::Result::TimedOut: ret = KERNEL_ERROR_ETIMEDOUT; break;
|
||||
case KernelSemaPrivate::Result::Canceled: ret = KERNEL_ERROR_ECANCELED; break;
|
||||
case KernelSemaPrivate::Result::Deleted: ret = KERNEL_ERROR_EACCES; break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelPollSema(KernelSema sem, int need) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (sem == nullptr) {
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
LOGF("\t Semaphore poll: %s, %d\n", sem->GetName().c_str(), need);
|
||||
|
||||
auto result = sem->Poll(need);
|
||||
|
||||
int ret = OK;
|
||||
|
||||
switch (result) {
|
||||
case KernelSemaPrivate::Result::Ok: ret = OK; break;
|
||||
case KernelSemaPrivate::Result::InvalCount: ret = KERNEL_ERROR_EINVAL; break;
|
||||
case KernelSemaPrivate::Result::TimedOut:
|
||||
case KernelSemaPrivate::Result::Canceled:
|
||||
case KernelSemaPrivate::Result::Deleted: ret = KERNEL_ERROR_EBUSY; break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelSignalSema(KernelSema sem, int count) {
|
||||
if (sem == nullptr) {
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
auto result = sem->Signal(count);
|
||||
|
||||
int ret = OK;
|
||||
|
||||
switch (result) {
|
||||
case KernelSemaPrivate::Result::Ok: ret = OK; break;
|
||||
case KernelSemaPrivate::Result::InvalCount:
|
||||
case KernelSemaPrivate::Result::TimedOut:
|
||||
case KernelSemaPrivate::Result::Canceled:
|
||||
case KernelSemaPrivate::Result::Deleted: ret = KERNEL_ERROR_EINVAL; break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI KernelCancelSema(KernelSema sem, int count, int* threads) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (sem == nullptr) {
|
||||
return KERNEL_ERROR_ESRCH;
|
||||
}
|
||||
|
||||
auto result = sem->Cancel(count, threads);
|
||||
|
||||
int ret = OK;
|
||||
|
||||
switch (result) {
|
||||
case KernelSemaPrivate::Result::Ok: ret = OK; break;
|
||||
case KernelSemaPrivate::Result::InvalCount:
|
||||
case KernelSemaPrivate::Result::TimedOut:
|
||||
case KernelSemaPrivate::Result::Canceled:
|
||||
case KernelSemaPrivate::Result::Deleted: ret = KERNEL_ERROR_EINVAL; break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace Libs::LibKernel::Semaphore
|
||||
|
||||
namespace Libs::Posix {
|
||||
|
||||
LIB_NAME("Posix", "libkernel");
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint16_t POSIX_SEM_MAGIC = 0x09fa;
|
||||
constexpr int POSIX_SEM_VALUE_MAX = 0x7fffffff;
|
||||
constexpr uint32_t SIGNAL_APC_POLL_MICROS = 10000;
|
||||
|
||||
struct PosixSemGuest {
|
||||
uint16_t magic;
|
||||
uint16_t nameid;
|
||||
volatile uint32_t has_waiters;
|
||||
volatile uint32_t count;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PosixSemGuest) == 16);
|
||||
|
||||
class PosixSemPrivate {
|
||||
public:
|
||||
PosixSemPrivate(PosixSemGuest* guest, unsigned int value, uint32_t flags)
|
||||
: m_guest(guest), m_count(static_cast<int>(value)) {
|
||||
SyncGuest(flags);
|
||||
}
|
||||
|
||||
int Wait(uint32_t* micros) {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
uint32_t elapsed = 0;
|
||||
const bool timed_wait = (micros != nullptr);
|
||||
Common::Timer timer;
|
||||
timer.Start();
|
||||
|
||||
while (m_count <= 0) {
|
||||
if (timed_wait && elapsed >= *micros) {
|
||||
SyncGuest();
|
||||
return POSIX_ETIMEDOUT;
|
||||
}
|
||||
|
||||
m_waiters++;
|
||||
SyncGuest();
|
||||
|
||||
if (timed_wait) {
|
||||
m_cond_var.WaitFor(&m_mutex, *micros - elapsed);
|
||||
} else {
|
||||
m_cond_var.WaitFor(&m_mutex, SIGNAL_APC_POLL_MICROS);
|
||||
}
|
||||
|
||||
m_mutex.Unlock();
|
||||
LibKernel::KernelDispatchPendingSignalForCurrentThread();
|
||||
m_mutex.Lock();
|
||||
|
||||
m_waiters--;
|
||||
elapsed = static_cast<uint32_t>(timer.GetTimeS() * 1000000.0);
|
||||
}
|
||||
|
||||
m_count--;
|
||||
SyncGuest();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int TryWait() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (m_count <= 0) {
|
||||
SyncGuest();
|
||||
return POSIX_EAGAIN;
|
||||
}
|
||||
|
||||
m_count--;
|
||||
SyncGuest();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int Post() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
if (m_count == POSIX_SEM_VALUE_MAX) {
|
||||
return POSIX_EOVERFLOW;
|
||||
}
|
||||
|
||||
m_count++;
|
||||
SyncGuest();
|
||||
m_cond_var.Signal();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
int GetValue() {
|
||||
Common::LockGuard lock(m_mutex);
|
||||
|
||||
return m_count;
|
||||
}
|
||||
|
||||
private:
|
||||
void SyncGuest(uint32_t flags = std::numeric_limits<uint32_t>::max()) {
|
||||
if (m_guest != nullptr) {
|
||||
m_guest->magic = POSIX_SEM_MAGIC;
|
||||
m_guest->nameid = 0;
|
||||
m_guest->has_waiters = (m_waiters > 0 ? 1u : 0u);
|
||||
m_guest->count = static_cast<uint32_t>(m_count);
|
||||
if (flags != std::numeric_limits<uint32_t>::max()) {
|
||||
m_guest->flags = flags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Common::Mutex m_mutex;
|
||||
Common::CondVar m_cond_var;
|
||||
PosixSemGuest* m_guest = nullptr;
|
||||
int m_count = 0;
|
||||
uint32_t m_waiters = 0;
|
||||
};
|
||||
|
||||
Common::Mutex g_posix_sem_mutex;
|
||||
std::unordered_map<PosixSemGuest*, PosixSemPrivate*> g_posix_sems;
|
||||
|
||||
static int PosixToKernel(int posix_errno) {
|
||||
return (posix_errno >= 0 && posix_errno <= 102 ? posix_errno - 2147352576
|
||||
: LibKernel::KERNEL_ERROR_EINVAL);
|
||||
}
|
||||
|
||||
static PosixSemPrivate* GetSem(void* sem) {
|
||||
if (sem == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Common::LockGuard lock(g_posix_sem_mutex);
|
||||
|
||||
const auto it = g_posix_sems.find(static_cast<PosixSemGuest*>(sem));
|
||||
return (it != g_posix_sems.end() ? it->second : nullptr);
|
||||
}
|
||||
|
||||
static int SetErrnoReturn(int posix_errno) {
|
||||
*GetErrorAddr() = posix_errno;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int SemInitImpl(void* sem, int pshared, unsigned int value, const char* name) {
|
||||
if (sem == nullptr || value > POSIX_SEM_VALUE_MAX) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
auto* guest = static_cast<PosixSemGuest*>(sem);
|
||||
auto* obj = new PosixSemPrivate(guest, value, static_cast<uint32_t>(pshared));
|
||||
|
||||
{
|
||||
Common::LockGuard lock(g_posix_sem_mutex);
|
||||
|
||||
if (auto it = g_posix_sems.find(guest); it != g_posix_sems.end()) {
|
||||
delete it->second;
|
||||
it->second = obj;
|
||||
} else {
|
||||
g_posix_sems.insert({guest, obj});
|
||||
}
|
||||
}
|
||||
|
||||
static std::atomic<uint32_t> log_count {0};
|
||||
const auto count = log_count.fetch_add(1);
|
||||
if (count < 32) {
|
||||
LOGF("\t POSIX semaphore init: %s, value = %u, pshared = %d\n",
|
||||
(name != nullptr ? name : "sem"), value, pshared);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int SemDestroyImpl(void* sem) {
|
||||
if (sem == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
auto* guest = static_cast<PosixSemGuest*>(sem);
|
||||
PosixSemPrivate* obj = nullptr;
|
||||
|
||||
{
|
||||
Common::LockGuard lock(g_posix_sem_mutex);
|
||||
|
||||
const auto it = g_posix_sems.find(guest);
|
||||
if (it == g_posix_sems.end()) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
obj = it->second;
|
||||
g_posix_sems.erase(it);
|
||||
}
|
||||
|
||||
delete obj;
|
||||
guest->magic = 0;
|
||||
guest->has_waiters = 0;
|
||||
guest->count = 0;
|
||||
guest->flags = 0;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
static int SemTimedwaitImpl(void* sem, uint32_t* micros) {
|
||||
auto* obj = GetSem(sem);
|
||||
if (obj == nullptr) {
|
||||
return POSIX_EINVAL;
|
||||
}
|
||||
|
||||
return obj->Wait(micros);
|
||||
}
|
||||
|
||||
static bool AbsTimespecToMicros(const LibKernel::KernelTimespec* abstime, uint32_t* micros) {
|
||||
if (abstime == nullptr || micros == nullptr || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
|
||||
abstime->tv_nsec >= 1000000000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LibKernel::KernelTimespec now {};
|
||||
if (LibKernel::KernelClockGettime(0, &now) != OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int64_t sec_delta = abstime->tv_sec - now.tv_sec;
|
||||
const int64_t nsec_delta = abstime->tv_nsec - now.tv_nsec;
|
||||
const int64_t usec_delta = sec_delta * 1000000 + nsec_delta / 1000;
|
||||
|
||||
if (usec_delta <= 0) {
|
||||
*micros = 0;
|
||||
} else if (usec_delta > std::numeric_limits<uint32_t>::max()) {
|
||||
*micros = std::numeric_limits<uint32_t>::max();
|
||||
} else {
|
||||
*micros = static_cast<uint32_t>(usec_delta);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int KYTY_SYSV_ABI sem_init(void* sem, int pshared, unsigned int value) {
|
||||
PRINT_NAME();
|
||||
|
||||
const int result = SemInitImpl(sem, pshared, value, nullptr);
|
||||
return (result == OK ? 0 : SetErrnoReturn(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI sem_destroy(void* sem) {
|
||||
PRINT_NAME();
|
||||
|
||||
const int result = SemDestroyImpl(sem);
|
||||
return (result == OK ? 0 : SetErrnoReturn(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI sem_wait(void* sem) {
|
||||
const int result = SemTimedwaitImpl(sem, nullptr);
|
||||
return (result == OK ? 0 : SetErrnoReturn(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI sem_trywait(void* sem) {
|
||||
auto* obj = GetSem(sem);
|
||||
if (obj == nullptr) {
|
||||
return SetErrnoReturn(POSIX_EINVAL);
|
||||
}
|
||||
|
||||
const int result = obj->TryWait();
|
||||
return (result == OK ? 0 : SetErrnoReturn(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI sem_reltimedwait_np(void* sem, uint32_t usec) {
|
||||
const int result = SemTimedwaitImpl(sem, &usec);
|
||||
return (result == OK ? 0 : SetErrnoReturn(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI sem_timedwait(void* sem, const LibKernel::KernelTimespec* abstime) {
|
||||
uint32_t micros = 0;
|
||||
if (!AbsTimespecToMicros(abstime, µs)) {
|
||||
return SetErrnoReturn(POSIX_EINVAL);
|
||||
}
|
||||
|
||||
const int result = SemTimedwaitImpl(sem, µs);
|
||||
return (result == OK ? 0 : SetErrnoReturn(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI sem_post(void* sem) {
|
||||
auto* obj = GetSem(sem);
|
||||
if (obj == nullptr) {
|
||||
return SetErrnoReturn(POSIX_EINVAL);
|
||||
}
|
||||
|
||||
const int result = obj->Post();
|
||||
return (result == OK ? 0 : SetErrnoReturn(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI sem_getvalue(void* sem, int* value) {
|
||||
auto* obj = GetSem(sem);
|
||||
if (obj == nullptr || value == nullptr) {
|
||||
return SetErrnoReturn(POSIX_EINVAL);
|
||||
}
|
||||
|
||||
*value = obj->GetValue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Libs::Posix
|
||||
|
||||
namespace Libs::LibKernel::Semaphore {
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSemInit(void* sem, int flag, unsigned int value, const char* name) {
|
||||
PRINT_NAME();
|
||||
|
||||
if (flag != 0) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
const int result = Posix::SemInitImpl(sem, 0, value, name);
|
||||
return (result == OK ? OK : Posix::PosixToKernel(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSemDestroy(void* sem) {
|
||||
PRINT_NAME();
|
||||
|
||||
const int result = Posix::SemDestroyImpl(sem);
|
||||
return (result == OK ? OK : Posix::PosixToKernel(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSemWait(void* sem) {
|
||||
const int result = Posix::SemTimedwaitImpl(sem, nullptr);
|
||||
return (result == OK ? OK : Posix::PosixToKernel(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSemTrywait(void* sem) {
|
||||
auto* obj = Posix::GetSem(sem);
|
||||
if (obj == nullptr) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
const int result = obj->TryWait();
|
||||
return (result == OK ? OK : Posix::PosixToKernel(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSemTimedwait(void* sem, KernelUseconds usec) {
|
||||
uint32_t micros = usec;
|
||||
const int result = Posix::SemTimedwaitImpl(sem, µs);
|
||||
return (result == OK ? OK : Posix::PosixToKernel(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSemPost(void* sem) {
|
||||
auto* obj = Posix::GetSem(sem);
|
||||
if (obj == nullptr) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
const int result = obj->Post();
|
||||
return (result == OK ? OK : Posix::PosixToKernel(result));
|
||||
}
|
||||
|
||||
int KYTY_SYSV_ABI PthreadSemGetvalue(void* sem, int* value) {
|
||||
auto* obj = Posix::GetSem(sem);
|
||||
if (obj == nullptr || value == nullptr) {
|
||||
return KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
|
||||
*value = obj->GetValue();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
} // namespace Libs::LibKernel::Semaphore
|
||||
@@ -0,0 +1,44 @@
|
||||
#ifndef EMULATOR_INCLUDE_EMULATOR_KERNEL_SEMAPHORE_H_
|
||||
#define EMULATOR_INCLUDE_EMULATOR_KERNEL_SEMAPHORE_H_
|
||||
|
||||
#include "common/abi.h"
|
||||
#include "common/common.h"
|
||||
#include "kernel/pthread.h"
|
||||
|
||||
namespace Libs::LibKernel::Semaphore {
|
||||
|
||||
class KernelSemaPrivate;
|
||||
|
||||
using KernelSema = KernelSemaPrivate*;
|
||||
|
||||
int KYTY_SYSV_ABI KernelCreateSema(KernelSema* sem, const char* name, uint32_t attr, int init,
|
||||
int max, void* opt);
|
||||
int KYTY_SYSV_ABI KernelDeleteSema(KernelSema sem);
|
||||
int KYTY_SYSV_ABI KernelWaitSema(KernelSema sem, int need, KernelUseconds* time);
|
||||
int KYTY_SYSV_ABI KernelPollSema(KernelSema sem, int need);
|
||||
int KYTY_SYSV_ABI KernelSignalSema(KernelSema sem, int count);
|
||||
int KYTY_SYSV_ABI KernelCancelSema(KernelSema sem, int count, int* threads);
|
||||
int KYTY_SYSV_ABI PthreadSemInit(void* sem, int flag, unsigned int value, const char* name);
|
||||
int KYTY_SYSV_ABI PthreadSemDestroy(void* sem);
|
||||
int KYTY_SYSV_ABI PthreadSemWait(void* sem);
|
||||
int KYTY_SYSV_ABI PthreadSemTrywait(void* sem);
|
||||
int KYTY_SYSV_ABI PthreadSemTimedwait(void* sem, KernelUseconds usec);
|
||||
int KYTY_SYSV_ABI PthreadSemPost(void* sem);
|
||||
int KYTY_SYSV_ABI PthreadSemGetvalue(void* sem, int* value);
|
||||
|
||||
} // namespace Libs::LibKernel::Semaphore
|
||||
|
||||
namespace Libs::Posix {
|
||||
|
||||
int KYTY_SYSV_ABI sem_init(void* sem, int pshared, unsigned int value);
|
||||
int KYTY_SYSV_ABI sem_destroy(void* sem);
|
||||
int KYTY_SYSV_ABI sem_wait(void* sem);
|
||||
int KYTY_SYSV_ABI sem_trywait(void* sem);
|
||||
int KYTY_SYSV_ABI sem_reltimedwait_np(void* sem, uint32_t usec);
|
||||
int KYTY_SYSV_ABI sem_timedwait(void* sem, const LibKernel::KernelTimespec* abstime);
|
||||
int KYTY_SYSV_ABI sem_post(void* sem);
|
||||
int KYTY_SYSV_ABI sem_getvalue(void* sem, int* value);
|
||||
|
||||
} // namespace Libs::Posix
|
||||
|
||||
#endif /* EMULATOR_INCLUDE_EMULATOR_KERNEL_SEMAPHORE_H_ */
|
||||
Reference in New Issue
Block a user