summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar bunnei2020-12-30 22:06:05 -0800
committerGravatar GitHub2020-12-30 22:06:05 -0800
commit25d607f5f63929369fb74f386a920b69bb24f442 (patch)
tree9ee5a023f033d99561a0358c5c71aeecc92c9d64 /src/core/hle/kernel
parentMerge pull request #5263 from lioncash/uninit (diff)
parenthle: kernel: service_thread: Make thread naming more consistent. (diff)
downloadyuzu-25d607f5f63929369fb74f386a920b69bb24f442.tar.gz
yuzu-25d607f5f63929369fb74f386a920b69bb24f442.tar.xz
yuzu-25d607f5f63929369fb74f386a920b69bb24f442.zip
Merge pull request #5208 from bunnei/service-threads
Service threads
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp37
-rw-r--r--src/core/hle/kernel/hle_ipc.h17
-rw-r--r--src/core/hle/kernel/kernel.cpp113
-rw-r--r--src/core/hle/kernel/kernel.h17
-rw-r--r--src/core/hle/kernel/server_session.cpp29
-rw-r--r--src/core/hle/kernel/server_session.h12
-rw-r--r--src/core/hle/kernel/service_thread.cpp110
-rw-r--r--src/core/hle/kernel/service_thread.h28
8 files changed, 237 insertions, 126 deletions
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index e75e80ad0..83decf6cf 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -46,43 +46,6 @@ void SessionRequestHandler::ClientDisconnected(
46 boost::range::remove_erase(connected_sessions, server_session); 46 boost::range::remove_erase(connected_sessions, server_session);
47} 47}
48 48
49std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread(
50 const std::string& reason, u64 timeout, WakeupCallback&& callback,
51 std::shared_ptr<WritableEvent> writable_event) {
52 // Put the client thread to sleep until the wait event is signaled or the timeout expires.
53
54 if (!writable_event) {
55 // Create event if not provided
56 const auto pair = WritableEvent::CreateEventPair(kernel, "HLE Pause Event: " + reason);
57 writable_event = pair.writable;
58 }
59
60 Handle event_handle = InvalidHandle;
61 {
62 KScopedSchedulerLockAndSleep lock(kernel, event_handle, thread.get(), timeout);
63 thread->SetHLECallback(
64 [context = *this, callback](std::shared_ptr<Thread> thread) mutable -> bool {
65 ThreadWakeupReason reason = thread->GetSignalingResult() == RESULT_TIMEOUT
66 ? ThreadWakeupReason::Timeout
67 : ThreadWakeupReason::Signal;
68 callback(thread, context, reason);
69 context.WriteToOutgoingCommandBuffer(*thread);
70 return true;
71 });
72 const auto readable_event{writable_event->GetReadableEvent()};
73 writable_event->Clear();
74 thread->SetHLESyncObject(readable_event.get());
75 thread->SetStatus(ThreadStatus::WaitHLEEvent);
76 thread->SetSynchronizationResults(nullptr, RESULT_TIMEOUT);
77 readable_event->AddWaitingThread(thread);
78 }
79 thread->SetHLETimeEvent(event_handle);
80
81 is_thread_waiting = true;
82
83 return writable_event;
84}
85
86HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, 49HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory,
87 std::shared_ptr<ServerSession> server_session, 50 std::shared_ptr<ServerSession> server_session,
88 std::shared_ptr<Thread> thread) 51 std::shared_ptr<Thread> thread)
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index c31a65476..b112e1ebd 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -129,23 +129,6 @@ public:
129 using WakeupCallback = std::function<void( 129 using WakeupCallback = std::function<void(
130 std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>; 130 std::shared_ptr<Thread> thread, HLERequestContext& context, ThreadWakeupReason reason)>;
131 131
132 /**
133 * Puts the specified guest thread to sleep until the returned event is signaled or until the
134 * specified timeout expires.
135 * @param reason Reason for pausing the thread, to be used for debugging purposes.
136 * @param timeout Timeout in nanoseconds after which the thread will be awoken and the callback
137 * invoked with a Timeout reason.
138 * @param callback Callback to be invoked when the thread is resumed. This callback must write
139 * the entire command response once again, regardless of the state of it before this function
140 * was called.
141 * @param writable_event Event to use to wake up the thread. If unspecified, an event will be
142 * created.
143 * @returns Event that when signaled will resume the thread and call the callback function.
144 */
145 std::shared_ptr<WritableEvent> SleepClientThread(
146 const std::string& reason, u64 timeout, WakeupCallback&& callback,
147 std::shared_ptr<WritableEvent> writable_event = nullptr);
148
149 /// Populates this context with data from the requesting process/thread. 132 /// Populates this context with data from the requesting process/thread.
150 ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table, 133 ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
151 u32_le* src_cmdbuf); 134 u32_le* src_cmdbuf);
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 04cae3a43..e8ece8164 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -8,13 +8,14 @@
8#include <functional> 8#include <functional>
9#include <memory> 9#include <memory>
10#include <thread> 10#include <thread>
11#include <unordered_map> 11#include <unordered_set>
12#include <utility> 12#include <utility>
13 13
14#include "common/assert.h" 14#include "common/assert.h"
15#include "common/logging/log.h" 15#include "common/logging/log.h"
16#include "common/microprofile.h" 16#include "common/microprofile.h"
17#include "common/thread.h" 17#include "common/thread.h"
18#include "common/thread_worker.h"
18#include "core/arm/arm_interface.h" 19#include "core/arm/arm_interface.h"
19#include "core/arm/cpu_interrupt_handler.h" 20#include "core/arm/cpu_interrupt_handler.h"
20#include "core/arm/exclusive_monitor.h" 21#include "core/arm/exclusive_monitor.h"
@@ -35,6 +36,7 @@
35#include "core/hle/kernel/physical_core.h" 36#include "core/hle/kernel/physical_core.h"
36#include "core/hle/kernel/process.h" 37#include "core/hle/kernel/process.h"
37#include "core/hle/kernel/resource_limit.h" 38#include "core/hle/kernel/resource_limit.h"
39#include "core/hle/kernel/service_thread.h"
38#include "core/hle/kernel/shared_memory.h" 40#include "core/hle/kernel/shared_memory.h"
39#include "core/hle/kernel/synchronization.h" 41#include "core/hle/kernel/synchronization.h"
40#include "core/hle/kernel/thread.h" 42#include "core/hle/kernel/thread.h"
@@ -60,6 +62,8 @@ struct KernelCore::Impl {
60 RegisterHostThread(); 62 RegisterHostThread();
61 63
62 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel); 64 global_scheduler_context = std::make_unique<Kernel::GlobalSchedulerContext>(kernel);
65 service_thread_manager =
66 std::make_unique<Common::ThreadWorker>(1, "yuzu:ServiceThreadManager");
63 67
64 InitializePhysicalCores(); 68 InitializePhysicalCores();
65 InitializeSystemResourceLimit(kernel); 69 InitializeSystemResourceLimit(kernel);
@@ -76,6 +80,12 @@ struct KernelCore::Impl {
76 } 80 }
77 81
78 void Shutdown() { 82 void Shutdown() {
83 process_list.clear();
84
85 // Ensures all service threads gracefully shutdown
86 service_thread_manager.reset();
87 service_threads.clear();
88
79 next_object_id = 0; 89 next_object_id = 0;
80 next_kernel_process_id = Process::InitialKIPIDMin; 90 next_kernel_process_id = Process::InitialKIPIDMin;
81 next_user_process_id = Process::ProcessIDMin; 91 next_user_process_id = Process::ProcessIDMin;
@@ -89,8 +99,6 @@ struct KernelCore::Impl {
89 99
90 cores.clear(); 100 cores.clear();
91 101
92 process_list.clear();
93
94 current_process = nullptr; 102 current_process = nullptr;
95 103
96 system_resource_limit = nullptr; 104 system_resource_limit = nullptr;
@@ -103,10 +111,8 @@ struct KernelCore::Impl {
103 111
104 exclusive_monitor.reset(); 112 exclusive_monitor.reset();
105 113
106 num_host_threads = 0; 114 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
107 std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(), 115 next_host_thread_id = Core::Hardware::NUM_CPU_CORES;
108 std::thread::id{});
109 std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
110 } 116 }
111 117
112 void InitializePhysicalCores() { 118 void InitializePhysicalCores() {
@@ -186,52 +192,46 @@ struct KernelCore::Impl {
186 } 192 }
187 } 193 }
188 194
195 /// Creates a new host thread ID, should only be called by GetHostThreadId
196 u32 AllocateHostThreadId(std::optional<std::size_t> core_id) {
197 if (core_id) {
198 // The first for slots are reserved for CPU core threads
199 ASSERT(*core_id < Core::Hardware::NUM_CPU_CORES);
200 return static_cast<u32>(*core_id);
201 } else {
202 return next_host_thread_id++;
203 }
204 }
205
206 /// Gets the host thread ID for the caller, allocating a new one if this is the first time
207 u32 GetHostThreadId(std::optional<std::size_t> core_id = std::nullopt) {
208 const thread_local auto host_thread_id{AllocateHostThreadId(core_id)};
209 return host_thread_id;
210 }
211
212 /// Registers a CPU core thread by allocating a host thread ID for it
189 void RegisterCoreThread(std::size_t core_id) { 213 void RegisterCoreThread(std::size_t core_id) {
190 const std::thread::id this_id = std::this_thread::get_id(); 214 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
215 const auto this_id = GetHostThreadId(core_id);
191 if (!is_multicore) { 216 if (!is_multicore) {
192 single_core_thread_id = this_id; 217 single_core_thread_id = this_id;
193 } 218 }
194 const auto end =
195 register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
196 const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
197 ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
198 ASSERT(it == end);
199 InsertHostThread(static_cast<u32>(core_id));
200 } 219 }
201 220
221 /// Registers a new host thread by allocating a host thread ID for it
202 void RegisterHostThread() { 222 void RegisterHostThread() {
203 const std::thread::id this_id = std::this_thread::get_id(); 223 [[maybe_unused]] const auto this_id = GetHostThreadId();
204 const auto end =
205 register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
206 const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
207 if (it == end) {
208 InsertHostThread(registered_thread_ids++);
209 }
210 }
211
212 void InsertHostThread(u32 value) {
213 const size_t index = num_host_threads++;
214 ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads");
215 register_host_thread_values[index] = value;
216 register_host_thread_keys[index] = std::this_thread::get_id();
217 } 224 }
218 225
219 [[nodiscard]] u32 GetCurrentHostThreadID() const { 226 [[nodiscard]] u32 GetCurrentHostThreadID() {
220 const std::thread::id this_id = std::this_thread::get_id(); 227 const auto this_id = GetHostThreadId();
221 if (!is_multicore && single_core_thread_id == this_id) { 228 if (!is_multicore && single_core_thread_id == this_id) {
222 return static_cast<u32>(system.GetCpuManager().CurrentCore()); 229 return static_cast<u32>(system.GetCpuManager().CurrentCore());
223 } 230 }
224 const auto end = 231 return this_id;
225 register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
226 const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
227 if (it == end) {
228 return Core::INVALID_HOST_THREAD_ID;
229 }
230 return register_host_thread_values[static_cast<size_t>(
231 std::distance(register_host_thread_keys.begin(), it))];
232 } 232 }
233 233
234 Core::EmuThreadHandle GetCurrentEmuThreadID() const { 234 [[nodiscard]] Core::EmuThreadHandle GetCurrentEmuThreadID() {
235 Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle(); 235 Core::EmuThreadHandle result = Core::EmuThreadHandle::InvalidHandle();
236 result.host_handle = GetCurrentHostThreadID(); 236 result.host_handle = GetCurrentHostThreadID();
237 if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) { 237 if (result.host_handle >= Core::Hardware::NUM_CPU_CORES) {
@@ -325,15 +325,8 @@ struct KernelCore::Impl {
325 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor; 325 std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
326 std::vector<Kernel::PhysicalCore> cores; 326 std::vector<Kernel::PhysicalCore> cores;
327 327
328 // 0-3 IDs represent core threads, >3 represent others 328 // Next host thead ID to use, 0-3 IDs represent core threads, >3 represent others
329 std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES}; 329 std::atomic<u32> next_host_thread_id{Core::Hardware::NUM_CPU_CORES};
330
331 // Number of host threads is a relatively high number to avoid overflowing
332 static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
333 std::atomic<size_t> num_host_threads{0};
334 std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
335 register_host_thread_keys{};
336 std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{};
337 330
338 // Kernel memory management 331 // Kernel memory management
339 std::unique_ptr<Memory::MemoryManager> memory_manager; 332 std::unique_ptr<Memory::MemoryManager> memory_manager;
@@ -345,12 +338,19 @@ struct KernelCore::Impl {
345 std::shared_ptr<Kernel::SharedMemory> irs_shared_mem; 338 std::shared_ptr<Kernel::SharedMemory> irs_shared_mem;
346 std::shared_ptr<Kernel::SharedMemory> time_shared_mem; 339 std::shared_ptr<Kernel::SharedMemory> time_shared_mem;
347 340
341 // Threads used for services
342 std::unordered_set<std::shared_ptr<Kernel::ServiceThread>> service_threads;
343
344 // Service threads are managed by a worker thread, so that a calling service thread can queue up
345 // the release of itself
346 std::unique_ptr<Common::ThreadWorker> service_thread_manager;
347
348 std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{}; 348 std::array<std::shared_ptr<Thread>, Core::Hardware::NUM_CPU_CORES> suspend_threads{};
349 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{}; 349 std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
350 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{}; 350 std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
351 351
352 bool is_multicore{}; 352 bool is_multicore{};
353 std::thread::id single_core_thread_id{}; 353 u32 single_core_thread_id{};
354 354
355 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{}; 355 std::array<u64, Core::Hardware::NUM_CPU_CORES> svc_ticks{};
356 356
@@ -639,4 +639,19 @@ void KernelCore::ExitSVCProfile() {
639 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]); 639 MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[core]);
640} 640}
641 641
642std::weak_ptr<Kernel::ServiceThread> KernelCore::CreateServiceThread(const std::string& name) {
643 auto service_thread = std::make_shared<Kernel::ServiceThread>(*this, 1, name);
644 impl->service_thread_manager->QueueWork(
645 [this, service_thread] { impl->service_threads.emplace(service_thread); });
646 return service_thread;
647}
648
649void KernelCore::ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread) {
650 impl->service_thread_manager->QueueWork([this, service_thread] {
651 if (auto strong_ptr = service_thread.lock()) {
652 impl->service_threads.erase(strong_ptr);
653 }
654 });
655}
656
642} // namespace Kernel 657} // namespace Kernel
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 5846c3f39..e3169f5a7 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -42,6 +42,7 @@ class Process;
42class ResourceLimit; 42class ResourceLimit;
43class KScheduler; 43class KScheduler;
44class SharedMemory; 44class SharedMemory;
45class ServiceThread;
45class Synchronization; 46class Synchronization;
46class Thread; 47class Thread;
47class TimeManager; 48class TimeManager;
@@ -227,6 +228,22 @@ public:
227 228
228 void ExitSVCProfile(); 229 void ExitSVCProfile();
229 230
231 /**
232 * Creates an HLE service thread, which are used to execute service routines asynchronously.
233 * While these are allocated per ServerSession, these need to be owned and managed outside of
234 * ServerSession to avoid a circular dependency.
235 * @param name String name for the ServerSession creating this thread, used for debug purposes.
236 * @returns The a weak pointer newly created service thread.
237 */
238 std::weak_ptr<Kernel::ServiceThread> CreateServiceThread(const std::string& name);
239
240 /**
241 * Releases a HLE service thread, instructing KernelCore to free it. This should be called when
242 * the ServerSession associated with the thread is destroyed.
243 * @param service_thread Service thread to release.
244 */
245 void ReleaseServiceThread(std::weak_ptr<Kernel::ServiceThread> service_thread);
246
230private: 247private:
231 friend class Object; 248 friend class Object;
232 friend class Process; 249 friend class Process;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index a35c8aa4b..b40fe3916 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -25,19 +25,19 @@
25namespace Kernel { 25namespace Kernel {
26 26
27ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} 27ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {}
28ServerSession::~ServerSession() = default; 28
29ServerSession::~ServerSession() {
30 kernel.ReleaseServiceThread(service_thread);
31}
29 32
30ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel, 33ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel,
31 std::shared_ptr<Session> parent, 34 std::shared_ptr<Session> parent,
32 std::string name) { 35 std::string name) {
33 std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; 36 std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
34 37
35 session->request_event =
36 Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) {
37 session->CompleteSyncRequest();
38 });
39 session->name = std::move(name); 38 session->name = std::move(name);
40 session->parent = std::move(parent); 39 session->parent = std::move(parent);
40 session->service_thread = kernel.CreateServiceThread(session->name);
41 41
42 return MakeResult(std::move(session)); 42 return MakeResult(std::move(session));
43} 43}
@@ -142,16 +142,16 @@ ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
142 std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread)); 142 std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
143 143
144 context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); 144 context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
145 request_queue.Push(std::move(context)); 145
146 if (auto strong_ptr = service_thread.lock()) {
147 strong_ptr->QueueSyncRequest(*this, std::move(context));
148 return RESULT_SUCCESS;
149 }
146 150
147 return RESULT_SUCCESS; 151 return RESULT_SUCCESS;
148} 152}
149 153
150ResultCode ServerSession::CompleteSyncRequest() { 154ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
151 ASSERT(!request_queue.Empty());
152
153 auto& context = *request_queue.Front();
154
155 ResultCode result = RESULT_SUCCESS; 155 ResultCode result = RESULT_SUCCESS;
156 // If the session has been converted to a domain, handle the domain request 156 // If the session has been converted to a domain, handle the domain request
157 if (IsDomain() && context.HasDomainMessageHeader()) { 157 if (IsDomain() && context.HasDomainMessageHeader()) {
@@ -177,18 +177,13 @@ ResultCode ServerSession::CompleteSyncRequest() {
177 } 177 }
178 } 178 }
179 179
180 request_queue.Pop();
181
182 return result; 180 return result;
183} 181}
184 182
185ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, 183ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
186 Core::Memory::Memory& memory, 184 Core::Memory::Memory& memory,
187 Core::Timing::CoreTiming& core_timing) { 185 Core::Timing::CoreTiming& core_timing) {
188 const ResultCode result = QueueSyncRequest(std::move(thread), memory); 186 return QueueSyncRequest(std::move(thread), memory);
189 const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
190 core_timing.ScheduleEvent(delay, request_event, {});
191 return result;
192} 187}
193 188
194} // namespace Kernel 189} // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index d23e9ec68..e8d1d99ea 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -10,6 +10,7 @@
10#include <vector> 10#include <vector>
11 11
12#include "common/threadsafe_queue.h" 12#include "common/threadsafe_queue.h"
13#include "core/hle/kernel/service_thread.h"
13#include "core/hle/kernel/synchronization_object.h" 14#include "core/hle/kernel/synchronization_object.h"
14#include "core/hle/result.h" 15#include "core/hle/result.h"
15 16
@@ -43,6 +44,8 @@ class Thread;
43 * TLS buffer and control is transferred back to it. 44 * TLS buffer and control is transferred back to it.
44 */ 45 */
45class ServerSession final : public SynchronizationObject { 46class ServerSession final : public SynchronizationObject {
47 friend class ServiceThread;
48
46public: 49public:
47 explicit ServerSession(KernelCore& kernel); 50 explicit ServerSession(KernelCore& kernel);
48 ~ServerSession() override; 51 ~ServerSession() override;
@@ -132,7 +135,7 @@ private:
132 ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory); 135 ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
133 136
134 /// Completes a sync request from the emulated application. 137 /// Completes a sync request from the emulated application.
135 ResultCode CompleteSyncRequest(); 138 ResultCode CompleteSyncRequest(HLERequestContext& context);
136 139
137 /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an 140 /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
138 /// object handle. 141 /// object handle.
@@ -163,11 +166,8 @@ private:
163 /// The name of this session (optional) 166 /// The name of this session (optional)
164 std::string name; 167 std::string name;
165 168
166 /// Core timing event used to schedule the service request at some point in the future 169 /// Thread to dispatch service requests
167 std::shared_ptr<Core::Timing::EventType> request_event; 170 std::weak_ptr<ServiceThread> service_thread;
168
169 /// Queue of scheduled service requests
170 Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue;
171}; 171};
172 172
173} // namespace Kernel 173} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
new file mode 100644
index 000000000..ee46f3e21
--- /dev/null
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -0,0 +1,110 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <condition_variable>
6#include <functional>
7#include <mutex>
8#include <thread>
9#include <vector>
10#include <queue>
11
12#include "common/assert.h"
13#include "common/scope_exit.h"
14#include "common/thread.h"
15#include "core/core.h"
16#include "core/hle/kernel/kernel.h"
17#include "core/hle/kernel/server_session.h"
18#include "core/hle/kernel/service_thread.h"
19#include "core/hle/lock.h"
20#include "video_core/renderer_base.h"
21
22namespace Kernel {
23
24class ServiceThread::Impl final {
25public:
26 explicit Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name);
27 ~Impl();
28
29 void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
30
31private:
32 std::vector<std::thread> threads;
33 std::queue<std::function<void()>> requests;
34 std::mutex queue_mutex;
35 std::condition_variable condition;
36 const std::string service_name;
37 bool stop{};
38};
39
40ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std::string& name)
41 : service_name{name} {
42 for (std::size_t i = 0; i < num_threads; ++i)
43 threads.emplace_back([this, &kernel] {
44 Common::SetCurrentThreadName(std::string{"yuzu:HleService:" + service_name}.c_str());
45
46 // Wait for first request before trying to acquire a render context
47 {
48 std::unique_lock lock{queue_mutex};
49 condition.wait(lock, [this] { return stop || !requests.empty(); });
50 }
51
52 kernel.RegisterHostThread();
53
54 while (true) {
55 std::function<void()> task;
56
57 {
58 std::unique_lock lock{queue_mutex};
59 condition.wait(lock, [this] { return stop || !requests.empty(); });
60 if (stop || requests.empty()) {
61 return;
62 }
63 task = std::move(requests.front());
64 requests.pop();
65 }
66
67 task();
68 }
69 });
70}
71
72void ServiceThread::Impl::QueueSyncRequest(ServerSession& session,
73 std::shared_ptr<HLERequestContext>&& context) {
74 {
75 std::unique_lock lock{queue_mutex};
76
77 // ServerSession owns the service thread, so we cannot caption a strong pointer here in the
78 // event that the ServerSession is terminated.
79 std::weak_ptr<ServerSession> weak_ptr{SharedFrom(&session)};
80 requests.emplace([weak_ptr, context{std::move(context)}]() {
81 if (auto strong_ptr = weak_ptr.lock()) {
82 strong_ptr->CompleteSyncRequest(*context);
83 }
84 });
85 }
86 condition.notify_one();
87}
88
89ServiceThread::Impl::~Impl() {
90 {
91 std::unique_lock lock{queue_mutex};
92 stop = true;
93 }
94 condition.notify_all();
95 for (std::thread& thread : threads) {
96 thread.join();
97 }
98}
99
100ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name)
101 : impl{std::make_unique<Impl>(kernel, num_threads, name)} {}
102
103ServiceThread::~ServiceThread() = default;
104
105void ServiceThread::QueueSyncRequest(ServerSession& session,
106 std::shared_ptr<HLERequestContext>&& context) {
107 impl->QueueSyncRequest(session, std::move(context));
108}
109
110} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
new file mode 100644
index 000000000..025ab8fb5
--- /dev/null
+++ b/src/core/hle/kernel/service_thread.h
@@ -0,0 +1,28 @@
1// Copyright 2020 yuzu emulator team
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <string>
9
10namespace Kernel {
11
12class HLERequestContext;
13class KernelCore;
14class ServerSession;
15
16class ServiceThread final {
17public:
18 explicit ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name);
19 ~ServiceThread();
20
21 void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
22
23private:
24 class Impl;
25 std::unique_ptr<Impl> impl;
26};
27
28} // namespace Kernel