diff --git a/README.md b/README.md index e5ad40b60..54ea81e8e 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 2411. +This is the source code for early-access 2413. ## Legal Notice diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index 55e6fb9f7..754b41ff6 100755 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -341,10 +341,6 @@ public: return *thread; } - bool IsThreadWaiting() const { - return is_thread_waiting; - } - private: friend class IPC::ResponseBuilder; @@ -379,7 +375,6 @@ private: u32 domain_offset{}; std::shared_ptr manager; - bool is_thread_waiting{}; KernelCore& kernel; Core::Memory::Memory& memory; diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index f900b2e7a..1b2a01869 100755 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp @@ -739,6 +739,12 @@ void KScheduler::ScheduleImpl() { next_thread = idle_thread; } + // We never want to schedule a dummy thread, as these are only used by host threads for locking. + if (next_thread->GetThreadType() == ThreadType::Dummy) { + ASSERT_MSG(false, "Dummy threads should never be scheduled!"); + next_thread = idle_thread; + } + // If we're not actually switching thread, there's nothing to do. if (next_thread == current_thread.load()) { previous_thread->EnableDispatch(); diff --git a/src/core/hle/kernel/k_server_session.cpp b/src/core/hle/kernel/k_server_session.cpp index d4e4a6b06..4d94eb9cf 100755 --- a/src/core/hle/kernel/k_server_session.cpp +++ b/src/core/hle/kernel/k_server_session.cpp @@ -8,7 +8,6 @@ #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" -#include "common/scope_exit.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/hle_ipc.h" @@ -123,20 +122,10 @@ ResultCode KServerSession::QueueSyncRequest(KThread* thread, Core::Memory::Memor context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); - // In the event that something fails here, stub a result to prevent the game from crashing. - // This is a work-around in the event that somehow we process a service request after the - // session has been closed by the game. This has been observed to happen rarely in Pokemon - // Sword/Shield and is likely a result of us using host threads/scheduling for services. - // TODO(bunnei): Find a better solution here. - auto error_guard = SCOPE_GUARD({ CompleteSyncRequest(*context); }); - // Ensure we have a session request handler if (manager->HasSessionRequestHandler(*context)) { if (auto strong_ptr = manager->GetServiceThread().lock()) { strong_ptr->QueueSyncRequest(*parent, std::move(context)); - - // We succeeded. - error_guard.Cancel(); } else { ASSERT_MSG(false, "strong_ptr is nullptr!"); } @@ -171,13 +160,8 @@ ResultCode KServerSession::CompleteSyncRequest(HLERequestContext& context) { convert_to_domain = false; } - // Some service requests require the thread to block - { - KScopedSchedulerLock lock(kernel); - if (!context.IsThreadWaiting()) { - context.GetThread().EndWait(result); - } - } + // The calling thread is waiting for this request to complete, so wake it up. + context.GetThread().EndWait(result); return result; } diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 7a5e6fc08..a599723e6 100755 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -106,7 +106,7 @@ KThread::~KThread() = default; ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top, s32 prio, s32 virt_core, KProcess* owner, ThreadType type) { // Assert parameters are valid. - ASSERT((type == ThreadType::Main) || + ASSERT((type == ThreadType::Main) || (type == ThreadType::Dummy) || (Svc::HighestThreadPriority <= prio && prio <= Svc::LowestThreadPriority)); ASSERT((owner != nullptr) || (type != ThreadType::User)); ASSERT(0 <= virt_core && virt_core < static_cast(Common::BitSize())); @@ -140,7 +140,7 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s UNREACHABLE_MSG("KThread::Initialize: Unknown ThreadType {}", static_cast(type)); break; } - thread_type_for_debugging = type; + thread_type = type; // Set the ideal core ID and affinity mask. virtual_ideal_core_id = virt_core; @@ -262,7 +262,7 @@ ResultCode KThread::InitializeThread(KThread* thread, KThreadFunction func, uint } ResultCode KThread::InitializeDummyThread(KThread* thread) { - return thread->Initialize({}, {}, {}, DefaultThreadPriority, 3, {}, ThreadType::Dummy); + return thread->Initialize({}, {}, {}, DummyThreadPriority, 3, {}, ThreadType::Dummy); } ResultCode KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32 virt_core) { @@ -1097,6 +1097,12 @@ void KThread::EndWait(ResultCode wait_result_) { // Lock the scheduler. KScopedSchedulerLock sl(kernel); + // Dummy threads are just used by host threads for locking, and will never have a wait_queue. + if (thread_type == ThreadType::Dummy) { + ASSERT_MSG(false, "Dummy threads should never call EndWait!"); + return; + } + // If we're waiting, notify our queue that we're available. if (GetState() == ThreadState::Waiting) { wait_queue->EndWait(this, wait_result_); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index cc427f6cf..77b53a198 100755 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -112,6 +112,7 @@ private: public: static constexpr s32 DefaultThreadPriority = 44; static constexpr s32 IdleThreadPriority = Svc::LowestThreadPriority + 1; + static constexpr s32 DummyThreadPriority = Svc::LowestThreadPriority + 2; explicit KThread(KernelCore& kernel_); ~KThread() override; @@ -553,8 +554,8 @@ public: return wait_reason_for_debugging; } - [[nodiscard]] ThreadType GetThreadTypeForDebugging() const { - return thread_type_for_debugging; + [[nodiscard]] ThreadType GetThreadType() const { + return thread_type; } void SetWaitObjectsForDebugging(const std::span& objects) { @@ -753,12 +754,12 @@ private: // For emulation std::shared_ptr host_context{}; bool is_single_core{}; + ThreadType thread_type{}; // For debugging std::vector wait_objects_for_debugging; VAddr mutex_wait_address_for_debugging{}; ThreadWaitReasonForDebugging wait_reason_for_debugging{}; - ThreadType thread_type_for_debugging{}; public: using ConditionVariableThreadTreeType = ConditionVariableThreadTree; diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp index 03f3dec10..4eb3a5988 100755 --- a/src/core/hle/kernel/service_thread.cpp +++ b/src/core/hle/kernel/service_thread.cpp @@ -12,6 +12,7 @@ #include "common/scope_exit.h" #include "common/thread.h" #include "core/hle/kernel/k_session.h" +#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/service_thread.h" @@ -50,6 +51,10 @@ ServiceThread::Impl::Impl(KernelCore& kernel, std::size_t num_threads, const std kernel.RegisterHostThread(); + // Ensure the dummy thread allocated for this host thread is closed on exit. + auto* dummy_thread = kernel.GetCurrentEmuThread(); + SCOPE_EXIT({ dummy_thread->Close(); }); + while (true) { std::function task; diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index 1f41c46c4..2d1a2d9cb 100755 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp @@ -95,7 +95,7 @@ std::vector> WaitTreeItem::MakeThreadItemList( std::size_t row = 0; auto add_threads = [&](const std::vector& threads) { for (std::size_t i = 0; i < threads.size(); ++i) { - if (threads[i]->GetThreadTypeForDebugging() == Kernel::ThreadType::User) { + if (threads[i]->GetThreadType() == Kernel::ThreadType::User) { item_list.push_back(std::make_unique(*threads[i], system)); item_list.back()->row = row; } @@ -153,7 +153,7 @@ QString WaitTreeCallstack::GetText() const { std::vector> WaitTreeCallstack::GetChildren() const { std::vector> list; - if (thread.GetThreadTypeForDebugging() != Kernel::ThreadType::User) { + if (thread.GetThreadType() != Kernel::ThreadType::User) { return list; }