Restructure ConditionalVariableSignal to avoid potential deadlock

Since InsertThread can block for paused threads, we need to ensure we unlock syncWaiterMutex when calling it.
This commit is contained in:
Billy Laws 2022-10-25 21:05:39 +01:00 committed by Mark Collins
parent f4a8328cef
commit 388245789f

View File

@ -230,6 +230,7 @@ namespace skyline::kernel::type {
} }
if (timeout > 0 && !state.scheduler->TimedWaitSchedule(std::chrono::nanoseconds(timeout))) { if (timeout > 0 && !state.scheduler->TimedWaitSchedule(std::chrono::nanoseconds(timeout))) {
{
std::unique_lock lock(syncWaiterMutex); std::unique_lock lock(syncWaiterMutex);
auto queue{syncWaiters.equal_range(key)}; auto queue{syncWaiters.equal_range(key)};
auto iterator{std::find(queue.first, queue.second, SyncWaiters::value_type{key, state.thread})}; auto iterator{std::find(queue.first, queue.second, SyncWaiters::value_type{key, state.thread})};
@ -237,7 +238,7 @@ namespace skyline::kernel::type {
if (syncWaiters.erase(iterator) == queue.second) if (syncWaiters.erase(iterator) == queue.second)
__atomic_store_n(key, false, __ATOMIC_SEQ_CST); __atomic_store_n(key, false, __ATOMIC_SEQ_CST);
lock.unlock(); }
state.scheduler->InsertThread(state.thread); state.scheduler->InsertThread(state.thread);
state.scheduler->WaitSchedule(); state.scheduler->WaitSchedule();
@ -259,16 +260,26 @@ namespace skyline::kernel::type {
void KProcess::ConditionalVariableSignal(u32 *key, i32 amount) { void KProcess::ConditionalVariableSignal(u32 *key, i32 amount) {
TRACE_EVENT_FMT("kernel", "ConditionalVariableSignal 0x{:X}", key); TRACE_EVENT_FMT("kernel", "ConditionalVariableSignal 0x{:X}", key);
i32 waiterCount{amount};
std::shared_ptr<type::KThread> thread;
do {
if (thread) {
state.scheduler->InsertThread(thread);
thread = {};
}
std::scoped_lock lock{syncWaiterMutex}; std::scoped_lock lock{syncWaiterMutex};
auto queue{syncWaiters.equal_range(key)}; auto queue{syncWaiters.equal_range(key)};
auto it{queue.first}; if (queue.first != queue.second && (amount <= 0 || waiterCount)) {
for (i32 waiterCount{amount}; it != queue.second && (amount <= 0 || waiterCount); it = syncWaiters.erase(it), waiterCount--) thread = queue.first->second;
state.scheduler->InsertThread(it->second); syncWaiters.erase(queue.first);
waiterCount--;
if (it == queue.second) } else if (queue.first == queue.second) {
__atomic_store_n(key, false, __ATOMIC_SEQ_CST); // We need to update the boolean flag denoting that there are no more threads waiting on this conditional variable __atomic_store_n(key, false, __ATOMIC_SEQ_CST); // We need to update the boolean flag denoting that there are no more threads waiting on this conditional variable
} }
} while (thread);
}
Result KProcess::WaitForAddress(u32 *address, u32 value, i64 timeout, ArbitrationType type) { Result KProcess::WaitForAddress(u32 *address, u32 value, i64 timeout, ArbitrationType type) {
TRACE_EVENT_FMT("kernel", "WaitForAddress 0x{:X}", address); TRACE_EVENT_FMT("kernel", "WaitForAddress 0x{:X}", address);