diff options
| author | 2020-12-30 22:06:05 -0800 | |
|---|---|---|
| committer | 2020-12-30 22:06:05 -0800 | |
| commit | 25d607f5f63929369fb74f386a920b69bb24f442 (patch) | |
| tree | 9ee5a023f033d99561a0358c5c71aeecc92c9d64 /src/core/hle/kernel | |
| parent | Merge pull request #5263 from lioncash/uninit (diff) | |
| parent | hle: kernel: service_thread: Make thread naming more consistent. (diff) | |
| download | yuzu-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.cpp | 37 | ||||
| -rw-r--r-- | src/core/hle/kernel/hle_ipc.h | 17 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 113 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.h | 17 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_session.cpp | 29 | ||||
| -rw-r--r-- | src/core/hle/kernel/server_session.h | 12 | ||||
| -rw-r--r-- | src/core/hle/kernel/service_thread.cpp | 110 | ||||
| -rw-r--r-- | src/core/hle/kernel/service_thread.h | 28 |
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 | ||
| 49 | std::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 | |||
| 86 | HLERequestContext::HLERequestContext(KernelCore& kernel, Core::Memory::Memory& memory, | 49 | HLERequestContext::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 | ||
| 642 | std::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 | |||
| 649 | void 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; | |||
| 42 | class ResourceLimit; | 42 | class ResourceLimit; |
| 43 | class KScheduler; | 43 | class KScheduler; |
| 44 | class SharedMemory; | 44 | class SharedMemory; |
| 45 | class ServiceThread; | ||
| 45 | class Synchronization; | 46 | class Synchronization; |
| 46 | class Thread; | 47 | class Thread; |
| 47 | class TimeManager; | 48 | class 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 | |||
| 230 | private: | 247 | private: |
| 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 @@ | |||
| 25 | namespace Kernel { | 25 | namespace Kernel { |
| 26 | 26 | ||
| 27 | ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} | 27 | ServerSession::ServerSession(KernelCore& kernel) : SynchronizationObject{kernel} {} |
| 28 | ServerSession::~ServerSession() = default; | 28 | |
| 29 | ServerSession::~ServerSession() { | ||
| 30 | kernel.ReleaseServiceThread(service_thread); | ||
| 31 | } | ||
| 29 | 32 | ||
| 30 | ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel, | 33 | ResultVal<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 | ||
| 150 | ResultCode ServerSession::CompleteSyncRequest() { | 154 | ResultCode 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 | ||
| 185 | ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | 183 | ResultCode 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 | */ |
| 45 | class ServerSession final : public SynchronizationObject { | 46 | class ServerSession final : public SynchronizationObject { |
| 47 | friend class ServiceThread; | ||
| 48 | |||
| 46 | public: | 49 | public: |
| 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 | |||
| 22 | namespace Kernel { | ||
| 23 | |||
| 24 | class ServiceThread::Impl final { | ||
| 25 | public: | ||
| 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 | |||
| 31 | private: | ||
| 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 | |||
| 40 | ServiceThread::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 | |||
| 72 | void 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 | |||
| 89 | ServiceThread::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 | |||
| 100 | ServiceThread::ServiceThread(KernelCore& kernel, std::size_t num_threads, const std::string& name) | ||
| 101 | : impl{std::make_unique<Impl>(kernel, num_threads, name)} {} | ||
| 102 | |||
| 103 | ServiceThread::~ServiceThread() = default; | ||
| 104 | |||
| 105 | void 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 | |||
| 10 | namespace Kernel { | ||
| 11 | |||
| 12 | class HLERequestContext; | ||
| 13 | class KernelCore; | ||
| 14 | class ServerSession; | ||
| 15 | |||
| 16 | class ServiceThread final { | ||
| 17 | public: | ||
| 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 | |||
| 23 | private: | ||
| 24 | class Impl; | ||
| 25 | std::unique_ptr<Impl> impl; | ||
| 26 | }; | ||
| 27 | |||
| 28 | } // namespace Kernel | ||