mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-18 01:46:47 +00:00
tipc: implement special-case templating used by 13.0.0 pgl
This commit is contained in:
parent
ff5f376c33
commit
2541f6dd71
2 changed files with 695 additions and 492 deletions
|
@ -38,10 +38,12 @@ namespace ams::tipc {
|
||||||
template<size_t N>
|
template<size_t N>
|
||||||
struct DummyDeferralManager : public DummyDeferralManagerBase {};
|
struct DummyDeferralManager : public DummyDeferralManagerBase {};
|
||||||
|
|
||||||
template<size_t ThreadStackSize, typename... PortInfos>
|
namespace impl {
|
||||||
|
|
||||||
|
template<size_t ThreadStackSize, bool IsDeferralSupported, size_t NumPorts, typename... PortInfos>
|
||||||
class ServerManagerImpl {
|
class ServerManagerImpl {
|
||||||
private:
|
private:
|
||||||
static constexpr inline size_t NumPorts = sizeof...(PortInfos);
|
static_assert(NumPorts == sizeof...(PortInfos));
|
||||||
static constexpr inline size_t MaxSessions = (PortInfos::MaxSessions + ...);
|
static constexpr inline size_t MaxSessions = (PortInfos::MaxSessions + ...);
|
||||||
|
|
||||||
/* Verify that we have at least one port. */
|
/* Verify that we have at least one port. */
|
||||||
|
@ -60,7 +62,7 @@ namespace ams::tipc {
|
||||||
template<size_t Ix> requires (Ix < NumPorts)
|
template<size_t Ix> requires (Ix < NumPorts)
|
||||||
using PortInfo = typename std::tuple_element<Ix, std::tuple<PortInfos...>>::type;
|
using PortInfo = typename std::tuple_element<Ix, std::tuple<PortInfos...>>::type;
|
||||||
|
|
||||||
static constexpr inline bool IsDeferralSupported = (PortInfos::CanDeferInvokeRequest || ...);
|
static_assert(IsDeferralSupported == (PortInfos::CanDeferInvokeRequest || ...));
|
||||||
|
|
||||||
template<size_t Sessions>
|
template<size_t Sessions>
|
||||||
using DeferralManagerImplType = typename std::conditional<IsDeferralSupported, DeferralManager<Sessions>, DummyDeferralManager<Sessions>>::type;
|
using DeferralManagerImplType = typename std::conditional<IsDeferralSupported, DeferralManager<Sessions>, DummyDeferralManager<Sessions>>::type;
|
||||||
|
@ -625,6 +627,219 @@ namespace ams::tipc {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<size_t ThreadStackSize, typename PortInfo>
|
||||||
|
class ServerManagerImpl<ThreadStackSize, false, 1, PortInfo> {
|
||||||
|
private:
|
||||||
|
static constexpr inline size_t NumPorts = 1;
|
||||||
|
static constexpr inline size_t MaxSessions = PortInfo::MaxSessions;
|
||||||
|
|
||||||
|
/* Verify that it's possible to service this many sessions, with our port manager count. */
|
||||||
|
static_assert(MaxSessions <= svc::ArgumentHandleCountMax);
|
||||||
|
public:
|
||||||
|
class PortManagerBase {
|
||||||
|
protected:
|
||||||
|
os::MultiWaitType m_multi_wait;
|
||||||
|
ObjectManagerBase *m_object_manager;
|
||||||
|
public:
|
||||||
|
constexpr PortManagerBase() : m_multi_wait(), m_object_manager() { /* ... */ }
|
||||||
|
|
||||||
|
void InitializeBase(ObjectManagerBase *om) {
|
||||||
|
/* Initialize our multi wait. */
|
||||||
|
os::InitializeMultiWait(std::addressof(m_multi_wait));
|
||||||
|
|
||||||
|
/* Initialize our object manager. */
|
||||||
|
m_object_manager = om;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterPort(os::NativeHandle port_handle) {
|
||||||
|
/* Create an object holder for the port. */
|
||||||
|
tipc::ObjectHolder object;
|
||||||
|
|
||||||
|
/* Setup the object. */
|
||||||
|
object.InitializeAsPort(port_handle);
|
||||||
|
|
||||||
|
/* Register the object. */
|
||||||
|
m_object_manager->AddObject(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
os::NativeHandle ProcessRequest(ObjectHolder &object) {
|
||||||
|
/* Process the request. */
|
||||||
|
const Result result = m_object_manager->ProcessRequest(object);
|
||||||
|
if (R_SUCCEEDED(result)) {
|
||||||
|
/* We should reply only if the request isn't deferred. */
|
||||||
|
return object.GetHandle();
|
||||||
|
} else {
|
||||||
|
/* Processing failed, so close the session if we need to. */
|
||||||
|
if (!tipc::ResultSessionClosed::Includes(result)) {
|
||||||
|
this->CloseSession(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We shouldn't reply on failure. */
|
||||||
|
return os::InvalidNativeHandle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReplyAndReceive(ObjectHolder *out_object, os::NativeHandle reply_target) {
|
||||||
|
/* If we don't have a reply target, clear our message buffer. */
|
||||||
|
if (reply_target == os::InvalidNativeHandle) {
|
||||||
|
svc::ipc::MessageBuffer(svc::ipc::GetMessageBuffer()).SetNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to reply/receive. */
|
||||||
|
const Result result = [&] ALWAYS_INLINE_LAMBDA () -> Result {
|
||||||
|
os::MultiWaitHolderType *signaled_holder = nullptr;
|
||||||
|
ON_SCOPE_EXIT { AMS_ABORT_UNLESS(signaled_holder == nullptr); };
|
||||||
|
return m_object_manager->ReplyAndReceive(std::addressof(signaled_holder), out_object, reply_target, std::addressof(m_multi_wait));
|
||||||
|
}();
|
||||||
|
|
||||||
|
/* Handle the result. */
|
||||||
|
if (R_FAILED(result)) {
|
||||||
|
/* Close the object. */
|
||||||
|
this->CloseSession(*out_object);
|
||||||
|
|
||||||
|
/* We don't have anything to process. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddSession(os::NativeHandle session_handle, tipc::ServiceObjectBase *service_object) {
|
||||||
|
/* Create an object holder for the session. */
|
||||||
|
tipc::ObjectHolder object;
|
||||||
|
|
||||||
|
/* Setup the object. */
|
||||||
|
object.InitializeAsSession(session_handle, true, service_object);
|
||||||
|
|
||||||
|
/* Register the object. */
|
||||||
|
m_object_manager->AddObject(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseSession(ObjectHolder &object) {
|
||||||
|
/* Get the object's handle. */
|
||||||
|
const auto handle = object.GetHandle();
|
||||||
|
|
||||||
|
/* Close the object with our manager. */
|
||||||
|
m_object_manager->CloseObject(handle);
|
||||||
|
|
||||||
|
/* Close the handle itself. */
|
||||||
|
R_ABORT_UNLESS(svc::CloseHandle(handle));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PortManagerImpl final : public PortManagerBase {
|
||||||
|
private:
|
||||||
|
tipc::ObjectManager<1 + MaxSessions> m_object_manager_impl;
|
||||||
|
public:
|
||||||
|
constexpr PortManagerImpl() : PortManagerBase(), m_object_manager_impl() {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize() {
|
||||||
|
/* Initialize our base. */
|
||||||
|
this->InitializeBase(std::addressof(m_object_manager_impl));
|
||||||
|
|
||||||
|
/* Initialize our object manager. */
|
||||||
|
m_object_manager_impl.Initialize(std::addressof(this->m_multi_wait));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using PortManager = PortManagerImpl;
|
||||||
|
private:
|
||||||
|
PortManager m_port_manager;
|
||||||
|
PortInfo::Allocator m_port_allocator;
|
||||||
|
public:
|
||||||
|
constexpr ServerManagerImpl() : m_port_manager(), m_port_allocator() { /* ... */ }
|
||||||
|
|
||||||
|
void Initialize() {
|
||||||
|
/* Initialize our port manager. */
|
||||||
|
m_port_manager.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterPort(os::NativeHandle port_handle) {
|
||||||
|
m_port_manager.RegisterPort(port_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterPort(sm::ServiceName service_name, size_t max_sessions) {
|
||||||
|
/* Register service. */
|
||||||
|
os::NativeHandle port_handle;
|
||||||
|
R_ABORT_UNLESS(sm::RegisterService(std::addressof(port_handle), service_name, max_sessions, false));
|
||||||
|
|
||||||
|
/* Register the port handle. */
|
||||||
|
this->RegisterPort(port_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LoopAuto() {
|
||||||
|
/* Process for the only port. */
|
||||||
|
this->LoopProcess(m_port_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
tipc::ServiceObjectBase *AllocateObject() {
|
||||||
|
/* Allocate the object. */
|
||||||
|
auto * const new_object = m_port_allocator.Allocate();
|
||||||
|
AMS_ABORT_UNLESS(new_object != nullptr);
|
||||||
|
|
||||||
|
/* If we should, set the object's deleter. */
|
||||||
|
if constexpr (IsServiceObjectDeleter<typename PortInfo::Allocator>) {
|
||||||
|
new_object->SetDeleter(std::addressof(m_port_allocator));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_object;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AddSession(os::NativeHandle *out, tipc::ServiceObjectBase *object) {
|
||||||
|
/* Create a handle for the session. */
|
||||||
|
svc::Handle session_handle;
|
||||||
|
R_TRY(svc::CreateSession(std::addressof(session_handle), static_cast<svc::Handle *>(out), false, 0));
|
||||||
|
|
||||||
|
/* Add the session to our manager. */
|
||||||
|
m_port_manager.AddSession(session_handle, object);
|
||||||
|
|
||||||
|
return ResultSuccess();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void LoopProcess(PortManagerBase &port_manager) {
|
||||||
|
/* Process requests forever. */
|
||||||
|
os::NativeHandle reply_target = os::InvalidNativeHandle;
|
||||||
|
while (true) {
|
||||||
|
/* Reply to our pending request, and wait to receive a new one. */
|
||||||
|
tipc::ObjectHolder signaled_object{};
|
||||||
|
while (!port_manager.ReplyAndReceive(std::addressof(signaled_object), reply_target)) {
|
||||||
|
reply_target = os::InvalidNativeHandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A session was signaled, accessible via signaled_object. */
|
||||||
|
switch (signaled_object.GetType()) {
|
||||||
|
case ObjectHolder::ObjectType_Port:
|
||||||
|
{
|
||||||
|
/* Try to accept a new session */
|
||||||
|
svc::Handle session_handle;
|
||||||
|
if (R_SUCCEEDED(svc::AcceptSession(std::addressof(session_handle), signaled_object.GetHandle()))) {
|
||||||
|
port_manager.AddSession(session_handle, this->AllocateObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have nothing to reply to. */
|
||||||
|
reply_target = os::InvalidNativeHandle;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ObjectHolder::ObjectType_Session:
|
||||||
|
{
|
||||||
|
/* Process the request */
|
||||||
|
reply_target = port_manager.ProcessRequest(signaled_object);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t ThreadStackSize, typename... PortInfos>
|
||||||
|
using ServerManagerImpl = impl::ServerManagerImpl<ThreadStackSize, (PortInfos::CanDeferInvokeRequest || ...), sizeof...(PortInfos), PortInfos...>;
|
||||||
|
|
||||||
|
|
||||||
template<typename... PortInfos>
|
template<typename... PortInfos>
|
||||||
using ServerManager = ServerManagerImpl<os::MemoryPageSize, PortInfos...>;
|
using ServerManager = ServerManagerImpl<os::MemoryPageSize, PortInfos...>;
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace ams::pgl::srv {
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("pgl");
|
constexpr sm::ServiceName ShellServiceName = sm::ServiceName::Encode("pgl");
|
||||||
constexpr size_t ShellMaxSessions = 8; /* Official maximum is 8. */
|
constexpr size_t ShellMaxSessions = 8; /* Official maximum is 6. */
|
||||||
|
|
||||||
using CmifServerManager = ams::sf::hipc::ServerManager<PortIndex_Count>;
|
using CmifServerManager = ams::sf::hipc::ServerManager<PortIndex_Count>;
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ namespace ams::pgl::srv {
|
||||||
globals.server_manager.Initialize();
|
globals.server_manager.Initialize();
|
||||||
|
|
||||||
/* Register the pgl service. */
|
/* Register the pgl service. */
|
||||||
globals.server_manager.RegisterPort<PortIndex_Shell>(ShellServiceName, ShellMaxSessions);
|
globals.server_manager.RegisterPort(ShellServiceName, ShellMaxSessions);
|
||||||
} else {
|
} else {
|
||||||
/* Get the globals. */
|
/* Get the globals. */
|
||||||
auto &globals = GetGlobalsForCmif();
|
auto &globals = GetGlobalsForCmif();
|
||||||
|
@ -192,16 +192,4 @@ namespace ams::pgl::srv {
|
||||||
return ResultSuccess();
|
return ResultSuccess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ams::tipc::ServiceObjectBase *AllocateShellEventObserverForTipc() {
|
|
||||||
auto &allocator = GetGlobalsForTipc().observer_allocator;
|
|
||||||
|
|
||||||
auto *object = allocator.Allocate();
|
|
||||||
if (object != nullptr) {
|
|
||||||
object->SetDeleter(std::addressof(allocator));
|
|
||||||
}
|
|
||||||
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue