// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include #include #include #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/kernel/thread.h" #include "core/hle/lock.h" #include "core/hle/result.h" namespace Kernel { /** * Callback that will wake up the thread it was scheduled for * @param thread_handle The handle of the thread that's been awoken * @param cycles_late The number of CPU cycles that have passed since the desired wakeup time */ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) { const auto proper_handle = static_cast(thread_handle); const auto& system = Core::System::GetInstance(); // Lock the global kernel mutex when we enter the kernel HLE. std::lock_guard lock(HLE::g_hle_lock); SharedPtr thread = system.Kernel().RetrieveThreadFromWakeupCallbackHandleTable(proper_handle); if (thread == nullptr) { LOG_CRITICAL(Kernel, "Callback fired for invalid thread {:08X}", proper_handle); return; } bool resume = true; if (thread->GetStatus() == ThreadStatus::WaitSynchAny || thread->GetStatus() == ThreadStatus::WaitSynchAll || thread->GetStatus() == ThreadStatus::WaitHLEEvent) { // Remove the thread from each of its waiting objects' waitlists for (const auto& object : thread->GetWaitObjects()) { object->RemoveWaitingThread(thread.get()); } thread->ClearWaitObjects(); // Invoke the wakeup callback before clearing the wait objects if (thread->HasWakeupCallback()) { resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0); } } if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 || thread->GetWaitHandle() != 0) { ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); thread->SetMutexWaitAddress(0); thread->SetCondVarWaitAddress(0); thread->SetWaitHandle(0); auto* const lock_owner = thread->GetLockOwner(); // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance // and don't have a lock owner unless SignalProcessWideKey was called first and the thread // wasn't awakened due to the mutex already being acquired. if (lock_owner != nullptr) { lock_owner->RemoveMutexWaiter(thread); } } if (thread->GetArbiterWaitAddress() != 0) { ASSERT(thread->GetStatus() == ThreadStatus::WaitArb); thread->SetArbiterWaitAddress(0); } if (resume) { thread->ResumeFromWait(); } } struct KernelCore::Impl { void Initialize(KernelCore& kernel) { Shutdown(); InitializeSystemResourceLimit(kernel); InitializeThreads(); } void Shutdown() { next_object_id = 0; next_process_id = Process::ProcessIDMin; next_thread_id = 1; process_list.clear(); current_process = nullptr; system_resource_limit = nullptr; thread_wakeup_callback_handle_table.Clear(); thread_wakeup_event_type = nullptr; named_ports.clear(); } // Creates the default system resource limit void InitializeSystemResourceLimit(KernelCore& kernel) { system_resource_limit = ResourceLimit::Create(kernel, "System"); // If setting the default system values fails, then something seriously wrong has occurred. ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000) .IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess()); ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); } void InitializeThreads() { thread_wakeup_event_type = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback); } std::atomic next_object_id{0}; std::atomic next_process_id{Process::ProcessIDMin}; std::atomic next_thread_id{1}; // Lists all processes that exist in the current session. std::vector> process_list; Process* current_process = nullptr; SharedPtr system_resource_limit; CoreTiming::EventType* thread_wakeup_event_type = nullptr; // TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, // allowing us to simply use a pool index or similar. Kernel::HandleTable thread_wakeup_callback_handle_table; /// Map of named ports managed by the kernel, which can be retrieved using /// the ConnectToPort SVC. NamedPortTable named_ports; }; KernelCore::KernelCore() : impl{std::make_unique()} {} KernelCore::~KernelCore() { Shutdown(); } void KernelCore::Initialize() { impl->Initialize(*this); } void KernelCore::Shutdown() { impl->Shutdown(); } SharedPtr KernelCore::GetSystemResourceLimit() const { return impl->system_resource_limit; } SharedPtr KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const { return impl->thread_wakeup_callback_handle_table.Get(handle); } void KernelCore::AppendNewProcess(SharedPtr process) { impl->process_list.push_back(std::move(process)); } void KernelCore::MakeCurrentProcess(Process* process) { impl->current_process = process; } Process* KernelCore::CurrentProcess() { return impl->current_process; } const Process* KernelCore::CurrentProcess() const { return impl->current_process; } void KernelCore::AddNamedPort(std::string name, SharedPtr port) { impl->named_ports.emplace(std::move(name), std::move(port)); } KernelCore::NamedPortTable::iterator KernelCore::FindNamedPort(const std::string& name) { return impl->named_ports.find(name); } KernelCore::NamedPortTable::const_iterator KernelCore::FindNamedPort( const std::string& name) const { return impl->named_ports.find(name); } bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const { return port != impl->named_ports.cend(); } u32 KernelCore::CreateNewObjectID() { return impl->next_object_id++; } u64 KernelCore::CreateNewThreadID() { return impl->next_thread_id++; } u64 KernelCore::CreateNewProcessID() { return impl->next_process_id++; } CoreTiming::EventType* KernelCore::ThreadWakeupCallbackEventType() const { return impl->thread_wakeup_event_type; } Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() { return impl->thread_wakeup_callback_handle_table; } const Kernel::HandleTable& KernelCore::ThreadWakeupCallbackHandleTable() const { return impl->thread_wakeup_callback_handle_table; } } // namespace Kernel