@@ -82,9 +82,6 @@ class ParametricStorageUniquer {
82
82
// / The set containing the allocated storage instances.
83
83
StorageTypeSet instances;
84
84
85
- // / Allocator to use when constructing derived instances.
86
- StorageAllocator allocator;
87
-
88
85
#if LLVM_ENABLE_THREADS != 0
89
86
// / A mutex to keep uniquing thread-safe.
90
87
llvm::sys::SmartRWMutex<true > mutex;
@@ -93,13 +90,12 @@ class ParametricStorageUniquer {
93
90
94
91
// / Get or create an instance of a param derived type in an thread-unsafe
95
92
// / fashion.
96
- BaseStorage *
97
- getOrCreateUnsafe (Shard &shard, LookupKey &key,
98
- function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
93
+ BaseStorage *getOrCreateUnsafe (Shard &shard, LookupKey &key,
94
+ function_ref<BaseStorage *()> ctorFn) {
99
95
auto existing = shard.instances .insert_as ({key.hashValue }, key);
100
96
BaseStorage *&storage = existing.first ->storage ;
101
97
if (existing.second )
102
- storage = ctorFn (shard. allocator );
98
+ storage = ctorFn ();
103
99
return storage;
104
100
}
105
101
@@ -135,10 +131,9 @@ class ParametricStorageUniquer {
135
131
}
136
132
}
137
133
// / Get or create an instance of a parametric type.
138
- BaseStorage *
139
- getOrCreate (bool threadingIsEnabled, unsigned hashValue,
140
- function_ref<bool (const BaseStorage *)> isEqual,
141
- function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
134
+ BaseStorage *getOrCreate (bool threadingIsEnabled, unsigned hashValue,
135
+ function_ref<bool (const BaseStorage *)> isEqual,
136
+ function_ref<BaseStorage *()> ctorFn) {
142
137
Shard &shard = getShard (hashValue);
143
138
ParametricStorageUniquer::LookupKey lookupKey{hashValue, isEqual};
144
139
if (!threadingIsEnabled)
@@ -163,17 +158,20 @@ class ParametricStorageUniquer {
163
158
llvm::sys::SmartScopedWriter<true > typeLock (shard.mutex );
164
159
return localInst = getOrCreateUnsafe (shard, lookupKey, ctorFn);
165
160
}
161
+
166
162
// / Run a mutation function on the provided storage object in a thread-safe
167
163
// / way.
168
- LogicalResult
169
- mutate (bool threadingIsEnabled, BaseStorage *storage,
170
- function_ref<LogicalResult(StorageAllocator &)> mutationFn) {
171
- Shard &shard = getShardFor (storage);
164
+ LogicalResult mutate (bool threadingIsEnabled, BaseStorage *storage,
165
+ function_ref<LogicalResult()> mutationFn) {
172
166
if (!threadingIsEnabled)
173
- return mutationFn (shard. allocator );
167
+ return mutationFn ();
174
168
169
+ // Get a shard to use for mutating this storage instance. It doesn't need to
170
+ // be the same shard as the original allocation, but does need to be
171
+ // deterministic.
172
+ Shard &shard = getShard (llvm::hash_value (storage));
175
173
llvm::sys::SmartScopedWriter<true > lock (shard.mutex );
176
- return mutationFn (shard. allocator );
174
+ return mutationFn ();
177
175
}
178
176
179
177
private:
@@ -197,18 +195,6 @@ class ParametricStorageUniquer {
197
195
return *shard;
198
196
}
199
197
200
- // / Return the shard that allocated the provided storage object.
201
- Shard &getShardFor (BaseStorage *storage) {
202
- for (size_t i = 0 ; i != numShards; ++i) {
203
- if (Shard *shard = shards[i].load (std::memory_order_acquire)) {
204
- llvm::sys::SmartScopedReader<true > lock (shard->mutex );
205
- if (shard->allocator .allocated (storage))
206
- return *shard;
207
- }
208
- }
209
- llvm_unreachable (" expected storage object to have a valid shard" );
210
- }
211
-
212
198
// / A thread local cache for storage objects. This helps to reduce the lock
213
199
// / contention when an object already existing in the cache.
214
200
ThreadLocalCache<StorageTypeSet> localCache;
@@ -281,8 +267,9 @@ struct StorageUniquerImpl {
281
267
assert (parametricUniquers.count (id) &&
282
268
" creating unregistered storage instance" );
283
269
ParametricStorageUniquer &storageUniquer = *parametricUniquers[id];
284
- return storageUniquer.getOrCreate (threadingIsEnabled, hashValue, isEqual,
285
- ctorFn);
270
+ return storageUniquer.getOrCreate (
271
+ threadingIsEnabled, hashValue, isEqual,
272
+ [&] { return ctorFn (getThreadSafeAllocator ()); });
286
273
}
287
274
288
275
// / Run a mutation function on the provided storage object in a thread-safe
@@ -293,7 +280,34 @@ struct StorageUniquerImpl {
293
280
assert (parametricUniquers.count (id) &&
294
281
" mutating unregistered storage instance" );
295
282
ParametricStorageUniquer &storageUniquer = *parametricUniquers[id];
296
- return storageUniquer.mutate (threadingIsEnabled, storage, mutationFn);
283
+ return storageUniquer.mutate (threadingIsEnabled, storage, [&] {
284
+ return mutationFn (getThreadSafeAllocator ());
285
+ });
286
+ }
287
+
288
+ // / Return an allocator that can be used to safely allocate instances on the
289
+ // / current thread.
290
+ StorageAllocator &getThreadSafeAllocator () {
291
+ #if LLVM_ENABLE_THREADS != 0
292
+ if (!threadingIsEnabled)
293
+ return allocator;
294
+
295
+ // If the allocator has not been initialized, create a new one.
296
+ StorageAllocator *&threadAllocator = threadSafeAllocator.get ();
297
+ if (!threadAllocator) {
298
+ threadAllocator = new StorageAllocator ();
299
+
300
+ // Record this allocator, given that we don't want it to be destroyed when
301
+ // the thread dies.
302
+ llvm::sys::SmartScopedLock<true > lock (threadAllocatorMutex);
303
+ threadAllocators.push_back (
304
+ std::unique_ptr<StorageAllocator>(threadAllocator));
305
+ }
306
+
307
+ return *threadAllocator;
308
+ #else
309
+ return allocator;
310
+ #endif
297
311
}
298
312
299
313
// ===--------------------------------------------------------------------===//
@@ -314,6 +328,22 @@ struct StorageUniquerImpl {
314
328
// Instance Storage
315
329
// ===--------------------------------------------------------------------===//
316
330
331
+ #if LLVM_ENABLE_THREADS != 0
332
+ // / A thread local set of allocators used for uniquing parametric instances,
333
+ // / or other data allocated in thread volatile situations.
334
+ ThreadLocalCache<StorageAllocator *> threadSafeAllocator;
335
+
336
+ // / All of the allocators that have been created for thread based allocation.
337
+ std::vector<std::unique_ptr<StorageAllocator>> threadAllocators;
338
+
339
+ // / A mutex used for safely adding a new thread allocator.
340
+ llvm::sys::SmartMutex<true > threadAllocatorMutex;
341
+ #endif
342
+
343
+ // / Main allocator used for uniquing singleton instances, and other state when
344
+ // / thread safety is guaranteed.
345
+ StorageAllocator allocator;
346
+
317
347
// / Map of type ids to the storage uniquer to use for registered objects.
318
348
DenseMap<TypeID, std::unique_ptr<ParametricStorageUniquer>>
319
349
parametricUniquers;
@@ -322,9 +352,6 @@ struct StorageUniquerImpl {
322
352
// / singleton.
323
353
DenseMap<TypeID, BaseStorage *> singletonInstances;
324
354
325
- // / Allocator used for uniquing singleton instances.
326
- StorageAllocator singletonAllocator;
327
-
328
355
// / Flag specifying if multi-threading is enabled within the uniquer.
329
356
bool threadingIsEnabled = true ;
330
357
};
@@ -378,7 +405,7 @@ void StorageUniquer::registerSingletonImpl(
378
405
TypeID id, function_ref<BaseStorage *(StorageAllocator &)> ctorFn) {
379
406
assert (!impl->singletonInstances .count (id) &&
380
407
" storage class already registered" );
381
- impl->singletonInstances .try_emplace (id, ctorFn (impl->singletonAllocator ));
408
+ impl->singletonInstances .try_emplace (id, ctorFn (impl->allocator ));
382
409
}
383
410
384
411
// / Implementation for mutating an instance of a derived storage.
0 commit comments