mirror of
				https://git.suyu.dev/suyu/suyu
				synced 2025-11-04 00:49:02 -06:00 
			
		
		
		
	Merge pull request #201 from Subv/ipc_delay_
Kernel/IPC: Add a small delay after each SyncRequest to prevent thread starvation.
This commit is contained in:
		@@ -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;
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
        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 (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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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:
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user