server_session.cpp 8.5 KB

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