1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#pragma once
#ifdef __cpp_concepts
# include "../ds/ds.h"
# include "sizeclasstable.h"
# include <stddef.h>
namespace snmalloc
{
/**
* The core of the static pagemap accessor interface: {get,set}_metadata.
*
* get_metadata takes a boolean template parameter indicating whether it may
* be accessing memory that is not known to be committed.
*/
template<typename Pagemap>
concept IsReadablePagemap =
requires(address_t addr, size_t sz, const typename Pagemap::Entry& t) {
{
Pagemap::template get_metaentry<true>(addr)
} -> ConceptSame<const typename Pagemap::Entry&>;
{
Pagemap::template get_metaentry<false>(addr)
} -> ConceptSame<const typename Pagemap::Entry&>;
};
/**
* The core of the static pagemap accessor interface: {get,set}_metadata.
*
* get_metadata_mut takes a boolean template parameter indicating whether it
* may be accessing memory that is not known to be committed.
*
* set_metadata updates the entry in the pagemap.
*/
template<typename Pagemap>
concept IsWritablePagemap = IsReadablePagemap<Pagemap> &&
requires(address_t addr, size_t sz, const typename Pagemap::Entry& t) {
{
Pagemap::template get_metaentry_mut<true>(addr)
} -> ConceptSame<typename Pagemap::Entry&>;
{
Pagemap::template get_metaentry_mut<false>(addr)
} -> ConceptSame<typename Pagemap::Entry&>;
{
Pagemap::set_metaentry(addr, sz, t)
} -> ConceptSame<void>;
};
/**
* The pagemap can also be told to commit backing storage for a range of
* addresses. This is broken out to a separate concept so that we can
* annotate which functions expect to do this vs. which merely use the core
* interface above. In practice, use IsWritablePagemapWithRegister below,
* which combines this and the core concept, above.
*/
template<typename Pagemap>
concept IsPagemapWithRegister = requires(capptr::Arena<void> p, size_t sz) {
{
Pagemap::register_range(p, sz)
} -> ConceptSame<bool>;
};
/**
* The full pagemap accessor interface, with all of {get,set}_metadata and
* register_range. Use this to annotate callers that need the full interface
* and use IsReadablePagemap for callers that merely need {get,set}_metadata,
* but note that the difference is just for humans and not compilers (since
* concept checking is lower bounding and does not constrain the templatized
* code to use only those affordances given by the concept).
*/
template<typename Pagemap>
concept IsWritablePagemapWithRegister =
IsWritablePagemap<Pagemap> && IsPagemapWithRegister<Pagemap>;
/**
* The configuration also defines domestication (that is, the difference
* between Tame and Wild CapPtr bounds). It exports the intended affordance
* for testing a Wild pointer and either returning nullptr or the original
* pointer, now Tame.
*/
template<typename Config>
concept IsConfigDomestication =
requires(typename Config::LocalState* ls, capptr::AllocWild<void> ptr) {
{
Config::capptr_domesticate(ls, ptr)
} -> ConceptSame<capptr::Alloc<void>>;
{
Config::capptr_domesticate(ls, ptr.template as_static<char>())
} -> ConceptSame<capptr::Alloc<char>>;
};
class CommonConfig;
struct Flags;
template<typename LocalState, typename PagemapEntry, typename Backend>
concept IsBackend =
requires(
LocalState& local_state,
size_t size,
uintptr_t ras,
sizeclass_t sizeclass) {
{
Backend::alloc_chunk(local_state, size, ras, sizeclass)
} -> ConceptSame<
stl::Pair<capptr::Chunk<void>, typename Backend::SlabMetadata*>>;
} &&
requires(LocalState* local_state, size_t size) {
{
Backend::template alloc_meta_data<void*>(local_state, size)
} -> ConceptSame<capptr::Alloc<void>>;
} &&
requires(
LocalState& local_state,
typename Backend::SlabMetadata& slab_metadata,
capptr::Alloc<void> alloc,
size_t size,
sizeclass_t sizeclass) {
{
Backend::dealloc_chunk(
local_state, slab_metadata, alloc, size, sizeclass)
} -> ConceptSame<void>;
} &&
requires(address_t p) {
{
Backend::template get_metaentry<true>(p)
} -> ConceptSame<const PagemapEntry&>;
{
Backend::template get_metaentry<false>(p)
} -> ConceptSame<const PagemapEntry&>;
};
/**
* Config objects of type T must obey a number of constraints. They
* must...
*
* * inherit from CommonConfig (see commonconfig.h)
* * specify which PAL is in use via T::Pal
* * define a T::LocalState type (and alias it as T::Pagemap::LocalState)
* * define T::Options of type snmalloc::Flags
* * expose the global allocator pool via T::pool() if pool allocation is
* used.
*
*/
template<typename Config>
concept IsConfig = stl::is_base_of_v<CommonConfig, Config> &&
IsPAL<typename Config::Pal> &&
IsBackend<typename Config::LocalState,
typename Config::PagemapEntry,
typename Config::Backend> &&
requires() {
typename Config::LocalState;
typename Config::Backend;
typename Config::PagemapEntry;
{
Config::Options
} -> ConceptSameModRef<const Flags>;
} &&
(
requires() {
Config::Options.AllocIsPoolAllocated == true;
typename Config::GlobalPoolState;
{
Config::pool()
} -> ConceptSame<typename Config::GlobalPoolState&>;
} ||
requires() {
Config::Options.AllocIsPoolAllocated == false;
});
/**
* The lazy version of the above; please see ds_core/concept.h and use
* sparingly.
*/
template<typename Config>
concept IsConfigLazy = !
is_type_complete_v<Config> || IsConfig<Config>;
} // namespace snmalloc
#endif