diff options
| author | Subv <subv2112@gmail.com> | 2018-02-18 13:22:19 -0500 | 
|---|---|---|
| committer | Subv <subv2112@gmail.com> | 2018-02-18 13:25:48 -0500 | 
| commit | 94ee8fc97b6e3373d801d70efdd1b604ad03b85e (patch) | |
| tree | cfff767e6c98925e2394fdd337e6d862b5512d8b /src/core | |
| parent | f6e548fbc08e89bdb3683d055d121eb4710927f0 (diff) | |
 Kernel/IPC: Add a small delay after each SyncRequest to prevent thread starvation.
Ported from citra PR #3091
The delay specified here is from a Nintendo 3DS, and should be measured in a Nintendo Switch.
This change is enough to prevent Puyo Puyo Tetris's main thread starvation.
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/hle/kernel/server_session.cpp | 97 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_session.h | 14 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.cpp | 1 | ||||
| -rw-r--r-- | src/core/hle/kernel/thread.h | 1 | 
4 files changed, 63 insertions, 50 deletions
| diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 54481f7f1..5608418c3 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -57,6 +57,33 @@ void ServerSession::Acquire(Thread* thread) {      pending_requesting_threads.pop_back();  } +ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { +    auto& domain_message_header = context.GetDomainMessageHeader(); +    if (domain_message_header) { +        // If there is a DomainMessageHeader, then this is CommandType "Request" +        const u32 object_id{context.GetDomainMessageHeader()->object_id}; +        switch (domain_message_header->command) { +        case IPC::DomainMessageHeader::CommandType::SendMessage: +            return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); + +        case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { +            LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); + +            domain_request_handlers[object_id - 1] = nullptr; + +            IPC::ResponseBuilder rb{context, 2}; +            rb.Push(RESULT_SUCCESS); +            return RESULT_SUCCESS; +        } +        } + +        LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); +        ASSERT(false); +    } + +    return RESULT_SUCCESS; +} +  ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {      // The ServerSession received a sync request, this means that there's new data available      // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or @@ -67,46 +94,39 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {      context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,                                                Kernel::g_handle_table); -    // If the session has been converted to a domain, handle the doomain request +    ResultCode result = RESULT_SUCCESS; +    // If the session has been converted to a domain, handle the domain request      if (IsDomain()) { -        auto& domain_message_header = context.GetDomainMessageHeader(); -        if (domain_message_header) { -            // If there is a DomainMessageHeader, then this is CommandType "Request" -            const u32 object_id{context.GetDomainMessageHeader()->object_id}; -            switch (domain_message_header->command) { -            case IPC::DomainMessageHeader::CommandType::SendMessage: -                return domain_request_handlers[object_id - 1]->HandleSyncRequest(context); - -            case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: { -                LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id); - -                domain_request_handlers[object_id - 1] = nullptr; - -                IPC::ResponseBuilder rb{context, 2}; -                rb.Push(RESULT_SUCCESS); -                return RESULT_SUCCESS; -            } -            } - -            LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); -            ASSERT(false); -        } +        result = HandleDomainSyncRequest(context);          // If there is no domain header, the regular session handler is used +    } else if (hle_handler != nullptr) { +        // If this ServerSession has an associated HLE handler, forward the request to it. +        result = hle_handler->HandleSyncRequest(context);      } -    // If this ServerSession has an associated HLE handler, forward the request to it. -    ResultCode result{RESULT_SUCCESS}; -    if (hle_handler != nullptr) { -        // Attempt to translate the incoming request's command buffer. -        ResultCode translate_result = TranslateHLERequest(this); -        if (translate_result.IsError()) -            return translate_result; - -        result = hle_handler->HandleSyncRequest(context); -    } else { -        // Add the thread to the list of threads that have issued a sync request with this -        // server. -        pending_requesting_threads.push_back(std::move(thread)); +    if (thread->status == THREADSTATUS_RUNNING) { +        // Put the thread to sleep until the server replies, it will be awoken in +        // svcReplyAndReceive for LLE servers. +        thread->status = THREADSTATUS_WAIT_IPC; + +        if (hle_handler != nullptr) { +            // For HLE services, we put the request threads to sleep for a short duration to +            // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for +            // other reasons like an async callback. The IPC overhead is needed to prevent +            // starvation when a thread only does sync requests to HLE services while a +            // lower-priority thread is waiting to run. + +            // This delay was approximated in a homebrew application by measuring the average time +            // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC +            // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have +            // a high variance and vary between models. +            static constexpr u64 IPCDelayNanoseconds = 39000; +            thread->WakeAfterDelay(IPCDelayNanoseconds); +        } else { +            // Add the thread to the list of threads that have issued a sync request with this +            // server. +            pending_requesting_threads.push_back(std::move(thread)); +        }      }      // If this ServerSession does not have an HLE implementation, just wake up the threads waiting @@ -140,9 +160,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n      return std::make_tuple(std::move(server_session), std::move(client_session));  } - -ResultCode TranslateHLERequest(ServerSession* server_session) { -    // TODO(Subv): Implement this function once multiple concurrent processes are supported. -    return RESULT_SUCCESS; -}  } // namespace Kernel diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index 144692106..2da807042 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -21,6 +21,7 @@ class ServerSession;  class Session;  class SessionRequestHandler;  class Thread; +class HLERequestContext;  /**   * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS @@ -116,17 +117,12 @@ private:       */      static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown"); +    /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an +    /// object handle. +    ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context); +      /// When set to True, converts the session to a domain at the end of the command      bool convert_to_domain{};  }; -/** - * Performs command buffer translation for an HLE IPC request. - * The command buffer from the ServerSession thread's TLS is copied into a - * buffer and all descriptors in the buffer are processed. - * TODO(Subv): Implement this function, currently we do not support multiple processes running at - * once, but once that is implemented we'll need to properly translate all descriptors - * in the command buffer. - */ -ResultCode TranslateHLERequest(ServerSession* server_session);  } // namespace Kernel diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1a33cc6cb..130b669a0 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -284,6 +284,7 @@ void Thread::ResumeFromWait() {      case THREADSTATUS_WAIT_SYNCH_ANY:      case THREADSTATUS_WAIT_ARB:      case THREADSTATUS_WAIT_SLEEP: +    case THREADSTATUS_WAIT_IPC:          break;      case THREADSTATUS_READY: diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 0a1ada27d..bbffaf4cf 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -40,6 +40,7 @@ enum ThreadStatus {      THREADSTATUS_READY,          ///< Ready to run      THREADSTATUS_WAIT_ARB,       ///< Waiting on an address arbiter      THREADSTATUS_WAIT_SLEEP,     ///< Waiting due to a SleepThread SVC +    THREADSTATUS_WAIT_IPC,       ///< Waiting for the reply from an IPC request      THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false      THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true      THREADSTATUS_DORMANT,        ///< Created but not yet made ready | 
