#pragma once #include "Emu/SysCalls/lv2/SC_Lwmutex.h" #define FIX_SPUQ(x) ((u64)x | 0x5350555100000000ULL) // arbitrary code to prevent "special" zero value in key argument enum EventQueueType { SYS_PPU_QUEUE = 1, SYS_SPU_QUEUE = 2, }; enum EventQueueDestroyMode { // DEFAULT = 0, SYS_EVENT_QUEUE_DESTROY_FORCE = 1, }; enum EventPortType { SYS_EVENT_PORT_LOCAL = 1, }; enum EventSourceType { SYS_SPU_THREAD_EVENT_USER = 1, /* SYS_SPU_THREAD_EVENT_DMA = 2, */ // not supported }; enum EventSourceKey : u64 { SYS_SPU_THREAD_EVENT_USER_KEY = 0xFFFFFFFF53505501, /* SYS_SPU_THREAD_EVENT_DMA_KEY = 0xFFFFFFFF53505502, */ }; struct sys_event_queue_attr { be_t protocol; // SYS_SYNC_PRIORITY or SYS_SYNC_FIFO be_t type; // SYS_PPU_QUEUE or SYS_SPU_QUEUE union { char name[8]; u64 name_u64; }; }; struct sys_event_data { be_t source; be_t data1; be_t data2; be_t data3; }; struct EventQueue; struct EventPort { u64 name; // generated or user-specified code that is passed to sys_event_data struct EventQueue* eq; // event queue this port has been connected to std::mutex m_mutex; // may be locked until the event sending is finished EventPort(u64 name = 0) : eq(nullptr) , name(name) { } }; class EventRingBuffer { std::vector data; std::mutex m_mutex; u32 buf_pos; u32 buf_count; public: const u32 size; EventRingBuffer(u32 size) : size(size) , buf_pos(0) , buf_count(0) { data.resize(size); } void clear() { std::lock_guard lock(m_mutex); buf_count = 0; buf_pos = 0; } bool push(u64 name, u64 d1, u64 d2, u64 d3) { std::lock_guard lock(m_mutex); if (buf_count >= size) return false; sys_event_data& ref = data[(buf_pos + buf_count++) % size]; ref.source = name; ref.data1 = d1; ref.data2 = d2; ref.data3 = d3; return true; } bool pop(sys_event_data& ref) { std::lock_guard lock(m_mutex); if (!buf_count) return false; sys_event_data& from = data[buf_pos]; buf_pos = (buf_pos + 1) % size; buf_count--; ref.source = from.source; ref.data1 = from.data1; ref.data2 = from.data2; ref.data3 = from.data3; return true; } u32 pop_all(sys_event_data* ptr, u32 max) { std::lock_guard lock(m_mutex); u32 res = 0; while (buf_count && max) { sys_event_data& from = data[buf_pos]; ptr->source = from.source; ptr->data1 = from.data1; ptr->data2 = from.data2; ptr->data3 = from.data3; buf_pos = (buf_pos + 1) % size; buf_count--; max--; ptr++; res++; } return res; } u32 count() const { return buf_count; } }; class EventPortList { std::vector data; std::mutex m_mutex; public: void clear() { std::lock_guard lock(m_mutex); for (u32 i = 0; i < data.size(); i++) { std::lock_guard lock2(data[i]->m_mutex); data[i]->eq = nullptr; // force all ports to disconnect } data.clear(); } void add(EventPort* port) { std::lock_guard lock(m_mutex); data.push_back(port); } void remove(EventPort* port) { std::lock_guard lock(m_mutex); for (u32 i = 0; i < data.size(); i++) { if (data[i] == port) { data.erase(data.begin() + i); return; } } } }; struct EventQueue { SleepQueue sq; EventPortList ports; EventRingBuffer events; SMutex owner; const union { u64 name_u64; char name[8]; }; const u32 protocol; const int type; const u64 key; EventQueue(u32 protocol, int type, u64 name, u64 key, int size) : type(type) , protocol(protocol) , name_u64(name) , key(key) , events(size) // size: max event count this queue can hold { } }; class EventManager { std::mutex m_lock; std::unordered_map key_map; public: void Init(); void Clear(); bool CheckKey(u64 key); bool RegisterKey(EventQueue* data, u64 key); bool GetEventQueue(u64 key, EventQueue*& data); bool UnregisterKey(u64 key); bool SendEvent(u64 key, u64 source, u64 d1, u64 d2, u64 d3); };