2016-06-14 23:03:30 +00:00
|
|
|
// Copyright 2016 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#include <tuple>
|
|
|
|
|
2017-06-05 04:52:19 +00:00
|
|
|
#include "core/hle/kernel/client_port.h"
|
2016-06-14 23:03:30 +00:00
|
|
|
#include "core/hle/kernel/client_session.h"
|
2017-06-05 04:52:19 +00:00
|
|
|
#include "core/hle/kernel/hle_ipc.h"
|
2016-06-14 23:03:30 +00:00
|
|
|
#include "core/hle/kernel/server_session.h"
|
2017-06-05 04:52:19 +00:00
|
|
|
#include "core/hle/kernel/session.h"
|
2016-06-14 23:03:30 +00:00
|
|
|
#include "core/hle/kernel/thread.h"
|
|
|
|
|
|
|
|
namespace Kernel {
|
|
|
|
|
2019-02-02 20:18:01 +00:00
|
|
|
ServerSession::ServerSession(KernelSystem& kernel) : WaitObject(kernel), kernel(kernel) {}
|
2016-12-08 20:01:10 +00:00
|
|
|
ServerSession::~ServerSession() {
|
2016-12-14 17:33:49 +00:00
|
|
|
// This destructor will be called automatically when the last ServerSession handle is closed by
|
|
|
|
// the emulated application.
|
2017-01-05 04:23:17 +00:00
|
|
|
|
|
|
|
// Decrease the port's connection count.
|
|
|
|
if (parent->port)
|
2018-08-24 17:31:20 +00:00
|
|
|
parent->port->ConnectionClosed();
|
2017-01-05 04:23:17 +00:00
|
|
|
|
|
|
|
// TODO(Subv): Wake up all the ClientSession's waiting threads and set
|
|
|
|
// the SendSyncRequest result to 0xC920181A.
|
|
|
|
|
|
|
|
parent->server = nullptr;
|
2016-12-08 20:01:10 +00:00
|
|
|
}
|
2016-06-14 23:03:30 +00:00
|
|
|
|
2019-03-23 20:04:19 +00:00
|
|
|
ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelSystem& kernel,
|
|
|
|
std::string name) {
|
|
|
|
auto server_session{std::make_shared<ServerSession>(kernel)};
|
2016-06-14 23:03:30 +00:00
|
|
|
|
|
|
|
server_session->name = std::move(name);
|
2017-01-05 04:23:17 +00:00
|
|
|
server_session->parent = nullptr;
|
2016-06-14 23:03:30 +00:00
|
|
|
|
2017-06-06 05:39:26 +00:00
|
|
|
return MakeResult(std::move(server_session));
|
2016-06-14 23:03:30 +00:00
|
|
|
}
|
|
|
|
|
Port various minor changes from yuzu PRs (#4725)
* common/thread: Remove unused functions
Many of these functions are carried over from Dolphin (where they aren't
used anymore). Given these have no use (and we really shouldn't be
screwing around with OS-specific thread scheduler handling from the
emulator, these can be removed.
The function for setting the thread name is left, however, since it can
have debugging utility usages.
* input_common/sdl: Use a type alias to shorten declaration of GetPollers
Just makes the definitions a little bit more tidy.
* input_common/sdl: Correct return values within implementations of GetPollers()
In both cases, we weren't actually returning anything, which is
undefined behavior.
* yuzu/debugger/graphics_surface: Fill in missing surface format listings
Fills in the missing surface types that were marked as unknown. The
order corresponds with the TextureFormat enum within
video_core/texture.h.
We also don't need to all of these strings as translatable (only the
first string, as it's an English word).
* yuzu/debugger/graphics_surface: Clean up connection overload deduction
We can utilize qOverload with the signal connections to make the
function deducing a little less ugly.
* yuzu/debugger/graphics_surface: Tidy up SaveSurface
- Use QStringLiteral where applicable.
- Use const where applicable
- Remove unnecessary precondition check (we already assert the pixbuf
being non null)
* yuzu/debugger/graphics_surface: Display error messages for file I/O errors
* core: Add missing override specifiers where applicable
Applies the override specifier where applicable. In the case of
destructors that are defaulted in their definition, they can
simply be removed.
This also removes the unnecessary inclusions being done in audin_u and
audrec_u, given their close proximity.
* kernel/thread: Make parameter of GetWaitObjectIndex() const qualified
The pointed to member is never actually modified, so it can be made
const.
* kernel/thread: Avoid sign conversion within GetCommandBufferAddress()
Previously this was performing a u64 + int sign conversion. When dealing
with addresses, we should generally be keeping the arithmetic in the
same signedness type.
This also gets rid of the static lifetime of the constant, as there's no
need to make a trivial type like this potentially live for the entire
duration of the program.
* kernel/codeset: Make CodeSet's memory data member a regular std::vector
The use of a shared_ptr is an implementation detail of the VMManager
itself when mapping memory. Because of that, we shouldn't require all
users of the CodeSet to have to allocate the shared_ptr ahead of time.
It's intended that CodeSet simply pass in the required direct data, and
that the memory manager takes care of it from that point on.
This means we just do the shared pointer allocation in a single place,
when loading modules, as opposed to in each loader.
* kernel/wait_object: Make ShouldWait() take thread members by pointer-to-const
Given this is intended as a querying function, it doesn't make sense to
allow the implementer to modify the state of the given thread.
2019-05-01 12:28:49 +00:00
|
|
|
bool ServerSession::ShouldWait(const Thread* thread) const {
|
2017-06-20 22:33:28 +00:00
|
|
|
// Closed sessions should never wait, an error will be returned from svcReplyAndReceive.
|
|
|
|
if (parent->client == nullptr)
|
|
|
|
return false;
|
|
|
|
// Wait if we have no pending requests, or if we're currently handling a request.
|
|
|
|
return pending_requesting_threads.empty() || currently_handling != nullptr;
|
2016-06-14 23:03:30 +00:00
|
|
|
}
|
|
|
|
|
2017-01-01 21:53:22 +00:00
|
|
|
void ServerSession::Acquire(Thread* thread) {
|
|
|
|
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
|
2017-11-07 04:07:08 +00:00
|
|
|
|
|
|
|
// If the client endpoint was closed, don't do anything. This ServerSession is now useless and
|
|
|
|
// will linger until its last handle is closed by the running application.
|
|
|
|
if (parent->client == nullptr)
|
|
|
|
return;
|
|
|
|
|
2017-06-20 22:33:28 +00:00
|
|
|
// We are now handling a request, pop it from the stack.
|
|
|
|
ASSERT(!pending_requesting_threads.empty());
|
|
|
|
currently_handling = pending_requesting_threads.back();
|
|
|
|
pending_requesting_threads.pop_back();
|
2016-06-14 23:03:30 +00:00
|
|
|
}
|
|
|
|
|
2019-03-23 20:04:19 +00:00
|
|
|
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread) {
|
2016-06-14 23:03:30 +00:00
|
|
|
// The ServerSession received a sync request, this means that there's new data available
|
2016-12-14 17:33:49 +00:00
|
|
|
// from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
|
|
|
|
// similar.
|
2016-12-01 03:50:13 +00:00
|
|
|
|
|
|
|
// If this ServerSession has an associated HLE handler, forward the request to it.
|
2016-12-09 17:52:12 +00:00
|
|
|
if (hle_handler != nullptr) {
|
2019-02-02 20:39:54 +00:00
|
|
|
std::array<u32_le, IPC::COMMAND_BUFFER_LENGTH + 2 * IPC::MAX_STATIC_BUFFERS> cmd_buf;
|
2019-02-02 20:18:01 +00:00
|
|
|
Kernel::Process* current_process = thread->owner_process;
|
2019-02-02 20:39:54 +00:00
|
|
|
kernel.memory.ReadBlock(*current_process, thread->GetCommandBufferAddress(), cmd_buf.data(),
|
|
|
|
cmd_buf.size() * sizeof(u32));
|
2019-02-02 20:18:01 +00:00
|
|
|
|
2019-04-02 16:30:03 +00:00
|
|
|
Kernel::HLERequestContext context(kernel, SharedFrom(this), thread.get());
|
2019-02-02 20:39:54 +00:00
|
|
|
context.PopulateFromIncomingCommandBuffer(cmd_buf.data(), *current_process);
|
2019-02-02 20:18:01 +00:00
|
|
|
|
|
|
|
hle_handler->HandleSyncRequest(context);
|
|
|
|
|
|
|
|
ASSERT(thread->status == Kernel::ThreadStatus::Running ||
|
|
|
|
thread->status == Kernel::ThreadStatus::WaitHleEvent);
|
|
|
|
// Only write the response immediately if the thread is still running. If the HLE handler
|
|
|
|
// put the thread to sleep then the writing of the command buffer will be deferred to the
|
|
|
|
// wakeup callback.
|
|
|
|
if (thread->status == Kernel::ThreadStatus::Running) {
|
2019-02-02 20:39:54 +00:00
|
|
|
context.WriteToOutgoingCommandBuffer(cmd_buf.data(), *current_process);
|
|
|
|
kernel.memory.WriteBlock(*current_process, thread->GetCommandBufferAddress(),
|
|
|
|
cmd_buf.data(), cmd_buf.size() * sizeof(u32));
|
2019-02-02 20:18:01 +00:00
|
|
|
}
|
2017-11-08 00:58:29 +00:00
|
|
|
}
|
|
|
|
|
2018-07-20 01:39:05 +00:00
|
|
|
if (thread->status == ThreadStatus::Running) {
|
2017-10-02 03:09:00 +00:00
|
|
|
// Put the thread to sleep until the server replies, it will be awoken in
|
2017-11-08 00:58:29 +00:00
|
|
|
// svcReplyAndReceive for LLE servers.
|
2018-07-20 01:39:05 +00:00
|
|
|
thread->status = ThreadStatus::WaitIPC;
|
2017-11-08 00:58:29 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
|
2017-11-09 04:14:40 +00:00
|
|
|
// 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;
|
2017-11-08 00:58:29 +00:00
|
|
|
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));
|
|
|
|
}
|
2016-12-09 17:52:12 +00:00
|
|
|
}
|
2016-12-01 03:50:13 +00:00
|
|
|
|
2016-12-14 17:33:49 +00:00
|
|
|
// If this ServerSession does not have an HLE implementation, just wake up the threads waiting
|
|
|
|
// on it.
|
2016-06-14 23:03:30 +00:00
|
|
|
WakeupAllWaitingThreads();
|
|
|
|
return RESULT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
2019-04-06 05:41:43 +00:00
|
|
|
KernelSystem::SessionPair KernelSystem::CreateSessionPair(const std::string& name,
|
|
|
|
std::shared_ptr<ClientPort> port) {
|
2018-10-13 20:11:20 +00:00
|
|
|
auto server_session = ServerSession::Create(*this, name + "_Server").Unwrap();
|
2019-03-23 20:04:19 +00:00
|
|
|
auto client_session{std::make_shared<ClientSession>(*this)};
|
2017-05-21 23:52:42 +00:00
|
|
|
client_session->name = name + "_Client";
|
2017-01-05 04:23:17 +00:00
|
|
|
|
|
|
|
std::shared_ptr<Session> parent(new Session);
|
|
|
|
parent->client = client_session.get();
|
|
|
|
parent->server = server_session.get();
|
|
|
|
parent->port = port;
|
|
|
|
|
|
|
|
client_session->parent = parent;
|
|
|
|
server_session->parent = parent;
|
2016-06-14 23:03:30 +00:00
|
|
|
|
2019-04-06 05:41:43 +00:00
|
|
|
return std::make_pair(std::move(server_session), std::move(client_session));
|
2016-06-14 23:03:30 +00:00
|
|
|
}
|
|
|
|
|
2017-06-20 22:33:28 +00:00
|
|
|
} // namespace Kernel
|