mirror of
https://github.com/yuzu-emu/yuzu-android.git
synced 2025-06-12 11:38:01 -05:00
service: refactor server architecture
Converts services to have their own processes
This commit is contained in:
@ -21,36 +21,18 @@
|
||||
#include "core/hle/kernel/k_server_session.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/service_thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
|
||||
ServiceThreadType thread_type)
|
||||
: kernel{kernel_}, service_thread{thread_type == ServiceThreadType::CreateNew
|
||||
? kernel.CreateServiceThread(service_name_)
|
||||
: kernel.GetDefaultServiceThread()} {}
|
||||
SessionRequestHandler::SessionRequestHandler(KernelCore& kernel_, const char* service_name_)
|
||||
: kernel{kernel_} {}
|
||||
|
||||
SessionRequestHandler::~SessionRequestHandler() {
|
||||
kernel.ReleaseServiceThread(service_thread);
|
||||
}
|
||||
SessionRequestHandler::~SessionRequestHandler() = default;
|
||||
|
||||
void SessionRequestHandler::AcceptSession(KServerPort* server_port) {
|
||||
auto* server_session = server_port->AcceptSession();
|
||||
ASSERT(server_session != nullptr);
|
||||
|
||||
RegisterSession(server_session, std::make_shared<SessionRequestManager>(kernel));
|
||||
}
|
||||
|
||||
void SessionRequestHandler::RegisterSession(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager) {
|
||||
manager->SetSessionHandler(shared_from_this());
|
||||
service_thread.RegisterServerSession(server_session, manager);
|
||||
server_session->Close();
|
||||
}
|
||||
|
||||
SessionRequestManager::SessionRequestManager(KernelCore& kernel_) : kernel{kernel_} {}
|
||||
SessionRequestManager::SessionRequestManager(KernelCore& kernel_,
|
||||
Service::ServerManager& server_manager_)
|
||||
: kernel{kernel_}, server_manager{server_manager_} {}
|
||||
|
||||
SessionRequestManager::~SessionRequestManager() = default;
|
||||
|
||||
|
@ -31,12 +31,8 @@ class ResponseBuilder;
|
||||
|
||||
namespace Service {
|
||||
class ServiceFrameworkBase;
|
||||
}
|
||||
|
||||
enum class ServiceThreadType {
|
||||
Default,
|
||||
CreateNew,
|
||||
};
|
||||
class ServerManager;
|
||||
} // namespace Service
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
@ -53,9 +49,6 @@ class KThread;
|
||||
class KReadableEvent;
|
||||
class KSession;
|
||||
class SessionRequestManager;
|
||||
class ServiceThread;
|
||||
|
||||
enum class ThreadWakeupReason;
|
||||
|
||||
/**
|
||||
* Interface implemented by HLE Session handlers.
|
||||
@ -64,8 +57,7 @@ enum class ThreadWakeupReason;
|
||||
*/
|
||||
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
|
||||
public:
|
||||
SessionRequestHandler(KernelCore& kernel_, const char* service_name_,
|
||||
ServiceThreadType thread_type);
|
||||
SessionRequestHandler(KernelCore& kernel_, const char* service_name_);
|
||||
virtual ~SessionRequestHandler();
|
||||
|
||||
/**
|
||||
@ -79,17 +71,8 @@ public:
|
||||
virtual Result HandleSyncRequest(Kernel::KServerSession& session,
|
||||
Kernel::HLERequestContext& context) = 0;
|
||||
|
||||
void AcceptSession(KServerPort* server_port);
|
||||
void RegisterSession(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager);
|
||||
|
||||
ServiceThread& GetServiceThread() const {
|
||||
return service_thread;
|
||||
}
|
||||
|
||||
protected:
|
||||
KernelCore& kernel;
|
||||
ServiceThread& service_thread;
|
||||
};
|
||||
|
||||
using SessionRequestHandlerWeakPtr = std::weak_ptr<SessionRequestHandler>;
|
||||
@ -102,7 +85,7 @@ using SessionRequestHandlerPtr = std::shared_ptr<SessionRequestHandler>;
|
||||
*/
|
||||
class SessionRequestManager final {
|
||||
public:
|
||||
explicit SessionRequestManager(KernelCore& kernel);
|
||||
explicit SessionRequestManager(KernelCore& kernel, Service::ServerManager& server_manager);
|
||||
~SessionRequestManager();
|
||||
|
||||
bool IsDomain() const {
|
||||
@ -155,15 +138,15 @@ public:
|
||||
session_handler = std::move(handler);
|
||||
}
|
||||
|
||||
ServiceThread& GetServiceThread() const {
|
||||
return session_handler->GetServiceThread();
|
||||
}
|
||||
|
||||
bool HasSessionRequestHandler(const HLERequestContext& context) const;
|
||||
|
||||
Result HandleDomainSyncRequest(KServerSession* server_session, HLERequestContext& context);
|
||||
Result CompleteSyncRequest(KServerSession* server_session, HLERequestContext& context);
|
||||
|
||||
Service::ServerManager& GetServerManager() {
|
||||
return server_manager;
|
||||
}
|
||||
|
||||
private:
|
||||
bool convert_to_domain{};
|
||||
bool is_domain{};
|
||||
@ -172,6 +155,7 @@ private:
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
Service::ServerManager& server_manager;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -119,7 +119,6 @@ void KProcess::DecrementRunningThreadCount() {
|
||||
|
||||
if (const auto prev = num_running_threads--; prev == 1) {
|
||||
// TODO(bunnei): Process termination to be implemented when multiprocess is supported.
|
||||
UNIMPLEMENTED_MSG("KProcess termination is not implemennted!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -357,9 +356,6 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:
|
||||
system_resource_size = metadata.GetSystemResourceSize();
|
||||
image_size = code_size;
|
||||
|
||||
// We currently do not support process-specific system resource
|
||||
UNIMPLEMENTED_IF(system_resource_size != 0);
|
||||
|
||||
KScopedResourceReservation memory_reservation(
|
||||
resource_limit, LimitableResource::PhysicalMemoryMax, code_size + system_resource_size);
|
||||
if (!memory_reservation.Succeeded()) {
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "core/hle/kernel/k_thread_queue.h"
|
||||
#include "core/hle/kernel/k_worker_task_manager.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/hle/kernel/svc_results.h"
|
||||
#include "core/hle/kernel/svc_types.h"
|
||||
#include "core/hle/result.h"
|
||||
@ -298,6 +299,25 @@ Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThr
|
||||
ThreadType::User, system.GetCpuManager().GetGuestThreadFunc()));
|
||||
}
|
||||
|
||||
Result KThread::InitializeServiceThread(Core::System& system, KThread* thread,
|
||||
std::function<void()>&& func, s32 prio, s32 virt_core,
|
||||
KProcess* owner) {
|
||||
system.Kernel().GlobalSchedulerContext().AddThread(thread);
|
||||
std::function<void()> func2{[&system, func{std::move(func)}] {
|
||||
// Similar to UserModeThreadStarter.
|
||||
system.Kernel().CurrentScheduler()->OnThreadStart();
|
||||
|
||||
// Run the guest function.
|
||||
func();
|
||||
|
||||
// Exit.
|
||||
Svc::ExitThread(system);
|
||||
}};
|
||||
|
||||
R_RETURN(InitializeThread(thread, {}, {}, {}, prio, virt_core, owner, ThreadType::HighPriority,
|
||||
std::move(func2)));
|
||||
}
|
||||
|
||||
void KThread::PostDestroy(uintptr_t arg) {
|
||||
KProcess* owner = reinterpret_cast<KProcess*>(arg & ~1ULL);
|
||||
const bool resource_limit_release_hint = (arg & 1);
|
||||
|
@ -434,6 +434,10 @@ public:
|
||||
VAddr user_stack_top, s32 prio, s32 virt_core,
|
||||
KProcess* owner);
|
||||
|
||||
[[nodiscard]] static Result InitializeServiceThread(Core::System& system, KThread* thread,
|
||||
std::function<void()>&& thread_func,
|
||||
s32 prio, s32 virt_core, KProcess* owner);
|
||||
|
||||
public:
|
||||
struct StackParameters {
|
||||
u8 svc_permission[0x10];
|
||||
|
@ -34,14 +34,15 @@
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/kernel/k_resource_limit.h"
|
||||
#include "core/hle/kernel/k_scheduler.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.h"
|
||||
#include "core/hle/kernel/k_shared_memory.h"
|
||||
#include "core/hle/kernel/k_system_resource.h"
|
||||
#include "core/hle/kernel/k_thread.h"
|
||||
#include "core/hle/kernel/k_worker_task_manager.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/physical_core.h"
|
||||
#include "core/hle/kernel/service_thread.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/hle/service/server_manager.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
@ -55,9 +56,7 @@ struct KernelCore::Impl {
|
||||
static constexpr size_t BlockInfoSlabHeapSize = 4000;
|
||||
static constexpr size_t ReservedDynamicPageCount = 64;
|
||||
|
||||
explicit Impl(Core::System& system_, KernelCore& kernel_)
|
||||
: service_threads_manager{1, "ServiceThreadsManager"},
|
||||
service_thread_barrier{2}, system{system_} {}
|
||||
explicit Impl(Core::System& system_, KernelCore& kernel_) : system{system_} {}
|
||||
|
||||
void SetMulticore(bool is_multi) {
|
||||
is_multicore = is_multi;
|
||||
@ -98,8 +97,6 @@ struct KernelCore::Impl {
|
||||
|
||||
InitializeHackSharedMemory();
|
||||
RegisterHostThread(nullptr);
|
||||
|
||||
default_service_thread = &CreateServiceThread(kernel, "DefaultServiceThread");
|
||||
}
|
||||
|
||||
void InitializeCores() {
|
||||
@ -140,11 +137,6 @@ struct KernelCore::Impl {
|
||||
|
||||
preemption_event = nullptr;
|
||||
|
||||
for (auto& iter : named_ports) {
|
||||
iter.second->Close();
|
||||
}
|
||||
named_ports.clear();
|
||||
|
||||
exclusive_monitor.reset();
|
||||
|
||||
// Cleanup persistent kernel objects
|
||||
@ -207,8 +199,9 @@ struct KernelCore::Impl {
|
||||
}
|
||||
|
||||
void CloseServices() {
|
||||
// Ensures all service threads gracefully shutdown.
|
||||
ClearServiceThreads();
|
||||
// Ensures all servers gracefully shutdown.
|
||||
std::scoped_lock lk{server_lock};
|
||||
server_managers.clear();
|
||||
}
|
||||
|
||||
void InitializePhysicalCores() {
|
||||
@ -761,55 +754,6 @@ struct KernelCore::Impl {
|
||||
"HidBus:SharedMemory");
|
||||
}
|
||||
|
||||
KClientPort* CreateNamedServicePort(std::string name) {
|
||||
auto search = service_interface_factory.find(name);
|
||||
if (search == service_interface_factory.end()) {
|
||||
UNIMPLEMENTED();
|
||||
return {};
|
||||
}
|
||||
|
||||
return &search->second(system.ServiceManager(), system);
|
||||
}
|
||||
|
||||
void RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
|
||||
auto search = service_interface_handlers.find(name);
|
||||
if (search == service_interface_handlers.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
search->second(system.ServiceManager(), server_port);
|
||||
}
|
||||
|
||||
Kernel::ServiceThread& CreateServiceThread(KernelCore& kernel, const std::string& name) {
|
||||
auto* ptr = new ServiceThread(kernel, name);
|
||||
|
||||
service_threads_manager.QueueWork(
|
||||
[this, ptr]() { service_threads.emplace(ptr, std::unique_ptr<ServiceThread>(ptr)); });
|
||||
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
void ReleaseServiceThread(Kernel::ServiceThread& service_thread) {
|
||||
auto* ptr = &service_thread;
|
||||
|
||||
if (ptr == default_service_thread) {
|
||||
// Nothing to do here, the service is using default_service_thread, which will be
|
||||
// released on shutdown.
|
||||
return;
|
||||
}
|
||||
|
||||
service_threads_manager.QueueWork([this, ptr]() { service_threads.erase(ptr); });
|
||||
}
|
||||
|
||||
void ClearServiceThreads() {
|
||||
service_threads_manager.QueueWork([this] {
|
||||
service_threads.clear();
|
||||
default_service_thread = nullptr;
|
||||
service_thread_barrier.Sync();
|
||||
});
|
||||
service_thread_barrier.Sync();
|
||||
}
|
||||
|
||||
std::mutex registered_objects_lock;
|
||||
std::mutex registered_in_use_objects_lock;
|
||||
|
||||
@ -839,14 +783,12 @@ struct KernelCore::Impl {
|
||||
|
||||
std::unique_ptr<KObjectNameGlobalData> object_name_global_data;
|
||||
|
||||
/// Map of named ports managed by the kernel, which can be retrieved using
|
||||
/// the ConnectToPort SVC.
|
||||
std::unordered_map<std::string, ServiceInterfaceFactory> service_interface_factory;
|
||||
std::unordered_map<std::string, ServiceInterfaceHandlerFn> service_interface_handlers;
|
||||
NamedPortTable named_ports;
|
||||
std::unordered_set<KAutoObject*> registered_objects;
|
||||
std::unordered_set<KAutoObject*> registered_in_use_objects;
|
||||
|
||||
std::mutex server_lock;
|
||||
std::vector<std::unique_ptr<Service::ServerManager>> server_managers;
|
||||
|
||||
std::unique_ptr<Core::ExclusiveMonitor> exclusive_monitor;
|
||||
std::array<std::unique_ptr<Kernel::PhysicalCore>, Core::Hardware::NUM_CPU_CORES> cores;
|
||||
|
||||
@ -881,12 +823,6 @@ struct KernelCore::Impl {
|
||||
// Memory layout
|
||||
std::unique_ptr<KMemoryLayout> memory_layout;
|
||||
|
||||
// Threads used for services
|
||||
std::unordered_map<ServiceThread*, std::unique_ptr<ServiceThread>> service_threads;
|
||||
ServiceThread* default_service_thread{};
|
||||
Common::ThreadWorker service_threads_manager;
|
||||
Common::Barrier service_thread_barrier;
|
||||
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads{};
|
||||
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
|
||||
|
||||
@ -1050,23 +986,6 @@ void KernelCore::PrepareReschedule(std::size_t id) {
|
||||
// TODO: Reimplement, this
|
||||
}
|
||||
|
||||
void KernelCore::RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory) {
|
||||
impl->service_interface_factory.emplace(std::move(name), factory);
|
||||
}
|
||||
|
||||
void KernelCore::RegisterInterfaceForNamedService(std::string name,
|
||||
ServiceInterfaceHandlerFn&& handler) {
|
||||
impl->service_interface_handlers.emplace(std::move(name), handler);
|
||||
}
|
||||
|
||||
KClientPort* KernelCore::CreateNamedServicePort(std::string name) {
|
||||
return impl->CreateNamedServicePort(std::move(name));
|
||||
}
|
||||
|
||||
void KernelCore::RegisterNamedServiceHandler(std::string name, KServerPort* server_port) {
|
||||
impl->RegisterNamedServiceHandler(std::move(name), server_port);
|
||||
}
|
||||
|
||||
void KernelCore::RegisterKernelObject(KAutoObject* object) {
|
||||
std::scoped_lock lk{impl->registered_objects_lock};
|
||||
impl->registered_objects.insert(object);
|
||||
@ -1087,8 +1006,19 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
|
||||
impl->registered_in_use_objects.erase(object);
|
||||
}
|
||||
|
||||
bool KernelCore::IsValidNamedPort(NamedPortTable::const_iterator port) const {
|
||||
return port != impl->named_ports.cend();
|
||||
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
|
||||
auto* manager = server_manager.get();
|
||||
|
||||
{
|
||||
std::scoped_lock lk{impl->server_lock};
|
||||
if (impl->is_shutting_down) {
|
||||
return;
|
||||
}
|
||||
|
||||
impl->server_managers.emplace_back(std::move(server_manager));
|
||||
}
|
||||
|
||||
manager->LoopProcess();
|
||||
}
|
||||
|
||||
u32 KernelCore::CreateNewObjectID() {
|
||||
@ -1127,6 +1057,87 @@ void KernelCore::RegisterHostThread(KThread* existing_thread) {
|
||||
}
|
||||
}
|
||||
|
||||
static std::jthread RunHostThreadFunc(KernelCore& kernel, KProcess* process,
|
||||
std::string&& thread_name, std::function<void()>&& func) {
|
||||
// Reserve a new thread from the process resource limit.
|
||||
KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax);
|
||||
ASSERT(thread_reservation.Succeeded());
|
||||
|
||||
// Initialize the thread.
|
||||
KThread* thread = KThread::Create(kernel);
|
||||
ASSERT(R_SUCCEEDED(KThread::InitializeDummyThread(thread, process)));
|
||||
|
||||
// Commit the thread reservation.
|
||||
thread_reservation.Commit();
|
||||
|
||||
return std::jthread(
|
||||
[&kernel, thread, thread_name{std::move(thread_name)}, func{std::move(func)}] {
|
||||
// Set the thread name.
|
||||
Common::SetCurrentThreadName(thread_name.c_str());
|
||||
|
||||
// Register the thread.
|
||||
kernel.RegisterHostThread(thread);
|
||||
|
||||
// Run the callback.
|
||||
func();
|
||||
|
||||
// Close the thread.
|
||||
// This will free the process if it is the last reference.
|
||||
thread->Close();
|
||||
});
|
||||
}
|
||||
|
||||
std::jthread KernelCore::RunOnHostCoreProcess(std::string&& process_name,
|
||||
std::function<void()> func) {
|
||||
// Make a new process.
|
||||
KProcess* process = KProcess::Create(*this);
|
||||
ASSERT(R_SUCCEEDED(KProcess::Initialize(process, System(), "", KProcess::ProcessType::Userland,
|
||||
GetSystemResourceLimit())));
|
||||
|
||||
// Ensure that we don't hold onto any extra references.
|
||||
SCOPE_EXIT({ process->Close(); });
|
||||
|
||||
// Run the host thread.
|
||||
return RunHostThreadFunc(*this, process, std::move(process_name), std::move(func));
|
||||
}
|
||||
|
||||
std::jthread KernelCore::RunOnHostCoreThread(std::string&& thread_name,
|
||||
std::function<void()> func) {
|
||||
// Get the current process.
|
||||
KProcess* process = GetCurrentProcessPointer(*this);
|
||||
|
||||
// Run the host thread.
|
||||
return RunHostThreadFunc(*this, process, std::move(thread_name), std::move(func));
|
||||
}
|
||||
|
||||
void KernelCore::RunOnGuestCoreProcess(std::string&& process_name, std::function<void()> func) {
|
||||
constexpr s32 ServiceThreadPriority = 16;
|
||||
constexpr s32 ServiceThreadCore = 3;
|
||||
|
||||
// Make a new process.
|
||||
KProcess* process = KProcess::Create(*this);
|
||||
ASSERT(R_SUCCEEDED(KProcess::Initialize(process, System(), "", KProcess::ProcessType::Userland,
|
||||
GetSystemResourceLimit())));
|
||||
|
||||
// Ensure that we don't hold onto any extra references.
|
||||
SCOPE_EXIT({ process->Close(); });
|
||||
|
||||
// Reserve a new thread from the process resource limit.
|
||||
KScopedResourceReservation thread_reservation(process, LimitableResource::ThreadCountMax);
|
||||
ASSERT(thread_reservation.Succeeded());
|
||||
|
||||
// Initialize the thread.
|
||||
KThread* thread = KThread::Create(*this);
|
||||
ASSERT(R_SUCCEEDED(KThread::InitializeServiceThread(
|
||||
System(), thread, std::move(func), ServiceThreadPriority, ServiceThreadCore, process)));
|
||||
|
||||
// Commit the thread reservation.
|
||||
thread_reservation.Commit();
|
||||
|
||||
// Begin running the thread.
|
||||
ASSERT(R_SUCCEEDED(thread->Run()));
|
||||
}
|
||||
|
||||
u32 KernelCore::GetCurrentHostThreadID() const {
|
||||
return impl->GetCurrentHostThreadID();
|
||||
}
|
||||
@ -1271,18 +1282,6 @@ void KernelCore::ExitSVCProfile() {
|
||||
MicroProfileLeave(MICROPROFILE_TOKEN(Kernel_SVC), impl->svc_ticks[CurrentPhysicalCoreIndex()]);
|
||||
}
|
||||
|
||||
Kernel::ServiceThread& KernelCore::CreateServiceThread(const std::string& name) {
|
||||
return impl->CreateServiceThread(*this, name);
|
||||
}
|
||||
|
||||
Kernel::ServiceThread& KernelCore::GetDefaultServiceThread() const {
|
||||
return *impl->default_service_thread;
|
||||
}
|
||||
|
||||
void KernelCore::ReleaseServiceThread(Kernel::ServiceThread& service_thread) {
|
||||
impl->ReleaseServiceThread(service_thread);
|
||||
}
|
||||
|
||||
Init::KSlabResourceCounts& KernelCore::SlabResourceCounts() {
|
||||
return impl->slab_resource_counts;
|
||||
}
|
||||
|
@ -9,6 +9,8 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "core/hardware_properties.h"
|
||||
#include "core/hle/kernel/k_auto_object.h"
|
||||
#include "core/hle/kernel/k_slab_heap.h"
|
||||
@ -24,6 +26,10 @@ class CoreTiming;
|
||||
struct EventType;
|
||||
} // namespace Core::Timing
|
||||
|
||||
namespace Service {
|
||||
class ServerManager;
|
||||
}
|
||||
|
||||
namespace Service::SM {
|
||||
class ServiceManager;
|
||||
}
|
||||
@ -65,13 +71,6 @@ class KTransferMemory;
|
||||
class KWorkerTaskManager;
|
||||
class KCodeMemory;
|
||||
class PhysicalCore;
|
||||
class ServiceThread;
|
||||
class Synchronization;
|
||||
|
||||
using ServiceInterfaceFactory =
|
||||
std::function<KClientPort&(Service::SM::ServiceManager&, Core::System&)>;
|
||||
|
||||
using ServiceInterfaceHandlerFn = std::function<void(Service::SM::ServiceManager&, KServerPort*)>;
|
||||
|
||||
namespace Init {
|
||||
struct KSlabResourceCounts;
|
||||
@ -80,15 +79,8 @@ struct KSlabResourceCounts;
|
||||
template <typename T>
|
||||
class KSlabHeap;
|
||||
|
||||
using EmuThreadHandle = uintptr_t;
|
||||
constexpr EmuThreadHandle EmuThreadHandleInvalid{};
|
||||
constexpr EmuThreadHandle EmuThreadHandleReserved{1ULL << 63};
|
||||
|
||||
/// Represents a single instance of the kernel.
|
||||
class KernelCore {
|
||||
private:
|
||||
using NamedPortTable = std::unordered_map<std::string, KClientPort*>;
|
||||
|
||||
public:
|
||||
/// Constructs an instance of the kernel using the given System
|
||||
/// instance as a context for any necessary system-related state,
|
||||
@ -196,18 +188,6 @@ public:
|
||||
|
||||
void InvalidateCpuInstructionCacheRange(VAddr addr, std::size_t size);
|
||||
|
||||
/// Registers a named HLE service, passing a factory used to open a port to that service.
|
||||
void RegisterNamedService(std::string name, ServiceInterfaceFactory&& factory);
|
||||
|
||||
/// Registers a setup function for the named HLE service.
|
||||
void RegisterInterfaceForNamedService(std::string name, ServiceInterfaceHandlerFn&& handler);
|
||||
|
||||
/// Opens a port to a service previously registered with RegisterNamedService.
|
||||
KClientPort* CreateNamedServicePort(std::string name);
|
||||
|
||||
/// Accepts a session on a port created by CreateNamedServicePort.
|
||||
void RegisterNamedServiceHandler(std::string name, KServerPort* server_port);
|
||||
|
||||
/// Registers all kernel objects with the global emulation state, this is purely for tracking
|
||||
/// leaks after emulation has been shutdown.
|
||||
void RegisterKernelObject(KAutoObject* object);
|
||||
@ -224,8 +204,8 @@ public:
|
||||
/// destroyed during the current emulation session.
|
||||
void UnregisterInUseObject(KAutoObject* object);
|
||||
|
||||
/// Determines whether or not the given port is a valid named port.
|
||||
bool IsValidNamedPort(NamedPortTable::const_iterator port) const;
|
||||
// Runs the given server manager until shutdown.
|
||||
void RunServer(std::unique_ptr<Service::ServerManager>&& server_manager);
|
||||
|
||||
/// Gets the current host_thread/guest_thread pointer.
|
||||
KThread* GetCurrentEmuThread() const;
|
||||
@ -242,6 +222,12 @@ public:
|
||||
/// Register the current thread as a non CPU core thread.
|
||||
void RegisterHostThread(KThread* existing_thread = nullptr);
|
||||
|
||||
void RunOnGuestCoreProcess(std::string&& process_name, std::function<void()> func);
|
||||
|
||||
std::jthread RunOnHostCoreProcess(std::string&& process_name, std::function<void()> func);
|
||||
|
||||
std::jthread RunOnHostCoreThread(std::string&& thread_name, std::function<void()> func);
|
||||
|
||||
/// Gets global data for KObjectName.
|
||||
KObjectNameGlobalData& ObjectNameGlobalData();
|
||||
|
||||
@ -310,33 +296,6 @@ public:
|
||||
|
||||
void ExitSVCProfile();
|
||||
|
||||
/**
|
||||
* Creates a host thread to execute HLE service requests, which are used to execute service
|
||||
* routines asynchronously. While these are allocated per ServerSession, these need to be owned
|
||||
* and managed outside of ServerSession to avoid a circular dependency. In general, most
|
||||
* services can just use the default service thread, and not need their own host service thread.
|
||||
* See GetDefaultServiceThread.
|
||||
* @param name String name for the ServerSession creating this thread, used for debug
|
||||
* purposes.
|
||||
* @returns A reference to the newly created service thread.
|
||||
*/
|
||||
Kernel::ServiceThread& CreateServiceThread(const std::string& name);
|
||||
|
||||
/**
|
||||
* Gets the default host service thread, which executes HLE service requests. Unless service
|
||||
* requests need to block on the host, the default service thread should be used in favor of
|
||||
* creating a new service thread.
|
||||
* @returns A reference to the default service thread.
|
||||
*/
|
||||
Kernel::ServiceThread& GetDefaultServiceThread() const;
|
||||
|
||||
/**
|
||||
* Releases a HLE service thread, instructing KernelCore to free it. This should be called when
|
||||
* the ServerSession associated with the thread is destroyed.
|
||||
* @param service_thread Service thread to release.
|
||||
*/
|
||||
void ReleaseServiceThread(Kernel::ServiceThread& service_thread);
|
||||
|
||||
/// Workaround for single-core mode when preempting threads while idle.
|
||||
bool IsPhantomModeForSingleCore() const;
|
||||
void SetIsPhantomModeForSingleCore(bool value);
|
||||
|
@ -1,206 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "common/polyfill_thread.h"
|
||||
#include "common/scope_exit.h"
|
||||
#include "common/thread.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_scoped_resource_reservation.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"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class ServiceThread::Impl final {
|
||||
public:
|
||||
explicit Impl(KernelCore& kernel, const std::string& service_name);
|
||||
~Impl();
|
||||
|
||||
void WaitAndProcessImpl();
|
||||
void SessionClosed(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager);
|
||||
void LoopProcess();
|
||||
|
||||
void RegisterServerSession(KServerSession* session,
|
||||
std::shared_ptr<SessionRequestManager> manager);
|
||||
|
||||
private:
|
||||
KernelCore& kernel;
|
||||
const std::string m_service_name;
|
||||
|
||||
std::jthread m_host_thread{};
|
||||
std::mutex m_session_mutex{};
|
||||
std::map<KServerSession*, std::shared_ptr<SessionRequestManager>> m_sessions{};
|
||||
KEvent* m_wakeup_event{};
|
||||
KThread* m_thread{};
|
||||
std::atomic<bool> m_shutdown_requested{};
|
||||
};
|
||||
|
||||
void ServiceThread::Impl::WaitAndProcessImpl() {
|
||||
// Create local list of waitable sessions.
|
||||
std::vector<KSynchronizationObject*> objs;
|
||||
std::vector<std::shared_ptr<SessionRequestManager>> managers;
|
||||
|
||||
{
|
||||
// Lock to get the set.
|
||||
std::scoped_lock lk{m_session_mutex};
|
||||
|
||||
// Reserve the needed quantity.
|
||||
objs.reserve(m_sessions.size() + 1);
|
||||
managers.reserve(m_sessions.size());
|
||||
|
||||
// Copy to our local list.
|
||||
for (const auto& [session, manager] : m_sessions) {
|
||||
objs.push_back(session);
|
||||
managers.push_back(manager);
|
||||
}
|
||||
|
||||
// Insert the wakeup event at the end.
|
||||
objs.push_back(&m_wakeup_event->GetReadableEvent());
|
||||
}
|
||||
|
||||
// Wait on the list of sessions.
|
||||
s32 index{-1};
|
||||
Result rc = KSynchronizationObject::Wait(kernel, &index, objs.data(),
|
||||
static_cast<s32>(objs.size()), -1);
|
||||
ASSERT(!rc.IsFailure());
|
||||
|
||||
// If this was the wakeup event, clear it and finish.
|
||||
if (index >= static_cast<s64>(objs.size() - 1)) {
|
||||
m_wakeup_event->Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// This event is from a server session.
|
||||
auto* server_session = static_cast<KServerSession*>(objs[index]);
|
||||
auto& manager = managers[index];
|
||||
|
||||
// Fetch the HLE request context.
|
||||
std::shared_ptr<HLERequestContext> context;
|
||||
rc = server_session->ReceiveRequest(&context, manager);
|
||||
|
||||
// If the session was closed, handle that.
|
||||
if (rc == ResultSessionClosed) {
|
||||
SessionClosed(server_session, manager);
|
||||
|
||||
// Finish.
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: handle other cases
|
||||
ASSERT(rc == ResultSuccess);
|
||||
|
||||
// Perform the request.
|
||||
Result service_rc = manager->CompleteSyncRequest(server_session, *context);
|
||||
|
||||
// Reply to the client.
|
||||
rc = server_session->SendReplyHLE();
|
||||
|
||||
if (rc == ResultSessionClosed || service_rc == IPC::ERR_REMOTE_PROCESS_DEAD) {
|
||||
SessionClosed(server_session, manager);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: handle other cases
|
||||
ASSERT(rc == ResultSuccess);
|
||||
ASSERT(service_rc == ResultSuccess);
|
||||
}
|
||||
|
||||
void ServiceThread::Impl::SessionClosed(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager) {
|
||||
{
|
||||
// Lock to get the set.
|
||||
std::scoped_lock lk{m_session_mutex};
|
||||
|
||||
// Erase the session.
|
||||
ASSERT(m_sessions.erase(server_session) == 1);
|
||||
}
|
||||
|
||||
// Close our reference to the server session.
|
||||
server_session->Close();
|
||||
}
|
||||
|
||||
void ServiceThread::Impl::LoopProcess() {
|
||||
Common::SetCurrentThreadName(m_service_name.c_str());
|
||||
|
||||
kernel.RegisterHostThread(m_thread);
|
||||
|
||||
while (!m_shutdown_requested.load()) {
|
||||
WaitAndProcessImpl();
|
||||
}
|
||||
}
|
||||
|
||||
void ServiceThread::Impl::RegisterServerSession(KServerSession* server_session,
|
||||
std::shared_ptr<SessionRequestManager> manager) {
|
||||
// Open the server session.
|
||||
server_session->Open();
|
||||
|
||||
{
|
||||
// Lock to get the set.
|
||||
std::scoped_lock lk{m_session_mutex};
|
||||
|
||||
// Insert the session and manager.
|
||||
m_sessions[server_session] = manager;
|
||||
}
|
||||
|
||||
// Signal the wakeup event.
|
||||
m_wakeup_event->Signal();
|
||||
}
|
||||
|
||||
ServiceThread::Impl::~Impl() {
|
||||
// Shut down the processing thread.
|
||||
m_shutdown_requested.store(true);
|
||||
m_wakeup_event->Signal();
|
||||
m_host_thread.join();
|
||||
|
||||
// Close all remaining sessions.
|
||||
for (const auto& [server_session, manager] : m_sessions) {
|
||||
server_session->Close();
|
||||
}
|
||||
|
||||
// Destroy remaining managers.
|
||||
m_sessions.clear();
|
||||
|
||||
// Close event.
|
||||
m_wakeup_event->GetReadableEvent().Close();
|
||||
m_wakeup_event->Close();
|
||||
|
||||
// Close thread.
|
||||
m_thread->Close();
|
||||
}
|
||||
|
||||
ServiceThread::Impl::Impl(KernelCore& kernel_, const std::string& service_name)
|
||||
: kernel{kernel_}, m_service_name{service_name} {
|
||||
// Initialize event.
|
||||
m_wakeup_event = KEvent::Create(kernel);
|
||||
m_wakeup_event->Initialize(nullptr);
|
||||
|
||||
// Initialize thread.
|
||||
m_thread = KThread::Create(kernel);
|
||||
ASSERT(KThread::InitializeDummyThread(m_thread, nullptr).IsSuccess());
|
||||
|
||||
// Start thread.
|
||||
m_host_thread = std::jthread([this] { LoopProcess(); });
|
||||
}
|
||||
|
||||
ServiceThread::ServiceThread(KernelCore& kernel, const std::string& name)
|
||||
: impl{std::make_unique<Impl>(kernel, name)} {}
|
||||
|
||||
ServiceThread::~ServiceThread() = default;
|
||||
|
||||
void ServiceThread::RegisterServerSession(KServerSession* session,
|
||||
std::shared_ptr<SessionRequestManager> manager) {
|
||||
impl->RegisterServerSession(session, manager);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
@ -1,29 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class HLERequestContext;
|
||||
class KernelCore;
|
||||
class KSession;
|
||||
class SessionRequestManager;
|
||||
|
||||
class ServiceThread final {
|
||||
public:
|
||||
explicit ServiceThread(KernelCore& kernel, const std::string& name);
|
||||
~ServiceThread();
|
||||
|
||||
void RegisterServerSession(KServerSession* session,
|
||||
std::shared_ptr<SessionRequestManager> manager);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl;
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
@ -126,6 +126,11 @@ Result GetInfo(Core::System& system, u64* result, InfoType info_id_type, Handle
|
||||
*result = process->GetTotalPhysicalMemoryUsedWithoutSystemResource();
|
||||
return ResultSuccess;
|
||||
|
||||
case InfoType::IsApplication:
|
||||
LOG_WARNING(Kernel_SVC, "(STUBBED) Assuming process is application");
|
||||
*result = true;
|
||||
return ResultSuccess;
|
||||
|
||||
case InfoType::FreeThreadCount:
|
||||
*result = process->GetFreeThreadCount();
|
||||
return ResultSuccess;
|
||||
|
@ -12,56 +12,40 @@
|
||||
|
||||
namespace Kernel::Svc {
|
||||
|
||||
/// Connect to an OS service given the port name, returns the handle to the port to out
|
||||
Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr port_name_address) {
|
||||
auto& memory = system.Memory();
|
||||
if (!memory.IsValidVirtualAddress(port_name_address)) {
|
||||
LOG_ERROR(Kernel_SVC,
|
||||
"Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
|
||||
port_name_address);
|
||||
return ResultNotFound;
|
||||
}
|
||||
Result ConnectToNamedPort(Core::System& system, Handle* out, VAddr user_name) {
|
||||
// Copy the provided name from user memory to kernel memory.
|
||||
auto string_name = system.Memory().ReadCString(user_name, KObjectName::NameLengthMax);
|
||||
|
||||
static constexpr std::size_t PortNameMaxLength = 11;
|
||||
// Read 1 char beyond the max allowed port name to detect names that are too long.
|
||||
const std::string port_name = memory.ReadCString(port_name_address, PortNameMaxLength + 1);
|
||||
if (port_name.size() > PortNameMaxLength) {
|
||||
LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
|
||||
port_name.size());
|
||||
return ResultOutOfRange;
|
||||
}
|
||||
std::array<char, KObjectName::NameLengthMax> name{};
|
||||
std::strncpy(name.data(), string_name.c_str(), KObjectName::NameLengthMax - 1);
|
||||
|
||||
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
|
||||
// Validate that the name is valid.
|
||||
R_UNLESS(name[sizeof(name) - 1] == '\x00', ResultOutOfRange);
|
||||
|
||||
// Get the current handle table.
|
||||
auto& kernel = system.Kernel();
|
||||
auto& handle_table = GetCurrentProcess(kernel).GetHandleTable();
|
||||
auto& handle_table = GetCurrentProcess(system.Kernel()).GetHandleTable();
|
||||
|
||||
// Find the client port.
|
||||
auto port = kernel.CreateNamedServicePort(port_name);
|
||||
if (!port) {
|
||||
LOG_ERROR(Kernel_SVC, "tried to connect to unknown port: {}", port_name);
|
||||
return ResultNotFound;
|
||||
}
|
||||
auto port = KObjectName::Find<KClientPort>(system.Kernel(), name.data());
|
||||
R_UNLESS(port.IsNotNull(), ResultNotFound);
|
||||
|
||||
// Reserve a handle for the port.
|
||||
// NOTE: Nintendo really does write directly to the output handle here.
|
||||
R_TRY(handle_table.Reserve(out));
|
||||
auto handle_guard = SCOPE_GUARD({ handle_table.Unreserve(*out); });
|
||||
ON_RESULT_FAILURE {
|
||||
handle_table.Unreserve(*out);
|
||||
};
|
||||
|
||||
// Create a session.
|
||||
KClientSession* session{};
|
||||
KClientSession* session;
|
||||
R_TRY(port->CreateSession(std::addressof(session)));
|
||||
|
||||
kernel.RegisterNamedServiceHandler(port_name, &port->GetParent()->GetServerPort());
|
||||
|
||||
// Register the session in the table, close the extra reference.
|
||||
handle_table.Register(*out, session);
|
||||
session->Close();
|
||||
|
||||
// We succeeded.
|
||||
handle_guard.Cancel();
|
||||
return ResultSuccess;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreatePort(Core::System& system, Handle* out_server, Handle* out_client,
|
||||
@ -77,9 +61,12 @@ Result ConnectToPort(Core::System& system, Handle* out_handle, Handle port) {
|
||||
|
||||
Result ManageNamedPort(Core::System& system, Handle* out_server_handle, uint64_t user_name,
|
||||
int32_t max_sessions) {
|
||||
// Copy the provided name from user memory to kernel memory.
|
||||
auto string_name = system.Memory().ReadCString(user_name, KObjectName::NameLengthMax);
|
||||
|
||||
// Copy the provided name from user memory to kernel memory.
|
||||
std::array<char, KObjectName::NameLengthMax> name{};
|
||||
system.Memory().ReadBlock(user_name, name.data(), sizeof(name));
|
||||
std::strncpy(name.data(), string_name.c_str(), KObjectName::NameLengthMax - 1);
|
||||
|
||||
// Validate that sessions and name are valid.
|
||||
R_UNLESS(max_sessions >= 0, ResultOutOfRange);
|
||||
|
Reference in New Issue
Block a user