hle: service: bsd: Update to work with service threads, removing SleepClientThread.
This commit is contained in:
parent
0c81b83ca9
commit
87d6588cb5
4 changed files with 45 additions and 250 deletions
|
@ -502,7 +502,6 @@ add_library(core STATIC
|
||||||
hle/service/sm/controller.h
|
hle/service/sm/controller.h
|
||||||
hle/service/sm/sm.cpp
|
hle/service/sm/sm.cpp
|
||||||
hle/service/sm/sm.h
|
hle/service/sm/sm.h
|
||||||
hle/service/sockets/blocking_worker.h
|
|
||||||
hle/service/sockets/bsd.cpp
|
hle/service/sockets/bsd.cpp
|
||||||
hle/service/sockets/bsd.h
|
hle/service/sockets/bsd.h
|
||||||
hle/service/sockets/ethc.cpp
|
hle/service/sockets/ethc.cpp
|
||||||
|
|
|
@ -1,161 +0,0 @@
|
||||||
// Copyright 2020 yuzu emulator team
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <string_view>
|
|
||||||
#include <thread>
|
|
||||||
#include <variant>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <fmt/format.h>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/microprofile.h"
|
|
||||||
#include "common/thread.h"
|
|
||||||
#include "core/core.h"
|
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
|
||||||
#include "core/hle/kernel/thread.h"
|
|
||||||
#include "core/hle/kernel/writable_event.h"
|
|
||||||
|
|
||||||
namespace Service::Sockets {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Worker abstraction to execute blocking calls on host without blocking the guest thread
|
|
||||||
*
|
|
||||||
* @tparam Service Service where the work is executed
|
|
||||||
* @tparam Types Types of work to execute
|
|
||||||
*/
|
|
||||||
template <class Service, class... Types>
|
|
||||||
class BlockingWorker {
|
|
||||||
using This = BlockingWorker<Service, Types...>;
|
|
||||||
using WorkVariant = std::variant<std::monostate, Types...>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/// Create a new worker
|
|
||||||
static std::unique_ptr<This> Create(Core::System& system, Service* service,
|
|
||||||
std::string_view name) {
|
|
||||||
return std::unique_ptr<This>(new This(system, service, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
~BlockingWorker() {
|
|
||||||
while (!is_available.load(std::memory_order_relaxed)) {
|
|
||||||
// Busy wait until work is finished
|
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
// Monostate means to exit the thread
|
|
||||||
work = std::monostate{};
|
|
||||||
work_event.Set();
|
|
||||||
thread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to capture the worker to send work after a success
|
|
||||||
* @returns True when the worker has been successfully captured
|
|
||||||
*/
|
|
||||||
bool TryCapture() {
|
|
||||||
bool expected = true;
|
|
||||||
return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed,
|
|
||||||
std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send work to this worker abstraction
|
|
||||||
* @see TryCapture must be called before attempting to call this function
|
|
||||||
*/
|
|
||||||
template <class Work>
|
|
||||||
void SendWork(Work new_work) {
|
|
||||||
ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured");
|
|
||||||
work = std::move(new_work);
|
|
||||||
work_event.Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate a callback for @see SleepClientThread
|
|
||||||
template <class Work>
|
|
||||||
auto Callback() {
|
|
||||||
return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx,
|
|
||||||
Kernel::ThreadWakeupReason reason) {
|
|
||||||
ASSERT(reason == Kernel::ThreadWakeupReason::Signal);
|
|
||||||
std::get<Work>(work).Response(ctx);
|
|
||||||
is_available.store(true);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get kernel event that will be signalled by the worker when the host operation finishes
|
|
||||||
std::shared_ptr<Kernel::WritableEvent> KernelEvent() const {
|
|
||||||
return kernel_event;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) {
|
|
||||||
auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name));
|
|
||||||
kernel_event = std::move(pair.writable);
|
|
||||||
thread = std::thread([this, &system, service, name] { Run(system, service, name); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void Run(Core::System& system, Service* service, std::string_view name) {
|
|
||||||
system.RegisterHostThread();
|
|
||||||
|
|
||||||
const std::string thread_name = fmt::format("yuzu:{}", name);
|
|
||||||
MicroProfileOnThreadCreate(thread_name.c_str());
|
|
||||||
Common::SetCurrentThreadName(thread_name.c_str());
|
|
||||||
|
|
||||||
bool keep_running = true;
|
|
||||||
while (keep_running) {
|
|
||||||
work_event.Wait();
|
|
||||||
|
|
||||||
const auto visit_fn = [service, &keep_running]<typename T>(T&& w) {
|
|
||||||
if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) {
|
|
||||||
keep_running = false;
|
|
||||||
} else {
|
|
||||||
w.Execute(service);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
std::visit(visit_fn, work);
|
|
||||||
|
|
||||||
kernel_event->Signal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread thread;
|
|
||||||
WorkVariant work;
|
|
||||||
Common::Event work_event;
|
|
||||||
std::shared_ptr<Kernel::WritableEvent> kernel_event;
|
|
||||||
std::atomic_bool is_available{true};
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class Service, class... Types>
|
|
||||||
class BlockingWorkerPool {
|
|
||||||
using Worker = BlockingWorker<Service, Types...>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit BlockingWorkerPool(Core::System& system_, Service* service_)
|
|
||||||
: system{system_}, service{service_} {}
|
|
||||||
|
|
||||||
/// Returns a captured worker thread, creating new ones if necessary
|
|
||||||
Worker* CaptureWorker() {
|
|
||||||
for (auto& worker : workers) {
|
|
||||||
if (worker->TryCapture()) {
|
|
||||||
return worker.get();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size()));
|
|
||||||
[[maybe_unused]] const bool success = new_worker->TryCapture();
|
|
||||||
ASSERT(success);
|
|
||||||
|
|
||||||
return workers.emplace_back(std::move(new_worker)).get();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Core::System& system;
|
|
||||||
Service* const service;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Worker>> workers;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace Service::Sockets
|
|
|
@ -178,13 +178,12 @@ void BSD::Poll(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
|
LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Poll", timeout != 0,
|
ExecuteWork(ctx, PollWork{
|
||||||
PollWork{
|
.nfds = nfds,
|
||||||
.nfds = nfds,
|
.timeout = timeout,
|
||||||
.timeout = timeout,
|
.read_buffer = ctx.ReadBuffer(),
|
||||||
.read_buffer = ctx.ReadBuffer(),
|
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
|
||||||
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Accept(Kernel::HLERequestContext& ctx) {
|
void BSD::Accept(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -193,11 +192,10 @@ void BSD::Accept(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={}", fd);
|
LOG_DEBUG(Service, "called. fd={}", fd);
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd),
|
ExecuteWork(ctx, AcceptWork{
|
||||||
AcceptWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
|
||||||
.write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Bind(Kernel::HLERequestContext& ctx) {
|
void BSD::Bind(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -215,11 +213,10 @@ void BSD::Connect(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
|
LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd),
|
ExecuteWork(ctx, ConnectWork{
|
||||||
ConnectWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.addr = ctx.ReadBuffer(),
|
||||||
.addr = ctx.ReadBuffer(),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
|
void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -327,12 +324,11 @@ void BSD::Recv(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
|
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd),
|
ExecuteWork(ctx, RecvWork{
|
||||||
RecvWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = flags,
|
||||||
.flags = flags,
|
.message = std::vector<u8>(ctx.GetWriteBufferSize()),
|
||||||
.message = std::vector<u8>(ctx.GetWriteBufferSize()),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
|
void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -344,13 +340,12 @@ void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
|
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
|
||||||
ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
|
ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd),
|
ExecuteWork(ctx, RecvFromWork{
|
||||||
RecvFromWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = flags,
|
||||||
.flags = flags,
|
.message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
|
||||||
.message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
|
.addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
|
||||||
.addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Send(Kernel::HLERequestContext& ctx) {
|
void BSD::Send(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -361,12 +356,11 @@ void BSD::Send(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
|
LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd),
|
ExecuteWork(ctx, SendWork{
|
||||||
SendWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = flags,
|
||||||
.flags = flags,
|
.message = ctx.ReadBuffer(),
|
||||||
.message = ctx.ReadBuffer(),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
|
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -377,13 +371,12 @@ void BSD::SendTo(Kernel::HLERequestContext& ctx) {
|
||||||
LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
|
LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
|
||||||
ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
|
ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd),
|
ExecuteWork(ctx, SendToWork{
|
||||||
SendToWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = flags,
|
||||||
.flags = flags,
|
.message = ctx.ReadBuffer(0),
|
||||||
.message = ctx.ReadBuffer(0),
|
.addr = ctx.ReadBuffer(1),
|
||||||
.addr = ctx.ReadBuffer(1),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Write(Kernel::HLERequestContext& ctx) {
|
void BSD::Write(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -392,12 +385,11 @@ void BSD::Write(Kernel::HLERequestContext& ctx) {
|
||||||
|
|
||||||
LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
|
LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
|
||||||
|
|
||||||
ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd),
|
ExecuteWork(ctx, SendWork{
|
||||||
SendWork{
|
.fd = fd,
|
||||||
.fd = fd,
|
.flags = 0,
|
||||||
.flags = 0,
|
.message = ctx.ReadBuffer(),
|
||||||
.message = ctx.ReadBuffer(),
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BSD::Close(Kernel::HLERequestContext& ctx) {
|
void BSD::Close(Kernel::HLERequestContext& ctx) {
|
||||||
|
@ -410,24 +402,9 @@ void BSD::Close(Kernel::HLERequestContext& ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Work>
|
template <typename Work>
|
||||||
void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
|
void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, Work work) {
|
||||||
bool is_blocking, Work work) {
|
work.Execute(this);
|
||||||
if (!is_blocking) {
|
|
||||||
work.Execute(this);
|
|
||||||
work.Response(ctx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Signal a dummy response to make IPC validation happy
|
|
||||||
// This will be overwritten by the SleepClientThread callback
|
|
||||||
work.Response(ctx);
|
work.Response(ctx);
|
||||||
|
|
||||||
auto worker = worker_pool.CaptureWorker();
|
|
||||||
|
|
||||||
ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(),
|
|
||||||
worker->Callback<Work>(), worker->KernelEvent());
|
|
||||||
|
|
||||||
worker->SendWork(std::move(work));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
|
||||||
|
@ -807,18 +784,6 @@ bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BSD::IsBlockingSocket(s32 fd) const noexcept {
|
|
||||||
// Inform invalid sockets as non-blocking
|
|
||||||
// This way we avoid using a worker thread as it will fail without blocking host
|
|
||||||
if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!file_descriptors[fd]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
|
||||||
IPC::ResponseBuilder rb{ctx, 4};
|
IPC::ResponseBuilder rb{ctx, 4};
|
||||||
|
|
||||||
|
@ -827,8 +792,7 @@ void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) co
|
||||||
rb.PushEnum(bsd_errno);
|
rb.PushEnum(bsd_errno);
|
||||||
}
|
}
|
||||||
|
|
||||||
BSD::BSD(Core::System& system_, const char* name)
|
BSD::BSD(Core::System& system_, const char* name) : ServiceFramework{system_, name} {
|
||||||
: ServiceFramework{system_, name}, worker_pool{system_, this} {
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const FunctionInfo functions[] = {
|
static const FunctionInfo functions[] = {
|
||||||
{0, &BSD::RegisterClient, "RegisterClient"},
|
{0, &BSD::RegisterClient, "RegisterClient"},
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/kernel/hle_ipc.h"
|
#include "core/hle/kernel/hle_ipc.h"
|
||||||
#include "core/hle/service/service.h"
|
#include "core/hle/service/service.h"
|
||||||
#include "core/hle/service/sockets/blocking_worker.h"
|
|
||||||
#include "core/hle/service/sockets/sockets.h"
|
#include "core/hle/service/sockets/sockets.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
@ -138,8 +137,7 @@ private:
|
||||||
void Close(Kernel::HLERequestContext& ctx);
|
void Close(Kernel::HLERequestContext& ctx);
|
||||||
|
|
||||||
template <typename Work>
|
template <typename Work>
|
||||||
void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
|
void ExecuteWork(Kernel::HLERequestContext& ctx, Work work);
|
||||||
bool is_blocking, Work work);
|
|
||||||
|
|
||||||
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
|
||||||
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
|
std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
|
||||||
|
@ -163,15 +161,10 @@ private:
|
||||||
|
|
||||||
s32 FindFreeFileDescriptorHandle() noexcept;
|
s32 FindFreeFileDescriptorHandle() noexcept;
|
||||||
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
bool IsFileDescriptorValid(s32 fd) const noexcept;
|
||||||
bool IsBlockingSocket(s32 fd) const noexcept;
|
|
||||||
|
|
||||||
void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
|
||||||
|
|
||||||
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
|
std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
|
||||||
|
|
||||||
BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork,
|
|
||||||
SendToWork>
|
|
||||||
worker_pool;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class BSDCFG final : public ServiceFramework<BSDCFG> {
|
class BSDCFG final : public ServiceFramework<BSDCFG> {
|
||||||
|
|
Loading…
Reference in a new issue