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