sys_event_queue: Fix ports disconnection after queue destruction

This commit is contained in:
Eladash 2020-04-09 19:05:43 +03:00 committed by Ivan
parent 37110098c7
commit 2b75df22d9
5 changed files with 46 additions and 21 deletions

View File

@ -2489,7 +2489,7 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
const auto queue = (std::lock_guard{group->mutex}, this->spup[spup].lock());
if (!queue)
if (!lv2_event_queue::check(queue))
{
spu_log.warning("sys_spu_thread_send_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected", spup, (value & 0x00ffffff), data);
ch_in_mbox.set_values(1, CELL_ENOTCONN);
@ -2521,7 +2521,7 @@ bool spu_thread::set_ch_value(u32 ch, u32 value)
const auto queue = (std::lock_guard{group->mutex}, this->spup[spup].lock());
if (!queue)
if (!lv2_event_queue::check(queue))
{
spu_log.warning("sys_spu_thread_throw_event(spup=%d, data0=0x%x, data1=0x%x): event queue not connected", spup, (value & 0x00ffffff), data);
return true;
@ -2904,14 +2904,14 @@ bool spu_thread::stop_and_signal(u32 code)
{
queue = v.second.lock();
if (queue)
if (lv2_event_queue::check(queue))
{
break;
}
}
}
if (!queue)
if (!lv2_event_queue::check(queue))
{
check_state();
return ch_in_mbox.set_values(1, CELL_EINVAL), true; // TODO: check error value
@ -3024,14 +3024,14 @@ bool spu_thread::stop_and_signal(u32 code)
{
if (spuq == v.first)
{
if ((queue = v.second.lock()))
if (queue = v.second.lock(); lv2_event_queue::check(queue))
{
break;
}
}
}
if (!queue)
if (!lv2_event_queue::check(queue))
{
return ch_in_mbox.set_values(1, CELL_EINVAL), true;
}

View File

@ -18,10 +18,29 @@ std::shared_ptr<lv2_event_queue> lv2_event_queue::find(u64 ipc_key)
if (ipc_key == SYS_EVENT_QUEUE_LOCAL)
{
// Invalid IPC key
return{};
return {};
}
return ipc_manager<lv2_event_queue, u64>::get(ipc_key);
auto queue = ipc_manager<lv2_event_queue, u64>::get(ipc_key);
if (queue && !queue->exists)
{
queue.reset();
}
return queue;
}
bool lv2_event_queue::check(const std::weak_ptr<lv2_event_queue>& wkptr)
{
const auto queue = wkptr.lock();
return queue && queue->exists;
}
bool lv2_event_queue::check(const std::shared_ptr<lv2_event_queue>& sptr)
{
return sptr && sptr->exists;
}
bool lv2_event_queue::send(lv2_event event)
@ -153,6 +172,7 @@ error_code sys_event_queue_destroy(ppu_thread& ppu, u32 equeue_id, s32 mode)
return CELL_EBUSY;
}
queue.exists = false;
return {};
});
@ -367,7 +387,7 @@ error_code sys_event_port_destroy(ppu_thread& ppu, u32 eport_id)
const auto port = idm::withdraw<lv2_obj, lv2_event_port>(eport_id, [](lv2_event_port& port) -> CellError
{
if (!port.queue.expired())
if (lv2_event_queue::check(port.queue))
{
return CELL_EISCONN;
}
@ -408,7 +428,7 @@ error_code sys_event_port_connect_local(u32 eport_id, u32 equeue_id)
return CELL_EINVAL;
}
if (!port->queue.expired())
if (lv2_event_queue::check(port->queue))
{
return CELL_EISCONN;
}
@ -445,7 +465,7 @@ error_code sys_event_port_connect_ipc(ppu_thread& ppu, u32 eport_id, u64 ipc_key
return CELL_EINVAL;
}
if (!port->queue.expired())
if (lv2_event_queue::check(port->queue))
{
return CELL_EISCONN;
}
@ -470,7 +490,7 @@ error_code sys_event_port_disconnect(ppu_thread& ppu, u32 eport_id)
return CELL_ESRCH;
}
if (port->queue.expired())
if (!lv2_event_queue::check(port->queue))
{
return CELL_ENOTCONN;
}
@ -490,7 +510,7 @@ error_code sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3)
const auto port = idm::get<lv2_obj, lv2_event_port>(eport_id, [&](lv2_event_port& port) -> CellError
{
if (const auto queue = port.queue.lock())
if (const auto queue = port.queue.lock(); lv2_event_queue::check(queue))
{
const u64 source = port.name ? port.name : (s64{process_getpid()} << 32) | u64{eport_id};

View File

@ -85,6 +85,7 @@ struct lv2_event_queue final : public lv2_obj
const u64 key;
const s32 size;
atomic_t<bool> exists = true; // Existence validation (workaround for shared-ptr ref-counting)
shared_mutex mutex;
std::deque<lv2_event> events;
std::deque<cpu_thread*> sq;
@ -107,6 +108,10 @@ struct lv2_event_queue final : public lv2_obj
// Get event queue by its global key
static std::shared_ptr<lv2_event_queue> find(u64 ipc_key);
// Check queue ptr validity (use 'exists' member)
static bool check(const std::weak_ptr<lv2_event_queue>&);
static bool check(const std::shared_ptr<lv2_event_queue>&);
};
struct lv2_event_port final : lv2_obj

View File

@ -1298,7 +1298,7 @@ error_code sys_spu_thread_group_connect_event(ppu_thread& ppu, u32 id, u32 eq, u
std::lock_guard lock(group->mutex);
if (!ep->expired())
if (lv2_event_queue::check(*ep))
{
return CELL_EBUSY;
}
@ -1340,7 +1340,7 @@ error_code sys_spu_thread_group_disconnect_event(ppu_thread& ppu, u32 id, u32 et
std::lock_guard lock(group->mutex);
if (ep->expired())
if (!lv2_event_queue::check(*ep))
{
return CELL_EINVAL;
}
@ -1373,7 +1373,7 @@ error_code sys_spu_thread_connect_event(ppu_thread& ppu, u32 id, u32 eq, u32 et,
auto& port = thread->spup[spup];
if (!port.expired())
if (lv2_event_queue::check(port))
{
return CELL_EISCONN;
}
@ -1406,7 +1406,7 @@ error_code sys_spu_thread_disconnect_event(ppu_thread& ppu, u32 id, u32 et, u8 s
auto& port = thread->spup[spup];
if (port.expired())
if (!lv2_event_queue::check(port))
{
return CELL_ENOTCONN;
}
@ -1546,7 +1546,7 @@ error_code sys_spu_thread_group_connect_event_all_threads(ppu_thread& ppu, u32 i
{
if (t)
{
if (!t->spup[port].expired())
if (lv2_event_queue::check(t->spup[port]))
{
found = false;
break;

View File

@ -82,7 +82,7 @@ error_code sys_timer_destroy(ppu_thread& ppu, u32 timer_id)
const auto timer = idm::withdraw<lv2_obj, lv2_timer>(timer_id, [&](lv2_timer& timer) -> CellError
{
if (std::shared_lock lock(timer.mutex); !timer.port.expired())
if (std::shared_lock lock(timer.mutex); lv2_event_queue::check(timer.port))
{
return CELL_EISCONN;
}
@ -231,7 +231,7 @@ error_code sys_timer_connect_event_queue(ppu_thread& ppu, u32 timer_id, u32 queu
std::lock_guard lock(timer.mutex);
if (!timer.port.expired())
if (lv2_event_queue::check(timer.port))
{
return CELL_EISCONN;
}
@ -269,7 +269,7 @@ error_code sys_timer_disconnect_event_queue(ppu_thread& ppu, u32 timer_id)
timer.state = SYS_TIMER_STATE_STOP;
if (timer.port.expired())
if (!lv2_event_queue::check(timer.port))
{
return CELL_ENOTCONN;
}