13
13
#define SWIFT_RUNTIME_CONCURRENTUTILS_H
14
14
#include < iterator>
15
15
#include < atomic>
16
+ #include < functional>
16
17
#include < stdint.h>
18
+ #include " llvm/Support/Allocator.h"
17
19
18
20
#if defined(__FreeBSD__)
19
21
#include < stdio.h>
@@ -125,48 +127,22 @@ template <class ElemTy> struct ConcurrentList {
125
127
std::atomic<ConcurrentListNode<ElemTy> *> First;
126
128
};
127
129
128
- template <class T , bool Delete> class AtomicMaybeOwningPointer ;
129
-
130
- template <class T >
131
- class AtomicMaybeOwningPointer <T, false > {
132
- public:
133
- std::atomic<T*> Value;
134
- constexpr AtomicMaybeOwningPointer (T *value) : Value(value) {}
135
- };
136
-
130
+ // / A utility function for ordering two pointers, which is useful
131
+ // / for implementing compareWithKey.
137
132
template <class T >
138
- class AtomicMaybeOwningPointer <T, true > {
139
- public:
140
- std::atomic<T*> Value;
141
- constexpr AtomicMaybeOwningPointer (T *value) : Value(value) {}
142
-
143
- ~AtomicMaybeOwningPointer () {
144
- // This can use relaxed memory order because the client has to ensure
145
- // that all accesses are safely completed and their effects fully
146
- // visible before destruction occurs anyway.
147
- ::delete Value.load (std::memory_order_relaxed);
148
- }
149
- };
150
-
151
- // / A concurrent map that is implemented using a binary tree. It supports
152
- // / concurrent insertions but does not support removals or rebalancing of
153
- // / the tree.
154
- // /
155
- // / The entry type must provide the following operations:
156
- // /
157
- // / /// For debugging purposes only. Summarize this key as an integer value.
158
- // / long getKeyIntValueForDump() const;
159
- // /
160
- // / /// A ternary comparison. KeyTy is the type of the key provided
161
- // / /// to find or getOrInsert.
162
- // / int compareWithKey(KeyTy key) const;
163
- // /
164
- // / /// Return the amount of extra trailing space required by an entry,
165
- // / /// where KeyTy is the type of the first argument to getOrInsert and
166
- // / /// ArgTys is the type of the remaining arguments.
167
- // / static size_t getExtraAllocationSize(KeyTy key, ArgTys...)
168
- template <class EntryTy , bool ProvideDestructor = true >
169
- class ConcurrentMap {
133
+ static inline int comparePointers (const T *left, const T *right) {
134
+ return (left == right ? 0 : std::less<const T *>()(left, right) ? -1 : 1 );
135
+ }
136
+
137
+ template <class EntryTy , bool ProvideDestructor, class Allocator >
138
+ class ConcurrentMapBase ;
139
+
140
+ // / The partial specialization of ConcurrentMapBase whose destructor is
141
+ // / trivial. The other implementation inherits from this, so this is a
142
+ // / base for all ConcurrentMaps.
143
+ template <class EntryTy , class Allocator >
144
+ class ConcurrentMapBase <EntryTy, false , Allocator> : protected Allocator {
145
+ protected:
170
146
struct Node {
171
147
std::atomic<Node*> Left;
172
148
std::atomic<Node*> Right;
@@ -179,15 +155,7 @@ class ConcurrentMap {
179
155
Node (const Node &) = delete ;
180
156
Node &operator =(const Node &) = delete ;
181
157
182
- ~Node () {
183
- // These can be relaxed accesses because there is no safe way for
184
- // another thread to race an access to this node with our destruction
185
- // of it.
186
- ::delete Left.load (std::memory_order_relaxed);
187
- ::delete Right.load (std::memory_order_relaxed);
188
- }
189
-
190
- #ifndef NDEBUG
158
+ #ifndef NDEBUG
191
159
void dump () const {
192
160
auto L = Left.load (std::memory_order_acquire);
193
161
auto R = Right.load (std::memory_order_acquire);
@@ -204,19 +172,105 @@ class ConcurrentMap {
204
172
printf (" \" %p\" :f2 -> \" %p\" :f0;\n " , this , R);
205
173
}
206
174
}
207
- #endif
175
+ #endif
208
176
};
209
177
210
- // / The root of the tree.
211
- AtomicMaybeOwningPointer<Node, ProvideDestructor> Root;
178
+ std::atomic<Node*> Root;
179
+
180
+ constexpr ConcurrentMapBase () : Root(nullptr ) {}
181
+
182
+ // Implicitly trivial destructor.
183
+ ~ConcurrentMapBase () = default ;
184
+
185
+ void destroyNode (Node *node) {
186
+ assert (node && " destroying null node" );
187
+ auto allocSize = sizeof (Node) + node->Payload .getExtraAllocationSize ();
188
+
189
+ // Destroy the node's payload.
190
+ node->~Node ();
191
+
192
+ // Deallocate the node.
193
+ this ->Deallocate (node, allocSize);
194
+ }
195
+ };
196
+
197
+ // / The partial specialization of ConcurrentMapBase which provides a
198
+ // / non-trivial destructor.
199
+ template <class EntryTy , class Allocator >
200
+ class ConcurrentMapBase <EntryTy, true , Allocator>
201
+ : protected ConcurrentMapBase<EntryTy, false , Allocator> {
202
+ protected:
203
+ using super = ConcurrentMapBase<EntryTy, false , Allocator>;
204
+ using Node = typename super::Node;
205
+
206
+ constexpr ConcurrentMapBase () {}
207
+
208
+ ~ConcurrentMapBase () {
209
+ destroyTree (this ->Root );
210
+ }
211
+
212
+ private:
213
+ void destroyTree (const std::atomic<Node*> &edge) {
214
+ // This can be a relaxed load because destruction is not allowed to race
215
+ // with other operations.
216
+ auto node = edge.load (std::memory_order_relaxed);
217
+ if (!node) return ;
218
+
219
+ // Destroy the node's children.
220
+ destroyTree (node->Left );
221
+ destroyTree (node->Right );
222
+
223
+ // Destroy the node itself.
224
+ this ->destroyNode (node);
225
+ }
226
+ };
227
+
228
+ // / A concurrent map that is implemented using a binary tree. It supports
229
+ // / concurrent insertions but does not support removals or rebalancing of
230
+ // / the tree.
231
+ // /
232
+ // / The entry type must provide the following operations:
233
+ // /
234
+ // / /// For debugging purposes only. Summarize this key as an integer value.
235
+ // / long getKeyIntValueForDump() const;
236
+ // /
237
+ // / /// A ternary comparison. KeyTy is the type of the key provided
238
+ // / /// to find or getOrInsert.
239
+ // / int compareWithKey(KeyTy key) const;
240
+ // /
241
+ // / /// Return the amount of extra trailing space required by an entry,
242
+ // / /// where KeyTy is the type of the first argument to getOrInsert and
243
+ // / /// ArgTys is the type of the remaining arguments.
244
+ // / static size_t getExtraAllocationSize(KeyTy key, ArgTys...)
245
+ // /
246
+ // / /// Return the amount of extra trailing space that was requested for
247
+ // / /// this entry. This method is only used to compute the size of the
248
+ // / /// object during node deallocation; it does not need to return a
249
+ // / /// correct value so long as the allocator's Deallocate implementation
250
+ // / /// ignores this argument.
251
+ // / size_t getExtraAllocationSize() const;
252
+ // /
253
+ // / If ProvideDestructor is false, the destructor will be trivial. This
254
+ // / can be appropriate when the object is declared at global scope.
255
+ template <class EntryTy , bool ProvideDestructor = true ,
256
+ class Allocator = llvm::MallocAllocator>
257
+ class ConcurrentMap
258
+ : private ConcurrentMapBase<EntryTy, ProvideDestructor, Allocator> {
259
+ using super = ConcurrentMapBase<EntryTy, ProvideDestructor, Allocator>;
260
+
261
+ using Node = typename super::Node;
262
+
263
+ // / Inherited from base class:
264
+ // / std::atomic<Node*> Root;
265
+ using super::Root;
212
266
213
267
// / This member stores the address of the last node that was found by the
214
268
// / search procedure. We cache the last search to accelerate code that
215
269
// / searches the same value in a loop.
216
270
std::atomic<Node*> LastSearch;
217
271
218
272
public:
219
- constexpr ConcurrentMap () : Root( nullptr ), LastSearch(nullptr ) {}
273
+ constexpr ConcurrentMap () : LastSearch(nullptr ) {}
220
274
221
275
ConcurrentMap (const ConcurrentMap &) = delete ;
222
276
ConcurrentMap &operator =(const ConcurrentMap &) = delete ;
@@ -226,9 +280,13 @@ class ConcurrentMap {
226
280
227
281
public:
228
282
283
+ Allocator &getAllocator () {
284
+ return *this ;
285
+ }
286
+
229
287
#ifndef NDEBUG
230
288
void dump () const {
231
- auto R = Root.Value . load (std::memory_order_acquire);
289
+ auto R = Root.load (std::memory_order_acquire);
232
290
printf (" digraph g {\n "
233
291
" graph [ rankdir = \" TB\" ];\n "
234
292
" node [ fontsize = \" 16\" ];\n "
@@ -252,7 +310,7 @@ class ConcurrentMap {
252
310
}
253
311
254
312
// Search the tree, starting from the root.
255
- Node *node = Root.Value . load (std::memory_order_acquire);
313
+ Node *node = Root.load (std::memory_order_acquire);
256
314
while (node) {
257
315
int comparisonResult = node->Payload .compareWithKey (key);
258
316
if (comparisonResult == 0 ) {
@@ -285,7 +343,7 @@ class ConcurrentMap {
285
343
Node *newNode = nullptr ;
286
344
287
345
// Start from the root.
288
- auto edge = &Root. Value ;
346
+ auto edge = &Root;
289
347
290
348
while (true ) {
291
349
// Load the edge.
@@ -302,7 +360,7 @@ class ConcurrentMap {
302
360
// If it's equal, we can use this node.
303
361
if (comparisonResult == 0 ) {
304
362
// Destroy the node we allocated before if we're carrying one around.
305
- :: delete newNode;
363
+ if ( newNode) this -> destroyNode (newNode) ;
306
364
307
365
// Cache and report that we found an existing node.
308
366
LastSearch.store (node, std::memory_order_release);
@@ -318,7 +376,7 @@ class ConcurrentMap {
318
376
if (!newNode) {
319
377
size_t allocSize =
320
378
sizeof (Node) + EntryTy::getExtraAllocationSize (key, args...);
321
- void *memory = :: operator new (allocSize);
379
+ void *memory = this -> Allocate (allocSize, alignof (Node) );
322
380
newNode = ::new (memory) Node (key, std::forward<ArgTys>(args)...);
323
381
}
324
382
0 commit comments