summaryrefslogtreecommitdiffstats
path: root/src/core/hle/svc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/svc.cpp')
-rw-r--r--src/core/hle/svc.cpp80
1 files changed, 47 insertions, 33 deletions
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index 2ca270de3..2b242ff98 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();
@@ -270,27 +272,27 @@ 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;
+ thread->wait_objects = {object};
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;
+ 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);
+ 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.
return ERR_SYNC_TIMEOUT;
}
- object->Acquire();
+ object->Acquire(thread);
return RESULT_SUCCESS;
}
@@ -324,19 +326,14 @@ 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(),
- [](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;
@@ -350,22 +347,20 @@ 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) {
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
thread->wait_objects = std::move(objects);
// 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
@@ -373,13 +368,14 @@ 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(),
- [](const ObjectPtr& object) { return !object->ShouldWait(); });
+ 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
Kernel::WaitObject* object = itr->get();
- object->Acquire();
+ object->Acquire(thread);
*out = std::distance(objects.begin(), itr);
return RESULT_SUCCESS;
}
@@ -392,28 +388,24 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha
return ERR_SYNC_TIMEOUT;
// Put the thread to sleep
- thread->status = THREADSTATUS_WAIT_SYNCH;
-
- // Clear the thread's waitlist, we won't use it for wait_all = false
- thread->wait_objects.clear();
+ thread->status = THREADSTATUS_WAIT_SYNCH_ANY;
// 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<int>(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.
}
+ 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.
// 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 +440,9 @@ static ResultCode ArbitrateAddress(Kernel::Handle handle, u32 address, u32 type,
auto res = arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(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 +569,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 +583,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 +603,13 @@ 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;
}
@@ -844,11 +849,18 @@ static ResultCode CancelTimer(Kernel::Handle handle) {
static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
+ // Don't attempt to yield execution if there are no available threads to run,
+ // this way we avoid a useless reschedule to the idle thread.
+ if (nanoseconds == 0 && !Kernel::HaveReadyThreads())
+ return;
+
// Sleep current thread and check for next thread to schedule
Kernel::WaitCurrentThread_Sleep();
// 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
@@ -890,7 +902,11 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si
return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
- if (addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) {
+ // TODO(Subv): Processes with memory type APPLICATION are not allowed
+ // to create memory blocks with addr = 0, any attempts to do so
+ // should return error 0xD92007EA.
+ if ((addr < Memory::PROCESS_IMAGE_VADDR || addr + size > Memory::SHARED_MEMORY_VADDR_END) &&
+ addr != 0) {
return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::OS,
ErrorSummary::InvalidArgument, ErrorLevel::Usage);
}
@@ -1184,8 +1200,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);
}