kernel: Implement a more accurate IPC dispatch.
This commit is contained in:
		
							parent
							
								
									e3ee017e91
								
							
						
					
					
						commit
						c3d3b173d3
					
				| @ -170,6 +170,7 @@ add_library(core STATIC | ||||
|     hle/kernel/server_port.h | ||||
|     hle/kernel/server_session.cpp | ||||
|     hle/kernel/server_session.h | ||||
|     hle/kernel/session.cpp | ||||
|     hle/kernel/session.h | ||||
|     hle/kernel/shared_memory.cpp | ||||
|     hle/kernel/shared_memory.h | ||||
|  | ||||
| @ -19,6 +19,7 @@ | ||||
| #include "core/hle/kernel/hle_ipc.h" | ||||
| #include "core/hle/kernel/object.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/kernel/session.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace IPC { | ||||
| @ -139,10 +140,9 @@ public: | ||||
|             context->AddDomainObject(std::move(iface)); | ||||
|         } else { | ||||
|             auto& kernel = Core::System::GetInstance().Kernel(); | ||||
|             auto [server, client] = | ||||
|                 Kernel::ServerSession::CreateSessionPair(kernel, iface->GetServiceName()); | ||||
|             iface->ClientConnected(server); | ||||
|             auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName()); | ||||
|             context->AddMoveObject(std::move(client)); | ||||
|             iface->ClientConnected(std::move(server)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| #include "core/hle/kernel/object.h" | ||||
| #include "core/hle/kernel/server_port.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/kernel/session.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| @ -20,28 +21,23 @@ std::shared_ptr<ServerPort> ClientPort::GetServerPort() const { | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::shared_ptr<ClientSession>> ClientPort::Connect() { | ||||
|     // Note: Threads do not wait for the server endpoint to call
 | ||||
|     // AcceptSession before returning from this call.
 | ||||
| 
 | ||||
|     if (active_sessions >= max_sessions) { | ||||
|         return ERR_MAX_CONNECTIONS_REACHED; | ||||
|     } | ||||
|     active_sessions++; | ||||
| 
 | ||||
|     // Create a new session pair, let the created sessions inherit the parent port's HLE handler.
 | ||||
|     auto [server, client] = | ||||
|         ServerSession::CreateSessionPair(kernel, server_port->GetName(), SharedFrom(this)); | ||||
|     auto [client, server] = Kernel::Session::Create(kernel, name); | ||||
| 
 | ||||
|     if (server_port->HasHLEHandler()) { | ||||
|         server_port->GetHLEHandler()->ClientConnected(server); | ||||
|         server_port->GetHLEHandler()->ClientConnected(std::move(server)); | ||||
|     } else { | ||||
|         server_port->AppendPendingSession(server); | ||||
|         server_port->AppendPendingSession(std::move(server)); | ||||
|     } | ||||
| 
 | ||||
|     // Wake the threads waiting on the ServerPort
 | ||||
|     server_port->WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     return MakeResult(client); | ||||
|     return MakeResult(std::move(client)); | ||||
| } | ||||
| 
 | ||||
| void ClientPort::ConnectionClosed() { | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Copyright 2019 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| @ -12,22 +12,44 @@ | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ClientSession::ClientSession(KernelCore& kernel) : Object{kernel} {} | ||||
| ClientSession::ClientSession(KernelCore& kernel) : WaitObject{kernel} {} | ||||
| 
 | ||||
| ClientSession::~ClientSession() { | ||||
|     // This destructor will be called automatically when the last ClientSession handle is closed by
 | ||||
|     // the emulated application.
 | ||||
|     if (auto server = parent->server.lock()) { | ||||
|         server->ClientDisconnected(); | ||||
|     if (parent->Server()) { | ||||
|         parent->Server()->ClientDisconnected(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| ResultCode ClientSession::SendSyncRequest(Thread* thread, Memory::Memory& memory) { | ||||
|     // Signal the server session that new data is available
 | ||||
|     if (auto server = parent->server.lock()) { | ||||
|         return server->HandleSyncRequest(SharedFrom(thread), memory); | ||||
| bool ClientSession::ShouldWait(const Thread* thread) const { | ||||
|     UNIMPLEMENTED(); | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| void ClientSession::Acquire(Thread* thread) { | ||||
|     UNIMPLEMENTED(); | ||||
| } | ||||
| 
 | ||||
| ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kernel, | ||||
|                                                                 std::shared_ptr<Session> parent, | ||||
|                                                                 std::string name) { | ||||
|     std::shared_ptr<ClientSession> client_session{std::make_shared<ClientSession>(kernel)}; | ||||
| 
 | ||||
|     client_session->name = std::move(name); | ||||
|     client_session->parent = std::move(parent); | ||||
| 
 | ||||
|     return MakeResult(std::move(client_session)); | ||||
| } | ||||
| 
 | ||||
| ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) { | ||||
|     // Keep ServerSession alive until we're done working with it.
 | ||||
|     if (!parent->Server()) { | ||||
|         return ERR_SESSION_CLOSED_BY_REMOTE; | ||||
|     } | ||||
| 
 | ||||
|     return ERR_SESSION_CLOSED_BY_REMOTE; | ||||
|     // Signal the server session that new data is available
 | ||||
|     return parent->Server()->HandleSyncRequest(std::move(thread), memory); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Copyright 2019 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| @ -6,7 +6,9 @@ | ||||
| 
 | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "core/hle/kernel/object.h" | ||||
| 
 | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| union ResultCode; | ||||
| 
 | ||||
| @ -18,15 +20,14 @@ namespace Kernel { | ||||
| 
 | ||||
| class KernelCore; | ||||
| class Session; | ||||
| class ServerSession; | ||||
| class Thread; | ||||
| 
 | ||||
| class ClientSession final : public Object { | ||||
| class ClientSession final : public WaitObject { | ||||
| public: | ||||
|     explicit ClientSession(KernelCore& kernel); | ||||
|     ~ClientSession() override; | ||||
| 
 | ||||
|     friend class ServerSession; | ||||
|     friend class Session; | ||||
| 
 | ||||
|     std::string GetTypeName() const override { | ||||
|         return "ClientSession"; | ||||
| @ -41,9 +42,17 @@ public: | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     ResultCode SendSyncRequest(Thread* thread, Memory::Memory& memory); | ||||
|     ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory); | ||||
| 
 | ||||
|     bool ShouldWait(const Thread* thread) const override; | ||||
| 
 | ||||
|     void Acquire(Thread* thread) override; | ||||
| 
 | ||||
| private: | ||||
|     static ResultVal<std::shared_ptr<ClientSession>> Create(KernelCore& kernel, | ||||
|                                                             std::shared_ptr<Session> parent, | ||||
|                                                             std::string name = "Unknown"); | ||||
| 
 | ||||
|     /// The parent session, which links to the server endpoint.
 | ||||
|     std::shared_ptr<Session> parent; | ||||
| 
 | ||||
|  | ||||
| @ -74,6 +74,8 @@ std::shared_ptr<WritableEvent> HLERequestContext::SleepClientThread( | ||||
|         thread->WakeAfterDelay(timeout); | ||||
|     } | ||||
| 
 | ||||
|     is_thread_waiting = true; | ||||
| 
 | ||||
|     return writable_event; | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -264,6 +264,18 @@ public: | ||||
| 
 | ||||
|     std::string Description() const; | ||||
| 
 | ||||
|     Thread& GetThread() { | ||||
|         return *thread; | ||||
|     } | ||||
| 
 | ||||
|     const Thread& GetThread() const { | ||||
|         return *thread; | ||||
|     } | ||||
| 
 | ||||
|     bool IsThreadWaiting() const { | ||||
|         return is_thread_waiting; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming); | ||||
| 
 | ||||
| @ -290,6 +302,7 @@ private: | ||||
|     u32_le command{}; | ||||
| 
 | ||||
|     std::vector<std::shared_ptr<SessionRequestHandler>> domain_request_handlers; | ||||
|     bool is_thread_waiting{}; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -27,6 +27,7 @@ bool Object::IsWaitable() const { | ||||
|     case HandleType::ResourceLimit: | ||||
|     case HandleType::ClientPort: | ||||
|     case HandleType::ClientSession: | ||||
|     case HandleType::Session: | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -29,6 +29,7 @@ enum class HandleType : u32 { | ||||
|     ServerPort, | ||||
|     ClientSession, | ||||
|     ServerSession, | ||||
|     Session, | ||||
| }; | ||||
| 
 | ||||
| class Object : NonCopyable, public std::enable_shared_from_this<Object> { | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| // Copyright 2016 Citra Emulator Project
 | ||||
| // Copyright 2019 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| @ -9,6 +9,7 @@ | ||||
| #include "common/common_types.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| @ -24,34 +25,29 @@ | ||||
| namespace Kernel { | ||||
| 
 | ||||
| ServerSession::ServerSession(KernelCore& kernel) : WaitObject{kernel} {} | ||||
| ServerSession::~ServerSession() { | ||||
|     // This destructor will be called automatically when the last ServerSession handle is closed by
 | ||||
|     // the emulated application.
 | ||||
| 
 | ||||
|     // Decrease the port's connection count.
 | ||||
|     if (parent->port) { | ||||
|         parent->port->ConnectionClosed(); | ||||
|     } | ||||
| } | ||||
| ServerSession::~ServerSession() = default; | ||||
| 
 | ||||
| ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kernel, | ||||
|                                                                 std::shared_ptr<Session> parent, | ||||
|                                                                 std::string name) { | ||||
|     std::shared_ptr<ServerSession> server_session = std::make_shared<ServerSession>(kernel); | ||||
|     std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)}; | ||||
| 
 | ||||
|     server_session->name = std::move(name); | ||||
|     server_session->parent = nullptr; | ||||
|     session->request_event = Core::Timing::CreateEvent( | ||||
|         name, [session](u64 userdata, s64 cycles_late) { session->CompleteSyncRequest(); }); | ||||
|     session->name = std::move(name); | ||||
|     session->parent = std::move(parent); | ||||
| 
 | ||||
|     return MakeResult(std::move(server_session)); | ||||
|     return MakeResult(std::move(session)); | ||||
| } | ||||
| 
 | ||||
| bool ServerSession::ShouldWait(const Thread* thread) const { | ||||
|     // Wait if we have no pending requests, or if we're currently handling a request.
 | ||||
|     if (auto client = parent->client.lock()) { | ||||
|         return pending_requesting_threads.empty() || currently_handling != nullptr; | ||||
|     // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
 | ||||
|     if (!parent->Client()) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
 | ||||
|     return {}; | ||||
|     // Wait if we have no pending requests, or if we're currently handling a request.
 | ||||
|     return pending_requesting_threads.empty() || currently_handling != nullptr; | ||||
| } | ||||
| 
 | ||||
| void ServerSession::Acquire(Thread* thread) { | ||||
| @ -128,14 +124,21 @@ ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& con | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | ||||
|                                             Memory::Memory& memory) { | ||||
|     // The ServerSession received a sync request, this means that there's new data available
 | ||||
|     // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
 | ||||
|     // similar.
 | ||||
|     Kernel::HLERequestContext context(SharedFrom(this), thread); | ||||
|     u32* cmd_buf = (u32*)memory.GetPointer(thread->GetTLSAddress()); | ||||
|     context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); | ||||
| ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory) { | ||||
|     u32* cmd_buf{reinterpret_cast<u32*>(memory.GetPointer(thread->GetTLSAddress()))}; | ||||
|     std::shared_ptr<Kernel::HLERequestContext> context{ | ||||
|         std::make_shared<Kernel::HLERequestContext>(SharedFrom(this), std::move(thread))}; | ||||
| 
 | ||||
|     context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf); | ||||
|     request_queue.Push(std::move(context)); | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| ResultCode ServerSession::CompleteSyncRequest() { | ||||
|     ASSERT(!request_queue.Empty()); | ||||
| 
 | ||||
|     auto& context = *request_queue.Front(); | ||||
| 
 | ||||
|     ResultCode result = RESULT_SUCCESS; | ||||
|     // If the session has been converted to a domain, handle the domain request
 | ||||
| @ -147,61 +150,27 @@ ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | ||||
|         result = hle_handler->HandleSyncRequest(context); | ||||
|     } | ||||
| 
 | ||||
|     if (thread->GetStatus() == ThreadStatus::Running) { | ||||
|         // Put the thread to sleep until the server replies, it will be awoken in
 | ||||
|         // svcReplyAndReceive for LLE servers.
 | ||||
|         thread->SetStatus(ThreadStatus::WaitIPC); | ||||
| 
 | ||||
|         if (hle_handler != nullptr) { | ||||
|             // For HLE services, we put the request threads to sleep for a short duration to
 | ||||
|             // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
 | ||||
|             // other reasons like an async callback. The IPC overhead is needed to prevent
 | ||||
|             // starvation when a thread only does sync requests to HLE services while a
 | ||||
|             // lower-priority thread is waiting to run.
 | ||||
| 
 | ||||
|             // This delay was approximated in a homebrew application by measuring the average time
 | ||||
|             // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
 | ||||
|             // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
 | ||||
|             // a high variance and vary between models.
 | ||||
|             static constexpr u64 IPCDelayNanoseconds = 39000; | ||||
|             thread->WakeAfterDelay(IPCDelayNanoseconds); | ||||
|         } else { | ||||
|             // Add the thread to the list of threads that have issued a sync request with this
 | ||||
|             // server.
 | ||||
|             pending_requesting_threads.push_back(std::move(thread)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // If this ServerSession does not have an HLE implementation, just wake up the threads waiting
 | ||||
|     // on it.
 | ||||
|     WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     // Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the
 | ||||
|     // end of the command such that only commands following this one are handled as domains
 | ||||
|     if (convert_to_domain) { | ||||
|         ASSERT_MSG(IsSession(), "ServerSession is already a domain instance."); | ||||
|         domain_request_handlers = {hle_handler}; | ||||
|         convert_to_domain = false; | ||||
|     } | ||||
| 
 | ||||
|     // Some service requests require the thread to block
 | ||||
|     if (!context.IsThreadWaiting()) { | ||||
|         context.GetThread().ResumeFromWait(); | ||||
|         context.GetThread().SetWaitSynchronizationResult(result); | ||||
|     } | ||||
| 
 | ||||
|     request_queue.Pop(); | ||||
| 
 | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel, | ||||
|                                                             const std::string& name, | ||||
|                                                             std::shared_ptr<ClientPort> port) { | ||||
|     auto server_session = ServerSession::Create(kernel, name + "_Server").Unwrap(); | ||||
|     std::shared_ptr<ClientSession> client_session = std::make_shared<ClientSession>(kernel); | ||||
|     client_session->name = name + "_Client"; | ||||
| 
 | ||||
|     std::shared_ptr<Session> parent = std::make_shared<Session>(); | ||||
|     parent->client = client_session; | ||||
|     parent->server = server_session; | ||||
|     parent->port = std::move(port); | ||||
| 
 | ||||
|     client_session->parent = parent; | ||||
|     server_session->parent = parent; | ||||
| 
 | ||||
|     return std::make_pair(std::move(server_session), std::move(client_session)); | ||||
| ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread, | ||||
|                                             Memory::Memory& memory) { | ||||
|     Core::System::GetInstance().CoreTiming().ScheduleEvent(20000, request_event, {}); | ||||
|     return QueueSyncRequest(std::move(thread), memory); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| // Copyright 2014 Citra Emulator Project
 | ||||
| // Copyright 2019 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| @ -9,7 +9,7 @@ | ||||
| #include <utility> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "core/hle/kernel/object.h" | ||||
| #include "common/threadsafe_queue.h" | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| @ -17,13 +17,14 @@ namespace Memory { | ||||
| class Memory; | ||||
| } | ||||
| 
 | ||||
| namespace Core::Timing { | ||||
| struct EventType; | ||||
| } | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class ClientPort; | ||||
| class ClientSession; | ||||
| class HLERequestContext; | ||||
| class KernelCore; | ||||
| class ServerSession; | ||||
| class Session; | ||||
| class SessionRequestHandler; | ||||
| class Thread; | ||||
| @ -45,6 +46,12 @@ public: | ||||
|     explicit ServerSession(KernelCore& kernel); | ||||
|     ~ServerSession() override; | ||||
| 
 | ||||
|     friend class Session; | ||||
| 
 | ||||
|     static ResultVal<std::shared_ptr<ServerSession>> Create(KernelCore& kernel, | ||||
|                                                             std::shared_ptr<Session> parent, | ||||
|                                                             std::string name = "Unknown"); | ||||
| 
 | ||||
|     std::string GetTypeName() const override { | ||||
|         return "ServerSession"; | ||||
|     } | ||||
| @ -66,18 +73,6 @@ public: | ||||
|         return parent.get(); | ||||
|     } | ||||
| 
 | ||||
|     using SessionPair = std::pair<std::shared_ptr<ServerSession>, std::shared_ptr<ClientSession>>; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Creates a pair of ServerSession and an associated ClientSession. | ||||
|      * @param kernel      The kernal instance to create the session pair under. | ||||
|      * @param name        Optional name of the ports. | ||||
|      * @param client_port Optional The ClientPort that spawned this session. | ||||
|      * @return The created session tuple | ||||
|      */ | ||||
|     static SessionPair CreateSessionPair(KernelCore& kernel, const std::string& name = "Unknown", | ||||
|                                          std::shared_ptr<ClientPort> client_port = nullptr); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Sets the HLE handler for the session. This handler will be called to service IPC requests | ||||
|      * instead of the regular IPC machinery. (The regular IPC machinery is currently not | ||||
| @ -128,15 +123,11 @@ public: | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     /**
 | ||||
|      * Creates a server session. The server session can have an optional HLE handler, | ||||
|      * which will be invoked to handle the IPC requests that this session receives. | ||||
|      * @param kernel The kernel instance to create this server session under. | ||||
|      * @param name Optional name of the server session. | ||||
|      * @return The created server session | ||||
|      */ | ||||
|     static ResultVal<std::shared_ptr<ServerSession>> Create(KernelCore& kernel, | ||||
|                                                             std::string name = "Unknown"); | ||||
|     /// Queues a sync request from the emulated application.
 | ||||
|     ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Memory::Memory& memory); | ||||
| 
 | ||||
|     /// Completes a sync request from the emulated application.
 | ||||
|     ResultCode CompleteSyncRequest(); | ||||
| 
 | ||||
|     /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
 | ||||
|     /// object handle.
 | ||||
| @ -166,6 +157,12 @@ private: | ||||
| 
 | ||||
|     /// The name of this session (optional)
 | ||||
|     std::string name; | ||||
| 
 | ||||
|     /// Core timing event used to schedule the service request at some point in the future
 | ||||
|     std::shared_ptr<Core::Timing::EventType> request_event; | ||||
| 
 | ||||
|     /// Queue of scheduled service requests
 | ||||
|     Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -1,12 +1,36 @@ | ||||
| // Copyright 2015 Citra Emulator Project
 | ||||
| // Copyright 2019 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include "common/assert.h" | ||||
| #include "core/hle/kernel/client_session.h" | ||||
| #include "core/hle/kernel/server_session.h" | ||||
| #include "core/hle/kernel/session.h" | ||||
| #include "core/hle/kernel/thread.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| Session::Session() {} | ||||
| Session::~Session() {} | ||||
| Session::Session(KernelCore& kernel) : WaitObject{kernel} {} | ||||
| Session::~Session() = default; | ||||
| 
 | ||||
| Session::SessionPair Session::Create(KernelCore& kernel, std::string name) { | ||||
|     auto session{std::make_shared<Session>(kernel)}; | ||||
|     auto client_session{Kernel::ClientSession::Create(kernel, session, name + "_Client").Unwrap()}; | ||||
|     auto server_session{Kernel::ServerSession::Create(kernel, session, name + "_Server").Unwrap()}; | ||||
| 
 | ||||
|     session->name = std::move(name); | ||||
|     session->client = client_session; | ||||
|     session->server = server_session; | ||||
| 
 | ||||
|     return std::make_pair(std::move(client_session), std::move(server_session)); | ||||
| } | ||||
| 
 | ||||
| bool Session::ShouldWait(const Thread* thread) const { | ||||
|     UNIMPLEMENTED(); | ||||
|     return {}; | ||||
| } | ||||
| 
 | ||||
| void Session::Acquire(Thread* thread) { | ||||
|     UNIMPLEMENTED(); | ||||
| } | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -1,27 +1,64 @@ | ||||
| // Copyright 2018 yuzu emulator team
 | ||||
| // Copyright 2019 yuzu emulator team
 | ||||
| // Licensed under GPLv2 or any later version
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "core/hle/kernel/object.h" | ||||
| #include <memory> | ||||
| #include <string> | ||||
| 
 | ||||
| #include "core/hle/kernel/wait_object.h" | ||||
| #include "core/hle/result.h" | ||||
| 
 | ||||
| namespace Kernel { | ||||
| 
 | ||||
| class ClientSession; | ||||
| class ClientPort; | ||||
| class ServerSession; | ||||
| 
 | ||||
| /**
 | ||||
|  * Parent structure to link the client and server endpoints of a session with their associated | ||||
|  * client port. The client port need not exist, as is the case for portless sessions like the | ||||
|  * FS File and Directory sessions. When one of the endpoints of a session is destroyed, its | ||||
|  * corresponding field in this structure will be set to nullptr. | ||||
|  * client port. | ||||
|  */ | ||||
| class Session final { | ||||
| class Session final : public WaitObject { | ||||
| public: | ||||
|     std::weak_ptr<ClientSession> client; ///< The client endpoint of the session.
 | ||||
|     std::weak_ptr<ServerSession> server; ///< The server endpoint of the session.
 | ||||
|     std::shared_ptr<ClientPort> port; ///< The port that this session is associated with (optional).
 | ||||
|     explicit Session(KernelCore& kernel); | ||||
|     ~Session() override; | ||||
| 
 | ||||
|     using SessionPair = std::pair<std::shared_ptr<ClientSession>, std::shared_ptr<ServerSession>>; | ||||
| 
 | ||||
|     static SessionPair Create(KernelCore& kernel, std::string name = "Unknown"); | ||||
| 
 | ||||
|     std::string GetName() const override { | ||||
|         return name; | ||||
|     } | ||||
| 
 | ||||
|     static constexpr HandleType HANDLE_TYPE = HandleType::Session; | ||||
|     HandleType GetHandleType() const override { | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
| 
 | ||||
|     bool ShouldWait(const Thread* thread) const override; | ||||
| 
 | ||||
|     void Acquire(Thread* thread) override; | ||||
| 
 | ||||
|     std::shared_ptr<ClientSession> Client() { | ||||
|         if (auto result{client.lock()}) { | ||||
|             return result; | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
|     std::shared_ptr<ServerSession> Server() { | ||||
|         if (auto result{server.lock()}) { | ||||
|             return result; | ||||
|         } | ||||
|         return {}; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     std::string name; | ||||
|     std::weak_ptr<ClientSession> client; | ||||
|     std::weak_ptr<ServerSession> server; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Kernel
 | ||||
|  | ||||
| @ -381,11 +381,12 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) { | ||||
| 
 | ||||
|     LOG_TRACE(Kernel_SVC, "called handle=0x{:08X}({})", handle, session->GetName()); | ||||
| 
 | ||||
|     system.PrepareReschedule(); | ||||
|     auto thread = system.CurrentScheduler().GetCurrentThread(); | ||||
|     thread->InvalidateWakeupCallback(); | ||||
|     thread->SetStatus(ThreadStatus::WaitIPC); | ||||
|     system.PrepareReschedule(thread->GetProcessorID()); | ||||
| 
 | ||||
|     // TODO(Subv): svcSendSyncRequest should put the caller thread to sleep while the server
 | ||||
|     // responds and cause a reschedule.
 | ||||
|     return session->SendSyncRequest(system.CurrentScheduler().GetCurrentThread(), system.Memory()); | ||||
|     return session->SendSyncRequest(SharedFrom(thread), system.Memory()); | ||||
| } | ||||
| 
 | ||||
| /// Get the ID for the specified thread.
 | ||||
|  | ||||
| @ -189,7 +189,7 @@ private: | ||||
|         LOG_DEBUG(Service_NFP, "called"); | ||||
| 
 | ||||
|         auto nfc_event = nfp_interface.GetNFCEvent(); | ||||
|         if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) { | ||||
|         if (!nfc_event->ShouldWait(&ctx.GetThread()) && !has_attached_handle) { | ||||
|             device_state = DeviceState::TagFound; | ||||
|             nfc_event->Clear(); | ||||
|         } | ||||
|  | ||||
| @ -186,7 +186,7 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co | ||||
|         UNIMPLEMENTED_MSG("command_type={}", static_cast<int>(context.GetCommandType())); | ||||
|     } | ||||
| 
 | ||||
|     context.WriteToOutgoingCommandBuffer(*Kernel::GetCurrentThread()); | ||||
|     context.WriteToOutgoingCommandBuffer(context.GetThread()); | ||||
| 
 | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| @ -201,7 +201,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) { | ||||
|     auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system); | ||||
|     system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false); | ||||
| 
 | ||||
|     SM::ServiceManager::InstallInterfaces(sm); | ||||
|     SM::ServiceManager::InstallInterfaces(sm, system.Kernel()); | ||||
| 
 | ||||
|     Account::InstallInterfaces(system); | ||||
|     AM::InstallInterfaces(*sm, nv_flinger, system); | ||||
|  | ||||
| @ -30,10 +30,7 @@ void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) { | ||||
| 
 | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     std::shared_ptr<Kernel::ClientSession> session{ctx.Session()->GetParent()->client}; | ||||
|     rb.PushMoveObjects(session); | ||||
| 
 | ||||
|     LOG_DEBUG(Service, "session={}", session->GetObjectId()); | ||||
|     rb.PushMoveObjects(ctx.Session()->GetParent()->Client()); | ||||
| } | ||||
| 
 | ||||
| void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) { | ||||
|  | ||||
| @ -36,10 +36,11 @@ static ResultCode ValidateServiceName(const std::string& name) { | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
| 
 | ||||
| void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self) { | ||||
| void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self, | ||||
|                                        Kernel::KernelCore& kernel) { | ||||
|     ASSERT(self->sm_interface.expired()); | ||||
| 
 | ||||
|     auto sm = std::make_shared<SM>(self); | ||||
|     auto sm = std::make_shared<SM>(self, kernel); | ||||
|     sm->InstallAsNamedPort(); | ||||
|     self->sm_interface = sm; | ||||
|     self->controller_interface = std::make_unique<Controller>(); | ||||
| @ -114,8 +115,6 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | ||||
| 
 | ||||
|     std::string name(name_buf.begin(), end); | ||||
| 
 | ||||
|     // TODO(yuriks): Permission checks go here
 | ||||
| 
 | ||||
|     auto client_port = service_manager->GetServicePort(name); | ||||
|     if (client_port.Failed()) { | ||||
|         IPC::ResponseBuilder rb{ctx, 2}; | ||||
| @ -127,14 +126,22 @@ void SM::GetService(Kernel::HLERequestContext& ctx) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     auto session = client_port.Unwrap()->Connect(); | ||||
|     ASSERT(session.Succeeded()); | ||||
|     if (session.Succeeded()) { | ||||
|         LOG_DEBUG(Service_SM, "called service={} -> session={}", name, (*session)->GetObjectId()); | ||||
|         IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; | ||||
|         rb.Push(session.Code()); | ||||
|         rb.PushMoveObjects(std::move(session).Unwrap()); | ||||
|     auto [client, server] = Kernel::Session::Create(kernel, name); | ||||
| 
 | ||||
|     const auto& server_port = client_port.Unwrap()->GetServerPort(); | ||||
|     if (server_port->GetHLEHandler()) { | ||||
|         server_port->GetHLEHandler()->ClientConnected(server); | ||||
|     } else { | ||||
|         server_port->AppendPendingSession(server); | ||||
|     } | ||||
| 
 | ||||
|     // Wake the threads waiting on the ServerPort
 | ||||
|     server_port->WakeupAllWaitingThreads(); | ||||
| 
 | ||||
|     LOG_DEBUG(Service_SM, "called service={} -> session={}", name, client->GetObjectId()); | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushMoveObjects(std::move(client)); | ||||
| } | ||||
| 
 | ||||
| void SM::RegisterService(Kernel::HLERequestContext& ctx) { | ||||
| @ -178,8 +185,8 @@ void SM::UnregisterService(Kernel::HLERequestContext& ctx) { | ||||
|     rb.Push(service_manager->UnregisterService(name)); | ||||
| } | ||||
| 
 | ||||
| SM::SM(std::shared_ptr<ServiceManager> service_manager) | ||||
|     : ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) { | ||||
| SM::SM(std::shared_ptr<ServiceManager> service_manager, Kernel::KernelCore& kernel) | ||||
|     : ServiceFramework{"sm:", 4}, service_manager{std::move(service_manager)}, kernel{kernel} { | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0x00000000, &SM::Initialize, "Initialize"}, | ||||
|         {0x00000001, &SM::GetService, "GetService"}, | ||||
|  | ||||
| @ -18,6 +18,7 @@ | ||||
| namespace Kernel { | ||||
| class ClientPort; | ||||
| class ClientSession; | ||||
| class KernelCore; | ||||
| class ServerPort; | ||||
| class SessionRequestHandler; | ||||
| } // namespace Kernel
 | ||||
| @ -29,7 +30,7 @@ class Controller; | ||||
| /// Interface to "sm:" service
 | ||||
| class SM final : public ServiceFramework<SM> { | ||||
| public: | ||||
|     explicit SM(std::shared_ptr<ServiceManager> service_manager); | ||||
|     explicit SM(std::shared_ptr<ServiceManager> service_manager, Kernel::KernelCore& kernel); | ||||
|     ~SM() override; | ||||
| 
 | ||||
| private: | ||||
| @ -39,11 +40,12 @@ private: | ||||
|     void UnregisterService(Kernel::HLERequestContext& ctx); | ||||
| 
 | ||||
|     std::shared_ptr<ServiceManager> service_manager; | ||||
|     Kernel::KernelCore& kernel; | ||||
| }; | ||||
| 
 | ||||
| class ServiceManager { | ||||
| public: | ||||
|     static void InstallInterfaces(std::shared_ptr<ServiceManager> self); | ||||
|     static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel); | ||||
| 
 | ||||
|     ServiceManager(); | ||||
|     ~ServiceManager(); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 bunnei
						bunnei