mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-29 14:04:19 +01:00
Recalculate highest-priority waiters during cvar/address signaling
`SignalToAddress`/`ConditionVariableSignal` need to wake waiters in priority order, while threads are inserted in order this doesn't remain the case as priority updates don't reinsert the thread into `syncWaiters`. It was determined that reinsertion into `syncWaiters` would be fairly complex due to locking the `syncWaitersMutex` with the thread's mutexes. To avoid this, this commit instead sorts waiters by priority at signal time to always wake threads in the right order.
This commit is contained in:
parent
626008d8e2
commit
7f7352ed59
@ -330,15 +330,16 @@ namespace skyline::kernel::type {
|
|||||||
auto queue{syncWaiters.equal_range(key)};
|
auto queue{syncWaiters.equal_range(key)};
|
||||||
|
|
||||||
if (queue.first != queue.second) {
|
if (queue.first != queue.second) {
|
||||||
// If we found a thread then we need to remove it from the queue
|
// If threads are waiting on us still then we need to remove the highest priority thread from the queue
|
||||||
thread = queue.first->second;
|
auto it{std::min_element(queue.first, queue.second, [](const SyncWaiters::value_type &lhs, const SyncWaiters::value_type &rhs) { return lhs.second->priority < rhs.second->priority; })};
|
||||||
|
thread = it->second;
|
||||||
conditionVariable = thread->waitConditionVariable;
|
conditionVariable = thread->waitConditionVariable;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (conditionVariable != key)
|
if (conditionVariable != key)
|
||||||
Logger::Warn("Condition variable mismatch: 0x{:X} != 0x{:X}", conditionVariable, key);
|
Logger::Warn("Condition variable mismatch: 0x{:X} != 0x{:X}", conditionVariable, key);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
syncWaiters.erase(queue.first);
|
syncWaiters.erase(it);
|
||||||
waiterCount--;
|
waiterCount--;
|
||||||
} else if (queue.first == queue.second) {
|
} else if (queue.first == queue.second) {
|
||||||
// If we didn't find a thread then we need to clear the boolean flag denoting that there are no more threads waiting on this conditional variable
|
// If we didn't find a thread then we need to clear the boolean flag denoting that there are no more threads waiting on this conditional variable
|
||||||
@ -488,8 +489,23 @@ namespace skyline::kernel::type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
i32 waiterCount{amount};
|
i32 waiterCount{amount};
|
||||||
for (auto it{queue.first}; it != queue.second && (amount <= 0 || waiterCount); it = syncWaiters.erase(it), waiterCount--)
|
boost::container::small_vector<SyncWaiters::iterator, 10> orderedThreads;
|
||||||
state.scheduler->InsertThread(it->second);
|
for (auto it{queue.first}; it != queue.second; it++) {
|
||||||
|
// While threads should generally be inserted in priority order, they may not always be due to the way the kernel handles priority updates
|
||||||
|
// As a result, we need to create a second list of threads ordered by priority to ensure that we wake up the highest priority threads first
|
||||||
|
auto thread{it->second};
|
||||||
|
orderedThreads.insert(std::upper_bound(orderedThreads.begin(), orderedThreads.end(), thread->priority.load(), [](const i8 priority, const SyncWaiters::iterator &it) { return it->second->priority > priority; }), it);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &it : orderedThreads) {
|
||||||
|
auto thread{it->second};
|
||||||
|
|
||||||
|
syncWaiters.erase(it);
|
||||||
|
state.scheduler->InsertThread(thread);
|
||||||
|
|
||||||
|
if (--waiterCount == 0 && amount > 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user