Browse Source
Merge pull request #4397 from ReinUsesLisp/bsd
Merge pull request #4397 from ReinUsesLisp/bsd
services: Implement most of bsd:s and GetCurrentIpAddress from nifmnce_cpp
committed by
GitHub
10 changed files with 1387 additions and 56 deletions
-
3src/core/CMakeLists.txt
-
13src/core/hle/service/nifm/nifm.cpp
-
2src/core/hle/service/service.cpp
-
162src/core/hle/service/sockets/blocking_worker.h
-
809src/core/hle/service/sockets/bsd.cpp
-
150src/core/hle/service/sockets/bsd.h
-
6src/core/hle/service/sockets/sockets.cpp
-
85src/core/hle/service/sockets/sockets.h
-
165src/core/hle/service/sockets/sockets_translate.cpp
-
48src/core/hle/service/sockets/sockets_translate.h
@ -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 |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue