Atmosphere/libraries/libstratosphere/include/stratosphere/tipc/tipc_deferral_manager.hpp

214 lines
8.1 KiB
C++
Raw Normal View History

/*
* Copyright (c) Atmosphère-NX
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <vapours.hpp>
#include <stratosphere/tipc/tipc_object_holder.hpp>
#include <stratosphere/tipc/tipc_service_object_base.hpp>
namespace ams::tipc {
template<typename T>
concept IsResumeKey = util::is_pod<T>::value && (0 < sizeof(T) && sizeof(T) <= sizeof(uintptr_t));
template<IsResumeKey ResumeKey>
static constexpr ALWAYS_INLINE uintptr_t ConvertToInternalResumeKey(ResumeKey key) {
if constexpr (std::same_as<ResumeKey, uintptr_t>) {
return key;
} else if constexpr (sizeof(key) == sizeof(uintptr_t)) {
return std::bit_cast<uintptr_t>(key);
} else {
uintptr_t converted = 0;
std::memcpy(std::addressof(converted), std::addressof(key), sizeof(key));
return converted;
}
}
class DeferralManagerBase;
namespace impl {
class DeferrableBaseTag{};
}
2021-11-07 02:33:08 +00:00
class DeferrableBaseImpl : public impl::DeferrableBaseTag {
private:
DeferralManagerBase *m_deferral_manager;
ObjectHolder m_object_holder;
uintptr_t m_resume_key;
2021-11-07 02:33:08 +00:00
const u32 m_message_buffer_size;
u8 m_message_buffer_base[0];
public:
2021-11-07 02:33:08 +00:00
ALWAYS_INLINE DeferrableBaseImpl(u32 mb_size) : m_deferral_manager(nullptr), m_object_holder(), m_resume_key(), m_message_buffer_size(mb_size) { /* ... */ }
2021-11-07 02:33:08 +00:00
~DeferrableBaseImpl();
ALWAYS_INLINE void SetDeferralManager(DeferralManagerBase *manager, os::NativeHandle reply_target, ServiceObjectBase *object) {
m_deferral_manager = manager;
m_object_holder.InitializeForDeferralManager(reply_target, object);
}
ALWAYS_INLINE bool TestResume(uintptr_t key) const {
return m_resume_key == key;
}
template<IsResumeKey ResumeKey>
ALWAYS_INLINE void RegisterRetry(ResumeKey key) {
m_resume_key = ConvertToInternalResumeKey(key);
2021-11-07 02:33:08 +00:00
std::memcpy(m_message_buffer_base, svc::ipc::GetMessageBuffer(), m_message_buffer_size);
}
template<IsResumeKey ResumeKey, typename F>
ALWAYS_INLINE Result RegisterRetryIfDeferred(ResumeKey key, F f) {
const Result result = f();
if (tipc::ResultRequestDeferred::Includes(result)) {
this->RegisterRetry(key);
}
return result;
}
template<typename PortManager>
ALWAYS_INLINE void TriggerResume(PortManager *port_manager) {
/* Clear resume key. */
m_resume_key = 0;
/* Restore message buffer. */
2021-11-07 02:33:08 +00:00
std::memcpy(svc::ipc::GetMessageBuffer(), m_message_buffer_base, m_message_buffer_size);
/* Process the request. */
return port_manager->ProcessDeferredRequest(m_object_holder);
}
2021-11-07 02:33:08 +00:00
protected:
static consteval size_t GetMessageBufferOffsetBase();
};
static_assert(std::is_standard_layout<DeferrableBaseImpl>::value);
template<typename Interface>
class DeferrableBase : public DeferrableBaseImpl {
private:
static constexpr size_t MessageBufferRequiredSize = Interface::MaximumRequestSize;
private:
u8 m_message_buffer[MessageBufferRequiredSize];
public:
DeferrableBase();
private:
static consteval size_t GetMessageBufferOffset();
};
2021-11-07 02:33:08 +00:00
consteval size_t DeferrableBaseImpl::GetMessageBufferOffsetBase() {
return AMS_OFFSETOF(DeferrableBaseImpl, m_message_buffer_base);
}
template<typename Interface>
consteval size_t DeferrableBase<Interface>::GetMessageBufferOffset() {
return AMS_OFFSETOF(DeferrableBase<Interface>, m_message_buffer);
}
template<typename Interface>
ALWAYS_INLINE DeferrableBase<Interface>::DeferrableBase() : DeferrableBaseImpl(MessageBufferRequiredSize) {
static_assert(GetMessageBufferOffsetBase() == GetMessageBufferOffset());
static_assert(sizeof(DeferrableBase<Interface>) >= sizeof(DeferrableBaseImpl) + MessageBufferRequiredSize);
}
template<class T>
concept IsDeferrable = std::derived_from<T, impl::DeferrableBaseTag>;
class DeferralManagerBase {
NON_COPYABLE(DeferralManagerBase);
NON_MOVEABLE(DeferralManagerBase);
private:
size_t m_object_count;
2021-11-07 02:33:08 +00:00
DeferrableBaseImpl *m_objects_base[0];
public:
ALWAYS_INLINE DeferralManagerBase() : m_object_count(0) { /* ... */ }
2021-11-07 02:33:08 +00:00
void AddObject(DeferrableBaseImpl &object, os::NativeHandle reply_target, ServiceObjectBase *service_object) {
/* Set ourselves as the manager for the object. */
object.SetDeferralManager(this, reply_target, service_object);
/* Add the object to our entries. */
AMS_ASSERT(m_object_count < N);
m_objects_base[m_object_count++] = std::addressof(object);
}
2021-11-07 02:33:08 +00:00
void RemoveObject(DeferrableBaseImpl *object) {
/* If the object is present, remove it. */
for (size_t i = 0; i < m_object_count; ++i) {
if (m_objects_base[i] == object) {
std::swap(m_objects_base[i], m_objects_base[--m_object_count]);
break;
}
}
}
ALWAYS_INLINE bool TestResume(uintptr_t resume_key) const {
/* Try to resume all entries. */
for (size_t i = 0; i < m_object_count; ++i) {
if (m_objects_base[i]->TestResume(resume_key)) {
return true;
}
}
return false;
}
template<typename PortManager>
ALWAYS_INLINE void TriggerResume(PortManager *port_manager, uintptr_t resume_key) const {
/* Try to resume all entries. */
for (size_t i = 0; i < m_object_count; ++i) {
if (m_objects_base[i]->TestResume(resume_key)) {
m_objects_base[i]->TriggerResume(port_manager);
}
}
}
protected:
static consteval size_t GetObjectPointersOffsetBase();
};
static_assert(std::is_standard_layout<DeferralManagerBase>::value);
2021-11-07 02:33:08 +00:00
inline DeferrableBaseImpl::~DeferrableBaseImpl() {
AMS_ASSUME(m_deferral_manager != nullptr);
m_deferral_manager->RemoveObject(this);
}
template<size_t N> requires (N > 0)
class DeferralManager final : public DeferralManagerBase {
private:
2021-11-07 02:33:08 +00:00
DeferrableBaseImpl *m_objects[N];
public:
DeferralManager();
private:
static consteval size_t GetObjectPointersOffset();
};
consteval size_t DeferralManagerBase::GetObjectPointersOffsetBase() {
return AMS_OFFSETOF(DeferralManagerBase, m_objects_base);
}
Minor header fixes to reduce parsing issues with Clang (#1700) * Work around Clang's incomplete C++20 support for omitting typename * vapours: fix Clang error about missing return in constexpr function * stratosphere: fix call to non-constexpr strlen in constexpr function strlen being constexpr is a non-compliant GCC extension; Clang explicitly rejects it: https://reviews.llvm.org/D23692 * stratosphere: add a bunch of missing override specifiers * stratosphere: work around Clang consteval bug Minimal example: https://godbolt.org/z/MoM64v93M The issue seems to be that Clang does not consider f(x) to be a constant expression if x comes from a template argument that isn't a non-type auto template argument (???) We can work around this by relaxing GetMessageHeaderForCheck (by using constexpr instead of consteval). This produces no functional changes because the result of GetMessageHeaderForCheck() is assigned to a constexpr variable, so the result is guaranteed to be computed at compile-time. * stratosphere: fix missing require clauses in definitions GCC not requiring the require clauses to be repeated for member definitions is actually a compiler bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96830 Clang rejects declarations with missing require clauses. * Fix ALWAYS_INLINE_LAMBDA and parameter list relative order While GCC doesn't seem to care about the position of the always_inline attribute relative to the parameter list, Clang is very picky and requires the attribute to appear after the parameter list (and before a trailing return type) * stratosphere: fix static constexpr member variable with incomplete type GCC accepts this for some reason (because of the lambda?) but Clang correctly rejects this.
2021-11-07 01:19:34 +00:00
template<size_t N> requires (N > 0)
consteval size_t DeferralManager<N>::GetObjectPointersOffset() {
return AMS_OFFSETOF(DeferralManager<N>, m_objects);
}
Minor header fixes to reduce parsing issues with Clang (#1700) * Work around Clang's incomplete C++20 support for omitting typename * vapours: fix Clang error about missing return in constexpr function * stratosphere: fix call to non-constexpr strlen in constexpr function strlen being constexpr is a non-compliant GCC extension; Clang explicitly rejects it: https://reviews.llvm.org/D23692 * stratosphere: add a bunch of missing override specifiers * stratosphere: work around Clang consteval bug Minimal example: https://godbolt.org/z/MoM64v93M The issue seems to be that Clang does not consider f(x) to be a constant expression if x comes from a template argument that isn't a non-type auto template argument (???) We can work around this by relaxing GetMessageHeaderForCheck (by using constexpr instead of consteval). This produces no functional changes because the result of GetMessageHeaderForCheck() is assigned to a constexpr variable, so the result is guaranteed to be computed at compile-time. * stratosphere: fix missing require clauses in definitions GCC not requiring the require clauses to be repeated for member definitions is actually a compiler bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96830 Clang rejects declarations with missing require clauses. * Fix ALWAYS_INLINE_LAMBDA and parameter list relative order While GCC doesn't seem to care about the position of the always_inline attribute relative to the parameter list, Clang is very picky and requires the attribute to appear after the parameter list (and before a trailing return type) * stratosphere: fix static constexpr member variable with incomplete type GCC accepts this for some reason (because of the lambda?) but Clang correctly rejects this.
2021-11-07 01:19:34 +00:00
template<size_t N> requires (N > 0)
inline DeferralManager<N>::DeferralManager() : DeferralManagerBase() {
static_assert(GetObjectPointersOffset() == GetObjectPointersOffsetBase());
2021-11-07 02:33:08 +00:00
static_assert(sizeof(DeferralManager<N>) == sizeof(DeferralManagerBase) + N * sizeof(DeferrableBaseImpl *));
}
}