xref: /llvm-project/compiler-rt/lib/scudo/standalone/quarantine.h (revision 6a057e7b51beaa54ebaece825ba1c87db0a7322d)
1ab8c8da6SKostya Kortchinsky //===-- quarantine.h --------------------------------------------*- C++ -*-===//
2ab8c8da6SKostya Kortchinsky //
3ab8c8da6SKostya Kortchinsky // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4ab8c8da6SKostya Kortchinsky // See https://llvm.org/LICENSE.txt for license information.
5ab8c8da6SKostya Kortchinsky // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6ab8c8da6SKostya Kortchinsky //
7ab8c8da6SKostya Kortchinsky //===----------------------------------------------------------------------===//
8ab8c8da6SKostya Kortchinsky 
9ab8c8da6SKostya Kortchinsky #ifndef SCUDO_QUARANTINE_H_
10ab8c8da6SKostya Kortchinsky #define SCUDO_QUARANTINE_H_
11ab8c8da6SKostya Kortchinsky 
12ab8c8da6SKostya Kortchinsky #include "list.h"
13ab8c8da6SKostya Kortchinsky #include "mutex.h"
14ab8c8da6SKostya Kortchinsky #include "string_utils.h"
156a4c3959SChia-hung Duan #include "thread_annotations.h"
16ab8c8da6SKostya Kortchinsky 
17ab8c8da6SKostya Kortchinsky namespace scudo {
18ab8c8da6SKostya Kortchinsky 
19ab8c8da6SKostya Kortchinsky struct QuarantineBatch {
20ab8c8da6SKostya Kortchinsky   // With the following count, a batch (and the header that protects it) occupy
21ab8c8da6SKostya Kortchinsky   // 4096 bytes on 32-bit platforms, and 8192 bytes on 64-bit.
22ab8c8da6SKostya Kortchinsky   static const u32 MaxCount = 1019;
23ab8c8da6SKostya Kortchinsky   QuarantineBatch *Next;
24ab8c8da6SKostya Kortchinsky   uptr Size;
25ab8c8da6SKostya Kortchinsky   u32 Count;
26ab8c8da6SKostya Kortchinsky   void *Batch[MaxCount];
27ab8c8da6SKostya Kortchinsky 
initQuarantineBatch28ab8c8da6SKostya Kortchinsky   void init(void *Ptr, uptr Size) {
29ab8c8da6SKostya Kortchinsky     Count = 1;
30ab8c8da6SKostya Kortchinsky     Batch[0] = Ptr;
31ab8c8da6SKostya Kortchinsky     this->Size = Size + sizeof(QuarantineBatch); // Account for the Batch Size.
32ab8c8da6SKostya Kortchinsky   }
33ab8c8da6SKostya Kortchinsky 
34ab8c8da6SKostya Kortchinsky   // The total size of quarantined nodes recorded in this batch.
getQuarantinedSizeQuarantineBatch35ab8c8da6SKostya Kortchinsky   uptr getQuarantinedSize() const { return Size - sizeof(QuarantineBatch); }
36ab8c8da6SKostya Kortchinsky 
push_backQuarantineBatch37ab8c8da6SKostya Kortchinsky   void push_back(void *Ptr, uptr Size) {
38ab8c8da6SKostya Kortchinsky     DCHECK_LT(Count, MaxCount);
39ab8c8da6SKostya Kortchinsky     Batch[Count++] = Ptr;
40ab8c8da6SKostya Kortchinsky     this->Size += Size;
41ab8c8da6SKostya Kortchinsky   }
42ab8c8da6SKostya Kortchinsky 
canMergeQuarantineBatch43ab8c8da6SKostya Kortchinsky   bool canMerge(const QuarantineBatch *const From) const {
44ab8c8da6SKostya Kortchinsky     return Count + From->Count <= MaxCount;
45ab8c8da6SKostya Kortchinsky   }
46ab8c8da6SKostya Kortchinsky 
mergeQuarantineBatch47ab8c8da6SKostya Kortchinsky   void merge(QuarantineBatch *const From) {
48ab8c8da6SKostya Kortchinsky     DCHECK_LE(Count + From->Count, MaxCount);
49ab8c8da6SKostya Kortchinsky     DCHECK_GE(Size, sizeof(QuarantineBatch));
50ab8c8da6SKostya Kortchinsky 
51ab8c8da6SKostya Kortchinsky     for (uptr I = 0; I < From->Count; ++I)
52ab8c8da6SKostya Kortchinsky       Batch[Count + I] = From->Batch[I];
53ab8c8da6SKostya Kortchinsky     Count += From->Count;
54ab8c8da6SKostya Kortchinsky     Size += From->getQuarantinedSize();
55ab8c8da6SKostya Kortchinsky 
56ab8c8da6SKostya Kortchinsky     From->Count = 0;
57ab8c8da6SKostya Kortchinsky     From->Size = sizeof(QuarantineBatch);
58ab8c8da6SKostya Kortchinsky   }
59ab8c8da6SKostya Kortchinsky 
shuffleQuarantineBatch60ab8c8da6SKostya Kortchinsky   void shuffle(u32 State) { ::scudo::shuffle(Batch, Count, &State); }
61ab8c8da6SKostya Kortchinsky };
62ab8c8da6SKostya Kortchinsky 
636fd6cfdfSPeter Collingbourne static_assert(sizeof(QuarantineBatch) <= (1U << 13), ""); // 8Kb.
64ab8c8da6SKostya Kortchinsky 
65ab8c8da6SKostya Kortchinsky // Per-thread cache of memory blocks.
66ab8c8da6SKostya Kortchinsky template <typename Callback> class QuarantineCache {
67ab8c8da6SKostya Kortchinsky public:
init()68a45877eeSKostya Kortchinsky   void init() { DCHECK_EQ(atomic_load_relaxed(&Size), 0U); }
69ab8c8da6SKostya Kortchinsky 
70ab8c8da6SKostya Kortchinsky   // Total memory used, including internal accounting.
getSize()71ab8c8da6SKostya Kortchinsky   uptr getSize() const { return atomic_load_relaxed(&Size); }
72ab8c8da6SKostya Kortchinsky   // Memory used for internal accounting.
getOverheadSize()73ab8c8da6SKostya Kortchinsky   uptr getOverheadSize() const { return List.size() * sizeof(QuarantineBatch); }
74ab8c8da6SKostya Kortchinsky 
enqueue(Callback Cb,void * Ptr,uptr Size)75ab8c8da6SKostya Kortchinsky   void enqueue(Callback Cb, void *Ptr, uptr Size) {
76ab8c8da6SKostya Kortchinsky     if (List.empty() || List.back()->Count == QuarantineBatch::MaxCount) {
77ab8c8da6SKostya Kortchinsky       QuarantineBatch *B =
78ab8c8da6SKostya Kortchinsky           reinterpret_cast<QuarantineBatch *>(Cb.allocate(sizeof(*B)));
79ab8c8da6SKostya Kortchinsky       DCHECK(B);
80ab8c8da6SKostya Kortchinsky       B->init(Ptr, Size);
81ab8c8da6SKostya Kortchinsky       enqueueBatch(B);
82ab8c8da6SKostya Kortchinsky     } else {
83ab8c8da6SKostya Kortchinsky       List.back()->push_back(Ptr, Size);
84ab8c8da6SKostya Kortchinsky       addToSize(Size);
85ab8c8da6SKostya Kortchinsky     }
86ab8c8da6SKostya Kortchinsky   }
87ab8c8da6SKostya Kortchinsky 
transfer(QuarantineCache * From)88ab8c8da6SKostya Kortchinsky   void transfer(QuarantineCache *From) {
89ab8c8da6SKostya Kortchinsky     List.append_back(&From->List);
90ab8c8da6SKostya Kortchinsky     addToSize(From->getSize());
91ab8c8da6SKostya Kortchinsky     atomic_store_relaxed(&From->Size, 0);
92ab8c8da6SKostya Kortchinsky   }
93ab8c8da6SKostya Kortchinsky 
enqueueBatch(QuarantineBatch * B)94ab8c8da6SKostya Kortchinsky   void enqueueBatch(QuarantineBatch *B) {
95ab8c8da6SKostya Kortchinsky     List.push_back(B);
96ab8c8da6SKostya Kortchinsky     addToSize(B->Size);
97ab8c8da6SKostya Kortchinsky   }
98ab8c8da6SKostya Kortchinsky 
dequeueBatch()99ab8c8da6SKostya Kortchinsky   QuarantineBatch *dequeueBatch() {
100ab8c8da6SKostya Kortchinsky     if (List.empty())
101ab8c8da6SKostya Kortchinsky       return nullptr;
102ab8c8da6SKostya Kortchinsky     QuarantineBatch *B = List.front();
103ab8c8da6SKostya Kortchinsky     List.pop_front();
104ab8c8da6SKostya Kortchinsky     subFromSize(B->Size);
105ab8c8da6SKostya Kortchinsky     return B;
106ab8c8da6SKostya Kortchinsky   }
107ab8c8da6SKostya Kortchinsky 
mergeBatches(QuarantineCache * ToDeallocate)108ab8c8da6SKostya Kortchinsky   void mergeBatches(QuarantineCache *ToDeallocate) {
109ab8c8da6SKostya Kortchinsky     uptr ExtractedSize = 0;
110ab8c8da6SKostya Kortchinsky     QuarantineBatch *Current = List.front();
111ab8c8da6SKostya Kortchinsky     while (Current && Current->Next) {
112ab8c8da6SKostya Kortchinsky       if (Current->canMerge(Current->Next)) {
113ab8c8da6SKostya Kortchinsky         QuarantineBatch *Extracted = Current->Next;
114ab8c8da6SKostya Kortchinsky         // Move all the chunks into the current batch.
115ab8c8da6SKostya Kortchinsky         Current->merge(Extracted);
116ab8c8da6SKostya Kortchinsky         DCHECK_EQ(Extracted->Count, 0);
117ab8c8da6SKostya Kortchinsky         DCHECK_EQ(Extracted->Size, sizeof(QuarantineBatch));
118ab8c8da6SKostya Kortchinsky         // Remove the next batch From the list and account for its Size.
119ab8c8da6SKostya Kortchinsky         List.extract(Current, Extracted);
120ab8c8da6SKostya Kortchinsky         ExtractedSize += Extracted->Size;
121ab8c8da6SKostya Kortchinsky         // Add it to deallocation list.
122ab8c8da6SKostya Kortchinsky         ToDeallocate->enqueueBatch(Extracted);
123ab8c8da6SKostya Kortchinsky       } else {
124ab8c8da6SKostya Kortchinsky         Current = Current->Next;
125ab8c8da6SKostya Kortchinsky       }
126ab8c8da6SKostya Kortchinsky     }
127ab8c8da6SKostya Kortchinsky     subFromSize(ExtractedSize);
128ab8c8da6SKostya Kortchinsky   }
129ab8c8da6SKostya Kortchinsky 
getStats(ScopedString * Str)130f7b1489fSKostya Kortchinsky   void getStats(ScopedString *Str) const {
131ab8c8da6SKostya Kortchinsky     uptr BatchCount = 0;
132ab8c8da6SKostya Kortchinsky     uptr TotalOverheadBytes = 0;
133ab8c8da6SKostya Kortchinsky     uptr TotalBytes = 0;
134ab8c8da6SKostya Kortchinsky     uptr TotalQuarantineChunks = 0;
135ab8c8da6SKostya Kortchinsky     for (const QuarantineBatch &Batch : List) {
136ab8c8da6SKostya Kortchinsky       BatchCount++;
137ab8c8da6SKostya Kortchinsky       TotalBytes += Batch.Size;
138ab8c8da6SKostya Kortchinsky       TotalOverheadBytes += Batch.Size - Batch.getQuarantinedSize();
139ab8c8da6SKostya Kortchinsky       TotalQuarantineChunks += Batch.Count;
140ab8c8da6SKostya Kortchinsky     }
141ab8c8da6SKostya Kortchinsky     const uptr QuarantineChunksCapacity =
142ab8c8da6SKostya Kortchinsky         BatchCount * QuarantineBatch::MaxCount;
143ab8c8da6SKostya Kortchinsky     const uptr ChunksUsagePercent =
144ab8c8da6SKostya Kortchinsky         (QuarantineChunksCapacity == 0)
145ab8c8da6SKostya Kortchinsky             ? 0
146ab8c8da6SKostya Kortchinsky             : TotalQuarantineChunks * 100 / QuarantineChunksCapacity;
147ab8c8da6SKostya Kortchinsky     const uptr TotalQuarantinedBytes = TotalBytes - TotalOverheadBytes;
148ab8c8da6SKostya Kortchinsky     const uptr MemoryOverheadPercent =
149ab8c8da6SKostya Kortchinsky         (TotalQuarantinedBytes == 0)
150ab8c8da6SKostya Kortchinsky             ? 0
151ab8c8da6SKostya Kortchinsky             : TotalOverheadBytes * 100 / TotalQuarantinedBytes;
152f7b1489fSKostya Kortchinsky     Str->append(
153f7b1489fSKostya Kortchinsky         "Stats: Quarantine: batches: %zu; bytes: %zu (user: %zu); chunks: %zu "
154f7b1489fSKostya Kortchinsky         "(capacity: %zu); %zu%% chunks used; %zu%% memory overhead\n",
155ab8c8da6SKostya Kortchinsky         BatchCount, TotalBytes, TotalQuarantinedBytes, TotalQuarantineChunks,
156ab8c8da6SKostya Kortchinsky         QuarantineChunksCapacity, ChunksUsagePercent, MemoryOverheadPercent);
157ab8c8da6SKostya Kortchinsky   }
158ab8c8da6SKostya Kortchinsky 
159ab8c8da6SKostya Kortchinsky private:
1606f2de9cbSKostya Kortchinsky   SinglyLinkedList<QuarantineBatch> List;
161d56ef852SVitaly Buka   atomic_uptr Size = {};
162ab8c8da6SKostya Kortchinsky 
addToSize(uptr add)163ab8c8da6SKostya Kortchinsky   void addToSize(uptr add) { atomic_store_relaxed(&Size, getSize() + add); }
subFromSize(uptr sub)164ab8c8da6SKostya Kortchinsky   void subFromSize(uptr sub) { atomic_store_relaxed(&Size, getSize() - sub); }
165ab8c8da6SKostya Kortchinsky };
166ab8c8da6SKostya Kortchinsky 
167ab8c8da6SKostya Kortchinsky // The callback interface is:
168ab8c8da6SKostya Kortchinsky // void Callback::recycle(Node *Ptr);
169ab8c8da6SKostya Kortchinsky // void *Callback::allocate(uptr Size);
170ab8c8da6SKostya Kortchinsky // void Callback::deallocate(void *Ptr);
171ab8c8da6SKostya Kortchinsky template <typename Callback, typename Node> class GlobalQuarantine {
172ab8c8da6SKostya Kortchinsky public:
173ab8c8da6SKostya Kortchinsky   typedef QuarantineCache<Callback> CacheT;
1748b062b61SKostya Kortchinsky   using ThisT = GlobalQuarantine<Callback, Node>;
175ab8c8da6SKostya Kortchinsky 
init(uptr Size,uptr CacheSize)1766a4c3959SChia-hung Duan   void init(uptr Size, uptr CacheSize) NO_THREAD_SAFETY_ANALYSIS {
1778b062b61SKostya Kortchinsky     DCHECK(isAligned(reinterpret_cast<uptr>(this), alignof(ThisT)));
178a45877eeSKostya Kortchinsky     DCHECK_EQ(atomic_load_relaxed(&MaxSize), 0U);
179a45877eeSKostya Kortchinsky     DCHECK_EQ(atomic_load_relaxed(&MinSize), 0U);
180a45877eeSKostya Kortchinsky     DCHECK_EQ(atomic_load_relaxed(&MaxCacheSize), 0U);
181ab8c8da6SKostya Kortchinsky     // Thread local quarantine size can be zero only when global quarantine size
182ab8c8da6SKostya Kortchinsky     // is zero (it allows us to perform just one atomic read per put() call).
183ab8c8da6SKostya Kortchinsky     CHECK((Size == 0 && CacheSize == 0) || CacheSize != 0);
184ab8c8da6SKostya Kortchinsky 
185ab8c8da6SKostya Kortchinsky     atomic_store_relaxed(&MaxSize, Size);
186ab8c8da6SKostya Kortchinsky     atomic_store_relaxed(&MinSize, Size / 10 * 9); // 90% of max size.
187ab8c8da6SKostya Kortchinsky     atomic_store_relaxed(&MaxCacheSize, CacheSize);
188ab8c8da6SKostya Kortchinsky 
189612f2385SKostya Kortchinsky     Cache.init();
190ab8c8da6SKostya Kortchinsky   }
191ab8c8da6SKostya Kortchinsky 
getMaxSize()192ab8c8da6SKostya Kortchinsky   uptr getMaxSize() const { return atomic_load_relaxed(&MaxSize); }
getCacheSize()193ab8c8da6SKostya Kortchinsky   uptr getCacheSize() const { return atomic_load_relaxed(&MaxCacheSize); }
194ab8c8da6SKostya Kortchinsky 
195*6a057e7bSChia-hung Duan   // This is supposed to be used in test only.
isEmpty()196*6a057e7bSChia-hung Duan   bool isEmpty() {
197*6a057e7bSChia-hung Duan     ScopedLock L(CacheMutex);
198*6a057e7bSChia-hung Duan     return Cache.getSize() == 0U;
199*6a057e7bSChia-hung Duan   }
200*6a057e7bSChia-hung Duan 
put(CacheT * C,Callback Cb,Node * Ptr,uptr Size)201ab8c8da6SKostya Kortchinsky   void put(CacheT *C, Callback Cb, Node *Ptr, uptr Size) {
202ab8c8da6SKostya Kortchinsky     C->enqueue(Cb, Ptr, Size);
203ab8c8da6SKostya Kortchinsky     if (C->getSize() > getCacheSize())
204ab8c8da6SKostya Kortchinsky       drain(C, Cb);
205ab8c8da6SKostya Kortchinsky   }
206ab8c8da6SKostya Kortchinsky 
drain(CacheT * C,Callback Cb)2076a4c3959SChia-hung Duan   void NOINLINE drain(CacheT *C, Callback Cb) EXCLUDES(CacheMutex) {
2086a4c3959SChia-hung Duan     bool needRecycle = false;
209ab8c8da6SKostya Kortchinsky     {
210aeb38262SKostya Kortchinsky       ScopedLock L(CacheMutex);
211ab8c8da6SKostya Kortchinsky       Cache.transfer(C);
2126a4c3959SChia-hung Duan       needRecycle = Cache.getSize() > getMaxSize();
213ab8c8da6SKostya Kortchinsky     }
2146a4c3959SChia-hung Duan 
2156a4c3959SChia-hung Duan     if (needRecycle && RecycleMutex.tryLock())
216ab8c8da6SKostya Kortchinsky       recycle(atomic_load_relaxed(&MinSize), Cb);
217ab8c8da6SKostya Kortchinsky   }
218ab8c8da6SKostya Kortchinsky 
drainAndRecycle(CacheT * C,Callback Cb)2196a4c3959SChia-hung Duan   void NOINLINE drainAndRecycle(CacheT *C, Callback Cb) EXCLUDES(CacheMutex) {
220ab8c8da6SKostya Kortchinsky     {
221aeb38262SKostya Kortchinsky       ScopedLock L(CacheMutex);
222ab8c8da6SKostya Kortchinsky       Cache.transfer(C);
223ab8c8da6SKostya Kortchinsky     }
2249ef6faf4SKostya Kortchinsky     RecycleMutex.lock();
225ab8c8da6SKostya Kortchinsky     recycle(0, Cb);
226ab8c8da6SKostya Kortchinsky   }
227ab8c8da6SKostya Kortchinsky 
getStats(ScopedString * Str)2286a4c3959SChia-hung Duan   void getStats(ScopedString *Str) EXCLUDES(CacheMutex) {
22970758b80SChia-hung Duan     ScopedLock L(CacheMutex);
230ab8c8da6SKostya Kortchinsky     // It assumes that the world is stopped, just as the allocator's printStats.
231f7b1489fSKostya Kortchinsky     Cache.getStats(Str);
232f7b1489fSKostya Kortchinsky     Str->append("Quarantine limits: global: %zuK; thread local: %zuK\n",
233f7b1489fSKostya Kortchinsky                 getMaxSize() >> 10, getCacheSize() >> 10);
234ab8c8da6SKostya Kortchinsky   }
235ab8c8da6SKostya Kortchinsky 
disable()2366a4c3959SChia-hung Duan   void disable() NO_THREAD_SAFETY_ANALYSIS {
2379ef6faf4SKostya Kortchinsky     // RecycleMutex must be locked 1st since we grab CacheMutex within recycle.
2389ef6faf4SKostya Kortchinsky     RecycleMutex.lock();
2399ef6faf4SKostya Kortchinsky     CacheMutex.lock();
2409ef6faf4SKostya Kortchinsky   }
2419ef6faf4SKostya Kortchinsky 
enable()2426a4c3959SChia-hung Duan   void enable() NO_THREAD_SAFETY_ANALYSIS {
2439ef6faf4SKostya Kortchinsky     CacheMutex.unlock();
2449ef6faf4SKostya Kortchinsky     RecycleMutex.unlock();
2459ef6faf4SKostya Kortchinsky   }
2469ef6faf4SKostya Kortchinsky 
247ab8c8da6SKostya Kortchinsky private:
248ab8c8da6SKostya Kortchinsky   // Read-only data.
249aeb38262SKostya Kortchinsky   alignas(SCUDO_CACHE_LINE_SIZE) HybridMutex CacheMutex;
2506a4c3959SChia-hung Duan   CacheT Cache GUARDED_BY(CacheMutex);
2519ef6faf4SKostya Kortchinsky   alignas(SCUDO_CACHE_LINE_SIZE) HybridMutex RecycleMutex;
252d56ef852SVitaly Buka   atomic_uptr MinSize = {};
253d56ef852SVitaly Buka   atomic_uptr MaxSize = {};
254d56ef852SVitaly Buka   alignas(SCUDO_CACHE_LINE_SIZE) atomic_uptr MaxCacheSize = {};
255ab8c8da6SKostya Kortchinsky 
recycle(uptr MinSize,Callback Cb)2566a4c3959SChia-hung Duan   void NOINLINE recycle(uptr MinSize, Callback Cb) RELEASE(RecycleMutex)
2576a4c3959SChia-hung Duan       EXCLUDES(CacheMutex) {
258ab8c8da6SKostya Kortchinsky     CacheT Tmp;
259ab8c8da6SKostya Kortchinsky     Tmp.init();
260ab8c8da6SKostya Kortchinsky     {
261aeb38262SKostya Kortchinsky       ScopedLock L(CacheMutex);
262ab8c8da6SKostya Kortchinsky       // Go over the batches and merge partially filled ones to
263ab8c8da6SKostya Kortchinsky       // save some memory, otherwise batches themselves (since the memory used
264ab8c8da6SKostya Kortchinsky       // by them is counted against quarantine limit) can overcome the actual
265ab8c8da6SKostya Kortchinsky       // user's quarantined chunks, which diminishes the purpose of the
266ab8c8da6SKostya Kortchinsky       // quarantine.
267ab8c8da6SKostya Kortchinsky       const uptr CacheSize = Cache.getSize();
268ab8c8da6SKostya Kortchinsky       const uptr OverheadSize = Cache.getOverheadSize();
269ab8c8da6SKostya Kortchinsky       DCHECK_GE(CacheSize, OverheadSize);
270ab8c8da6SKostya Kortchinsky       // Do the merge only when overhead exceeds this predefined limit (might
271ab8c8da6SKostya Kortchinsky       // require some tuning). It saves us merge attempt when the batch list
272ab8c8da6SKostya Kortchinsky       // quarantine is unlikely to contain batches suitable for merge.
273ab8c8da6SKostya Kortchinsky       constexpr uptr OverheadThresholdPercents = 100;
274ab8c8da6SKostya Kortchinsky       if (CacheSize > OverheadSize &&
275ab8c8da6SKostya Kortchinsky           OverheadSize * (100 + OverheadThresholdPercents) >
276ab8c8da6SKostya Kortchinsky               CacheSize * OverheadThresholdPercents) {
277ab8c8da6SKostya Kortchinsky         Cache.mergeBatches(&Tmp);
278ab8c8da6SKostya Kortchinsky       }
279ab8c8da6SKostya Kortchinsky       // Extract enough chunks from the quarantine to get below the max
280ab8c8da6SKostya Kortchinsky       // quarantine size and leave some leeway for the newly quarantined chunks.
281ab8c8da6SKostya Kortchinsky       while (Cache.getSize() > MinSize)
282ab8c8da6SKostya Kortchinsky         Tmp.enqueueBatch(Cache.dequeueBatch());
283ab8c8da6SKostya Kortchinsky     }
2849ef6faf4SKostya Kortchinsky     RecycleMutex.unlock();
285ab8c8da6SKostya Kortchinsky     doRecycle(&Tmp, Cb);
286ab8c8da6SKostya Kortchinsky   }
287ab8c8da6SKostya Kortchinsky 
doRecycle(CacheT * C,Callback Cb)288ab8c8da6SKostya Kortchinsky   void NOINLINE doRecycle(CacheT *C, Callback Cb) {
289ab8c8da6SKostya Kortchinsky     while (QuarantineBatch *B = C->dequeueBatch()) {
290ab8c8da6SKostya Kortchinsky       const u32 Seed = static_cast<u32>(
291ab8c8da6SKostya Kortchinsky           (reinterpret_cast<uptr>(B) ^ reinterpret_cast<uptr>(C)) >> 4);
292ab8c8da6SKostya Kortchinsky       B->shuffle(Seed);
293ab8c8da6SKostya Kortchinsky       constexpr uptr NumberOfPrefetch = 8UL;
294ab8c8da6SKostya Kortchinsky       CHECK(NumberOfPrefetch <= ARRAY_SIZE(B->Batch));
295ab8c8da6SKostya Kortchinsky       for (uptr I = 0; I < NumberOfPrefetch; I++)
296ab8c8da6SKostya Kortchinsky         PREFETCH(B->Batch[I]);
297ab8c8da6SKostya Kortchinsky       for (uptr I = 0, Count = B->Count; I < Count; I++) {
298ab8c8da6SKostya Kortchinsky         if (I + NumberOfPrefetch < Count)
299ab8c8da6SKostya Kortchinsky           PREFETCH(B->Batch[I + NumberOfPrefetch]);
300ab8c8da6SKostya Kortchinsky         Cb.recycle(reinterpret_cast<Node *>(B->Batch[I]));
301ab8c8da6SKostya Kortchinsky       }
302ab8c8da6SKostya Kortchinsky       Cb.deallocate(B);
303ab8c8da6SKostya Kortchinsky     }
304ab8c8da6SKostya Kortchinsky   }
305ab8c8da6SKostya Kortchinsky };
306ab8c8da6SKostya Kortchinsky 
307ab8c8da6SKostya Kortchinsky } // namespace scudo
308ab8c8da6SKostya Kortchinsky 
309ab8c8da6SKostya Kortchinsky #endif // SCUDO_QUARANTINE_H_
310