1
0
Fork 0
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:
Michael Scire 2021-10-15 21:20:52 -07:00
parent ff5f376c33
commit 2541f6dd71
2 changed files with 695 additions and 492 deletions

View file

@ -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...>;

View file

@ -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;
}
} }