From 38a90882a4230bb797ae3f6857e986d70f3bd265 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 1 Jan 2017 11:57:02 -0500 Subject: Kernel/Synch: Do not attempt a reschedule on every syscall. Not all syscalls should cause reschedules, this commit attempts to remedy that, however, it still does not cover all cases. --- src/core/hle/svc.cpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2ca270de3..5b538be22 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -248,6 +248,8 @@ static ResultCode SendSyncRequest(Kernel::Handle handle) { LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); + Core::System::GetInstance().PrepareReschedule(); + // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server // responds and cause a reschedule. return session->SendSyncRequest(); @@ -284,6 +286,8 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + Core::System::GetInstance().PrepareReschedule(); + // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread // resumes due to a signal in its wait objects. // Otherwise we retain the default value of timeout. @@ -366,6 +370,8 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + Core::System::GetInstance().PrepareReschedule(); + // This value gets set to -1 by default in this case, it is not modified after this. *out = -1; // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to @@ -414,6 +420,8 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + Core::System::GetInstance().PrepareReschedule(); + // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // signal in one of its wait objects. // Otherwise we retain the default value of timeout, and -1 in the out parameter @@ -448,6 +456,9 @@ static ResultCode ArbitrateAddress(Kernel::Handle handle, u32 address, u32 type, auto res = arbiter->ArbitrateAddress(static_cast(type), address, value, nanoseconds); + // TODO(Subv): Identify in which specific cases this call should cause a reschedule. + Core::System::GetInstance().PrepareReschedule(); + return res; } @@ -574,6 +585,8 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); + Core::System::GetInstance().PrepareReschedule(); + LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, name.c_str(), arg, stack_top, priority, processor_id, *out_handle); @@ -586,6 +599,7 @@ static void ExitThread() { LOG_TRACE(Kernel_SVC, "called, pc=0x%08X", Core::CPU().GetPC()); Kernel::ExitCurrentThread(); + Core::System::GetInstance().PrepareReschedule(); } /// Gets the priority for the specified thread @@ -605,6 +619,7 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { return ERR_INVALID_HANDLE; thread->SetPriority(priority); + Core::System::GetInstance().PrepareReschedule(); return RESULT_SUCCESS; } @@ -849,6 +864,8 @@ static void SleepThread(s64 nanoseconds) { // Create an event to wake the thread up after the specified nanosecond delay has passed Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds); + + Core::System::GetInstance().PrepareReschedule(); } /// This returns the total CPU ticks elapsed since the CPU was powered-on @@ -1184,8 +1201,6 @@ void CallSVC(u32 immediate) { if (info) { if (info->func) { info->func(); - // TODO(Subv): Not all service functions should cause a reschedule in all cases. - Core::System::GetInstance().PrepareReschedule(); } else { LOG_ERROR(Kernel_SVC, "unimplemented SVC function %s(..)", info->name); } -- cgit v1.2.3 From e6a7723f2f4b62279cd4f6d4b48eb02a9b60ffb6 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 1 Jan 2017 16:53:22 -0500 Subject: Kernel: Object ShouldWait and Acquire calls now take a thread as a parameter. This will be useful when implementing mutex priority inheritance. --- src/core/hle/svc.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 5b538be22..159ac0bf6 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -272,7 +272,7 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); - if (object->ShouldWait()) { + if (object->ShouldWait(thread)) { if (nano_seconds == 0) return ERR_SYNC_TIMEOUT; @@ -294,7 +294,7 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) return ERR_SYNC_TIMEOUT; } - object->Acquire(); + object->Acquire(thread); return RESULT_SUCCESS; } @@ -336,11 +336,11 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha if (wait_all) { bool all_available = std::all_of(objects.begin(), objects.end(), - [](const ObjectPtr& object) { return !object->ShouldWait(); }); + [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); }); if (all_available) { // We can acquire all objects right now, do so. for (auto& object : objects) - object->Acquire(); + object->Acquire(thread); // Note: In this case, the `out` parameter is not set, // and retains whatever value it had before. return RESULT_SUCCESS; @@ -380,12 +380,12 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha } else { // Find the first object that is acquirable in the provided list of objects auto itr = std::find_if(objects.begin(), objects.end(), - [](const ObjectPtr& object) { return !object->ShouldWait(); }); + [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); }); if (itr != objects.end()) { // We found a ready object, acquire it and set the result value Kernel::WaitObject* object = itr->get(); - object->Acquire(); + object->Acquire(thread); *out = std::distance(objects.begin(), itr); return RESULT_SUCCESS; } -- cgit v1.2.3 From 7abf1853907fe086753df0031262b668a2da88b0 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 1 Jan 2017 16:59:30 -0500 Subject: Kernel/Mutex: Implemented priority inheritance. The implementation is based on reverse engineering of the 3DS's kernel. A mutex holder's priority will be temporarily boosted to the best priority among any threads that want to acquire any of its held mutexes. When the holder releases the mutex, it's priority will be boosted to the best priority among the threads that want to acquire any of its remaining held mutexes. --- src/core/hle/svc.cpp | 9 --------- 1 file changed, 9 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 159ac0bf6..5d6359344 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -278,9 +278,6 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) return ERR_SYNC_TIMEOUT; object->AddWaitingThread(thread); - // TODO(Subv): Perform things like update the mutex lock owner's priority to - // prevent priority inversion. Currently this is done in Mutex::ShouldWait, - // but it should be moved to a function that is called from here. thread->status = THREADSTATUS_WAIT_SYNCH; // Create an event to wake the thread up after the specified nanosecond delay has passed @@ -359,9 +356,6 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Add the thread to each of the objects' waiting threads. for (auto& object : objects) { object->AddWaitingThread(thread); - // TODO(Subv): Perform things like update the mutex lock owner's priority to - // prevent priority inversion. Currently this is done in Mutex::ShouldWait, - // but it should be moved to a function that is called from here. } // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN @@ -409,9 +403,6 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Set the index of this object in the mapping of Objects -> index for this thread. thread->wait_objects_index[object->GetObjectId()] = static_cast(i); object->AddWaitingThread(thread); - // TODO(Subv): Perform things like update the mutex lock owner's priority to - // prevent priority inversion. Currently this is done in Mutex::ShouldWait, - // but it should be moved to a function that is called from here. } // Note: If no handles and no timeout were given, then the thread will deadlock, this is -- cgit v1.2.3 From b6a0355568ee327bef8957b9a2498897b96e1278 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 2 Jan 2017 13:53:10 -0500 Subject: Kernel/Mutex: Update a mutex priority when a thread stops waiting on it. --- src/core/hle/svc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 5d6359344..b6e34a9e9 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -373,8 +373,9 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha return ERR_SYNC_TIMEOUT; } else { // Find the first object that is acquirable in the provided list of objects - auto itr = std::find_if(objects.begin(), objects.end(), - [thread](const ObjectPtr& object) { return !object->ShouldWait(thread); }); + auto itr = std::find_if(objects.begin(), objects.end(), [thread](const ObjectPtr& object) { + return !object->ShouldWait(thread); + }); if (itr != objects.end()) { // We found a ready object, acquire it and set the result value -- cgit v1.2.3 From d3ff5b91e14356912589f9bac47fccbe79e07279 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 2 Jan 2017 19:38:08 -0500 Subject: Kernel/Mutex: Propagate thread priority changes to other threads inheriting the priority via mutexes --- src/core/hle/svc.cpp | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index b6e34a9e9..160f27c98 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -611,6 +611,12 @@ static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { return ERR_INVALID_HANDLE; thread->SetPriority(priority); + thread->UpdatePriority(); + + // Update the mutexes that this thread is waiting for + for (auto& mutex : thread->pending_mutexes) + mutex->UpdatePriority(); + Core::System::GetInstance().PrepareReschedule(); return RESULT_SUCCESS; } -- cgit v1.2.3 From cef5f45de2fd64f0728d4504d0ad7434cb8ac519 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Jan 2017 10:44:38 -0500 Subject: Kernel: Use different thread statuses when a thread calls WaitSynchronization1 and WaitSynchronizationN with wait_all = true. This commit removes the overly general THREADSTATUS_WAIT_SYNCH and replaces it with two more granular statuses: THREADSTATUS_WAIT_SYNCH_ANY when a thread waits on objects via WaitSynchronization1 or WaitSynchronizationN with wait_all = false. THREADSTATUS_WAIT_SYNCH_ALL when a thread waits on objects via WaitSynchronizationN with wait_all = true. --- src/core/hle/svc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 160f27c98..1e1ca5180 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -278,7 +278,7 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) return ERR_SYNC_TIMEOUT; object->AddWaitingThread(thread); - thread->status = THREADSTATUS_WAIT_SYNCH; + thread->status = THREADSTATUS_WAIT_SYNCH_ANY; // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); @@ -351,7 +351,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha return ERR_SYNC_TIMEOUT; // Put the thread to sleep - thread->status = THREADSTATUS_WAIT_SYNCH; + thread->status = THREADSTATUS_WAIT_SYNCH_ALL; // Add the thread to each of the objects' waiting threads. for (auto& object : objects) { @@ -393,7 +393,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha return ERR_SYNC_TIMEOUT; // Put the thread to sleep - thread->status = THREADSTATUS_WAIT_SYNCH; + thread->status = THREADSTATUS_WAIT_SYNCH_ANY; // Clear the thread's waitlist, we won't use it for wait_all = false thread->wait_objects.clear(); -- cgit v1.2.3 From fd95b6ee2606da4cd47c5f2916ad3b4f86c0e0f4 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Jan 2017 10:53:01 -0500 Subject: Kernel: Remove Thread::wait_objects_index and use wait_objects to hold all the objects that a thread is waiting on. --- src/core/hle/svc.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'src/core/hle/svc.cpp') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 1e1ca5180..855f3af82 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -277,6 +277,7 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) if (nano_seconds == 0) return ERR_SYNC_TIMEOUT; + thread->wait_objects = {object}; object->AddWaitingThread(thread); thread->status = THREADSTATUS_WAIT_SYNCH_ANY; @@ -325,11 +326,6 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha objects[i] = object; } - // Clear the mapping of wait object indices. - // We don't want any lingering state in this map. - // It will be repopulated later in the wait_all = false case. - thread->wait_objects_index.clear(); - if (wait_all) { bool all_available = std::all_of(objects.begin(), objects.end(), @@ -358,7 +354,6 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha object->AddWaitingThread(thread); } - // Set the thread's waitlist to the list of objects passed to WaitSynchronizationN thread->wait_objects = std::move(objects); // Create an event to wake the thread up after the specified nanosecond delay has passed @@ -395,17 +390,14 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Put the thread to sleep thread->status = THREADSTATUS_WAIT_SYNCH_ANY; - // Clear the thread's waitlist, we won't use it for wait_all = false - thread->wait_objects.clear(); - // Add the thread to each of the objects' waiting threads. for (size_t i = 0; i < objects.size(); ++i) { Kernel::WaitObject* object = objects[i].get(); - // Set the index of this object in the mapping of Objects -> index for this thread. - thread->wait_objects_index[object->GetObjectId()] = static_cast(i); object->AddWaitingThread(thread); } + thread->wait_objects = std::move(objects); + // Note: If no handles and no timeout were given, then the thread will deadlock, this is // consistent with hardware behavior. -- cgit v1.2.3