forked from eden-emu/eden
		
	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:
		
						commit
						ec39c9eb32
					
				
					 4 changed files with 62 additions and 49 deletions
				
			
		|  | @ -57,18 +57,7 @@ void ServerSession::Acquire(Thread* thread) { | ||||||
|     pending_requesting_threads.pop_back(); |     pending_requesting_threads.pop_back(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { | ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) { | ||||||
|     // 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
 |  | ||||||
|     // similar.
 |  | ||||||
| 
 |  | ||||||
|     Kernel::HLERequestContext context(this); |  | ||||||
|     u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); |  | ||||||
|     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
 |  | ||||||
|     if (IsDomain()) { |  | ||||||
|     auto& domain_message_header = context.GetDomainMessageHeader(); |     auto& domain_message_header = context.GetDomainMessageHeader(); | ||||||
|     if (domain_message_header) { |     if (domain_message_header) { | ||||||
|         // If there is a DomainMessageHeader, then this is CommandType "Request"
 |         // If there is a DomainMessageHeader, then this is CommandType "Request"
 | ||||||
|  | @ -91,23 +80,54 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { | ||||||
|         LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); |         LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value()); | ||||||
|         ASSERT(false); |         ASSERT(false); | ||||||
|     } |     } | ||||||
|         // If there is no domain header, the regular session handler is used
 | 
 | ||||||
|  |     return RESULT_SUCCESS; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|     // If this ServerSession has an associated HLE handler, forward the request to it.
 | ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { | ||||||
|     ResultCode result{RESULT_SUCCESS}; |     // The ServerSession received a sync request, this means that there's new data available
 | ||||||
|     if (hle_handler != nullptr) { |     // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
 | ||||||
|         // Attempt to translate the incoming request's command buffer.
 |     // similar.
 | ||||||
|         ResultCode translate_result = TranslateHLERequest(this); |  | ||||||
|         if (translate_result.IsError()) |  | ||||||
|             return translate_result; |  | ||||||
| 
 | 
 | ||||||
|  |     Kernel::HLERequestContext context(this); | ||||||
|  |     u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress()); | ||||||
|  |     context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, | ||||||
|  |                                               Kernel::g_handle_table); | ||||||
|  | 
 | ||||||
|  |     ResultCode result = RESULT_SUCCESS; | ||||||
|  |     // If the session has been converted to a domain, handle the domain request
 | ||||||
|  |     if (IsDomain()) { | ||||||
|  |         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); |         result = hle_handler->HandleSyncRequest(context); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     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 { |         } else { | ||||||
|             // Add the thread to the list of threads that have issued a sync request with this
 |             // Add the thread to the list of threads that have issued a sync request with this
 | ||||||
|             // server.
 |             // server.
 | ||||||
|             pending_requesting_threads.push_back(std::move(thread)); |             pending_requesting_threads.push_back(std::move(thread)); | ||||||
|         } |         } | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     // If this ServerSession does not have an HLE implementation, just wake up the threads waiting
 |     // If this ServerSession does not have an HLE implementation, just wake up the threads waiting
 | ||||||
|     // on it.
 |     // on it.
 | ||||||
|  | @ -140,9 +160,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n | ||||||
| 
 | 
 | ||||||
|     return std::make_tuple(std::move(server_session), std::move(client_session)); |     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
 | } // namespace Kernel
 | ||||||
|  |  | ||||||
|  | @ -21,6 +21,7 @@ class ServerSession; | ||||||
| class Session; | class Session; | ||||||
| class SessionRequestHandler; | class SessionRequestHandler; | ||||||
| class Thread; | class Thread; | ||||||
|  | class HLERequestContext; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS |  * 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"); |     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
 |     /// When set to True, converts the session to a domain at the end of the command
 | ||||||
|     bool convert_to_domain{}; |     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
 | } // namespace Kernel
 | ||||||
|  |  | ||||||
|  | @ -284,6 +284,7 @@ void Thread::ResumeFromWait() { | ||||||
|     case THREADSTATUS_WAIT_SYNCH_ANY: |     case THREADSTATUS_WAIT_SYNCH_ANY: | ||||||
|     case THREADSTATUS_WAIT_ARB: |     case THREADSTATUS_WAIT_ARB: | ||||||
|     case THREADSTATUS_WAIT_SLEEP: |     case THREADSTATUS_WAIT_SLEEP: | ||||||
|  |     case THREADSTATUS_WAIT_IPC: | ||||||
|         break; |         break; | ||||||
| 
 | 
 | ||||||
|     case THREADSTATUS_READY: |     case THREADSTATUS_READY: | ||||||
|  |  | ||||||
|  | @ -40,6 +40,7 @@ enum ThreadStatus { | ||||||
|     THREADSTATUS_READY,          ///< Ready to run
 |     THREADSTATUS_READY,          ///< Ready to run
 | ||||||
|     THREADSTATUS_WAIT_ARB,       ///< Waiting on an address arbiter
 |     THREADSTATUS_WAIT_ARB,       ///< Waiting on an address arbiter
 | ||||||
|     THREADSTATUS_WAIT_SLEEP,     ///< Waiting due to a SleepThread SVC
 |     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_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
 | ||||||
|     THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
 |     THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
 | ||||||
|     THREADSTATUS_DORMANT,        ///< Created but not yet made ready
 |     THREADSTATUS_DORMANT,        ///< Created but not yet made ready
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 bunnei
						bunnei