diff --git a/include/kernel/kernel.hpp b/include/kernel/kernel.hpp index 06f5dceb..e1d521c7 100644 --- a/include/kernel/kernel.hpp +++ b/include/kernel/kernel.hpp @@ -34,6 +34,7 @@ class Kernel { std::vector objects; std::vector portHandles; + std::vector mutexHandles; // Thread indices, sorted by priority std::vector threadIndices; diff --git a/src/core/kernel/kernel.cpp b/src/core/kernel/kernel.cpp index e7b34923..a3aad0fd 100644 --- a/src/core/kernel/kernel.cpp +++ b/src/core/kernel/kernel.cpp @@ -6,6 +6,7 @@ Kernel::Kernel(CPU& cpu, Memory& mem, GPU& gpu) : cpu(cpu), regs(cpu.regs()), mem(mem), handleCounter(0), serviceManager(regs, mem, gpu, currentProcess, *this) { objects.reserve(512); // Make room for a few objects to avoid further memory allocs later + mutexHandles.reserve(8); portHandles.reserve(32); threadIndices.reserve(appResourceLimits.maxThreads); @@ -139,6 +140,7 @@ void Kernel::reset() { deleteObjectData(object); } objects.clear(); + mutexHandles.clear(); portHandles.clear(); threadIndices.clear(); serviceManager.reset(); diff --git a/src/core/kernel/threads.cpp b/src/core/kernel/threads.cpp index 2e39b620..440045d3 100644 --- a/src/core/kernel/threads.cpp +++ b/src/core/kernel/threads.cpp @@ -168,6 +168,11 @@ Handle Kernel::makeMutex(bool locked) { moo->ownerThread = currentThreadIndex; } + // Push the new mutex to our list of mutex handles + // We need a list of mutex handles so that when a thread is killed, we can look which mutexes from this list the thread owns and free them + // Alternatively this could be a per-thread list, but I don't want to push_back and remove on every mutex lock and release + // Since some mutexes like the APT service mutex are locked and unlocked constantly, while ExitThread is a relatively "rare" SVC + mutexHandles.push_back(ret); return ret; } @@ -466,6 +471,23 @@ void Kernel::setThreadPriority() { void Kernel::exitThread() { logSVC("ExitThread\n"); + // Find which mutexes this thread owns, release them + for (auto handle : mutexHandles) { + KernelObject* object = getObject(handle, KernelObjectType::Mutex); + + // Make sure that the handle actually matches to a mutex, and if our exiting thread owns the mutex, release it + if (object != nullptr) { + Mutex* moo = object->getData(); + + if (moo->locked && moo->ownerThread == currentThreadIndex) { + // Release the mutex by setting lock count to 1 and releasing it once. We set lock count to 1 since it's a recursive mutex + // Therefore if its lock count was > 1, simply calling releaseMutex would not fully release it + moo->lockCount = 1; + releaseMutex(moo); + } + } + } + // Remove the index of this thread from the thread indices vector for (int i = 0; i < threadIndices.size(); i++) { if (threadIndices[i] == currentThreadIndex)