From 3e21ea1f65850753612eb22dad78dc25a9db0a9f Mon Sep 17 00:00:00 2001 From: Nathaniel Filardo Date: Wed, 2 Sep 2020 12:41:09 +0100 Subject: [PATCH] Add C++ concept for PAL This will not be used unless the C++ standard version is raised to 20. As concepts and C++20 more generally are quite new, this does not do so. Nevertheless, the use of concepts can improve the local development experience as type mismatches are discovered earlier (at template invocation rather than only during expansion). --- CMakeLists.txt | 3 ++ src/ds/concept.h | 40 ++++++++++++++++ src/mem/address_space.h | 2 +- src/mem/largealloc.h | 6 +-- src/mem/pool.h | 3 +- src/pal/pal.h | 4 +- src/pal/pal_concept.h | 104 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 156 insertions(+), 6 deletions(-) create mode 100644 src/ds/concept.h create mode 100644 src/pal/pal_concept.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 611d730..7191f63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -71,6 +71,9 @@ macro(clangformat_targets) else () message(STATUS "Generating clangformat target using ${CLANG_FORMAT}") file(GLOB_RECURSE ALL_SOURCE_FILES *.cc *.h *.hh) + # clangformat does not yet understand concepts well; for the moment, don't + # ask it to format them. See https://reviews.llvm.org/D79773 + list(FILTER ALL_SOURCE_FILES EXCLUDE REGEX "src/pal/pal_concept\.h$") add_custom_target( clangformat COMMAND ${CLANG_FORMAT} diff --git a/src/ds/concept.h b/src/ds/concept.h new file mode 100644 index 0000000..d935417 --- /dev/null +++ b/src/ds/concept.h @@ -0,0 +1,40 @@ +#pragma once + +/** + * C++20 concepts are referenced as if they were types in declarations within + * template parameters (e.g. "template ..."). That is, they + * take the place of the "typename"/"class" keyword on template parameters. + * If the compiler understands concepts, this macro expands as its argument; + * otherwise, it expands to the keyword "typename", so snmalloc templates that + * use concept-qualified parameters should use this to remain compatible across + * C++ versions: "template" + */ +#ifdef __cpp_concepts +# define SNMALLOC_CONCEPT(c) c +#else +# define SNMALLOC_CONCEPT(c) typename +#endif + +#ifdef __cpp_concepts +namespace snmalloc +{ + /** + * C++20 concepts are more than just new syntax; there's a new support + * library specified as well. As C++20 is quite new, however, there are some + * environments, notably Clang, that understand the syntax but do not yet + * offer the library. Fortunately, alternate pronouciations are possible. + */ +# ifdef _cpp_lib_concepts + /** + * ConceptSame is true if T and U are the same type and false otherwise. + * When specifying a concept, use ConceptSame to indicate that an + * expression must evaluate precisely to the type U. + */ + template + concept ConceptSame = std::same_as; +# else + template + concept ConceptSame = std::is_same::value; +# endif +} // namespace snmalloc +#endif diff --git a/src/mem/address_space.h b/src/mem/address_space.h index a727f0b..00dc21b 100644 --- a/src/mem/address_space.h +++ b/src/mem/address_space.h @@ -13,7 +13,7 @@ namespace snmalloc * It cannot unreserve memory, so this does not require the * usual complexity of a buddy allocator. */ - template + template class AddressSpaceManager : public PAL { /** diff --git a/src/mem/largealloc.h b/src/mem/largealloc.h index 2a0c7b3..ff85bb5 100644 --- a/src/mem/largealloc.h +++ b/src/mem/largealloc.h @@ -14,7 +14,7 @@ namespace snmalloc { - template + template class MemoryProviderStateMixin; class Largeslab : public Baseslab @@ -24,7 +24,7 @@ namespace snmalloc private: template friend class MPMCStack; - template + template friend class MemoryProviderStateMixin; std::atomic next; @@ -56,7 +56,7 @@ namespace snmalloc // This represents the state that the large allcoator needs to add to the // global state of the allocator. This is currently stored in the memory // provider, so we add this in. - template + template class MemoryProviderStateMixin : public PalNotificationObject, public PAL { /** diff --git a/src/mem/pool.h b/src/mem/pool.h index c460567..69de2ca 100644 --- a/src/mem/pool.h +++ b/src/mem/pool.h @@ -2,6 +2,7 @@ #include "../ds/flaglock.h" #include "../ds/mpmcstack.h" +#include "../pal/pal_concept.h" #include "pooled.h" namespace snmalloc @@ -21,7 +22,7 @@ namespace snmalloc { private: friend Pooled; - template + template friend class MemoryProviderStateMixin; std::atomic_flag lock = ATOMIC_FLAG_INIT; diff --git a/src/pal/pal.h b/src/pal/pal.h index 47f30e0..9ce3ffc 100644 --- a/src/pal/pal.h +++ b/src/pal/pal.h @@ -1,5 +1,7 @@ #pragma once +#include "../ds/concept.h" +#include "pal_concept.h" #include "pal_consts.h" // If simultating OE, then we need the underlying platform @@ -63,7 +65,7 @@ namespace snmalloc /** * Query whether the PAL supports a specific feature. */ - template + template constexpr static bool pal_supports = (PAL::pal_features & F) == F; // Used to keep Superslab metadata committed. diff --git a/src/pal/pal_concept.h b/src/pal/pal_concept.h new file mode 100644 index 0000000..33f54f4 --- /dev/null +++ b/src/pal/pal_concept.h @@ -0,0 +1,104 @@ +#pragma once + +#ifdef __cpp_concepts +# include "../ds/concept.h" +# include "pal_consts.h" + +# include + +namespace snmalloc +{ + /** + * PALs must advertize the bit vector of their supported features and the + * platform's page size. This concept enforces that these are indeed + * constants that fit in the desired types. (This is subtly different from + * saying that they are the required types; C++ may handle constants without + * much regard for their claimed type.) + */ + template + concept ConceptPAL_static_members = requires() + { + typename std::integral_constant; + typename std::integral_constant; + }; + + /** + * PALs expose an error reporting function which takes a const C string. + */ + template + concept ConceptPAL_error = requires(const char* const str) + { + { PAL::error(str) } -> ConceptSame; + }; + + /** + * PALs expose a basic library of memory operations. + */ + template + concept ConceptPAL_memops = requires(PAL p, void* vp, size_t sz) + { + { p.notify_not_using(vp, sz) } noexcept -> ConceptSame; + + /* For reasons unknown, these seem to tickle some bug in MSVC */ +# if !defined(_MSC_VER) + { p.template notify_using(vp, sz) } noexcept + -> ConceptSame; + { p.template notify_using(vp, sz) } noexcept + -> ConceptSame; +# endif + + { p.template zero(vp, sz) } noexcept -> ConceptSame; + { p.template zero(vp, sz) } noexcept -> ConceptSame; + }; + + /** + * Absent any feature flags, the PAL must support a crude primitive allocator + */ + template + concept ConceptPAL_reserve_at_least = requires(PAL p, void* vp, size_t sz) + { + { p.reserve_at_least(sz) } noexcept + -> ConceptSame>; + }; + + /** + * Some PALs expose a richer allocator which understands aligned allocations + */ + template + concept ConceptPAL_reserve_aligned = requires(PAL p, size_t sz) + { + { p.template reserve_aligned(sz) } noexcept -> ConceptSame; + { p.template reserve_aligned(sz) } noexcept -> ConceptSame; + }; + + /** + * Some PALs can provide memory pressure callbacks. + */ + template + concept ConceptPAL_mem_low_notify = + requires(PAL p, PalNotificationObject* pno) + { + { p.expensive_low_memory_check() } -> ConceptSame; + { p.register_for_low_memory_callback(pno) } -> ConceptSame; + }; + + /** + * PALs ascribe to the conjunction of several concepts. These are broken + * out by the shape of the requires() quantifiers required and by any + * requisite claimed pal_features. PALs not claiming particular features + * are, naturally, not bound by the corresponding concept. + */ + template + concept ConceptPAL = + ConceptPAL_static_members && + ConceptPAL_error && + ConceptPAL_memops && + (!(PAL::pal_features & LowMemoryNotification) || + ConceptPAL_mem_low_notify) && + (!!(PAL::pal_features & AlignedAllocation) || + ConceptPAL_reserve_at_least) && + (!(PAL::pal_features & AlignedAllocation) || + ConceptPAL_reserve_aligned); + +} // namespace snmalloc +#endif