Skip to content

Commit 3e21ea1

Browse files
nwf-msrmjp41
authored andcommitted
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).
1 parent a3d5477 commit 3e21ea1

File tree

7 files changed

+156
-6
lines changed

7 files changed

+156
-6
lines changed

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ macro(clangformat_targets)
7171
else ()
7272
message(STATUS "Generating clangformat target using ${CLANG_FORMAT}")
7373
file(GLOB_RECURSE ALL_SOURCE_FILES *.cc *.h *.hh)
74+
# clangformat does not yet understand concepts well; for the moment, don't
75+
# ask it to format them. See https://reviews.llvm.org/D79773
76+
list(FILTER ALL_SOURCE_FILES EXCLUDE REGEX "src/pal/pal_concept\.h$")
7477
add_custom_target(
7578
clangformat
7679
COMMAND ${CLANG_FORMAT}

src/ds/concept.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#pragma once
2+
3+
/**
4+
* C++20 concepts are referenced as if they were types in declarations within
5+
* template parameters (e.g. "template<FooConcept Foo> ..."). That is, they
6+
* take the place of the "typename"/"class" keyword on template parameters.
7+
* If the compiler understands concepts, this macro expands as its argument;
8+
* otherwise, it expands to the keyword "typename", so snmalloc templates that
9+
* use concept-qualified parameters should use this to remain compatible across
10+
* C++ versions: "template<SNMALLOC_CONCEPT(FooConcept) Foo>"
11+
*/
12+
#ifdef __cpp_concepts
13+
# define SNMALLOC_CONCEPT(c) c
14+
#else
15+
# define SNMALLOC_CONCEPT(c) typename
16+
#endif
17+
18+
#ifdef __cpp_concepts
19+
namespace snmalloc
20+
{
21+
/**
22+
* C++20 concepts are more than just new syntax; there's a new support
23+
* library specified as well. As C++20 is quite new, however, there are some
24+
* environments, notably Clang, that understand the syntax but do not yet
25+
* offer the library. Fortunately, alternate pronouciations are possible.
26+
*/
27+
# ifdef _cpp_lib_concepts
28+
/**
29+
* ConceptSame<T,U> is true if T and U are the same type and false otherwise.
30+
* When specifying a concept, use ConceptSame<U> to indicate that an
31+
* expression must evaluate precisely to the type U.
32+
*/
33+
template<typename T, typename U>
34+
concept ConceptSame = std::same_as<T, U>;
35+
# else
36+
template<typename T, typename U>
37+
concept ConceptSame = std::is_same<T, U>::value;
38+
# endif
39+
} // namespace snmalloc
40+
#endif

src/mem/address_space.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace snmalloc
1313
* It cannot unreserve memory, so this does not require the
1414
* usual complexity of a buddy allocator.
1515
*/
16-
template<typename PAL>
16+
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
1717
class AddressSpaceManager : public PAL
1818
{
1919
/**

src/mem/largealloc.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
namespace snmalloc
1616
{
17-
template<class PAL>
17+
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
1818
class MemoryProviderStateMixin;
1919

2020
class Largeslab : public Baseslab
@@ -24,7 +24,7 @@ namespace snmalloc
2424
private:
2525
template<class a, Construction c>
2626
friend class MPMCStack;
27-
template<class PAL>
27+
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
2828
friend class MemoryProviderStateMixin;
2929
std::atomic<Largeslab*> next;
3030

@@ -56,7 +56,7 @@ namespace snmalloc
5656
// This represents the state that the large allcoator needs to add to the
5757
// global state of the allocator. This is currently stored in the memory
5858
// provider, so we add this in.
59-
template<class PAL>
59+
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
6060
class MemoryProviderStateMixin : public PalNotificationObject, public PAL
6161
{
6262
/**

src/mem/pool.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "../ds/flaglock.h"
44
#include "../ds/mpmcstack.h"
5+
#include "../pal/pal_concept.h"
56
#include "pooled.h"
67

78
namespace snmalloc
@@ -21,7 +22,7 @@ namespace snmalloc
2122
{
2223
private:
2324
friend Pooled<T>;
24-
template<typename TT>
25+
template<SNMALLOC_CONCEPT(ConceptPAL) PAL>
2526
friend class MemoryProviderStateMixin;
2627

2728
std::atomic_flag lock = ATOMIC_FLAG_INIT;

src/pal/pal.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include "../ds/concept.h"
4+
#include "pal_concept.h"
35
#include "pal_consts.h"
46

57
// If simultating OE, then we need the underlying platform
@@ -63,7 +65,7 @@ namespace snmalloc
6365
/**
6466
* Query whether the PAL supports a specific feature.
6567
*/
66-
template<PalFeatures F, typename PAL = Pal>
68+
template<PalFeatures F, SNMALLOC_CONCEPT(ConceptPAL) PAL = Pal>
6769
constexpr static bool pal_supports = (PAL::pal_features & F) == F;
6870

6971
// Used to keep Superslab metadata committed.

src/pal/pal_concept.h

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#pragma once
2+
3+
#ifdef __cpp_concepts
4+
# include "../ds/concept.h"
5+
# include "pal_consts.h"
6+
7+
# include <utility>
8+
9+
namespace snmalloc
10+
{
11+
/**
12+
* PALs must advertize the bit vector of their supported features and the
13+
* platform's page size. This concept enforces that these are indeed
14+
* constants that fit in the desired types. (This is subtly different from
15+
* saying that they are the required types; C++ may handle constants without
16+
* much regard for their claimed type.)
17+
*/
18+
template<typename PAL>
19+
concept ConceptPAL_static_members = requires()
20+
{
21+
typename std::integral_constant<uint64_t, PAL::pal_features>;
22+
typename std::integral_constant<size_t, PAL::page_size>;
23+
};
24+
25+
/**
26+
* PALs expose an error reporting function which takes a const C string.
27+
*/
28+
template<typename PAL>
29+
concept ConceptPAL_error = requires(const char* const str)
30+
{
31+
{ PAL::error(str) } -> ConceptSame<void>;
32+
};
33+
34+
/**
35+
* PALs expose a basic library of memory operations.
36+
*/
37+
template<typename PAL>
38+
concept ConceptPAL_memops = requires(PAL p, void* vp, size_t sz)
39+
{
40+
{ p.notify_not_using(vp, sz) } noexcept -> ConceptSame<void>;
41+
42+
/* For reasons unknown, these seem to tickle some bug in MSVC */
43+
# if !defined(_MSC_VER)
44+
{ p.template notify_using<NoZero>(vp, sz) } noexcept
45+
-> ConceptSame<void>;
46+
{ p.template notify_using<YesZero>(vp, sz) } noexcept
47+
-> ConceptSame<void>;
48+
# endif
49+
50+
{ p.template zero<false>(vp, sz) } noexcept -> ConceptSame<void>;
51+
{ p.template zero<true>(vp, sz) } noexcept -> ConceptSame<void>;
52+
};
53+
54+
/**
55+
* Absent any feature flags, the PAL must support a crude primitive allocator
56+
*/
57+
template<typename PAL>
58+
concept ConceptPAL_reserve_at_least = requires(PAL p, void* vp, size_t sz)
59+
{
60+
{ p.reserve_at_least(sz) } noexcept
61+
-> ConceptSame<std::pair<void*, size_t>>;
62+
};
63+
64+
/**
65+
* Some PALs expose a richer allocator which understands aligned allocations
66+
*/
67+
template<typename PAL>
68+
concept ConceptPAL_reserve_aligned = requires(PAL p, size_t sz)
69+
{
70+
{ p.template reserve_aligned<false>(sz) } noexcept -> ConceptSame<void*>;
71+
{ p.template reserve_aligned<true>(sz) } noexcept -> ConceptSame<void*>;
72+
};
73+
74+
/**
75+
* Some PALs can provide memory pressure callbacks.
76+
*/
77+
template<typename PAL>
78+
concept ConceptPAL_mem_low_notify =
79+
requires(PAL p, PalNotificationObject* pno)
80+
{
81+
{ p.expensive_low_memory_check() } -> ConceptSame<bool>;
82+
{ p.register_for_low_memory_callback(pno) } -> ConceptSame<void>;
83+
};
84+
85+
/**
86+
* PALs ascribe to the conjunction of several concepts. These are broken
87+
* out by the shape of the requires() quantifiers required and by any
88+
* requisite claimed pal_features. PALs not claiming particular features
89+
* are, naturally, not bound by the corresponding concept.
90+
*/
91+
template<typename PAL>
92+
concept ConceptPAL =
93+
ConceptPAL_static_members<PAL> &&
94+
ConceptPAL_error<PAL> &&
95+
ConceptPAL_memops<PAL> &&
96+
(!(PAL::pal_features & LowMemoryNotification) ||
97+
ConceptPAL_mem_low_notify<PAL>) &&
98+
(!!(PAL::pal_features & AlignedAllocation) ||
99+
ConceptPAL_reserve_at_least<PAL>) &&
100+
(!(PAL::pal_features & AlignedAllocation) ||
101+
ConceptPAL_reserve_aligned<PAL>);
102+
103+
} // namespace snmalloc
104+
#endif

0 commit comments

Comments
 (0)