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