mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-30 21:32:50 +00:00
sys_event improved
This commit is contained in:
parent
2f7fe35f5c
commit
bc91ad0f4d
@ -1316,9 +1316,9 @@ void SPUThread::stop_and_signal(u32 code)
|
|||||||
{
|
{
|
||||||
group->state = SPU_THREAD_GROUP_STATUS_WAITING;
|
group->state = SPU_THREAD_GROUP_STATUS_WAITING;
|
||||||
|
|
||||||
for (auto& t : group->threads)
|
for (auto& thread : group->threads)
|
||||||
{
|
{
|
||||||
if (t) t->sleep(); // trigger status check
|
if (thread) thread->sleep(); // trigger status check
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1326,35 +1326,37 @@ void SPUThread::stop_and_signal(u32 code)
|
|||||||
throw EXCEPTION("Unexpected SPU Thread Group state (%d)", group->state);
|
throw EXCEPTION("Unexpected SPU Thread Group state (%d)", group->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// protocol is ignored in current implementation
|
if (queue->events.empty() || !queue->sq.empty())
|
||||||
queue->waiters++;
|
|
||||||
|
|
||||||
// wait on the event queue
|
|
||||||
while (queue->events.empty() && !queue->cancelled)
|
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
// add waiter; protocol is ignored in current implementation
|
||||||
|
sleep_queue_entry_t waiter(*this, queue->sq);
|
||||||
|
|
||||||
|
// wait on the event queue
|
||||||
|
while (!unsignal())
|
||||||
|
{
|
||||||
|
CHECK_EMU_STATUS;
|
||||||
|
|
||||||
if (is_stopped()) throw CPUThreadStop{};
|
if (is_stopped()) throw CPUThreadStop{};
|
||||||
|
|
||||||
queue->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
|
cv.wait(lv2_lock);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queue->cancelled)
|
if (queue->events.empty())
|
||||||
{
|
{
|
||||||
|
if (Emu.GetIdManager().check_id<lv2_event_queue_t>(queue->id))
|
||||||
|
{
|
||||||
|
throw EXCEPTION("Unexpected");
|
||||||
|
}
|
||||||
|
|
||||||
ch_in_mbox.set_values(1, CELL_ECANCELED);
|
ch_in_mbox.set_values(1, CELL_ECANCELED);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto& event = queue->events.front();
|
auto& event = queue->events.front();
|
||||||
ch_in_mbox.set_values(4, CELL_OK, static_cast<u32>(event.data1), static_cast<u32>(event.data2), static_cast<u32>(event.data3));
|
ch_in_mbox.set_values(4, CELL_OK, static_cast<u32>(std::get<1>(event)), static_cast<u32>(std::get<2>(event)), static_cast<u32>(std::get<3>(event)));
|
||||||
|
|
||||||
queue->events.pop_front();
|
queue->events.pop_front();
|
||||||
queue->waiters--;
|
|
||||||
|
|
||||||
if (queue->events.size())
|
|
||||||
{
|
|
||||||
queue->cv.notify_one();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// restore thread group status
|
// restore thread group status
|
||||||
@ -1371,9 +1373,9 @@ void SPUThread::stop_and_signal(u32 code)
|
|||||||
throw EXCEPTION("Unexpected SPU Thread Group state (%d)", group->state);
|
throw EXCEPTION("Unexpected SPU Thread Group state (%d)", group->state);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& t : group->threads)
|
for (auto& thread : group->threads)
|
||||||
{
|
{
|
||||||
if (t) t->awake(); // untrigger status check
|
if (thread) thread->awake(); // untrigger status check
|
||||||
}
|
}
|
||||||
|
|
||||||
group->cv.notify_all();
|
group->cv.notify_all();
|
||||||
@ -1408,11 +1410,11 @@ void SPUThread::stop_and_signal(u32 code)
|
|||||||
throw EXCEPTION("Invalid SPU Thread Group");
|
throw EXCEPTION("Invalid SPU Thread Group");
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto t : group->threads)
|
for (auto thread : group->threads)
|
||||||
{
|
{
|
||||||
if (t && t.get() != this)
|
if (thread && thread.get() != this)
|
||||||
{
|
{
|
||||||
t->stop();
|
thread->stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ enum
|
|||||||
|
|
||||||
using sleep_queue_t = std::deque<std::shared_ptr<CPUThread>>;
|
using sleep_queue_t = std::deque<std::shared_ptr<CPUThread>>;
|
||||||
|
|
||||||
static struct defer_sleep_t{} const defer_sleep;
|
static struct defer_sleep_t{} const defer_sleep{};
|
||||||
|
|
||||||
// automatic object handling a thread entry in the sleep queue
|
// automatic object handling a thread entry in the sleep queue
|
||||||
class sleep_queue_entry_t final
|
class sleep_queue_entry_t final
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
#include "Emu/Cell/PPUThread.h"
|
#include "Emu/Cell/PPUThread.h"
|
||||||
#include "Emu/Event.h"
|
#include "Emu/Event.h"
|
||||||
#include "sleep_queue.h"
|
|
||||||
#include "sys_process.h"
|
#include "sys_process.h"
|
||||||
#include "sys_event.h"
|
#include "sys_event.h"
|
||||||
|
|
||||||
@ -21,11 +20,25 @@ lv2_event_queue_t::lv2_event_queue_t(u32 protocol, s32 type, u64 name, u64 key,
|
|||||||
, name(name)
|
, name(name)
|
||||||
, key(key)
|
, key(key)
|
||||||
, size(size)
|
, size(size)
|
||||||
, cancelled(false)
|
|
||||||
, waiters(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lv2_event_queue_t::push(lv2_lock_t& lv2_lock, u64 source, u64 data1, u64 data2, u64 data3)
|
||||||
|
{
|
||||||
|
CHECK_LV2_LOCK(lv2_lock);
|
||||||
|
|
||||||
|
events.emplace_back(source, data1, data2, data3);
|
||||||
|
|
||||||
|
// notify waiter; protocol is ignored in current implementation
|
||||||
|
for (auto& waiter : sq)
|
||||||
|
{
|
||||||
|
if (waiter->signal())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s32 sys_event_queue_create(vm::ptr<u32> equeue_id, vm::ptr<sys_event_queue_attribute_t> attr, u64 event_queue_key, s32 size)
|
s32 sys_event_queue_create(vm::ptr<u32> equeue_id, vm::ptr<sys_event_queue_attribute_t> attr, u64 event_queue_key, s32 size)
|
||||||
{
|
{
|
||||||
sys_event.Warning("sys_event_queue_create(equeue_id=*0x%x, attr=*0x%x, event_queue_key=0x%llx, size=%d)", equeue_id, attr, event_queue_key, size);
|
sys_event.Warning("sys_event_queue_create(equeue_id=*0x%x, attr=*0x%x, event_queue_key=0x%llx, size=%d)", equeue_id, attr, event_queue_key, size);
|
||||||
@ -37,20 +50,18 @@ s32 sys_event_queue_create(vm::ptr<u32> equeue_id, vm::ptr<sys_event_queue_attri
|
|||||||
|
|
||||||
const u32 protocol = attr->protocol;
|
const u32 protocol = attr->protocol;
|
||||||
|
|
||||||
switch (protocol)
|
if (protocol != SYS_SYNC_FIFO && protocol != SYS_SYNC_PRIORITY)
|
||||||
{
|
{
|
||||||
case SYS_SYNC_FIFO: break;
|
sys_event.Error("sys_event_queue_create(): unknown protocol (0x%x)", protocol);
|
||||||
case SYS_SYNC_PRIORITY: break;
|
return CELL_EINVAL;
|
||||||
default: sys_event.Error("sys_event_queue_create(): unknown protocol (0x%x)", protocol); return CELL_EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const u32 type = attr->type;
|
const u32 type = attr->type;
|
||||||
|
|
||||||
switch (type)
|
if (type != SYS_PPU_QUEUE && type != SYS_SPU_QUEUE)
|
||||||
{
|
{
|
||||||
case SYS_PPU_QUEUE: break;
|
sys_event.Error("sys_event_queue_create(): unknown type (0x%x)", type);
|
||||||
case SYS_SPU_QUEUE: break;
|
return CELL_EINVAL;
|
||||||
default: sys_event.Error("sys_event_queue_create(): unknown type (0x%x)", type); return CELL_EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto queue = Emu.GetEventManager().MakeEventQueue(event_queue_key, protocol, type, attr->name_u64, event_queue_key, size);
|
const auto queue = Emu.GetEventManager().MakeEventQueue(event_queue_key, protocol, type, attr->name_u64, event_queue_key, size);
|
||||||
@ -83,24 +94,21 @@ s32 sys_event_queue_destroy(u32 equeue_id, s32 mode)
|
|||||||
return CELL_EINVAL;
|
return CELL_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mode && queue->waiters)
|
if (!mode && queue->sq.size())
|
||||||
{
|
{
|
||||||
return CELL_EBUSY;
|
return CELL_EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (queue->cancelled.exchange(true))
|
// cleanup
|
||||||
{
|
|
||||||
throw EXCEPTION("Unexpected value");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queue->waiters)
|
|
||||||
{
|
|
||||||
queue->cv.notify_all();
|
|
||||||
}
|
|
||||||
|
|
||||||
Emu.GetEventManager().UnregisterKey(queue->key);
|
Emu.GetEventManager().UnregisterKey(queue->key);
|
||||||
Emu.GetIdManager().remove<lv2_event_queue_t>(equeue_id);
|
Emu.GetIdManager().remove<lv2_event_queue_t>(equeue_id);
|
||||||
|
|
||||||
|
// signal all threads to return CELL_ECANCELED
|
||||||
|
for (auto& thread : queue->sq)
|
||||||
|
{
|
||||||
|
thread->signal();
|
||||||
|
}
|
||||||
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,15 +137,11 @@ s32 sys_event_queue_tryreceive(u32 equeue_id, vm::ptr<sys_event_t> event_array,
|
|||||||
|
|
||||||
s32 count = 0;
|
s32 count = 0;
|
||||||
|
|
||||||
while (!queue->waiters && count < size && queue->events.size())
|
while (queue->sq.empty() && count < size && queue->events.size())
|
||||||
{
|
{
|
||||||
auto& dest = event_array[count++];
|
auto& dest = event_array[count++];
|
||||||
|
|
||||||
auto& event = queue->events.front();
|
std::tie(dest.source, dest.data1, dest.data2, dest.data3) = queue->events.front();
|
||||||
dest.source = event.source;
|
|
||||||
dest.data1 = event.data1;
|
|
||||||
dest.data2 = event.data2;
|
|
||||||
dest.data3 = event.data3;
|
|
||||||
|
|
||||||
queue->events.pop_front();
|
queue->events.pop_front();
|
||||||
}
|
}
|
||||||
@ -147,9 +151,9 @@ s32 sys_event_queue_tryreceive(u32 equeue_id, vm::ptr<sys_event_t> event_array,
|
|||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 sys_event_queue_receive(PPUThread& CPU, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout)
|
s32 sys_event_queue_receive(PPUThread& ppu, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout)
|
||||||
{
|
{
|
||||||
sys_event.Log("sys_event_queue_receive(equeue_id=0x%x, event=*0x%x, timeout=0x%llx)", equeue_id, dummy_event, timeout);
|
sys_event.Log("sys_event_queue_receive(equeue_id=0x%x, *0x%x, timeout=0x%llx)", equeue_id, dummy_event, timeout);
|
||||||
|
|
||||||
const u64 start_time = get_system_time();
|
const u64 start_time = get_system_time();
|
||||||
|
|
||||||
@ -167,36 +171,47 @@ s32 sys_event_queue_receive(PPUThread& CPU, u32 equeue_id, vm::ptr<sys_event_t>
|
|||||||
return CELL_EINVAL;
|
return CELL_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// protocol is ignored in current implementation
|
if (queue->events.empty() || !queue->sq.empty())
|
||||||
queue->waiters++;
|
|
||||||
|
|
||||||
while (queue->events.empty())
|
|
||||||
{
|
{
|
||||||
CHECK_EMU_STATUS;
|
// add waiter; protocol is ignored in current implementation
|
||||||
|
sleep_queue_entry_t waiter(ppu, queue->sq);
|
||||||
|
|
||||||
if (queue->cancelled)
|
while (!ppu.unsignal())
|
||||||
{
|
{
|
||||||
return CELL_ECANCELED;
|
CHECK_EMU_STATUS;
|
||||||
}
|
|
||||||
|
|
||||||
if (timeout && get_system_time() - start_time > timeout)
|
if (timeout)
|
||||||
{
|
{
|
||||||
queue->waiters--;
|
const u64 passed = get_system_time() - start_time;
|
||||||
return CELL_ETIMEDOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
queue->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
|
if (passed >= timeout)
|
||||||
|
{
|
||||||
|
return CELL_ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppu.cv.wait_for(lv2_lock, std::chrono::microseconds(timeout - passed));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ppu.cv.wait(lv2_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// event data is returned in registers (second arg is not used)
|
if (queue->events.empty())
|
||||||
auto& event = queue->events.front();
|
{
|
||||||
CPU.GPR[4] = event.source;
|
if (Emu.GetIdManager().check_id<lv2_event_queue_t>(equeue_id))
|
||||||
CPU.GPR[5] = event.data1;
|
{
|
||||||
CPU.GPR[6] = event.data2;
|
throw EXCEPTION("Unexpected");
|
||||||
CPU.GPR[7] = event.data3;
|
}
|
||||||
|
|
||||||
|
return CELL_ECANCELED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// event data is returned in registers (dummy_event is not used)
|
||||||
|
std::tie(ppu.GPR[4], ppu.GPR[5], ppu.GPR[6], ppu.GPR[7]) = queue->events.front();
|
||||||
|
|
||||||
queue->events.pop_front();
|
queue->events.pop_front();
|
||||||
queue->waiters--;
|
|
||||||
|
|
||||||
return CELL_OK;
|
return CELL_OK;
|
||||||
}
|
}
|
||||||
@ -225,7 +240,7 @@ s32 sys_event_port_create(vm::ptr<u32> eport_id, s32 port_type, u64 name)
|
|||||||
|
|
||||||
if (port_type != SYS_EVENT_PORT_LOCAL)
|
if (port_type != SYS_EVENT_PORT_LOCAL)
|
||||||
{
|
{
|
||||||
sys_event.Error("sys_event_port_create(): invalid port_type (%d)", port_type);
|
sys_event.Error("sys_event_port_create(): unknown port type (%d)", port_type);
|
||||||
return CELL_EINVAL;
|
return CELL_EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "sleep_queue.h"
|
||||||
|
|
||||||
namespace vm { using namespace ps3; }
|
namespace vm { using namespace ps3; }
|
||||||
|
|
||||||
// Event Queue Type
|
// Event Queue Type
|
||||||
@ -68,22 +70,6 @@ struct sys_event_t
|
|||||||
be_t<u64> data3;
|
be_t<u64> data3;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct lv2_event_t
|
|
||||||
{
|
|
||||||
const u64 source;
|
|
||||||
const u64 data1;
|
|
||||||
const u64 data2;
|
|
||||||
const u64 data3;
|
|
||||||
|
|
||||||
lv2_event_t(u64 source, u64 data1, u64 data2, u64 data3)
|
|
||||||
: source(source)
|
|
||||||
, data1(data1)
|
|
||||||
, data2(data2)
|
|
||||||
, data3(data3)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct lv2_event_queue_t
|
struct lv2_event_queue_t
|
||||||
{
|
{
|
||||||
const u32 id;
|
const u32 id;
|
||||||
@ -93,26 +79,14 @@ struct lv2_event_queue_t
|
|||||||
const u64 key;
|
const u64 key;
|
||||||
const s32 size;
|
const s32 size;
|
||||||
|
|
||||||
std::deque<lv2_event_t> events;
|
// tuple elements: source, data1, data2, data3
|
||||||
std::atomic<bool> cancelled;
|
std::deque<std::tuple<u64, u64, u64, u64>> events;
|
||||||
|
|
||||||
// TODO: use sleep queue, possibly remove condition variable
|
sleep_queue_t sq;
|
||||||
std::condition_variable cv;
|
|
||||||
std::atomic<u32> waiters;
|
|
||||||
|
|
||||||
lv2_event_queue_t(u32 protocol, s32 type, u64 name, u64 key, s32 size);
|
lv2_event_queue_t(u32 protocol, s32 type, u64 name, u64 key, s32 size);
|
||||||
|
|
||||||
void push(lv2_lock_t& lv2_lock, u64 source, u64 data1, u64 data2, u64 data3)
|
void push(lv2_lock_t& lv2_lock, u64 source, u64 data1, u64 data2, u64 data3);
|
||||||
{
|
|
||||||
CHECK_LV2_LOCK(lv2_lock);
|
|
||||||
|
|
||||||
events.emplace_back(source, data1, data2, data3);
|
|
||||||
|
|
||||||
if (waiters)
|
|
||||||
{
|
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
REG_ID_TYPE(lv2_event_queue_t, 0x8D); // SYS_EVENT_QUEUE_OBJECT
|
REG_ID_TYPE(lv2_event_queue_t, 0x8D); // SYS_EVENT_QUEUE_OBJECT
|
||||||
@ -121,6 +95,7 @@ struct lv2_event_port_t
|
|||||||
{
|
{
|
||||||
const s32 type; // port type, must be SYS_EVENT_PORT_LOCAL
|
const s32 type; // port type, must be SYS_EVENT_PORT_LOCAL
|
||||||
const u64 name; // passed as event source (generated from id and process id if not set)
|
const u64 name; // passed as event source (generated from id and process id if not set)
|
||||||
|
|
||||||
std::weak_ptr<lv2_event_queue_t> queue; // event queue this port is connected to
|
std::weak_ptr<lv2_event_queue_t> queue; // event queue this port is connected to
|
||||||
|
|
||||||
lv2_event_port_t(s32 type, u64 name)
|
lv2_event_port_t(s32 type, u64 name)
|
||||||
@ -137,7 +112,7 @@ class PPUThread;
|
|||||||
// SysCalls
|
// SysCalls
|
||||||
s32 sys_event_queue_create(vm::ptr<u32> equeue_id, vm::ptr<sys_event_queue_attribute_t> attr, u64 event_queue_key, s32 size);
|
s32 sys_event_queue_create(vm::ptr<u32> equeue_id, vm::ptr<sys_event_queue_attribute_t> attr, u64 event_queue_key, s32 size);
|
||||||
s32 sys_event_queue_destroy(u32 equeue_id, s32 mode);
|
s32 sys_event_queue_destroy(u32 equeue_id, s32 mode);
|
||||||
s32 sys_event_queue_receive(PPUThread& CPU, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout);
|
s32 sys_event_queue_receive(PPUThread& ppu, u32 equeue_id, vm::ptr<sys_event_t> dummy_event, u64 timeout);
|
||||||
s32 sys_event_queue_tryreceive(u32 equeue_id, vm::ptr<sys_event_t> event_array, s32 size, vm::ptr<u32> number);
|
s32 sys_event_queue_tryreceive(u32 equeue_id, vm::ptr<sys_event_t> event_array, s32 size, vm::ptr<u32> number);
|
||||||
s32 sys_event_queue_drain(u32 event_queue_id);
|
s32 sys_event_queue_drain(u32 event_queue_id);
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ u64 get_timebased_time()
|
|||||||
LARGE_INTEGER count;
|
LARGE_INTEGER count;
|
||||||
if (!QueryPerformanceCounter(&count))
|
if (!QueryPerformanceCounter(&count))
|
||||||
{
|
{
|
||||||
throw EXCEPTION("System error 0x%x", GetLastError());
|
throw EXCEPTION("Unexpected");
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 time = count.QuadPart;
|
const u64 time = count.QuadPart;
|
||||||
@ -82,7 +82,7 @@ u64 get_system_time()
|
|||||||
LARGE_INTEGER count;
|
LARGE_INTEGER count;
|
||||||
if (!QueryPerformanceCounter(&count))
|
if (!QueryPerformanceCounter(&count))
|
||||||
{
|
{
|
||||||
throw EXCEPTION("System error 0x%x", GetLastError());
|
throw EXCEPTION("Unexpected");
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 time = count.QuadPart;
|
const u64 time = count.QuadPart;
|
||||||
@ -122,7 +122,7 @@ s32 sys_time_get_current_time(vm::ptr<s64> sec, vm::ptr<s64> nsec)
|
|||||||
LARGE_INTEGER count;
|
LARGE_INTEGER count;
|
||||||
if (!QueryPerformanceCounter(&count))
|
if (!QueryPerformanceCounter(&count))
|
||||||
{
|
{
|
||||||
throw EXCEPTION("System error 0x%x", GetLastError());
|
throw EXCEPTION("Unexpected");
|
||||||
}
|
}
|
||||||
|
|
||||||
// get time difference in nanoseconds
|
// get time difference in nanoseconds
|
||||||
|
Loading…
x
Reference in New Issue
Block a user