From 33158c790838776346556573fb17dbc135f073d9 Mon Sep 17 00:00:00 2001 From: wheremyfoodat Date: Fri, 21 Apr 2023 01:08:13 +0300 Subject: [PATCH] [Kernel] Make WaitSyncN better --- include/kernel/kernel.hpp | 8 +- include/kernel/kernel_types.hpp | 3 +- src/core/kernel/events.cpp | 152 ++++++++++++++++++++++---------- src/core/kernel/kernel.cpp | 6 +- src/core/kernel/threads.cpp | 13 ++- src/core/services/apt.cpp | 2 + 6 files changed, 128 insertions(+), 56 deletions(-) diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 85adf952..5e27e911 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -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(); diff --git a/include/kernel/kernel_types.hpp b/include/kernel/kernel_types.hpp index cd93a2c7..db9f9494 100644 --- a/include/kernel/kernel_types.hpp +++ b/include/kernel/kernel_types.hpp @@ -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 diff --git a/src/core/kernel/events.cpp b/src/core/kernel/events.cpp index 409e5747..e923170f 100644 --- a/src/core/kernel/events.cpp +++ b/src/core/kernel/events.cpp @@ -1,5 +1,6 @@ #include "kernel.hpp" #include "cpu.hpp" +#include 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->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(); - 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; + std::vector 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"); + } } \ No newline at end of file diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index 93b16b4a..730886ac 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -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; diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 6333247f..f997c77d 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -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(); + 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()); } } diff --git a/src/core/services/apt.cpp b/src/core/services/apt.cpp index 9eb796d7..365facfb 100644 --- a/src/core/services/apt.cpp +++ b/src/core/services/apt.cpp @@ -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));