mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-07 14:45:41 +12:00
[Kernel] Make WaitSyncN better
This commit is contained in:
parent
f575d4db82
commit
33158c7908
6 changed files with 128 additions and 56 deletions
|
@ -62,6 +62,8 @@ public:
|
|||
Handle makeMutex(bool locked = false); // Needs to be public to be accessible to the APT/DSP services
|
||||
Handle makeSemaphore(u32 initialCount, u32 maximumCount); // Needs to be public to be accessible to the service manager port
|
||||
|
||||
// Signals an event, returns true on success or false if the event does not exist
|
||||
bool signalEvent(Handle e);
|
||||
private:
|
||||
void signalArbiter(u32 waitingAddress, s32 threadCount);
|
||||
void sleepThread(s64 ns);
|
||||
|
@ -103,9 +105,7 @@ private:
|
|||
|
||||
// SVC implementations
|
||||
void arbitrateAddress();
|
||||
void clearEvent();
|
||||
void createAddressArbiter();
|
||||
void createEvent();
|
||||
void createMemoryBlock();
|
||||
void createThread();
|
||||
void controlMemory();
|
||||
|
@ -123,10 +123,12 @@ private:
|
|||
void getThreadPriority();
|
||||
void sendSyncRequest();
|
||||
void setThreadPriority();
|
||||
void signalEvent();
|
||||
void svcClearEvent();
|
||||
void svcCloseHandle();
|
||||
void svcCreateEvent();
|
||||
void svcCreateMutex();
|
||||
void svcReleaseMutex();
|
||||
void svcSignalEvent();
|
||||
void svcSleepThread();
|
||||
void connectToPort();
|
||||
void outputDebugString();
|
||||
|
|
|
@ -105,7 +105,8 @@ enum class ThreadStatus {
|
|||
Ready, // Ready to run
|
||||
WaitArbiter, // Waiting on an address arbiter
|
||||
WaitSleep, // Waiting due to a SleepThread SVC
|
||||
WaitSync1, // Waiting for AT LEAST one sync object in its wait list to be ready
|
||||
WaitSync1, // Waiting for the single object in the wait list to be ready
|
||||
WaitSyncAny, // Wait for one object of the many that might be in the wait list to be ready
|
||||
WaitSyncAll, // Waiting for ALL sync objects in its wait list to be ready
|
||||
WaitIPC, // Waiting for the reply from an IPC request
|
||||
Dormant, // Created but not yet made ready
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "kernel.hpp"
|
||||
#include "cpu.hpp"
|
||||
#include <utility>
|
||||
|
||||
const char* Kernel::resetTypeToString(u32 type) {
|
||||
switch (type) {
|
||||
|
@ -16,8 +17,45 @@ Handle Kernel::makeEvent(ResetType resetType) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool Kernel::signalEvent(Handle handle) {
|
||||
KernelObject* object = getObject(handle, KernelObjectType::Event);
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
Helpers::panic("Tried to signal non-existent event");
|
||||
return false;
|
||||
}
|
||||
|
||||
Event* event = object->getData<Event>();
|
||||
event->fired = true;
|
||||
|
||||
if (event->waitlist != 0) {
|
||||
Helpers::panic("Tried to signal event with a waitlist");
|
||||
}
|
||||
/*
|
||||
switch (event->resetType) {
|
||||
case ResetType::OneShot:
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
Thread& t = threads[i];
|
||||
|
||||
if (t.status == ThreadStatus::WaitSync1 && t.waitList[0] == handle) {
|
||||
t.status = ThreadStatus::Ready;
|
||||
break;
|
||||
}
|
||||
else if (t.status == ThreadStatus::WaitSyncAll) {
|
||||
Helpers::panic("Trying to SignalEvent when a thread is waiting on multiple objects");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Helpers::panic("Signaled event of unimplemented type: %d", event->resetType);
|
||||
}
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Result CreateEvent(Handle* event, ResetType resetType)
|
||||
void Kernel::createEvent() {
|
||||
void Kernel::svcCreateEvent() {
|
||||
const u32 outPointer = regs[0];
|
||||
const u32 resetType = regs[1];
|
||||
|
||||
|
@ -31,7 +69,7 @@ void Kernel::createEvent() {
|
|||
}
|
||||
|
||||
// Result ClearEvent(Handle event)
|
||||
void Kernel::clearEvent() {
|
||||
void Kernel::svcClearEvent() {
|
||||
const Handle handle = regs[0];
|
||||
const auto event = getObject(handle, KernelObjectType::Event);
|
||||
logSVC("ClearEvent(event handle = %X)\n", handle);
|
||||
|
@ -47,38 +85,15 @@ void Kernel::clearEvent() {
|
|||
}
|
||||
|
||||
// Result SignalEvent(Handle event)
|
||||
void Kernel::signalEvent() {
|
||||
void Kernel::svcSignalEvent() {
|
||||
const Handle handle = regs[0];
|
||||
const auto event = getObject(handle, KernelObjectType::Event);
|
||||
logSVC("SignalEvent(event handle = %X)\n", handle);
|
||||
|
||||
if (event == nullptr) [[unlikely]] {
|
||||
logThread("Signalled non-existent event: %X\n", handle);
|
||||
if (!signalEvent(handle)) {
|
||||
Helpers::panic("Signalled non-existent event: %X\n", handle);
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
} else {
|
||||
regs[0] = SVCResult::Success;
|
||||
//regs[0] = SVCResult::BadHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
auto eventData = event->getData<Event>();
|
||||
eventData->fired = true;
|
||||
|
||||
switch (eventData->resetType) {
|
||||
case ResetType::OneShot:
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
Thread& t = threads[i];
|
||||
|
||||
if (t.status == ThreadStatus::WaitSync1 && t.waitList[0] == handle) {
|
||||
t.status = ThreadStatus::Ready;
|
||||
break;
|
||||
} else if (t.status == ThreadStatus::WaitSyncAll) {
|
||||
Helpers::panic("Trying to SignalEvent when a thread is waiting on multiple objects");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Helpers::panic("Signaled event of unimplemented type: %d", eventData->resetType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -131,7 +146,7 @@ void Kernel::waitSynchronizationN() {
|
|||
// TODO: Are these arguments even correct?
|
||||
s32 ns1 = regs[0];
|
||||
u32 handles = regs[1];
|
||||
u32 handleCount = regs[2];
|
||||
s32 handleCount = regs[2];
|
||||
bool waitAll = regs[3] != 0;
|
||||
u32 ns2 = regs[4];
|
||||
s32 outPointer = regs[5]; // "out" pointer - shows which object got bonked if we're waiting on multiple objects
|
||||
|
@ -142,37 +157,78 @@ void Kernel::waitSynchronizationN() {
|
|||
if (waitAll && handleCount > 1)
|
||||
Helpers::panic("Trying to wait on more than 1 object");
|
||||
|
||||
auto& t = threads[currentThreadIndex];
|
||||
t.waitList.resize(handleCount);
|
||||
|
||||
for (uint i = 0; i < handleCount; i++) {
|
||||
if (handleCount < 0)
|
||||
Helpers::panic("WaitSyncN: Invalid handle count");
|
||||
|
||||
using WaitObject = std::pair<Handle, KernelObject*>;
|
||||
std::vector<WaitObject> waitObjects(handleCount);
|
||||
|
||||
// We don't actually need to wait if waitAll == true unless one of the objects is not ready
|
||||
bool allReady = true; // Default initialize to true, set to fault if one of the objects is not ready
|
||||
|
||||
// Tracks whether at least one object is ready, + the index of the first ready object
|
||||
// This is used when waitAll == false, because if one object is already available then we can skip the sleeping
|
||||
bool oneObjectReady = false;
|
||||
s32 firstReadyObjectIndex = 0;
|
||||
|
||||
for (s32 i = 0; i < handleCount; i++) {
|
||||
Handle handle = mem.read32(handles);
|
||||
handles += sizeof(Handle);
|
||||
|
||||
t.waitList[i] = handle;
|
||||
|
||||
auto object = getObject(handle);
|
||||
// Panic if one of the objects is not even an object
|
||||
if (object == nullptr) [[unlikely]] {
|
||||
Helpers::panic("WaitSynchronizationN: Bad object handle %X\n", handle);
|
||||
regs[0] = SVCResult::BadHandle;
|
||||
return;
|
||||
}
|
||||
|
||||
// Panic if one of the objects is not a valid sync object
|
||||
if (!isWaitable(object)) [[unlikely]] {
|
||||
Helpers::panic("Tried to wait on a non waitable object in WaitSyncN. Type: %s, handle: %X\n",
|
||||
Helpers::panic("Tried to wait on a non waitable object in WaitSyncN. Type: %s, handle: %X\n",
|
||||
object->getTypeName(), handle);
|
||||
}
|
||||
|
||||
// Add the current thread to the object's wait list
|
||||
object->getWaitlist() |= (1ull << currentThreadIndex);
|
||||
if (shouldWaitOnObject(object)) {
|
||||
allReady = false; // Derp, not all objects are ready :(
|
||||
} else { /// At least one object is ready to be acquired ahead of time. If it's the first one, write it down
|
||||
if (!oneObjectReady) {
|
||||
oneObjectReady = true;
|
||||
firstReadyObjectIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
waitObjects[i] = {handle, object};
|
||||
}
|
||||
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[1] = waitAll ? handleCount - 1 : 0; // Index of the handle that triggered the exit. STUBBED
|
||||
t.status = ThreadStatus::WaitSyncAll;
|
||||
t.waitAll = waitAll;
|
||||
t.outPointer = outPointer;
|
||||
t.waitingNanoseconds = ns;
|
||||
t.sleepTick = cpu.getTicks();
|
||||
switchToNextThread();
|
||||
auto& t = threads[currentThreadIndex];
|
||||
|
||||
// We only need to wait on one object. Easy...?!
|
||||
if (!waitAll) {
|
||||
// If there's ready objects, acquire the first one and return
|
||||
if (oneObjectReady) {
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[1] = firstReadyObjectIndex; // Return index of the acquired object
|
||||
acquireSyncObject(waitObjects[firstReadyObjectIndex].second, t); // Acquire object
|
||||
return;
|
||||
}
|
||||
|
||||
Helpers::panic("WaitSyncAny can't instantly acquire :(");
|
||||
regs[0] = SVCResult::Success;
|
||||
regs[1] = handleCount - 1; // FIX THIS
|
||||
t.waitList.resize(handleCount);
|
||||
t.status = ThreadStatus::WaitSyncAny;
|
||||
t.outPointer = outPointer;
|
||||
t.waitingNanoseconds = ns;
|
||||
t.sleepTick = cpu.getTicks();
|
||||
|
||||
for (s32 i = 0; i < handleCount; i++) {
|
||||
t.waitList[i] = waitObjects[i].first; // Add object to this thread's waitlist
|
||||
waitObjects[i].second->getWaitlist() |= (1ull << currentThreadIndex); // And add the thread to the object's waitlist
|
||||
}
|
||||
|
||||
switchToNextThread();
|
||||
} else {
|
||||
Helpers::panic("WaitSynchronizatioN with waitAll");
|
||||
}
|
||||
}
|
|
@ -35,9 +35,9 @@ void Kernel::serviceSVC(u32 svc) {
|
|||
case 0x0C: setThreadPriority(); break;
|
||||
case 0x13: svcCreateMutex(); break;
|
||||
case 0x14: svcReleaseMutex(); break;
|
||||
case 0x17: createEvent(); break;
|
||||
case 0x18: signalEvent(); break;
|
||||
case 0x19: clearEvent(); break;
|
||||
case 0x17: svcCreateEvent(); break;
|
||||
case 0x18: svcSignalEvent(); break;
|
||||
case 0x19: svcClearEvent(); break;
|
||||
case 0x1E: createMemoryBlock(); break;
|
||||
case 0x1F: mapMemoryBlock(); break;
|
||||
case 0x21: createAddressArbiter(); break;
|
||||
|
|
|
@ -47,12 +47,14 @@ void Kernel::sortThreads() {
|
|||
bool Kernel::canThreadRun(const Thread& t) {
|
||||
if (t.status == ThreadStatus::Ready) {
|
||||
return true;
|
||||
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1 || t.status == ThreadStatus::WaitSyncAll) {
|
||||
} else if (t.status == ThreadStatus::WaitSleep || t.status == ThreadStatus::WaitSync1
|
||||
|| t.status == ThreadStatus::WaitSyncAny || t.status == ThreadStatus::WaitSyncAll) {
|
||||
const u64 elapsedTicks = cpu.getTicks() - t.sleepTick;
|
||||
|
||||
constexpr double ticksPerSec = double(CPU::ticksPerSec);
|
||||
constexpr double nsPerTick = ticksPerSec / 1000000000.0;
|
||||
|
||||
// TODO: Set r0 to the correct error code on timeout for WaitSync{1/Any/All}
|
||||
const s64 elapsedNs = s64(double(elapsedTicks) * nsPerTick);
|
||||
return elapsedNs >= t.waitingNanoseconds;
|
||||
}
|
||||
|
@ -193,6 +195,7 @@ void Kernel::sleepThreadOnArbiter(u32 waitingAddress) {
|
|||
switchToNextThread();
|
||||
}
|
||||
|
||||
// Acquires an object that is **ready to be acquired** without waiting on it
|
||||
void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
|
||||
switch (object->type) {
|
||||
case KernelObjectType::Mutex: {
|
||||
|
@ -202,6 +205,14 @@ void Kernel::acquireSyncObject(KernelObject* object, const Thread& thread) {
|
|||
break;
|
||||
}
|
||||
|
||||
case KernelObjectType::Event: {
|
||||
Event* e = object->getData<Event>();
|
||||
if (e->resetType == ResetType::OneShot) { // One-shot events automatically get cleared after waking up a thread
|
||||
e->fired = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: Helpers::panic("Acquiring unimplemented sync object %s", object->getTypeName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,6 +101,8 @@ void APTService::initialize(u32 messagePointer) {
|
|||
if (!notificationEvent.has_value() || !resumeEvent.has_value()) {
|
||||
notificationEvent = kernel.makeEvent(ResetType::OneShot);
|
||||
resumeEvent = kernel.makeEvent(ResetType::OneShot);
|
||||
|
||||
kernel.signalEvent(resumeEvent.value()); // Seems to be signalled on startup
|
||||
}
|
||||
|
||||
mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 3));
|
||||
|
|
Loading…
Add table
Reference in a new issue