xref: /llvm-project/llvm/include/llvm/Support/ThreadSafeAllocator.h (revision 18d199116fe2150549110da68ac0ca8cfd80f9c8)
1 //===- ThreadSafeAllocator.h ------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #ifndef LLVM_SUPPORT_THREADSAFEALLOCATOR_H
10 #define LLVM_SUPPORT_THREADSAFEALLOCATOR_H
11 
12 #include "llvm/ADT/STLExtras.h"
13 #include "llvm/Support/Allocator.h"
14 #include <atomic>
15 
16 namespace llvm {
17 
18 /// Thread-safe allocator adaptor. Uses a spin lock on the assumption that
19 /// contention here is extremely rare.
20 ///
21 /// TODO: Using a spin lock on every allocation can be quite expensive when
22 /// contention is high. Since this is mainly used for BumpPtrAllocator and
23 /// SpecificBumpPtrAllocator, it'd be better to have a specific thread-safe
24 /// BumpPtrAllocator implementation that only use a fair lock when allocating a
25 /// new slab but otherwise using atomic and be lock-free.
26 template <class AllocatorType> class ThreadSafeAllocator {
27   struct LockGuard {
LockGuardLockGuard28     LockGuard(std::atomic_flag &Flag) : Flag(Flag) {
29       if (LLVM_UNLIKELY(Flag.test_and_set(std::memory_order_acquire)))
30         while (Flag.test_and_set(std::memory_order_acquire)) {
31         }
32     }
~LockGuardLockGuard33     ~LockGuard() { Flag.clear(std::memory_order_release); }
34     std::atomic_flag &Flag;
35   };
36 
37 public:
Allocate(size_t N)38   auto Allocate(size_t N) {
39     return applyLocked([N](AllocatorType &Alloc) { return Alloc.Allocate(N); });
40   }
41 
Allocate(size_t Size,size_t Align)42   auto Allocate(size_t Size, size_t Align) {
43     return applyLocked([Size, Align](AllocatorType &Alloc) {
44       return Alloc.Allocate(Size, Align);
45     });
46   }
47 
48   template <typename FnT,
49             typename T = typename llvm::function_traits<FnT>::result_t>
applyLocked(FnT Fn)50   T applyLocked(FnT Fn) {
51     LockGuard Lock(Flag);
52     return Fn(Alloc);
53   }
54 
55 private:
56   AllocatorType Alloc;
57   std::atomic_flag Flag = ATOMIC_FLAG_INIT;
58 };
59 
60 } // namespace llvm
61 
62 #endif // LLVM_SUPPORT_THREADSAFEALLOCATOR_H
63