mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 23:25:40 +12:00
[Kernel] Add proper WakeUpAllThreads
This commit is contained in:
parent
2063e84bb1
commit
de537fedfb
2 changed files with 48 additions and 36 deletions
|
@ -35,41 +35,15 @@ bool Kernel::signalEvent(Handle handle) {
|
||||||
|
|
||||||
// Check if there's any thread waiting on this event
|
// Check if there's any thread waiting on this event
|
||||||
if (event->waitlist != 0) {
|
if (event->waitlist != 0) {
|
||||||
// Wake up every single thread in the waitlist using a bit scanning algorithm
|
// One-shot events get cleared once they are acquired by some thread and only wake up 1 thread at a time
|
||||||
while (event->waitlist != 0) {
|
if (event->resetType == ResetType::OneShot) {
|
||||||
const uint index = std::countr_zero(event->waitlist); // Get one of the set bits to see which thread is waiting
|
int index = wakeupOneThread(event->waitlist, handle); // Wake up one thread with the highest priority
|
||||||
event->waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit
|
event->waitlist ^= (1ull << index); // Remove thread from waitlist
|
||||||
|
|
||||||
// Get the thread we'll be signalling
|
|
||||||
Thread& t = threads[index];
|
|
||||||
switch (t.status) {
|
|
||||||
case ThreadStatus::WaitSync1:
|
|
||||||
t.status = ThreadStatus::Ready;
|
|
||||||
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ThreadStatus::WaitSyncAny:
|
|
||||||
t.status = ThreadStatus::Ready;
|
|
||||||
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
|
|
||||||
|
|
||||||
// Get the index of the event in the object's waitlist, write it to r1
|
|
||||||
for (size_t i = 0; i < t.waitList.size(); i++) {
|
|
||||||
if (t.waitList[i] == handle) {
|
|
||||||
t.gprs[1] = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ThreadStatus::WaitSyncAll:
|
|
||||||
Helpers::panic("SignalEvent: Thread on WaitSyncAll");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// One-shot events get cleared once they are acquired by some thread
|
|
||||||
if (event->resetType == ResetType::OneShot)
|
|
||||||
event->fired = false;
|
event->fired = false;
|
||||||
|
} else {
|
||||||
|
wakeupAllThreads(event->waitlist, handle);
|
||||||
|
event->waitlist = 0; // No threads waiting;
|
||||||
|
}
|
||||||
|
|
||||||
// We must reschedule our threads if we signalled one. Some games such as FE: Awakening rely on this
|
// We must reschedule our threads if we signalled one. Some games such as FE: Awakening rely on this
|
||||||
// If this does not happen, we can have phenomena such as a thread waiting up a higher priority thread,
|
// If this does not happen, we can have phenomena such as a thread waiting up a higher priority thread,
|
||||||
|
|
|
@ -288,6 +288,40 @@ int Kernel::wakeupOneThread(u64 waitlist, Handle handle) {
|
||||||
return threadIndex;
|
return threadIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wake up every single thread in the waitlist using a bit scanning algorithm
|
||||||
|
void Kernel::wakeupAllThreads(u64 waitlist, Handle handle) {
|
||||||
|
while (waitlist != 0) {
|
||||||
|
const uint index = std::countr_zero(waitlist); // Get one of the set bits to see which thread is waiting
|
||||||
|
waitlist ^= (1ull << index); // Remove thread from waitlist by toggling its bit
|
||||||
|
|
||||||
|
// Get the thread we'll be signalling
|
||||||
|
Thread& t = threads[index];
|
||||||
|
switch (t.status) {
|
||||||
|
case ThreadStatus::WaitSync1:
|
||||||
|
t.status = ThreadStatus::Ready;
|
||||||
|
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ThreadStatus::WaitSyncAny:
|
||||||
|
t.status = ThreadStatus::Ready;
|
||||||
|
t.gprs[0] = SVCResult::Success; // The thread did not timeout, so write success to r0
|
||||||
|
|
||||||
|
// Get the index of the event in the object's waitlist, write it to r1
|
||||||
|
for (size_t i = 0; i < t.waitList.size(); i++) {
|
||||||
|
if (t.waitList[i] == handle) {
|
||||||
|
t.gprs[1] = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ThreadStatus::WaitSyncAll:
|
||||||
|
Helpers::panic("WakeupAllThreads: Thread on WaitSyncAll");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Make a thread sleep for a certain amount of nanoseconds at minimum
|
// Make a thread sleep for a certain amount of nanoseconds at minimum
|
||||||
void Kernel::sleepThread(s64 ns) {
|
void Kernel::sleepThread(s64 ns) {
|
||||||
if (ns < 0) {
|
if (ns < 0) {
|
||||||
|
@ -419,8 +453,12 @@ void Kernel::exitThread() {
|
||||||
aliveThreadCount--;
|
aliveThreadCount--;
|
||||||
|
|
||||||
// Check if any threads are sleeping, waiting for this thread to terminate, and wake them up
|
// Check if any threads are sleeping, waiting for this thread to terminate, and wake them up
|
||||||
if (t.threadsWaitingForTermination != 0)
|
// This is how thread joining is implemented in the kernel - you wait on a thread, like any other wait object.
|
||||||
Helpers::panic("TODO: Implement threads sleeping until another thread terminates");
|
if (t.threadsWaitingForTermination != 0) {
|
||||||
|
// TODO: Handle cloned handles? Not sure how those interact with wait object signalling
|
||||||
|
wakeupAllThreads(t.threadsWaitingForTermination, t.handle);
|
||||||
|
t.threadsWaitingForTermination = 0; // No other threads waiting
|
||||||
|
}
|
||||||
|
|
||||||
switchToNextThread();
|
switchToNextThread();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue