More thread work

This commit is contained in:
wheremyfoodat 2022-12-07 02:08:18 +02:00
parent 7547159a48
commit ca4726aba1
6 changed files with 82 additions and 28 deletions

View file

@ -60,6 +60,7 @@ private:
void switchToNextThread(); void switchToNextThread();
void rescheduleThreads(); void rescheduleThreads();
bool canThreadRun(const Thread& t); bool canThreadRun(const Thread& t);
bool shouldWaitOnObject(KernelObject* object);
std::optional<Handle> getPortHandle(const char* name); std::optional<Handle> getPortHandle(const char* name);
void deleteObjectData(KernelObject& object); void deleteObjectData(KernelObject& object);

View file

@ -121,8 +121,12 @@ struct Thread {
u64 waitingNanoseconds; u64 waitingNanoseconds;
// The tick this thread went to sleep on // The tick this thread went to sleep on
u64 sleepTick; u64 sleepTick;
// For WaitSynchronization: A vector of objects this thread is waiting for // For WaitSynchronization(N): A vector of objects this thread is waiting for
std::vector<Handle> waitList; std::vector<Handle> waitList;
// For WaitSynchronizationN: Shows whether the object should wait for all objects in the wait list or just one
bool waitAll;
// For WaitSynchronizationN: The "out" pointer
u32 outPointer;
// Thread context used for switching between threads // Thread context used for switching between threads
std::array<u32, 16> gprs; std::array<u32, 16> gprs;

View file

@ -37,6 +37,7 @@ void Kernel::clearEvent() {
logSVC("ClearEvent(event handle = %X)\n", handle); logSVC("ClearEvent(event handle = %X)\n", handle);
if (event == nullptr) [[unlikely]] { if (event == nullptr) [[unlikely]] {
Helpers::panic("Tried to clear non-existent event");
regs[0] = SVCResult::BadHandle; regs[0] = SVCResult::BadHandle;
return; return;
} }
@ -52,16 +53,34 @@ void Kernel::signalEvent() {
logSVC("SignalEvent(event handle = %X)\n", handle); logSVC("SignalEvent(event handle = %X)\n", handle);
printf("Stubbed SignalEvent!!\n"); printf("Stubbed SignalEvent!!\n");
/*
if (event == nullptr) [[unlikely]] { if (event == nullptr) [[unlikely]] {
regs[0] = SVCResult::BadHandle; Helpers::warn("Signalled non-existent event: %X\n", handle);
regs[0] = SVCResult::Success;
//regs[0] = SVCResult::BadHandle;
return; return;
} }
event->getData<Event>()->fired = true;
*/
regs[0] = SVCResult::Success; 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);
}
} }
// Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds) // Result WaitSynchronization1(Handle handle, s64 timeout_nanoseconds)
@ -82,6 +101,9 @@ void Kernel::waitSynchronization1() {
Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle); Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle);
} }
if (!shouldWaitOnObject(object)) {
regs[0] = SVCResult::Success;
} else {
regs[0] = SVCResult::Success; regs[0] = SVCResult::Success;
auto& t = threads[currentThreadIndex]; auto& t = threads[currentThreadIndex];
@ -91,6 +113,7 @@ void Kernel::waitSynchronization1() {
t.waitingNanoseconds = ns; t.waitingNanoseconds = ns;
t.waitList[0] = handle; t.waitList[0] = handle;
switchToNextThread(); switchToNextThread();
}
} }
// Result WaitSynchronizationN(s32* out, Handle* handles, s32 handlecount, bool waitAll, s64 timeout_nanoseconds) // Result WaitSynchronizationN(s32* out, Handle* handles, s32 handlecount, bool waitAll, s64 timeout_nanoseconds)
@ -101,11 +124,13 @@ void Kernel::waitSynchronizationN() {
u32 handleCount = regs[2]; u32 handleCount = regs[2];
bool waitAll = regs[3] != 0; bool waitAll = regs[3] != 0;
u32 ns2 = regs[4]; u32 ns2 = regs[4];
s32 pointer = regs[5]; s32 outPointer = regs[5]; // "out" pointer - shows which object got bonked if we're waiting on multiple objects
s64 ns = s64(ns1) | (s64(ns2) << 32); s64 ns = s64(ns1) | (s64(ns2) << 32);
logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d, timeout = %lld)\n", handles, handleCount, ns); logSVC("WaitSynchronizationN (handle pointer: %08X, count: %d, timeout = %lld)\n", handles, handleCount, ns);
ThreadStatus newStatus = waitAll ? ThreadStatus::WaitSyncAll : ThreadStatus::WaitSync1;
if (waitAll && handleCount > 1)
Helpers::panic("Trying to wait on more than 1 object");
auto& t = threads[currentThreadIndex]; auto& t = threads[currentThreadIndex];
t.waitList.resize(handleCount); t.waitList.resize(handleCount);
@ -124,12 +149,14 @@ void Kernel::waitSynchronizationN() {
} }
if (!isWaitable(object)) [[unlikely]] { if (!isWaitable(object)) [[unlikely]] {
Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle); //Helpers::panic("Tried to wait on a non waitable object. Type: %s, handle: %X\n", object->getTypeName(), handle);
} }
} }
regs[0] = SVCResult::Success; regs[0] = SVCResult::Success;
t.status = newStatus; t.status = ThreadStatus::WaitSyncAll;
t.waitAll = waitAll;
t.outPointer = outPointer;
t.waitingNanoseconds = ns; t.waitingNanoseconds = ns;
t.sleepTick = cpu.getTicks(); t.sleepTick = cpu.getTicks();
switchToNextThread(); switchToNextThread();

View file

@ -10,10 +10,15 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu)
threadIndices.reserve(appResourceLimits.maxThreads); threadIndices.reserve(appResourceLimits.maxThreads);
for (int i = 0; i < threads.size(); i++) { for (int i = 0; i < threads.size(); i++) {
threads[i].index = i; Thread& t = threads[i];
threads[i].tlsBase = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize;
threads[i].status = ThreadStatus::Dead; t.index = i;
threads[i].waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later t.tlsBase = VirtualAddrs::TLSBase + i * VirtualAddrs::TLSSize;
t.status = ThreadStatus::Dead;
t.waitList.reserve(10); // Reserve some space for the wait list to avoid further memory allocs later
// The state below isn't necessary to initialize but we do it anyways out of caution
t.outPointer = 0;
t.waitAll = false;
} }
setVersion(1, 69); setVersion(1, 69);

View file

@ -82,8 +82,8 @@ void Kernel::switchToNextThread() {
std::optional<int> newThreadIndex = getNextThread(); std::optional<int> newThreadIndex = getNextThread();
if (!newThreadIndex.has_value()) { if (!newThreadIndex.has_value()) {
Helpers::warn("Kernel tried to switch to the next thread but none found. Switching to thread 0\n"); Helpers::warn("Kernel tried to switch to the next thread but none found. Switching to random thread\n");
switchThread(0); switchThread(rand() % threadCount);
} else { } else {
switchThread(newThreadIndex.value()); switchThread(newThreadIndex.value());
} }
@ -302,3 +302,15 @@ bool Kernel::isWaitable(const KernelObject* object) {
return type == Event || type == Mutex || type == Port || type == Semaphore || type == Timer || type == Thread; return type == Event || type == Mutex || type == Port || type == Semaphore || type == Timer || type == Thread;
} }
// Returns whether we should wait on a sync object or not
bool Kernel::shouldWaitOnObject(KernelObject* object) {
switch (object->type) {
case KernelObjectType::Event: // We should wait on an event only if it has not been signalled
return !object->getData<Event>()->fired;
default:
Helpers::warn("Not sure whether to wait on object (type: %s)", object->getTypeName());
return true;
}
}

View file

@ -117,6 +117,11 @@ u32 Memory::read32(u32 vaddr) {
case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit; case ConfigMem::AppMemAlloc: return appResourceLimits.maxCommit;
case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does case 0x1FF81000: return 0; // TODO: Figure out what this config mem address does
default: default:
if (vaddr >= VirtualAddrs::VramStart && vaddr < VirtualAddrs::VramStart + VirtualAddrs::VramSize) {
Helpers::warn("VRAM read!\n");
return 0;
}
Helpers::panic("Unimplemented 32-bit read, addr: %08X", vaddr); Helpers::panic("Unimplemented 32-bit read, addr: %08X", vaddr);
break; break;
} }