server_session.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. // Copyright 2016 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <tuple>
  5. #include <utility>
  6. #include "common/assert.h"
  7. #include "common/common_types.h"
  8. #include "common/logging/log.h"
  9. #include "core/core.h"
  10. #include "core/hle/ipc_helpers.h"
  11. #include "core/hle/kernel/client_port.h"
  12. #include "core/hle/kernel/client_session.h"
  13. #include "core/hle/kernel/handle_table.h"
  14. #include "core/hle/kernel/hle_ipc.h"
  15. #include "core/hle/kernel/kernel.h"
  16. #include "core/hle/kernel/process.h"
  17. #include "core/hle/kernel/server_session.h"
  18. #include "core/hle/kernel/session.h"
  19. #include "core/hle/kernel/thread.h"
  20. namespace Kernel {
  21. ServerSession::ServerSession(KernelCore& kernel) : WaitObject{kernel} {}
  22. ServerSession::~ServerSession() {
  23. // This destructor will be called automatically when the last ServerSession handle is closed by
  24. // the emulated application.
  25. // Decrease the port's connection count.
  26. if (parent->port) {
  27. parent->port->ConnectionClosed();
  28. }
  29. parent->server = nullptr;
  30. }
  31. ResultVal<SharedPtr<ServerSession>> ServerSession::Create(KernelCore& kernel, std::string name) {
  32. SharedPtr<ServerSession> server_session(new ServerSession(kernel));
  33. server_session->name = std::move(name);
  34. server_session->parent = nullptr;
  35. return MakeResult(std::move(server_session));
  36. }
  37. bool ServerSession::ShouldWait(const Thread* thread) const {
  38. // Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
  39. if (parent->client == nullptr)
  40. return false;
  41. // Wait if we have no pending requests, or if we're currently handling a request.
  42. return pending_requesting_threads.empty() || currently_handling != nullptr;
  43. }
  44. void ServerSession::Acquire(Thread* thread) {
  45. ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
  46. // We are now handling a request, pop it from the stack.
  47. // TODO(Subv): What happens if the client endpoint is closed before any requests are made?
  48. ASSERT(!pending_requesting_threads.empty());
  49. currently_handling = pending_requesting_threads.back();
  50. pending_requesting_threads.pop_back();
  51. }
  52. void ServerSession::ClientDisconnected() {
  53. // We keep a shared pointer to the hle handler to keep it alive throughout
  54. // the call to ClientDisconnected, as ClientDisconnected invalidates the
  55. // hle_handler member itself during the course of the function executing.
  56. std::shared_ptr<SessionRequestHandler> handler = hle_handler;
  57. if (handler) {
  58. // Note that after this returns, this server session's hle_handler is
  59. // invalidated (set to null).
  60. handler->ClientDisconnected(this);
  61. }
  62. // Clean up the list of client threads with pending requests, they are unneeded now that the
  63. // client endpoint is closed.
  64. pending_requesting_threads.clear();
  65. currently_handling = nullptr;
  66. }
  67. void ServerSession::AppendDomainRequestHandler(std::shared_ptr<SessionRequestHandler> handler) {
  68. domain_request_handlers.push_back(std::move(handler));
  69. }
  70. std::size_t ServerSession::NumDomainRequestHandlers() const {
  71. return domain_request_handlers.size();
  72. }
  73. ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
  74. if (!context.HasDomainMessageHeader()) {
  75. return RESULT_SUCCESS;
  76. }
  77. // Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
  78. context.SetDomainRequestHandlers(domain_request_handlers);
  79. // If there is a DomainMessageHeader, then this is CommandType "Request"
  80. const auto& domain_message_header = context.GetDomainMessageHeader();
  81. const u32 object_id{domain_message_header.object_id};
  82. switch (domain_message_header.command) {
  83. case IPC::DomainMessageHeader::CommandType::SendMessage:
  84. if (object_id > domain_request_handlers.size()) {
  85. LOG_CRITICAL(IPC,
  86. "object_id {} is too big! This probably means a recent service call "
  87. "to {} needed to return a new interface!",
  88. object_id, name);
  89. UNREACHABLE();
  90. return RESULT_SUCCESS; // Ignore error if asserts are off
  91. }
  92. return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
  93. case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
  94. LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x{:08X}", object_id);
  95. domain_request_handlers[object_id - 1] = nullptr;
  96. IPC::ResponseBuilder rb{context, 2};
  97. rb.Push(RESULT_SUCCESS);
  98. return RESULT_SUCCESS;
  99. }
  100. }
  101. LOG_CRITICAL(IPC, "Unknown domain command={}",
  102. static_cast<int>(domain_message_header.command.Value()));
  103. ASSERT(false);
  104. return RESULT_SUCCESS;
  105. }
  106. ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
  107. // The ServerSession received a sync request, this means that there's new data available
  108. // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
  109. // similar.
  110. Kernel::HLERequestContext context(this, thread);
  111. u32* cmd_buf = (u32*)Memory::GetPointer(thread->GetTLSAddress());
  112. context.PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
  113. ResultCode result = RESULT_SUCCESS;
  114. // If the session has been converted to a domain, handle the domain request
  115. if (IsDomain() && context.HasDomainMessageHeader()) {
  116. result = HandleDomainSyncRequest(context);
  117. // If there is no domain header, the regular session handler is used
  118. } else if (hle_handler != nullptr) {
  119. // If this ServerSession has an associated HLE handler, forward the request to it.
  120. result = hle_handler->HandleSyncRequest(context);
  121. }
  122. if (thread->GetStatus() == ThreadStatus::Running) {
  123. // Put the thread to sleep until the server replies, it will be awoken in
  124. // svcReplyAndReceive for LLE servers.
  125. thread->SetStatus(ThreadStatus::WaitIPC);
  126. if (hle_handler != nullptr) {
  127. // For HLE services, we put the request threads to sleep for a short duration to
  128. // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
  129. // other reasons like an async callback. The IPC overhead is needed to prevent
  130. // starvation when a thread only does sync requests to HLE services while a
  131. // lower-priority thread is waiting to run.
  132. // This delay was approximated in a homebrew application by measuring the average time
  133. // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
  134. // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
  135. // a high variance and vary between models.
  136. static constexpr u64 IPCDelayNanoseconds = 39000;
  137. thread->WakeAfterDelay(IPCDelayNanoseconds);
  138. } else {
  139. // Add the thread to the list of threads that have issued a sync request with this
  140. // server.
  141. pending_requesting_threads.push_back(std::move(thread));
  142. }
  143. }
  144. // If this ServerSession does not have an HLE implementation, just wake up the threads waiting
  145. // on it.
  146. WakeupAllWaitingThreads();
  147. // Handle scenario when ConvertToDomain command was issued, as we must do the conversion at the
  148. // end of the command such that only commands following this one are handled as domains
  149. if (convert_to_domain) {
  150. ASSERT_MSG(IsSession(), "ServerSession is already a domain instance.");
  151. domain_request_handlers = {hle_handler};
  152. convert_to_domain = false;
  153. }
  154. return result;
  155. }
  156. ServerSession::SessionPair ServerSession::CreateSessionPair(KernelCore& kernel,
  157. const std::string& name,
  158. SharedPtr<ClientPort> port) {
  159. auto server_session = ServerSession::Create(kernel, name + "_Server").Unwrap();
  160. SharedPtr<ClientSession> client_session(new ClientSession(kernel));
  161. client_session->name = name + "_Client";
  162. std::shared_ptr<Session> parent(new Session);
  163. parent->client = client_session.get();
  164. parent->server = server_session.get();
  165. parent->port = std::move(port);
  166. client_session->parent = parent;
  167. server_session->parent = parent;
  168. return std::make_pair(std::move(server_session), std::move(client_session));
  169. }
  170. } // namespace Kernel