mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +00:00 
			
		
		
		
	Merge pull request #3101 from Subv/hle_thread_pause2
Kernel/Threads: Add a new thread status that will allow using a Kernel::Event to put a guest thread to sleep inside an HLE handler until said event is signaled
This commit is contained in:
		
						commit
						48512d9011
					
				
					 4 changed files with 69 additions and 3 deletions
				
			
		|  | @ -6,6 +6,7 @@ | ||||||
| #include <vector> | #include <vector> | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | #include "core/hle/kernel/event.h" | ||||||
| #include "core/hle/kernel/handle_table.h" | #include "core/hle/kernel/handle_table.h" | ||||||
| #include "core/hle/kernel/hle_ipc.h" | #include "core/hle/kernel/hle_ipc.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
|  | @ -30,6 +31,40 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s | ||||||
|         connected_sessions.end()); |         connected_sessions.end()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, | ||||||
|  |                                                       const std::string& reason, | ||||||
|  |                                                       std::chrono::nanoseconds timeout, | ||||||
|  |                                                       WakeupCallback&& callback) { | ||||||
|  |     // Put the client thread to sleep until the wait event is signaled or the timeout expires.
 | ||||||
|  |     thread->wakeup_callback = [ context = *this, callback ]( | ||||||
|  |         ThreadWakeupReason reason, SharedPtr<Thread> thread, SharedPtr<WaitObject> object) mutable { | ||||||
|  |         ASSERT(thread->status == THREADSTATUS_WAIT_HLE_EVENT); | ||||||
|  |         callback(thread, context, reason); | ||||||
|  | 
 | ||||||
|  |         auto& process = thread->owner_process; | ||||||
|  |         // We must copy the entire command buffer *plus* the entire static buffers area, since
 | ||||||
|  |         // the translation might need to read from it in order to retrieve the StaticBuffer
 | ||||||
|  |         // target addresses.
 | ||||||
|  |         std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buff; | ||||||
|  |         Memory::ReadBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(), | ||||||
|  |                           cmd_buff.size() * sizeof(u32)); | ||||||
|  |         context.WriteToOutgoingCommandBuffer(cmd_buff.data(), *process, Kernel::g_handle_table); | ||||||
|  |         // Copy the translated command buffer back into the thread's command buffer area.
 | ||||||
|  |         Memory::WriteBlock(*process, thread->GetCommandBufferAddress(), cmd_buff.data(), | ||||||
|  |                            cmd_buff.size() * sizeof(u32)); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, "HLE Pause Event: " + reason); | ||||||
|  |     thread->status = THREADSTATUS_WAIT_HLE_EVENT; | ||||||
|  |     thread->wait_objects = {event}; | ||||||
|  |     event->AddWaitingThread(thread); | ||||||
|  | 
 | ||||||
|  |     if (timeout.count() > 0) | ||||||
|  |         thread->WakeAfterDelay(timeout.count()); | ||||||
|  | 
 | ||||||
|  |     return event; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| HLERequestContext::HLERequestContext(SharedPtr<ServerSession> session) | HLERequestContext::HLERequestContext(SharedPtr<ServerSession> session) | ||||||
|     : session(std::move(session)) { |     : session(std::move(session)) { | ||||||
|     cmd_buf[0] = 0; |     cmd_buf[0] = 0; | ||||||
|  |  | ||||||
|  | @ -6,7 +6,9 @@ | ||||||
| 
 | 
 | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| #include <array> | #include <array> | ||||||
|  | #include <chrono> | ||||||
| #include <memory> | #include <memory> | ||||||
|  | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <boost/container/small_vector.hpp> | #include <boost/container/small_vector.hpp> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  | @ -23,6 +25,8 @@ namespace Kernel { | ||||||
| 
 | 
 | ||||||
| class HandleTable; | class HandleTable; | ||||||
| class Process; | class Process; | ||||||
|  | class Thread; | ||||||
|  | class Event; | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Interface implemented by HLE Session handlers. |  * Interface implemented by HLE Session handlers. | ||||||
|  | @ -166,6 +170,24 @@ public: | ||||||
|         return session; |         return session; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     using WakeupCallback = std::function<void(SharedPtr<Thread> thread, HLERequestContext& context, | ||||||
|  |                                               ThreadWakeupReason reason)>; | ||||||
|  | 
 | ||||||
|  |     /**
 | ||||||
|  |      * Puts the specified guest thread to sleep until the returned event is signaled or until the | ||||||
|  |      * specified timeout expires. | ||||||
|  |      * @param thread Thread to be put to sleep. | ||||||
|  |      * @param reason Reason for pausing the thread, to be used for debugging purposes. | ||||||
|  |      * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback | ||||||
|  |      * invoked with a Timeout reason. | ||||||
|  |      * @param callback Callback to be invoked when the thread is resumed. This callback must write | ||||||
|  |      * the entire command response once again, regardless of the state of it before this function | ||||||
|  |      * was called. | ||||||
|  |      * @returns Event that when signaled will resume the thread and call the callback function. | ||||||
|  |      */ | ||||||
|  |     SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason, | ||||||
|  |                                        std::chrono::nanoseconds timeout, WakeupCallback&& callback); | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|      * Resolves a object id from the request command buffer into a pointer to an object. See the |      * Resolves a object id from the request command buffer into a pointer to an object. See the | ||||||
|      * "HLE handle protocol" section in the class documentation for more details. |      * "HLE handle protocol" section in the class documentation for more details. | ||||||
|  |  | ||||||
|  | @ -196,7 +196,8 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || |     if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || | ||||||
|         thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { |         thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB || | ||||||
|  |         thread->status == THREADSTATUS_WAIT_HLE_EVENT) { | ||||||
| 
 | 
 | ||||||
|         // Invoke the wakeup callback before clearing the wait objects
 |         // Invoke the wakeup callback before clearing the wait objects
 | ||||||
|         if (thread->wakeup_callback) |         if (thread->wakeup_callback) | ||||||
|  |  | ||||||
|  | @ -182,8 +182,16 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses | ||||||
|     LOG_TRACE(Service, "%s", |     LOG_TRACE(Service, "%s", | ||||||
|               MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); |               MakeFunctionString(info->name, GetServiceName().c_str(), cmd_buf).c_str()); | ||||||
|     handler_invoker(this, info->handler_callback, context); |     handler_invoker(this, info->handler_callback, context); | ||||||
|     context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, | 
 | ||||||
|                                          Kernel::g_handle_table); |     auto thread = Kernel::GetCurrentThread(); | ||||||
|  |     ASSERT(thread->status == THREADSTATUS_RUNNING || thread->status == THREADSTATUS_WAIT_HLE_EVENT); | ||||||
|  |     // Only write the response immediately if the thread is still running. If the HLE handler put
 | ||||||
|  |     // the thread to sleep then the writing of the command buffer will be deferred to the wakeup
 | ||||||
|  |     // callback.
 | ||||||
|  |     if (thread->status == THREADSTATUS_RUNNING) { | ||||||
|  |         context.WriteToOutgoingCommandBuffer(cmd_buf, *Kernel::g_current_process, | ||||||
|  |                                              Kernel::g_handle_table); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue