mirror of https://git.suyu.dev/suyu/suyu
Merge pull request #4397 from ReinUsesLisp/bsd
services: Implement most of bsd:s and GetCurrentIpAddress from nifmmerge-requests/60/head
commit
e126021ffe
@ -0,0 +1,162 @@
|
|||||||
|
// 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](auto&& w) {
|
||||||
|
using T = std::decay_t<decltype(w)>;
|
||||||
|
if constexpr (std::is_same_v<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
|
@ -0,0 +1,165 @@
|
|||||||
|
// Copyright 2020 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hle/service/sockets/sockets.h"
|
||||||
|
#include "core/hle/service/sockets/sockets_translate.h"
|
||||||
|
#include "core/network/network.h"
|
||||||
|
|
||||||
|
namespace Service::Sockets {
|
||||||
|
|
||||||
|
Errno Translate(Network::Errno value) {
|
||||||
|
switch (value) {
|
||||||
|
case Network::Errno::SUCCESS:
|
||||||
|
return Errno::SUCCESS;
|
||||||
|
case Network::Errno::BADF:
|
||||||
|
return Errno::BADF;
|
||||||
|
case Network::Errno::AGAIN:
|
||||||
|
return Errno::AGAIN;
|
||||||
|
case Network::Errno::INVAL:
|
||||||
|
return Errno::INVAL;
|
||||||
|
case Network::Errno::MFILE:
|
||||||
|
return Errno::MFILE;
|
||||||
|
case Network::Errno::NOTCONN:
|
||||||
|
return Errno::NOTCONN;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented errno={}", static_cast<int>(value));
|
||||||
|
return Errno::SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
|
||||||
|
return {value.first, Translate(value.second)};
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::Domain Translate(Domain domain) {
|
||||||
|
switch (domain) {
|
||||||
|
case Domain::INET:
|
||||||
|
return Network::Domain::INET;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Domain Translate(Network::Domain domain) {
|
||||||
|
switch (domain) {
|
||||||
|
case Network::Domain::INET:
|
||||||
|
return Domain::INET;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::Type Translate(Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case Type::STREAM:
|
||||||
|
return Network::Type::STREAM;
|
||||||
|
case Type::DGRAM:
|
||||||
|
return Network::Type::DGRAM;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::Protocol Translate(Type type, Protocol protocol) {
|
||||||
|
switch (protocol) {
|
||||||
|
case Protocol::UNSPECIFIED:
|
||||||
|
LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type");
|
||||||
|
switch (type) {
|
||||||
|
case Type::DGRAM:
|
||||||
|
return Network::Protocol::UDP;
|
||||||
|
case Type::STREAM:
|
||||||
|
return Network::Protocol::TCP;
|
||||||
|
default:
|
||||||
|
return Network::Protocol::TCP;
|
||||||
|
}
|
||||||
|
case Protocol::TCP:
|
||||||
|
return Network::Protocol::TCP;
|
||||||
|
case Protocol::UDP:
|
||||||
|
return Network::Protocol::UDP;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol));
|
||||||
|
return Network::Protocol::TCP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 TranslatePollEventsToHost(u16 flags) {
|
||||||
|
u16 result = 0;
|
||||||
|
const auto translate = [&result, &flags](u16 from, u16 to) {
|
||||||
|
if ((flags & from) != 0) {
|
||||||
|
flags &= ~from;
|
||||||
|
result |= to;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
translate(POLL_IN, Network::POLL_IN);
|
||||||
|
translate(POLL_PRI, Network::POLL_PRI);
|
||||||
|
translate(POLL_OUT, Network::POLL_OUT);
|
||||||
|
translate(POLL_ERR, Network::POLL_ERR);
|
||||||
|
translate(POLL_HUP, Network::POLL_HUP);
|
||||||
|
translate(POLL_NVAL, Network::POLL_NVAL);
|
||||||
|
|
||||||
|
UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 TranslatePollEventsToGuest(u16 flags) {
|
||||||
|
u16 result = 0;
|
||||||
|
const auto translate = [&result, &flags](u16 from, u16 to) {
|
||||||
|
if ((flags & from) != 0) {
|
||||||
|
flags &= ~from;
|
||||||
|
result |= to;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
translate(Network::POLL_IN, POLL_IN);
|
||||||
|
translate(Network::POLL_PRI, POLL_PRI);
|
||||||
|
translate(Network::POLL_OUT, POLL_OUT);
|
||||||
|
translate(Network::POLL_ERR, POLL_ERR);
|
||||||
|
translate(Network::POLL_HUP, POLL_HUP);
|
||||||
|
translate(Network::POLL_NVAL, POLL_NVAL);
|
||||||
|
|
||||||
|
UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::SockAddrIn Translate(SockAddrIn value) {
|
||||||
|
ASSERT(value.len == 0 || value.len == sizeof(value));
|
||||||
|
|
||||||
|
Network::SockAddrIn result;
|
||||||
|
result.family = Translate(static_cast<Domain>(value.family));
|
||||||
|
result.ip = value.ip;
|
||||||
|
result.portno = value.portno >> 8 | value.portno << 8;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
SockAddrIn Translate(Network::SockAddrIn value) {
|
||||||
|
SockAddrIn result;
|
||||||
|
result.len = sizeof(result);
|
||||||
|
result.family = static_cast<u8>(Translate(value.family));
|
||||||
|
result.portno = value.portno >> 8 | value.portno << 8;
|
||||||
|
result.ip = value.ip;
|
||||||
|
result.zeroes = {};
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Network::ShutdownHow Translate(ShutdownHow how) {
|
||||||
|
switch (how) {
|
||||||
|
case ShutdownHow::RD:
|
||||||
|
return Network::ShutdownHow::RD;
|
||||||
|
case ShutdownHow::WR:
|
||||||
|
return Network::ShutdownHow::WR;
|
||||||
|
case ShutdownHow::RDWR:
|
||||||
|
return Network::ShutdownHow::RDWR;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED_MSG("Unimplemented how={}", static_cast<int>(how));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Service::Sockets
|
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2020 yuzu emulator team
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/hle/service/sockets/sockets.h"
|
||||||
|
#include "core/network/network.h"
|
||||||
|
|
||||||
|
namespace Service::Sockets {
|
||||||
|
|
||||||
|
/// Translate abstract errno to guest errno
|
||||||
|
Errno Translate(Network::Errno value);
|
||||||
|
|
||||||
|
/// Translate abstract return value errno pair to guest return value errno pair
|
||||||
|
std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
|
||||||
|
|
||||||
|
/// Translate guest domain to abstract domain
|
||||||
|
Network::Domain Translate(Domain domain);
|
||||||
|
|
||||||
|
/// Translate abstract domain to guest domain
|
||||||
|
Domain Translate(Network::Domain domain);
|
||||||
|
|
||||||
|
/// Translate guest type to abstract type
|
||||||
|
Network::Type Translate(Type type);
|
||||||
|
|
||||||
|
/// Translate guest protocol to abstract protocol
|
||||||
|
Network::Protocol Translate(Type type, Protocol protocol);
|
||||||
|
|
||||||
|
/// Translate abstract poll event flags to guest poll event flags
|
||||||
|
u16 TranslatePollEventsToHost(u16 flags);
|
||||||
|
|
||||||
|
/// Translate guest poll event flags to abstract poll event flags
|
||||||
|
u16 TranslatePollEventsToGuest(u16 flags);
|
||||||
|
|
||||||
|
/// Translate guest socket address structure to abstract socket address structure
|
||||||
|
Network::SockAddrIn Translate(SockAddrIn value);
|
||||||
|
|
||||||
|
/// Translate abstract socket address structure to guest socket address structure
|
||||||
|
SockAddrIn Translate(Network::SockAddrIn value);
|
||||||
|
|
||||||
|
/// Translate guest shutdown mode to abstract shutdown mode
|
||||||
|
Network::ShutdownHow Translate(ShutdownHow how);
|
||||||
|
|
||||||
|
} // namespace Service::Sockets
|
Loading…
Reference in New Issue