xref: /aosp_15_r20/external/scudo/standalone/primary64.h (revision 76559068c068bd27e82aff38fac3bfc865233bca)
1*76559068SAndroid Build Coastguard Worker //===-- primary64.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_PRIMARY64_H_
10*76559068SAndroid Build Coastguard Worker #define SCUDO_PRIMARY64_H_
11*76559068SAndroid Build Coastguard Worker 
12*76559068SAndroid Build Coastguard Worker #include "allocator_common.h"
13*76559068SAndroid Build Coastguard Worker #include "bytemap.h"
14*76559068SAndroid Build Coastguard Worker #include "common.h"
15*76559068SAndroid Build Coastguard Worker #include "condition_variable.h"
16*76559068SAndroid Build Coastguard Worker #include "list.h"
17*76559068SAndroid Build Coastguard Worker #include "local_cache.h"
18*76559068SAndroid Build Coastguard Worker #include "mem_map.h"
19*76559068SAndroid Build Coastguard Worker #include "memtag.h"
20*76559068SAndroid Build Coastguard Worker #include "options.h"
21*76559068SAndroid Build Coastguard Worker #include "release.h"
22*76559068SAndroid Build Coastguard Worker #include "stats.h"
23*76559068SAndroid Build Coastguard Worker #include "string_utils.h"
24*76559068SAndroid Build Coastguard Worker #include "thread_annotations.h"
25*76559068SAndroid Build Coastguard Worker 
26*76559068SAndroid Build Coastguard Worker namespace scudo {
27*76559068SAndroid Build Coastguard Worker 
28*76559068SAndroid Build Coastguard Worker // SizeClassAllocator64 is an allocator tuned for 64-bit address space.
29*76559068SAndroid Build Coastguard Worker //
30*76559068SAndroid Build Coastguard Worker // It starts by reserving NumClasses * 2^RegionSizeLog bytes, equally divided in
31*76559068SAndroid Build Coastguard Worker // Regions, specific to each size class. Note that the base of that mapping is
32*76559068SAndroid Build Coastguard Worker // random (based to the platform specific map() capabilities). If
33*76559068SAndroid Build Coastguard Worker // PrimaryEnableRandomOffset is set, each Region actually starts at a random
34*76559068SAndroid Build Coastguard Worker // offset from its base.
35*76559068SAndroid Build Coastguard Worker //
36*76559068SAndroid Build Coastguard Worker // Regions are mapped incrementally on demand to fulfill allocation requests,
37*76559068SAndroid Build Coastguard Worker // those mappings being split into equally sized Blocks based on the size class
38*76559068SAndroid Build Coastguard Worker // they belong to. The Blocks created are shuffled to prevent predictable
39*76559068SAndroid Build Coastguard Worker // address patterns (the predictability increases with the size of the Blocks).
40*76559068SAndroid Build Coastguard Worker //
41*76559068SAndroid Build Coastguard Worker // The 1st Region (for size class 0) holds the TransferBatches. This is a
42*76559068SAndroid Build Coastguard Worker // structure used to transfer arrays of available pointers from the class size
43*76559068SAndroid Build Coastguard Worker // freelist to the thread specific freelist, and back.
44*76559068SAndroid Build Coastguard Worker //
45*76559068SAndroid Build Coastguard Worker // The memory used by this allocator is never unmapped, but can be partially
46*76559068SAndroid Build Coastguard Worker // released if the platform allows for it.
47*76559068SAndroid Build Coastguard Worker 
48*76559068SAndroid Build Coastguard Worker template <typename Config> class SizeClassAllocator64 {
49*76559068SAndroid Build Coastguard Worker public:
50*76559068SAndroid Build Coastguard Worker   typedef typename Config::CompactPtrT CompactPtrT;
51*76559068SAndroid Build Coastguard Worker   typedef typename Config::SizeClassMap SizeClassMap;
52*76559068SAndroid Build Coastguard Worker   typedef typename Config::ConditionVariableT ConditionVariableT;
53*76559068SAndroid Build Coastguard Worker   static const uptr CompactPtrScale = Config::getCompactPtrScale();
54*76559068SAndroid Build Coastguard Worker   static const uptr RegionSizeLog = Config::getRegionSizeLog();
55*76559068SAndroid Build Coastguard Worker   static const uptr GroupSizeLog = Config::getGroupSizeLog();
56*76559068SAndroid Build Coastguard Worker   static_assert(RegionSizeLog >= GroupSizeLog,
57*76559068SAndroid Build Coastguard Worker                 "Group size shouldn't be greater than the region size");
58*76559068SAndroid Build Coastguard Worker   static const uptr GroupScale = GroupSizeLog - CompactPtrScale;
59*76559068SAndroid Build Coastguard Worker   typedef SizeClassAllocator64<Config> ThisT;
60*76559068SAndroid Build Coastguard Worker   typedef SizeClassAllocatorLocalCache<ThisT> CacheT;
61*76559068SAndroid Build Coastguard Worker   typedef TransferBatch<ThisT> TransferBatchT;
62*76559068SAndroid Build Coastguard Worker   typedef BatchGroup<ThisT> BatchGroupT;
63*76559068SAndroid Build Coastguard Worker 
64*76559068SAndroid Build Coastguard Worker   // BachClass is used to store internal metadata so it needs to be at least as
65*76559068SAndroid Build Coastguard Worker   // large as the largest data structure.
getSizeByClassId(uptr ClassId)66*76559068SAndroid Build Coastguard Worker   static uptr getSizeByClassId(uptr ClassId) {
67*76559068SAndroid Build Coastguard Worker     return (ClassId == SizeClassMap::BatchClassId)
68*76559068SAndroid Build Coastguard Worker                ? roundUp(Max(sizeof(TransferBatchT), sizeof(BatchGroupT)),
69*76559068SAndroid Build Coastguard Worker                          1U << CompactPtrScale)
70*76559068SAndroid Build Coastguard Worker                : SizeClassMap::getSizeByClassId(ClassId);
71*76559068SAndroid Build Coastguard Worker   }
72*76559068SAndroid Build Coastguard Worker 
canAllocate(uptr Size)73*76559068SAndroid Build Coastguard Worker   static bool canAllocate(uptr Size) { return Size <= SizeClassMap::MaxSize; }
74*76559068SAndroid Build Coastguard Worker 
conditionVariableEnabled()75*76559068SAndroid Build Coastguard Worker   static bool conditionVariableEnabled() {
76*76559068SAndroid Build Coastguard Worker     return Config::hasConditionVariableT();
77*76559068SAndroid Build Coastguard Worker   }
78*76559068SAndroid Build Coastguard Worker 
init(s32 ReleaseToOsInterval)79*76559068SAndroid Build Coastguard Worker   void init(s32 ReleaseToOsInterval) NO_THREAD_SAFETY_ANALYSIS {
80*76559068SAndroid Build Coastguard Worker     DCHECK(isAligned(reinterpret_cast<uptr>(this), alignof(ThisT)));
81*76559068SAndroid Build Coastguard Worker 
82*76559068SAndroid Build Coastguard Worker     const uptr PageSize = getPageSizeCached();
83*76559068SAndroid Build Coastguard Worker     const uptr GroupSize = (1UL << GroupSizeLog);
84*76559068SAndroid Build Coastguard Worker     const uptr PagesInGroup = GroupSize / PageSize;
85*76559068SAndroid Build Coastguard Worker     const uptr MinSizeClass = getSizeByClassId(1);
86*76559068SAndroid Build Coastguard Worker     // When trying to release pages back to memory, visiting smaller size
87*76559068SAndroid Build Coastguard Worker     // classes is expensive. Therefore, we only try to release smaller size
88*76559068SAndroid Build Coastguard Worker     // classes when the amount of free blocks goes over a certain threshold (See
89*76559068SAndroid Build Coastguard Worker     // the comment in releaseToOSMaybe() for more details). For example, for
90*76559068SAndroid Build Coastguard Worker     // size class 32, we only do the release when the size of free blocks is
91*76559068SAndroid Build Coastguard Worker     // greater than 97% of pages in a group. However, this may introduce another
92*76559068SAndroid Build Coastguard Worker     // issue that if the number of free blocks is bouncing between 97% ~ 100%.
93*76559068SAndroid Build Coastguard Worker     // Which means we may try many page releases but only release very few of
94*76559068SAndroid Build Coastguard Worker     // them (less than 3% in a group). Even though we have
95*76559068SAndroid Build Coastguard Worker     // `&ReleaseToOsIntervalMs` which slightly reduce the frequency of these
96*76559068SAndroid Build Coastguard Worker     // calls but it will be better to have another guard to mitigate this issue.
97*76559068SAndroid Build Coastguard Worker     //
98*76559068SAndroid Build Coastguard Worker     // Here we add another constraint on the minimum size requirement. The
99*76559068SAndroid Build Coastguard Worker     // constraint is determined by the size of in-use blocks in the minimal size
100*76559068SAndroid Build Coastguard Worker     // class. Take size class 32 as an example,
101*76559068SAndroid Build Coastguard Worker     //
102*76559068SAndroid Build Coastguard Worker     //   +-     one memory group      -+
103*76559068SAndroid Build Coastguard Worker     //   +----------------------+------+
104*76559068SAndroid Build Coastguard Worker     //   |  97% of free blocks  |      |
105*76559068SAndroid Build Coastguard Worker     //   +----------------------+------+
106*76559068SAndroid Build Coastguard Worker     //                           \    /
107*76559068SAndroid Build Coastguard Worker     //                      3% in-use blocks
108*76559068SAndroid Build Coastguard Worker     //
109*76559068SAndroid Build Coastguard Worker     //   * The release size threshold is 97%.
110*76559068SAndroid Build Coastguard Worker     //
111*76559068SAndroid Build Coastguard Worker     // The 3% size in a group is about 7 pages. For two consecutive
112*76559068SAndroid Build Coastguard Worker     // releaseToOSMaybe(), we require the difference between `PushedBlocks`
113*76559068SAndroid Build Coastguard Worker     // should be greater than 7 pages. This mitigates the page releasing
114*76559068SAndroid Build Coastguard Worker     // thrashing which is caused by memory usage bouncing around the threshold.
115*76559068SAndroid Build Coastguard Worker     // The smallest size class takes longest time to do the page release so we
116*76559068SAndroid Build Coastguard Worker     // use its size of in-use blocks as a heuristic.
117*76559068SAndroid Build Coastguard Worker     SmallerBlockReleasePageDelta =
118*76559068SAndroid Build Coastguard Worker         PagesInGroup * (1 + MinSizeClass / 16U) / 100;
119*76559068SAndroid Build Coastguard Worker 
120*76559068SAndroid Build Coastguard Worker     u32 Seed;
121*76559068SAndroid Build Coastguard Worker     const u64 Time = getMonotonicTimeFast();
122*76559068SAndroid Build Coastguard Worker     if (!getRandom(reinterpret_cast<void *>(&Seed), sizeof(Seed)))
123*76559068SAndroid Build Coastguard Worker       Seed = static_cast<u32>(Time ^ (reinterpret_cast<uptr>(&Seed) >> 12));
124*76559068SAndroid Build Coastguard Worker 
125*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++)
126*76559068SAndroid Build Coastguard Worker       getRegionInfo(I)->RandState = getRandomU32(&Seed);
127*76559068SAndroid Build Coastguard Worker 
128*76559068SAndroid Build Coastguard Worker     if (Config::getEnableContiguousRegions()) {
129*76559068SAndroid Build Coastguard Worker       ReservedMemoryT ReservedMemory = {};
130*76559068SAndroid Build Coastguard Worker       // Reserve the space required for the Primary.
131*76559068SAndroid Build Coastguard Worker       CHECK(ReservedMemory.create(/*Addr=*/0U, RegionSize * NumClasses,
132*76559068SAndroid Build Coastguard Worker                                   "scudo:primary_reserve"));
133*76559068SAndroid Build Coastguard Worker       const uptr PrimaryBase = ReservedMemory.getBase();
134*76559068SAndroid Build Coastguard Worker 
135*76559068SAndroid Build Coastguard Worker       for (uptr I = 0; I < NumClasses; I++) {
136*76559068SAndroid Build Coastguard Worker         MemMapT RegionMemMap = ReservedMemory.dispatch(
137*76559068SAndroid Build Coastguard Worker             PrimaryBase + (I << RegionSizeLog), RegionSize);
138*76559068SAndroid Build Coastguard Worker         RegionInfo *Region = getRegionInfo(I);
139*76559068SAndroid Build Coastguard Worker 
140*76559068SAndroid Build Coastguard Worker         initRegion(Region, I, RegionMemMap, Config::getEnableRandomOffset());
141*76559068SAndroid Build Coastguard Worker       }
142*76559068SAndroid Build Coastguard Worker       shuffle(RegionInfoArray, NumClasses, &Seed);
143*76559068SAndroid Build Coastguard Worker     }
144*76559068SAndroid Build Coastguard Worker 
145*76559068SAndroid Build Coastguard Worker     // The binding should be done after region shuffling so that it won't bind
146*76559068SAndroid Build Coastguard Worker     // the FLLock from the wrong region.
147*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++)
148*76559068SAndroid Build Coastguard Worker       getRegionInfo(I)->FLLockCV.bindTestOnly(getRegionInfo(I)->FLLock);
149*76559068SAndroid Build Coastguard Worker 
150*76559068SAndroid Build Coastguard Worker     // The default value in the primary config has the higher priority.
151*76559068SAndroid Build Coastguard Worker     if (Config::getDefaultReleaseToOsIntervalMs() != INT32_MIN)
152*76559068SAndroid Build Coastguard Worker       ReleaseToOsInterval = Config::getDefaultReleaseToOsIntervalMs();
153*76559068SAndroid Build Coastguard Worker     setOption(Option::ReleaseInterval, static_cast<sptr>(ReleaseToOsInterval));
154*76559068SAndroid Build Coastguard Worker   }
155*76559068SAndroid Build Coastguard Worker 
unmapTestOnly()156*76559068SAndroid Build Coastguard Worker   void unmapTestOnly() {
157*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++) {
158*76559068SAndroid Build Coastguard Worker       RegionInfo *Region = getRegionInfo(I);
159*76559068SAndroid Build Coastguard Worker       {
160*76559068SAndroid Build Coastguard Worker         ScopedLock ML(Region->MMLock);
161*76559068SAndroid Build Coastguard Worker         MemMapT MemMap = Region->MemMapInfo.MemMap;
162*76559068SAndroid Build Coastguard Worker         if (MemMap.isAllocated())
163*76559068SAndroid Build Coastguard Worker           MemMap.unmap();
164*76559068SAndroid Build Coastguard Worker       }
165*76559068SAndroid Build Coastguard Worker       *Region = {};
166*76559068SAndroid Build Coastguard Worker     }
167*76559068SAndroid Build Coastguard Worker   }
168*76559068SAndroid Build Coastguard Worker 
169*76559068SAndroid Build Coastguard Worker   // When all blocks are freed, it has to be the same size as `AllocatedUser`.
verifyAllBlocksAreReleasedTestOnly()170*76559068SAndroid Build Coastguard Worker   void verifyAllBlocksAreReleasedTestOnly() {
171*76559068SAndroid Build Coastguard Worker     // `BatchGroup` and `TransferBatch` also use the blocks from BatchClass.
172*76559068SAndroid Build Coastguard Worker     uptr BatchClassUsedInFreeLists = 0;
173*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++) {
174*76559068SAndroid Build Coastguard Worker       // We have to count BatchClassUsedInFreeLists in other regions first.
175*76559068SAndroid Build Coastguard Worker       if (I == SizeClassMap::BatchClassId)
176*76559068SAndroid Build Coastguard Worker         continue;
177*76559068SAndroid Build Coastguard Worker       RegionInfo *Region = getRegionInfo(I);
178*76559068SAndroid Build Coastguard Worker       ScopedLock ML(Region->MMLock);
179*76559068SAndroid Build Coastguard Worker       ScopedLock FL(Region->FLLock);
180*76559068SAndroid Build Coastguard Worker       const uptr BlockSize = getSizeByClassId(I);
181*76559068SAndroid Build Coastguard Worker       uptr TotalBlocks = 0;
182*76559068SAndroid Build Coastguard Worker       for (BatchGroupT &BG : Region->FreeListInfo.BlockList) {
183*76559068SAndroid Build Coastguard Worker         // `BG::Batches` are `TransferBatches`. +1 for `BatchGroup`.
184*76559068SAndroid Build Coastguard Worker         BatchClassUsedInFreeLists += BG.Batches.size() + 1;
185*76559068SAndroid Build Coastguard Worker         for (const auto &It : BG.Batches)
186*76559068SAndroid Build Coastguard Worker           TotalBlocks += It.getCount();
187*76559068SAndroid Build Coastguard Worker       }
188*76559068SAndroid Build Coastguard Worker 
189*76559068SAndroid Build Coastguard Worker       DCHECK_EQ(TotalBlocks, Region->MemMapInfo.AllocatedUser / BlockSize);
190*76559068SAndroid Build Coastguard Worker       DCHECK_EQ(Region->FreeListInfo.PushedBlocks,
191*76559068SAndroid Build Coastguard Worker                 Region->FreeListInfo.PoppedBlocks);
192*76559068SAndroid Build Coastguard Worker     }
193*76559068SAndroid Build Coastguard Worker 
194*76559068SAndroid Build Coastguard Worker     RegionInfo *Region = getRegionInfo(SizeClassMap::BatchClassId);
195*76559068SAndroid Build Coastguard Worker     ScopedLock ML(Region->MMLock);
196*76559068SAndroid Build Coastguard Worker     ScopedLock FL(Region->FLLock);
197*76559068SAndroid Build Coastguard Worker     const uptr BlockSize = getSizeByClassId(SizeClassMap::BatchClassId);
198*76559068SAndroid Build Coastguard Worker     uptr TotalBlocks = 0;
199*76559068SAndroid Build Coastguard Worker     for (BatchGroupT &BG : Region->FreeListInfo.BlockList) {
200*76559068SAndroid Build Coastguard Worker       if (LIKELY(!BG.Batches.empty())) {
201*76559068SAndroid Build Coastguard Worker         for (const auto &It : BG.Batches)
202*76559068SAndroid Build Coastguard Worker           TotalBlocks += It.getCount();
203*76559068SAndroid Build Coastguard Worker       } else {
204*76559068SAndroid Build Coastguard Worker         // `BatchGroup` with empty freelist doesn't have `TransferBatch` record
205*76559068SAndroid Build Coastguard Worker         // itself.
206*76559068SAndroid Build Coastguard Worker         ++TotalBlocks;
207*76559068SAndroid Build Coastguard Worker       }
208*76559068SAndroid Build Coastguard Worker     }
209*76559068SAndroid Build Coastguard Worker     DCHECK_EQ(TotalBlocks + BatchClassUsedInFreeLists,
210*76559068SAndroid Build Coastguard Worker               Region->MemMapInfo.AllocatedUser / BlockSize);
211*76559068SAndroid Build Coastguard Worker     DCHECK_GE(Region->FreeListInfo.PoppedBlocks,
212*76559068SAndroid Build Coastguard Worker               Region->FreeListInfo.PushedBlocks);
213*76559068SAndroid Build Coastguard Worker     const uptr BlocksInUse =
214*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.PoppedBlocks - Region->FreeListInfo.PushedBlocks;
215*76559068SAndroid Build Coastguard Worker     DCHECK_EQ(BlocksInUse, BatchClassUsedInFreeLists);
216*76559068SAndroid Build Coastguard Worker   }
217*76559068SAndroid Build Coastguard Worker 
popBlocks(CacheT * C,uptr ClassId,CompactPtrT * ToArray,const u16 MaxBlockCount)218*76559068SAndroid Build Coastguard Worker   u16 popBlocks(CacheT *C, uptr ClassId, CompactPtrT *ToArray,
219*76559068SAndroid Build Coastguard Worker                 const u16 MaxBlockCount) {
220*76559068SAndroid Build Coastguard Worker     DCHECK_LT(ClassId, NumClasses);
221*76559068SAndroid Build Coastguard Worker     RegionInfo *Region = getRegionInfo(ClassId);
222*76559068SAndroid Build Coastguard Worker     u16 PopCount = 0;
223*76559068SAndroid Build Coastguard Worker 
224*76559068SAndroid Build Coastguard Worker     {
225*76559068SAndroid Build Coastguard Worker       ScopedLock L(Region->FLLock);
226*76559068SAndroid Build Coastguard Worker       PopCount = popBlocksImpl(C, ClassId, Region, ToArray, MaxBlockCount);
227*76559068SAndroid Build Coastguard Worker       if (PopCount != 0U)
228*76559068SAndroid Build Coastguard Worker         return PopCount;
229*76559068SAndroid Build Coastguard Worker     }
230*76559068SAndroid Build Coastguard Worker 
231*76559068SAndroid Build Coastguard Worker     bool ReportRegionExhausted = false;
232*76559068SAndroid Build Coastguard Worker 
233*76559068SAndroid Build Coastguard Worker     if (conditionVariableEnabled()) {
234*76559068SAndroid Build Coastguard Worker       PopCount = popBlocksWithCV(C, ClassId, Region, ToArray, MaxBlockCount,
235*76559068SAndroid Build Coastguard Worker                                  ReportRegionExhausted);
236*76559068SAndroid Build Coastguard Worker     } else {
237*76559068SAndroid Build Coastguard Worker       while (true) {
238*76559068SAndroid Build Coastguard Worker         // When two threads compete for `Region->MMLock`, we only want one of
239*76559068SAndroid Build Coastguard Worker         // them to call populateFreeListAndPopBlocks(). To avoid both of them
240*76559068SAndroid Build Coastguard Worker         // doing that, always check the freelist before mapping new pages.
241*76559068SAndroid Build Coastguard Worker         ScopedLock ML(Region->MMLock);
242*76559068SAndroid Build Coastguard Worker         {
243*76559068SAndroid Build Coastguard Worker           ScopedLock FL(Region->FLLock);
244*76559068SAndroid Build Coastguard Worker           PopCount = popBlocksImpl(C, ClassId, Region, ToArray, MaxBlockCount);
245*76559068SAndroid Build Coastguard Worker           if (PopCount != 0U)
246*76559068SAndroid Build Coastguard Worker             return PopCount;
247*76559068SAndroid Build Coastguard Worker         }
248*76559068SAndroid Build Coastguard Worker 
249*76559068SAndroid Build Coastguard Worker         const bool RegionIsExhausted = Region->Exhausted;
250*76559068SAndroid Build Coastguard Worker         if (!RegionIsExhausted) {
251*76559068SAndroid Build Coastguard Worker           PopCount = populateFreeListAndPopBlocks(C, ClassId, Region, ToArray,
252*76559068SAndroid Build Coastguard Worker                                                   MaxBlockCount);
253*76559068SAndroid Build Coastguard Worker         }
254*76559068SAndroid Build Coastguard Worker         ReportRegionExhausted = !RegionIsExhausted && Region->Exhausted;
255*76559068SAndroid Build Coastguard Worker         break;
256*76559068SAndroid Build Coastguard Worker       }
257*76559068SAndroid Build Coastguard Worker     }
258*76559068SAndroid Build Coastguard Worker 
259*76559068SAndroid Build Coastguard Worker     if (UNLIKELY(ReportRegionExhausted)) {
260*76559068SAndroid Build Coastguard Worker       Printf("Can't populate more pages for size class %zu.\n",
261*76559068SAndroid Build Coastguard Worker              getSizeByClassId(ClassId));
262*76559068SAndroid Build Coastguard Worker 
263*76559068SAndroid Build Coastguard Worker       // Theoretically, BatchClass shouldn't be used up. Abort immediately  when
264*76559068SAndroid Build Coastguard Worker       // it happens.
265*76559068SAndroid Build Coastguard Worker       if (ClassId == SizeClassMap::BatchClassId)
266*76559068SAndroid Build Coastguard Worker         reportOutOfBatchClass();
267*76559068SAndroid Build Coastguard Worker     }
268*76559068SAndroid Build Coastguard Worker 
269*76559068SAndroid Build Coastguard Worker     return PopCount;
270*76559068SAndroid Build Coastguard Worker   }
271*76559068SAndroid Build Coastguard Worker 
272*76559068SAndroid Build Coastguard Worker   // Push the array of free blocks to the designated batch group.
pushBlocks(CacheT * C,uptr ClassId,CompactPtrT * Array,u32 Size)273*76559068SAndroid Build Coastguard Worker   void pushBlocks(CacheT *C, uptr ClassId, CompactPtrT *Array, u32 Size) {
274*76559068SAndroid Build Coastguard Worker     DCHECK_LT(ClassId, NumClasses);
275*76559068SAndroid Build Coastguard Worker     DCHECK_GT(Size, 0);
276*76559068SAndroid Build Coastguard Worker 
277*76559068SAndroid Build Coastguard Worker     RegionInfo *Region = getRegionInfo(ClassId);
278*76559068SAndroid Build Coastguard Worker     if (ClassId == SizeClassMap::BatchClassId) {
279*76559068SAndroid Build Coastguard Worker       ScopedLock L(Region->FLLock);
280*76559068SAndroid Build Coastguard Worker       pushBatchClassBlocks(Region, Array, Size);
281*76559068SAndroid Build Coastguard Worker       if (conditionVariableEnabled())
282*76559068SAndroid Build Coastguard Worker         Region->FLLockCV.notifyAll(Region->FLLock);
283*76559068SAndroid Build Coastguard Worker       return;
284*76559068SAndroid Build Coastguard Worker     }
285*76559068SAndroid Build Coastguard Worker 
286*76559068SAndroid Build Coastguard Worker     // TODO(chiahungduan): Consider not doing grouping if the group size is not
287*76559068SAndroid Build Coastguard Worker     // greater than the block size with a certain scale.
288*76559068SAndroid Build Coastguard Worker 
289*76559068SAndroid Build Coastguard Worker     bool SameGroup = true;
290*76559068SAndroid Build Coastguard Worker     if (GroupSizeLog < RegionSizeLog) {
291*76559068SAndroid Build Coastguard Worker       // Sort the blocks so that blocks belonging to the same group can be
292*76559068SAndroid Build Coastguard Worker       // pushed together.
293*76559068SAndroid Build Coastguard Worker       for (u32 I = 1; I < Size; ++I) {
294*76559068SAndroid Build Coastguard Worker         if (compactPtrGroup(Array[I - 1]) != compactPtrGroup(Array[I]))
295*76559068SAndroid Build Coastguard Worker           SameGroup = false;
296*76559068SAndroid Build Coastguard Worker         CompactPtrT Cur = Array[I];
297*76559068SAndroid Build Coastguard Worker         u32 J = I;
298*76559068SAndroid Build Coastguard Worker         while (J > 0 && compactPtrGroup(Cur) < compactPtrGroup(Array[J - 1])) {
299*76559068SAndroid Build Coastguard Worker           Array[J] = Array[J - 1];
300*76559068SAndroid Build Coastguard Worker           --J;
301*76559068SAndroid Build Coastguard Worker         }
302*76559068SAndroid Build Coastguard Worker         Array[J] = Cur;
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       ScopedLock L(Region->FLLock);
308*76559068SAndroid Build Coastguard Worker       pushBlocksImpl(C, ClassId, Region, Array, Size, SameGroup);
309*76559068SAndroid Build Coastguard Worker       if (conditionVariableEnabled())
310*76559068SAndroid Build Coastguard Worker         Region->FLLockCV.notifyAll(Region->FLLock);
311*76559068SAndroid Build Coastguard Worker     }
312*76559068SAndroid Build Coastguard Worker   }
313*76559068SAndroid Build Coastguard Worker 
disable()314*76559068SAndroid Build Coastguard Worker   void disable() NO_THREAD_SAFETY_ANALYSIS {
315*76559068SAndroid Build Coastguard Worker     // The BatchClassId must be locked last since other classes can use it.
316*76559068SAndroid Build Coastguard Worker     for (sptr I = static_cast<sptr>(NumClasses) - 1; I >= 0; I--) {
317*76559068SAndroid Build Coastguard Worker       if (static_cast<uptr>(I) == SizeClassMap::BatchClassId)
318*76559068SAndroid Build Coastguard Worker         continue;
319*76559068SAndroid Build Coastguard Worker       getRegionInfo(static_cast<uptr>(I))->MMLock.lock();
320*76559068SAndroid Build Coastguard Worker       getRegionInfo(static_cast<uptr>(I))->FLLock.lock();
321*76559068SAndroid Build Coastguard Worker     }
322*76559068SAndroid Build Coastguard Worker     getRegionInfo(SizeClassMap::BatchClassId)->MMLock.lock();
323*76559068SAndroid Build Coastguard Worker     getRegionInfo(SizeClassMap::BatchClassId)->FLLock.lock();
324*76559068SAndroid Build Coastguard Worker   }
325*76559068SAndroid Build Coastguard Worker 
enable()326*76559068SAndroid Build Coastguard Worker   void enable() NO_THREAD_SAFETY_ANALYSIS {
327*76559068SAndroid Build Coastguard Worker     getRegionInfo(SizeClassMap::BatchClassId)->FLLock.unlock();
328*76559068SAndroid Build Coastguard Worker     getRegionInfo(SizeClassMap::BatchClassId)->MMLock.unlock();
329*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++) {
330*76559068SAndroid Build Coastguard Worker       if (I == SizeClassMap::BatchClassId)
331*76559068SAndroid Build Coastguard Worker         continue;
332*76559068SAndroid Build Coastguard Worker       getRegionInfo(I)->FLLock.unlock();
333*76559068SAndroid Build Coastguard Worker       getRegionInfo(I)->MMLock.unlock();
334*76559068SAndroid Build Coastguard Worker     }
335*76559068SAndroid Build Coastguard Worker   }
336*76559068SAndroid Build Coastguard Worker 
iterateOverBlocks(F Callback)337*76559068SAndroid Build Coastguard Worker   template <typename F> void iterateOverBlocks(F Callback) {
338*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++) {
339*76559068SAndroid Build Coastguard Worker       if (I == SizeClassMap::BatchClassId)
340*76559068SAndroid Build Coastguard Worker         continue;
341*76559068SAndroid Build Coastguard Worker       RegionInfo *Region = getRegionInfo(I);
342*76559068SAndroid Build Coastguard Worker       // TODO: The call of `iterateOverBlocks` requires disabling
343*76559068SAndroid Build Coastguard Worker       // SizeClassAllocator64. We may consider locking each region on demand
344*76559068SAndroid Build Coastguard Worker       // only.
345*76559068SAndroid Build Coastguard Worker       Region->FLLock.assertHeld();
346*76559068SAndroid Build Coastguard Worker       Region->MMLock.assertHeld();
347*76559068SAndroid Build Coastguard Worker       const uptr BlockSize = getSizeByClassId(I);
348*76559068SAndroid Build Coastguard Worker       const uptr From = Region->RegionBeg;
349*76559068SAndroid Build Coastguard Worker       const uptr To = From + Region->MemMapInfo.AllocatedUser;
350*76559068SAndroid Build Coastguard Worker       for (uptr Block = From; Block < To; Block += BlockSize)
351*76559068SAndroid Build Coastguard Worker         Callback(Block);
352*76559068SAndroid Build Coastguard Worker     }
353*76559068SAndroid Build Coastguard Worker   }
354*76559068SAndroid Build Coastguard Worker 
getStats(ScopedString * Str)355*76559068SAndroid Build Coastguard Worker   void getStats(ScopedString *Str) {
356*76559068SAndroid Build Coastguard Worker     // TODO(kostyak): get the RSS per region.
357*76559068SAndroid Build Coastguard Worker     uptr TotalMapped = 0;
358*76559068SAndroid Build Coastguard Worker     uptr PoppedBlocks = 0;
359*76559068SAndroid Build Coastguard Worker     uptr PushedBlocks = 0;
360*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++) {
361*76559068SAndroid Build Coastguard Worker       RegionInfo *Region = getRegionInfo(I);
362*76559068SAndroid Build Coastguard Worker       {
363*76559068SAndroid Build Coastguard Worker         ScopedLock L(Region->MMLock);
364*76559068SAndroid Build Coastguard Worker         TotalMapped += Region->MemMapInfo.MappedUser;
365*76559068SAndroid Build Coastguard Worker       }
366*76559068SAndroid Build Coastguard Worker       {
367*76559068SAndroid Build Coastguard Worker         ScopedLock L(Region->FLLock);
368*76559068SAndroid Build Coastguard Worker         PoppedBlocks += Region->FreeListInfo.PoppedBlocks;
369*76559068SAndroid Build Coastguard Worker         PushedBlocks += Region->FreeListInfo.PushedBlocks;
370*76559068SAndroid Build Coastguard Worker       }
371*76559068SAndroid Build Coastguard Worker     }
372*76559068SAndroid Build Coastguard Worker     const s32 IntervalMs = atomic_load_relaxed(&ReleaseToOsIntervalMs);
373*76559068SAndroid Build Coastguard Worker     Str->append("Stats: SizeClassAllocator64: %zuM mapped (%uM rss) in %zu "
374*76559068SAndroid Build Coastguard Worker                 "allocations; remains %zu; ReleaseToOsIntervalMs = %d\n",
375*76559068SAndroid Build Coastguard Worker                 TotalMapped >> 20, 0U, PoppedBlocks,
376*76559068SAndroid Build Coastguard Worker                 PoppedBlocks - PushedBlocks, IntervalMs >= 0 ? IntervalMs : -1);
377*76559068SAndroid Build Coastguard Worker 
378*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++) {
379*76559068SAndroid Build Coastguard Worker       RegionInfo *Region = getRegionInfo(I);
380*76559068SAndroid Build Coastguard Worker       ScopedLock L1(Region->MMLock);
381*76559068SAndroid Build Coastguard Worker       ScopedLock L2(Region->FLLock);
382*76559068SAndroid Build Coastguard Worker       getStats(Str, I, Region);
383*76559068SAndroid Build Coastguard Worker     }
384*76559068SAndroid Build Coastguard Worker   }
385*76559068SAndroid Build Coastguard Worker 
getFragmentationInfo(ScopedString * Str)386*76559068SAndroid Build Coastguard Worker   void getFragmentationInfo(ScopedString *Str) {
387*76559068SAndroid Build Coastguard Worker     Str->append(
388*76559068SAndroid Build Coastguard Worker         "Fragmentation Stats: SizeClassAllocator64: page size = %zu bytes\n",
389*76559068SAndroid Build Coastguard Worker         getPageSizeCached());
390*76559068SAndroid Build Coastguard Worker 
391*76559068SAndroid Build Coastguard Worker     for (uptr I = 1; I < NumClasses; I++) {
392*76559068SAndroid Build Coastguard Worker       RegionInfo *Region = getRegionInfo(I);
393*76559068SAndroid Build Coastguard Worker       ScopedLock L(Region->MMLock);
394*76559068SAndroid Build Coastguard Worker       getRegionFragmentationInfo(Region, I, Str);
395*76559068SAndroid Build Coastguard Worker     }
396*76559068SAndroid Build Coastguard Worker   }
397*76559068SAndroid Build Coastguard Worker 
getMemoryGroupFragmentationInfo(ScopedString * Str)398*76559068SAndroid Build Coastguard Worker   void getMemoryGroupFragmentationInfo(ScopedString *Str) {
399*76559068SAndroid Build Coastguard Worker     Str->append(
400*76559068SAndroid Build Coastguard Worker         "Fragmentation Stats: SizeClassAllocator64: page size = %zu bytes\n",
401*76559068SAndroid Build Coastguard Worker         getPageSizeCached());
402*76559068SAndroid Build Coastguard Worker 
403*76559068SAndroid Build Coastguard Worker     for (uptr I = 1; I < NumClasses; I++) {
404*76559068SAndroid Build Coastguard Worker       RegionInfo *Region = getRegionInfo(I);
405*76559068SAndroid Build Coastguard Worker       ScopedLock L(Region->MMLock);
406*76559068SAndroid Build Coastguard Worker       getMemoryGroupFragmentationInfoInRegion(Region, I, Str);
407*76559068SAndroid Build Coastguard Worker     }
408*76559068SAndroid Build Coastguard Worker   }
409*76559068SAndroid Build Coastguard Worker 
setOption(Option O,sptr Value)410*76559068SAndroid Build Coastguard Worker   bool setOption(Option O, sptr Value) {
411*76559068SAndroid Build Coastguard Worker     if (O == Option::ReleaseInterval) {
412*76559068SAndroid Build Coastguard Worker       const s32 Interval = Max(
413*76559068SAndroid Build Coastguard Worker           Min(static_cast<s32>(Value), Config::getMaxReleaseToOsIntervalMs()),
414*76559068SAndroid Build Coastguard Worker           Config::getMinReleaseToOsIntervalMs());
415*76559068SAndroid Build Coastguard Worker       atomic_store_relaxed(&ReleaseToOsIntervalMs, Interval);
416*76559068SAndroid Build Coastguard Worker       return true;
417*76559068SAndroid Build Coastguard Worker     }
418*76559068SAndroid Build Coastguard Worker     // Not supported by the Primary, but not an error either.
419*76559068SAndroid Build Coastguard Worker     return true;
420*76559068SAndroid Build Coastguard Worker   }
421*76559068SAndroid Build Coastguard Worker 
tryReleaseToOS(uptr ClassId,ReleaseToOS ReleaseType)422*76559068SAndroid Build Coastguard Worker   uptr tryReleaseToOS(uptr ClassId, ReleaseToOS ReleaseType) {
423*76559068SAndroid Build Coastguard Worker     RegionInfo *Region = getRegionInfo(ClassId);
424*76559068SAndroid Build Coastguard Worker     // Note that the tryLock() may fail spuriously, given that it should rarely
425*76559068SAndroid Build Coastguard Worker     // happen and page releasing is fine to skip, we don't take certain
426*76559068SAndroid Build Coastguard Worker     // approaches to ensure one page release is done.
427*76559068SAndroid Build Coastguard Worker     if (Region->MMLock.tryLock()) {
428*76559068SAndroid Build Coastguard Worker       uptr BytesReleased = releaseToOSMaybe(Region, ClassId, ReleaseType);
429*76559068SAndroid Build Coastguard Worker       Region->MMLock.unlock();
430*76559068SAndroid Build Coastguard Worker       return BytesReleased;
431*76559068SAndroid Build Coastguard Worker     }
432*76559068SAndroid Build Coastguard Worker     return 0;
433*76559068SAndroid Build Coastguard Worker   }
434*76559068SAndroid Build Coastguard Worker 
releaseToOS(ReleaseToOS ReleaseType)435*76559068SAndroid Build Coastguard Worker   uptr releaseToOS(ReleaseToOS ReleaseType) {
436*76559068SAndroid Build Coastguard Worker     uptr TotalReleasedBytes = 0;
437*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < NumClasses; I++) {
438*76559068SAndroid Build Coastguard Worker       if (I == SizeClassMap::BatchClassId)
439*76559068SAndroid Build Coastguard Worker         continue;
440*76559068SAndroid Build Coastguard Worker       RegionInfo *Region = getRegionInfo(I);
441*76559068SAndroid Build Coastguard Worker       ScopedLock L(Region->MMLock);
442*76559068SAndroid Build Coastguard Worker       TotalReleasedBytes += releaseToOSMaybe(Region, I, ReleaseType);
443*76559068SAndroid Build Coastguard Worker     }
444*76559068SAndroid Build Coastguard Worker     return TotalReleasedBytes;
445*76559068SAndroid Build Coastguard Worker   }
446*76559068SAndroid Build Coastguard Worker 
getRegionInfoArrayAddress()447*76559068SAndroid Build Coastguard Worker   const char *getRegionInfoArrayAddress() const {
448*76559068SAndroid Build Coastguard Worker     return reinterpret_cast<const char *>(RegionInfoArray);
449*76559068SAndroid Build Coastguard Worker   }
450*76559068SAndroid Build Coastguard Worker 
getRegionInfoArraySize()451*76559068SAndroid Build Coastguard Worker   static uptr getRegionInfoArraySize() { return sizeof(RegionInfoArray); }
452*76559068SAndroid Build Coastguard Worker 
getCompactPtrBaseByClassId(uptr ClassId)453*76559068SAndroid Build Coastguard Worker   uptr getCompactPtrBaseByClassId(uptr ClassId) {
454*76559068SAndroid Build Coastguard Worker     return getRegionInfo(ClassId)->RegionBeg;
455*76559068SAndroid Build Coastguard Worker   }
456*76559068SAndroid Build Coastguard Worker 
compactPtr(uptr ClassId,uptr Ptr)457*76559068SAndroid Build Coastguard Worker   CompactPtrT compactPtr(uptr ClassId, uptr Ptr) {
458*76559068SAndroid Build Coastguard Worker     DCHECK_LE(ClassId, SizeClassMap::LargestClassId);
459*76559068SAndroid Build Coastguard Worker     return compactPtrInternal(getCompactPtrBaseByClassId(ClassId), Ptr);
460*76559068SAndroid Build Coastguard Worker   }
461*76559068SAndroid Build Coastguard Worker 
decompactPtr(uptr ClassId,CompactPtrT CompactPtr)462*76559068SAndroid Build Coastguard Worker   void *decompactPtr(uptr ClassId, CompactPtrT CompactPtr) {
463*76559068SAndroid Build Coastguard Worker     DCHECK_LE(ClassId, SizeClassMap::LargestClassId);
464*76559068SAndroid Build Coastguard Worker     return reinterpret_cast<void *>(
465*76559068SAndroid Build Coastguard Worker         decompactPtrInternal(getCompactPtrBaseByClassId(ClassId), CompactPtr));
466*76559068SAndroid Build Coastguard Worker   }
467*76559068SAndroid Build Coastguard Worker 
findNearestBlock(const char * RegionInfoData,uptr Ptr)468*76559068SAndroid Build Coastguard Worker   static BlockInfo findNearestBlock(const char *RegionInfoData,
469*76559068SAndroid Build Coastguard Worker                                     uptr Ptr) NO_THREAD_SAFETY_ANALYSIS {
470*76559068SAndroid Build Coastguard Worker     const RegionInfo *RegionInfoArray =
471*76559068SAndroid Build Coastguard Worker         reinterpret_cast<const RegionInfo *>(RegionInfoData);
472*76559068SAndroid Build Coastguard Worker 
473*76559068SAndroid Build Coastguard Worker     uptr ClassId;
474*76559068SAndroid Build Coastguard Worker     uptr MinDistance = -1UL;
475*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I != NumClasses; ++I) {
476*76559068SAndroid Build Coastguard Worker       if (I == SizeClassMap::BatchClassId)
477*76559068SAndroid Build Coastguard Worker         continue;
478*76559068SAndroid Build Coastguard Worker       uptr Begin = RegionInfoArray[I].RegionBeg;
479*76559068SAndroid Build Coastguard Worker       // TODO(chiahungduan): In fact, We need to lock the RegionInfo::MMLock.
480*76559068SAndroid Build Coastguard Worker       // However, the RegionInfoData is passed with const qualifier and lock the
481*76559068SAndroid Build Coastguard Worker       // mutex requires modifying RegionInfoData, which means we need to remove
482*76559068SAndroid Build Coastguard Worker       // the const qualifier. This may lead to another undefined behavior (The
483*76559068SAndroid Build Coastguard Worker       // first one is accessing `AllocatedUser` without locking. It's better to
484*76559068SAndroid Build Coastguard Worker       // pass `RegionInfoData` as `void *` then we can lock the mutex properly.
485*76559068SAndroid Build Coastguard Worker       uptr End = Begin + RegionInfoArray[I].MemMapInfo.AllocatedUser;
486*76559068SAndroid Build Coastguard Worker       if (Begin > End || End - Begin < SizeClassMap::getSizeByClassId(I))
487*76559068SAndroid Build Coastguard Worker         continue;
488*76559068SAndroid Build Coastguard Worker       uptr RegionDistance;
489*76559068SAndroid Build Coastguard Worker       if (Begin <= Ptr) {
490*76559068SAndroid Build Coastguard Worker         if (Ptr < End)
491*76559068SAndroid Build Coastguard Worker           RegionDistance = 0;
492*76559068SAndroid Build Coastguard Worker         else
493*76559068SAndroid Build Coastguard Worker           RegionDistance = Ptr - End;
494*76559068SAndroid Build Coastguard Worker       } else {
495*76559068SAndroid Build Coastguard Worker         RegionDistance = Begin - Ptr;
496*76559068SAndroid Build Coastguard Worker       }
497*76559068SAndroid Build Coastguard Worker 
498*76559068SAndroid Build Coastguard Worker       if (RegionDistance < MinDistance) {
499*76559068SAndroid Build Coastguard Worker         MinDistance = RegionDistance;
500*76559068SAndroid Build Coastguard Worker         ClassId = I;
501*76559068SAndroid Build Coastguard Worker       }
502*76559068SAndroid Build Coastguard Worker     }
503*76559068SAndroid Build Coastguard Worker 
504*76559068SAndroid Build Coastguard Worker     BlockInfo B = {};
505*76559068SAndroid Build Coastguard Worker     if (MinDistance <= 8192) {
506*76559068SAndroid Build Coastguard Worker       B.RegionBegin = RegionInfoArray[ClassId].RegionBeg;
507*76559068SAndroid Build Coastguard Worker       B.RegionEnd =
508*76559068SAndroid Build Coastguard Worker           B.RegionBegin + RegionInfoArray[ClassId].MemMapInfo.AllocatedUser;
509*76559068SAndroid Build Coastguard Worker       B.BlockSize = SizeClassMap::getSizeByClassId(ClassId);
510*76559068SAndroid Build Coastguard Worker       B.BlockBegin =
511*76559068SAndroid Build Coastguard Worker           B.RegionBegin + uptr(sptr(Ptr - B.RegionBegin) / sptr(B.BlockSize) *
512*76559068SAndroid Build Coastguard Worker                                sptr(B.BlockSize));
513*76559068SAndroid Build Coastguard Worker       while (B.BlockBegin < B.RegionBegin)
514*76559068SAndroid Build Coastguard Worker         B.BlockBegin += B.BlockSize;
515*76559068SAndroid Build Coastguard Worker       while (B.RegionEnd < B.BlockBegin + B.BlockSize)
516*76559068SAndroid Build Coastguard Worker         B.BlockBegin -= B.BlockSize;
517*76559068SAndroid Build Coastguard Worker     }
518*76559068SAndroid Build Coastguard Worker     return B;
519*76559068SAndroid Build Coastguard Worker   }
520*76559068SAndroid Build Coastguard Worker 
521*76559068SAndroid Build Coastguard Worker   AtomicOptions Options;
522*76559068SAndroid Build Coastguard Worker 
523*76559068SAndroid Build Coastguard Worker private:
524*76559068SAndroid Build Coastguard Worker   static const uptr RegionSize = 1UL << RegionSizeLog;
525*76559068SAndroid Build Coastguard Worker   static const uptr NumClasses = SizeClassMap::NumClasses;
526*76559068SAndroid Build Coastguard Worker 
527*76559068SAndroid Build Coastguard Worker   static const uptr MapSizeIncrement = Config::getMapSizeIncrement();
528*76559068SAndroid Build Coastguard Worker   // Fill at most this number of batches from the newly map'd memory.
529*76559068SAndroid Build Coastguard Worker   static const u32 MaxNumBatches = SCUDO_ANDROID ? 4U : 8U;
530*76559068SAndroid Build Coastguard Worker 
531*76559068SAndroid Build Coastguard Worker   struct ReleaseToOsInfo {
532*76559068SAndroid Build Coastguard Worker     uptr BytesInFreeListAtLastCheckpoint;
533*76559068SAndroid Build Coastguard Worker     uptr RangesReleased;
534*76559068SAndroid Build Coastguard Worker     uptr LastReleasedBytes;
535*76559068SAndroid Build Coastguard Worker     // The minimum size of pushed blocks to trigger page release.
536*76559068SAndroid Build Coastguard Worker     uptr TryReleaseThreshold;
537*76559068SAndroid Build Coastguard Worker     // The number of bytes not triggering `releaseToOSMaybe()` because of
538*76559068SAndroid Build Coastguard Worker     // the length of release interval.
539*76559068SAndroid Build Coastguard Worker     uptr PendingPushedBytesDelta;
540*76559068SAndroid Build Coastguard Worker     u64 LastReleaseAtNs;
541*76559068SAndroid Build Coastguard Worker   };
542*76559068SAndroid Build Coastguard Worker 
543*76559068SAndroid Build Coastguard Worker   struct BlocksInfo {
544*76559068SAndroid Build Coastguard Worker     SinglyLinkedList<BatchGroupT> BlockList = {};
545*76559068SAndroid Build Coastguard Worker     uptr PoppedBlocks = 0;
546*76559068SAndroid Build Coastguard Worker     uptr PushedBlocks = 0;
547*76559068SAndroid Build Coastguard Worker   };
548*76559068SAndroid Build Coastguard Worker 
549*76559068SAndroid Build Coastguard Worker   struct PagesInfo {
550*76559068SAndroid Build Coastguard Worker     MemMapT MemMap = {};
551*76559068SAndroid Build Coastguard Worker     // Bytes mapped for user memory.
552*76559068SAndroid Build Coastguard Worker     uptr MappedUser = 0;
553*76559068SAndroid Build Coastguard Worker     // Bytes allocated for user memory.
554*76559068SAndroid Build Coastguard Worker     uptr AllocatedUser = 0;
555*76559068SAndroid Build Coastguard Worker   };
556*76559068SAndroid Build Coastguard Worker 
557*76559068SAndroid Build Coastguard Worker   struct UnpaddedRegionInfo {
558*76559068SAndroid Build Coastguard Worker     // Mutex for operations on freelist
559*76559068SAndroid Build Coastguard Worker     HybridMutex FLLock;
560*76559068SAndroid Build Coastguard Worker     ConditionVariableT FLLockCV GUARDED_BY(FLLock);
561*76559068SAndroid Build Coastguard Worker     // Mutex for memmap operations
562*76559068SAndroid Build Coastguard Worker     HybridMutex MMLock ACQUIRED_BEFORE(FLLock);
563*76559068SAndroid Build Coastguard Worker     // `RegionBeg` is initialized before thread creation and won't be changed.
564*76559068SAndroid Build Coastguard Worker     uptr RegionBeg = 0;
565*76559068SAndroid Build Coastguard Worker     u32 RandState GUARDED_BY(MMLock) = 0;
566*76559068SAndroid Build Coastguard Worker     BlocksInfo FreeListInfo GUARDED_BY(FLLock);
567*76559068SAndroid Build Coastguard Worker     PagesInfo MemMapInfo GUARDED_BY(MMLock);
568*76559068SAndroid Build Coastguard Worker     ReleaseToOsInfo ReleaseInfo GUARDED_BY(MMLock) = {};
569*76559068SAndroid Build Coastguard Worker     bool Exhausted GUARDED_BY(MMLock) = false;
570*76559068SAndroid Build Coastguard Worker     bool isPopulatingFreeList GUARDED_BY(FLLock) = false;
571*76559068SAndroid Build Coastguard Worker   };
572*76559068SAndroid Build Coastguard Worker   struct RegionInfo : UnpaddedRegionInfo {
573*76559068SAndroid Build Coastguard Worker     char Padding[SCUDO_CACHE_LINE_SIZE -
574*76559068SAndroid Build Coastguard Worker                  (sizeof(UnpaddedRegionInfo) % SCUDO_CACHE_LINE_SIZE)] = {};
575*76559068SAndroid Build Coastguard Worker   };
576*76559068SAndroid Build Coastguard Worker   static_assert(sizeof(RegionInfo) % SCUDO_CACHE_LINE_SIZE == 0, "");
577*76559068SAndroid Build Coastguard Worker 
getRegionInfo(uptr ClassId)578*76559068SAndroid Build Coastguard Worker   RegionInfo *getRegionInfo(uptr ClassId) {
579*76559068SAndroid Build Coastguard Worker     DCHECK_LT(ClassId, NumClasses);
580*76559068SAndroid Build Coastguard Worker     return &RegionInfoArray[ClassId];
581*76559068SAndroid Build Coastguard Worker   }
582*76559068SAndroid Build Coastguard Worker 
getRegionBaseByClassId(uptr ClassId)583*76559068SAndroid Build Coastguard Worker   uptr getRegionBaseByClassId(uptr ClassId) {
584*76559068SAndroid Build Coastguard Worker     RegionInfo *Region = getRegionInfo(ClassId);
585*76559068SAndroid Build Coastguard Worker     Region->MMLock.assertHeld();
586*76559068SAndroid Build Coastguard Worker 
587*76559068SAndroid Build Coastguard Worker     if (!Config::getEnableContiguousRegions() &&
588*76559068SAndroid Build Coastguard Worker         !Region->MemMapInfo.MemMap.isAllocated()) {
589*76559068SAndroid Build Coastguard Worker       return 0U;
590*76559068SAndroid Build Coastguard Worker     }
591*76559068SAndroid Build Coastguard Worker     return Region->MemMapInfo.MemMap.getBase();
592*76559068SAndroid Build Coastguard Worker   }
593*76559068SAndroid Build Coastguard Worker 
compactPtrInternal(uptr Base,uptr Ptr)594*76559068SAndroid Build Coastguard Worker   static CompactPtrT compactPtrInternal(uptr Base, uptr Ptr) {
595*76559068SAndroid Build Coastguard Worker     return static_cast<CompactPtrT>((Ptr - Base) >> CompactPtrScale);
596*76559068SAndroid Build Coastguard Worker   }
597*76559068SAndroid Build Coastguard Worker 
decompactPtrInternal(uptr Base,CompactPtrT CompactPtr)598*76559068SAndroid Build Coastguard Worker   static uptr decompactPtrInternal(uptr Base, CompactPtrT CompactPtr) {
599*76559068SAndroid Build Coastguard Worker     return Base + (static_cast<uptr>(CompactPtr) << CompactPtrScale);
600*76559068SAndroid Build Coastguard Worker   }
601*76559068SAndroid Build Coastguard Worker 
compactPtrGroup(CompactPtrT CompactPtr)602*76559068SAndroid Build Coastguard Worker   static uptr compactPtrGroup(CompactPtrT CompactPtr) {
603*76559068SAndroid Build Coastguard Worker     const uptr Mask = (static_cast<uptr>(1) << GroupScale) - 1;
604*76559068SAndroid Build Coastguard Worker     return static_cast<uptr>(CompactPtr) & ~Mask;
605*76559068SAndroid Build Coastguard Worker   }
decompactGroupBase(uptr Base,uptr CompactPtrGroupBase)606*76559068SAndroid Build Coastguard Worker   static uptr decompactGroupBase(uptr Base, uptr CompactPtrGroupBase) {
607*76559068SAndroid Build Coastguard Worker     DCHECK_EQ(CompactPtrGroupBase % (static_cast<uptr>(1) << (GroupScale)), 0U);
608*76559068SAndroid Build Coastguard Worker     return Base + (CompactPtrGroupBase << CompactPtrScale);
609*76559068SAndroid Build Coastguard Worker   }
610*76559068SAndroid Build Coastguard Worker 
isSmallBlock(uptr BlockSize)611*76559068SAndroid Build Coastguard Worker   ALWAYS_INLINE static bool isSmallBlock(uptr BlockSize) {
612*76559068SAndroid Build Coastguard Worker     const uptr PageSize = getPageSizeCached();
613*76559068SAndroid Build Coastguard Worker     return BlockSize < PageSize / 16U;
614*76559068SAndroid Build Coastguard Worker   }
615*76559068SAndroid Build Coastguard Worker 
getMinReleaseAttemptSize(uptr BlockSize)616*76559068SAndroid Build Coastguard Worker   ALWAYS_INLINE uptr getMinReleaseAttemptSize(uptr BlockSize) {
617*76559068SAndroid Build Coastguard Worker     return roundUp(BlockSize, getPageSizeCached());
618*76559068SAndroid Build Coastguard Worker   }
619*76559068SAndroid Build Coastguard Worker 
initRegion(RegionInfo * Region,uptr ClassId,MemMapT MemMap,bool EnableRandomOffset)620*76559068SAndroid Build Coastguard Worker   ALWAYS_INLINE void initRegion(RegionInfo *Region, uptr ClassId,
621*76559068SAndroid Build Coastguard Worker                                 MemMapT MemMap, bool EnableRandomOffset)
622*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock) {
623*76559068SAndroid Build Coastguard Worker     DCHECK(!Region->MemMapInfo.MemMap.isAllocated());
624*76559068SAndroid Build Coastguard Worker     DCHECK(MemMap.isAllocated());
625*76559068SAndroid Build Coastguard Worker 
626*76559068SAndroid Build Coastguard Worker     const uptr PageSize = getPageSizeCached();
627*76559068SAndroid Build Coastguard Worker 
628*76559068SAndroid Build Coastguard Worker     Region->MemMapInfo.MemMap = MemMap;
629*76559068SAndroid Build Coastguard Worker 
630*76559068SAndroid Build Coastguard Worker     Region->RegionBeg = MemMap.getBase();
631*76559068SAndroid Build Coastguard Worker     if (EnableRandomOffset) {
632*76559068SAndroid Build Coastguard Worker       Region->RegionBeg +=
633*76559068SAndroid Build Coastguard Worker           (getRandomModN(&Region->RandState, 16) + 1) * PageSize;
634*76559068SAndroid Build Coastguard Worker     }
635*76559068SAndroid Build Coastguard Worker 
636*76559068SAndroid Build Coastguard Worker     const uptr BlockSize = getSizeByClassId(ClassId);
637*76559068SAndroid Build Coastguard Worker     // Releasing small blocks is expensive, set a higher threshold to avoid
638*76559068SAndroid Build Coastguard Worker     // frequent page releases.
639*76559068SAndroid Build Coastguard Worker     if (isSmallBlock(BlockSize)) {
640*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.TryReleaseThreshold =
641*76559068SAndroid Build Coastguard Worker           PageSize * SmallerBlockReleasePageDelta;
642*76559068SAndroid Build Coastguard Worker     } else {
643*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.TryReleaseThreshold =
644*76559068SAndroid Build Coastguard Worker           getMinReleaseAttemptSize(BlockSize);
645*76559068SAndroid Build Coastguard Worker     }
646*76559068SAndroid Build Coastguard Worker   }
647*76559068SAndroid Build Coastguard Worker 
pushBatchClassBlocks(RegionInfo * Region,CompactPtrT * Array,u32 Size)648*76559068SAndroid Build Coastguard Worker   void pushBatchClassBlocks(RegionInfo *Region, CompactPtrT *Array, u32 Size)
649*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->FLLock) {
650*76559068SAndroid Build Coastguard Worker     DCHECK_EQ(Region, getRegionInfo(SizeClassMap::BatchClassId));
651*76559068SAndroid Build Coastguard Worker 
652*76559068SAndroid Build Coastguard Worker     // Free blocks are recorded by TransferBatch in freelist for all
653*76559068SAndroid Build Coastguard Worker     // size-classes. In addition, TransferBatch is allocated from BatchClassId.
654*76559068SAndroid Build Coastguard Worker     // In order not to use additional block to record the free blocks in
655*76559068SAndroid Build Coastguard Worker     // BatchClassId, they are self-contained. I.e., A TransferBatch records the
656*76559068SAndroid Build Coastguard Worker     // block address of itself. See the figure below:
657*76559068SAndroid Build Coastguard Worker     //
658*76559068SAndroid Build Coastguard Worker     // TransferBatch at 0xABCD
659*76559068SAndroid Build Coastguard Worker     // +----------------------------+
660*76559068SAndroid Build Coastguard Worker     // | Free blocks' addr          |
661*76559068SAndroid Build Coastguard Worker     // | +------+------+------+     |
662*76559068SAndroid Build Coastguard Worker     // | |0xABCD|...   |...   |     |
663*76559068SAndroid Build Coastguard Worker     // | +------+------+------+     |
664*76559068SAndroid Build Coastguard Worker     // +----------------------------+
665*76559068SAndroid Build Coastguard Worker     //
666*76559068SAndroid Build Coastguard Worker     // When we allocate all the free blocks in the TransferBatch, the block used
667*76559068SAndroid Build Coastguard Worker     // by TransferBatch is also free for use. We don't need to recycle the
668*76559068SAndroid Build Coastguard Worker     // TransferBatch. Note that the correctness is maintained by the invariant,
669*76559068SAndroid Build Coastguard Worker     //
670*76559068SAndroid Build Coastguard Worker     //   Each popBlocks() request returns the entire TransferBatch. Returning
671*76559068SAndroid Build Coastguard Worker     //   part of the blocks in a TransferBatch is invalid.
672*76559068SAndroid Build Coastguard Worker     //
673*76559068SAndroid Build Coastguard Worker     // This ensures that TransferBatch won't leak the address itself while it's
674*76559068SAndroid Build Coastguard Worker     // still holding other valid data.
675*76559068SAndroid Build Coastguard Worker     //
676*76559068SAndroid Build Coastguard Worker     // Besides, BatchGroup is also allocated from BatchClassId and has its
677*76559068SAndroid Build Coastguard Worker     // address recorded in the TransferBatch too. To maintain the correctness,
678*76559068SAndroid Build Coastguard Worker     //
679*76559068SAndroid Build Coastguard Worker     //   The address of BatchGroup is always recorded in the last TransferBatch
680*76559068SAndroid Build Coastguard Worker     //   in the freelist (also imply that the freelist should only be
681*76559068SAndroid Build Coastguard Worker     //   updated with push_front). Once the last TransferBatch is popped,
682*76559068SAndroid Build Coastguard Worker     //   the block used by BatchGroup is also free for use.
683*76559068SAndroid Build Coastguard Worker     //
684*76559068SAndroid Build Coastguard Worker     // With this approach, the blocks used by BatchGroup and TransferBatch are
685*76559068SAndroid Build Coastguard Worker     // reusable and don't need additional space for them.
686*76559068SAndroid Build Coastguard Worker 
687*76559068SAndroid Build Coastguard Worker     Region->FreeListInfo.PushedBlocks += Size;
688*76559068SAndroid Build Coastguard Worker     BatchGroupT *BG = Region->FreeListInfo.BlockList.front();
689*76559068SAndroid Build Coastguard Worker 
690*76559068SAndroid Build Coastguard Worker     if (BG == nullptr) {
691*76559068SAndroid Build Coastguard Worker       // Construct `BatchGroup` on the last element.
692*76559068SAndroid Build Coastguard Worker       BG = reinterpret_cast<BatchGroupT *>(
693*76559068SAndroid Build Coastguard Worker           decompactPtr(SizeClassMap::BatchClassId, Array[Size - 1]));
694*76559068SAndroid Build Coastguard Worker       --Size;
695*76559068SAndroid Build Coastguard Worker       BG->Batches.clear();
696*76559068SAndroid Build Coastguard Worker       // BatchClass hasn't enabled memory group. Use `0` to indicate there's no
697*76559068SAndroid Build Coastguard Worker       // memory group here.
698*76559068SAndroid Build Coastguard Worker       BG->CompactPtrGroupBase = 0;
699*76559068SAndroid Build Coastguard Worker       BG->BytesInBGAtLastCheckpoint = 0;
700*76559068SAndroid Build Coastguard Worker       BG->MaxCachedPerBatch =
701*76559068SAndroid Build Coastguard Worker           CacheT::getMaxCached(getSizeByClassId(SizeClassMap::BatchClassId));
702*76559068SAndroid Build Coastguard Worker 
703*76559068SAndroid Build Coastguard Worker       Region->FreeListInfo.BlockList.push_front(BG);
704*76559068SAndroid Build Coastguard Worker     }
705*76559068SAndroid Build Coastguard Worker 
706*76559068SAndroid Build Coastguard Worker     if (UNLIKELY(Size == 0))
707*76559068SAndroid Build Coastguard Worker       return;
708*76559068SAndroid Build Coastguard Worker 
709*76559068SAndroid Build Coastguard Worker     // This happens under 2 cases.
710*76559068SAndroid Build Coastguard Worker     //   1. just allocated a new `BatchGroup`.
711*76559068SAndroid Build Coastguard Worker     //   2. Only 1 block is pushed when the freelist is empty.
712*76559068SAndroid Build Coastguard Worker     if (BG->Batches.empty()) {
713*76559068SAndroid Build Coastguard Worker       // Construct the `TransferBatch` on the last element.
714*76559068SAndroid Build Coastguard Worker       TransferBatchT *TB = reinterpret_cast<TransferBatchT *>(
715*76559068SAndroid Build Coastguard Worker           decompactPtr(SizeClassMap::BatchClassId, Array[Size - 1]));
716*76559068SAndroid Build Coastguard Worker       TB->clear();
717*76559068SAndroid Build Coastguard Worker       // As mentioned above, addresses of `TransferBatch` and `BatchGroup` are
718*76559068SAndroid Build Coastguard Worker       // recorded in the TransferBatch.
719*76559068SAndroid Build Coastguard Worker       TB->add(Array[Size - 1]);
720*76559068SAndroid Build Coastguard Worker       TB->add(
721*76559068SAndroid Build Coastguard Worker           compactPtr(SizeClassMap::BatchClassId, reinterpret_cast<uptr>(BG)));
722*76559068SAndroid Build Coastguard Worker       --Size;
723*76559068SAndroid Build Coastguard Worker       BG->Batches.push_front(TB);
724*76559068SAndroid Build Coastguard Worker     }
725*76559068SAndroid Build Coastguard Worker 
726*76559068SAndroid Build Coastguard Worker     TransferBatchT *CurBatch = BG->Batches.front();
727*76559068SAndroid Build Coastguard Worker     DCHECK_NE(CurBatch, nullptr);
728*76559068SAndroid Build Coastguard Worker 
729*76559068SAndroid Build Coastguard Worker     for (u32 I = 0; I < Size;) {
730*76559068SAndroid Build Coastguard Worker       u16 UnusedSlots =
731*76559068SAndroid Build Coastguard Worker           static_cast<u16>(BG->MaxCachedPerBatch - CurBatch->getCount());
732*76559068SAndroid Build Coastguard Worker       if (UnusedSlots == 0) {
733*76559068SAndroid Build Coastguard Worker         CurBatch = reinterpret_cast<TransferBatchT *>(
734*76559068SAndroid Build Coastguard Worker             decompactPtr(SizeClassMap::BatchClassId, Array[I]));
735*76559068SAndroid Build Coastguard Worker         CurBatch->clear();
736*76559068SAndroid Build Coastguard Worker         // Self-contained
737*76559068SAndroid Build Coastguard Worker         CurBatch->add(Array[I]);
738*76559068SAndroid Build Coastguard Worker         ++I;
739*76559068SAndroid Build Coastguard Worker         // TODO(chiahungduan): Avoid the use of push_back() in `Batches` of
740*76559068SAndroid Build Coastguard Worker         // BatchClassId.
741*76559068SAndroid Build Coastguard Worker         BG->Batches.push_front(CurBatch);
742*76559068SAndroid Build Coastguard Worker         UnusedSlots = static_cast<u16>(BG->MaxCachedPerBatch - 1);
743*76559068SAndroid Build Coastguard Worker       }
744*76559068SAndroid Build Coastguard Worker       // `UnusedSlots` is u16 so the result will be also fit in u16.
745*76559068SAndroid Build Coastguard Worker       const u16 AppendSize = static_cast<u16>(Min<u32>(UnusedSlots, Size - I));
746*76559068SAndroid Build Coastguard Worker       CurBatch->appendFromArray(&Array[I], AppendSize);
747*76559068SAndroid Build Coastguard Worker       I += AppendSize;
748*76559068SAndroid Build Coastguard Worker     }
749*76559068SAndroid Build Coastguard Worker   }
750*76559068SAndroid Build Coastguard Worker 
751*76559068SAndroid Build Coastguard Worker   // Push the blocks to their batch group. The layout will be like,
752*76559068SAndroid Build Coastguard Worker   //
753*76559068SAndroid Build Coastguard Worker   // FreeListInfo.BlockList - > BG -> BG -> BG
754*76559068SAndroid Build Coastguard Worker   //                            |     |     |
755*76559068SAndroid Build Coastguard Worker   //                            v     v     v
756*76559068SAndroid Build Coastguard Worker   //                            TB    TB    TB
757*76559068SAndroid Build Coastguard Worker   //                            |
758*76559068SAndroid Build Coastguard Worker   //                            v
759*76559068SAndroid Build Coastguard Worker   //                            TB
760*76559068SAndroid Build Coastguard Worker   //
761*76559068SAndroid Build Coastguard Worker   // Each BlockGroup(BG) will associate with unique group id and the free blocks
762*76559068SAndroid Build Coastguard Worker   // are managed by a list of TransferBatch(TB). To reduce the time of inserting
763*76559068SAndroid Build Coastguard Worker   // blocks, BGs are sorted and the input `Array` are supposed to be sorted so
764*76559068SAndroid Build Coastguard Worker   // that we can get better performance of maintaining sorted property.
765*76559068SAndroid Build Coastguard Worker   // Use `SameGroup=true` to indicate that all blocks in the array are from the
766*76559068SAndroid Build Coastguard Worker   // same group then we will skip checking the group id of each block.
767*76559068SAndroid Build Coastguard Worker   void pushBlocksImpl(CacheT *C, uptr ClassId, RegionInfo *Region,
768*76559068SAndroid Build Coastguard Worker                       CompactPtrT *Array, u32 Size, bool SameGroup = false)
769*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->FLLock) {
770*76559068SAndroid Build Coastguard Worker     DCHECK_NE(ClassId, SizeClassMap::BatchClassId);
771*76559068SAndroid Build Coastguard Worker     DCHECK_GT(Size, 0U);
772*76559068SAndroid Build Coastguard Worker 
773*76559068SAndroid Build Coastguard Worker     auto CreateGroup = [&](uptr CompactPtrGroupBase) {
774*76559068SAndroid Build Coastguard Worker       BatchGroupT *BG =
775*76559068SAndroid Build Coastguard Worker           reinterpret_cast<BatchGroupT *>(C->getBatchClassBlock());
776*76559068SAndroid Build Coastguard Worker       BG->Batches.clear();
777*76559068SAndroid Build Coastguard Worker       TransferBatchT *TB =
778*76559068SAndroid Build Coastguard Worker           reinterpret_cast<TransferBatchT *>(C->getBatchClassBlock());
779*76559068SAndroid Build Coastguard Worker       TB->clear();
780*76559068SAndroid Build Coastguard Worker 
781*76559068SAndroid Build Coastguard Worker       BG->CompactPtrGroupBase = CompactPtrGroupBase;
782*76559068SAndroid Build Coastguard Worker       BG->Batches.push_front(TB);
783*76559068SAndroid Build Coastguard Worker       BG->BytesInBGAtLastCheckpoint = 0;
784*76559068SAndroid Build Coastguard Worker       BG->MaxCachedPerBatch = TransferBatchT::MaxNumCached;
785*76559068SAndroid Build Coastguard Worker 
786*76559068SAndroid Build Coastguard Worker       return BG;
787*76559068SAndroid Build Coastguard Worker     };
788*76559068SAndroid Build Coastguard Worker 
789*76559068SAndroid Build Coastguard Worker     auto InsertBlocks = [&](BatchGroupT *BG, CompactPtrT *Array, u32 Size) {
790*76559068SAndroid Build Coastguard Worker       SinglyLinkedList<TransferBatchT> &Batches = BG->Batches;
791*76559068SAndroid Build Coastguard Worker       TransferBatchT *CurBatch = Batches.front();
792*76559068SAndroid Build Coastguard Worker       DCHECK_NE(CurBatch, nullptr);
793*76559068SAndroid Build Coastguard Worker 
794*76559068SAndroid Build Coastguard Worker       for (u32 I = 0; I < Size;) {
795*76559068SAndroid Build Coastguard Worker         DCHECK_GE(BG->MaxCachedPerBatch, CurBatch->getCount());
796*76559068SAndroid Build Coastguard Worker         u16 UnusedSlots =
797*76559068SAndroid Build Coastguard Worker             static_cast<u16>(BG->MaxCachedPerBatch - CurBatch->getCount());
798*76559068SAndroid Build Coastguard Worker         if (UnusedSlots == 0) {
799*76559068SAndroid Build Coastguard Worker           CurBatch =
800*76559068SAndroid Build Coastguard Worker               reinterpret_cast<TransferBatchT *>(C->getBatchClassBlock());
801*76559068SAndroid Build Coastguard Worker           CurBatch->clear();
802*76559068SAndroid Build Coastguard Worker           Batches.push_front(CurBatch);
803*76559068SAndroid Build Coastguard Worker           UnusedSlots = BG->MaxCachedPerBatch;
804*76559068SAndroid Build Coastguard Worker         }
805*76559068SAndroid Build Coastguard Worker         // `UnusedSlots` is u16 so the result will be also fit in u16.
806*76559068SAndroid Build Coastguard Worker         u16 AppendSize = static_cast<u16>(Min<u32>(UnusedSlots, Size - I));
807*76559068SAndroid Build Coastguard Worker         CurBatch->appendFromArray(&Array[I], AppendSize);
808*76559068SAndroid Build Coastguard Worker         I += AppendSize;
809*76559068SAndroid Build Coastguard Worker       }
810*76559068SAndroid Build Coastguard Worker     };
811*76559068SAndroid Build Coastguard Worker 
812*76559068SAndroid Build Coastguard Worker     Region->FreeListInfo.PushedBlocks += Size;
813*76559068SAndroid Build Coastguard Worker     BatchGroupT *Cur = Region->FreeListInfo.BlockList.front();
814*76559068SAndroid Build Coastguard Worker 
815*76559068SAndroid Build Coastguard Worker     // In the following, `Cur` always points to the BatchGroup for blocks that
816*76559068SAndroid Build Coastguard Worker     // will be pushed next. `Prev` is the element right before `Cur`.
817*76559068SAndroid Build Coastguard Worker     BatchGroupT *Prev = nullptr;
818*76559068SAndroid Build Coastguard Worker 
819*76559068SAndroid Build Coastguard Worker     while (Cur != nullptr &&
820*76559068SAndroid Build Coastguard Worker            compactPtrGroup(Array[0]) > Cur->CompactPtrGroupBase) {
821*76559068SAndroid Build Coastguard Worker       Prev = Cur;
822*76559068SAndroid Build Coastguard Worker       Cur = Cur->Next;
823*76559068SAndroid Build Coastguard Worker     }
824*76559068SAndroid Build Coastguard Worker 
825*76559068SAndroid Build Coastguard Worker     if (Cur == nullptr ||
826*76559068SAndroid Build Coastguard Worker         compactPtrGroup(Array[0]) != Cur->CompactPtrGroupBase) {
827*76559068SAndroid Build Coastguard Worker       Cur = CreateGroup(compactPtrGroup(Array[0]));
828*76559068SAndroid Build Coastguard Worker       if (Prev == nullptr)
829*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.push_front(Cur);
830*76559068SAndroid Build Coastguard Worker       else
831*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.insert(Prev, Cur);
832*76559068SAndroid Build Coastguard Worker     }
833*76559068SAndroid Build Coastguard Worker 
834*76559068SAndroid Build Coastguard Worker     // All the blocks are from the same group, just push without checking group
835*76559068SAndroid Build Coastguard Worker     // id.
836*76559068SAndroid Build Coastguard Worker     if (SameGroup) {
837*76559068SAndroid Build Coastguard Worker       for (u32 I = 0; I < Size; ++I)
838*76559068SAndroid Build Coastguard Worker         DCHECK_EQ(compactPtrGroup(Array[I]), Cur->CompactPtrGroupBase);
839*76559068SAndroid Build Coastguard Worker 
840*76559068SAndroid Build Coastguard Worker       InsertBlocks(Cur, Array, Size);
841*76559068SAndroid Build Coastguard Worker       return;
842*76559068SAndroid Build Coastguard Worker     }
843*76559068SAndroid Build Coastguard Worker 
844*76559068SAndroid Build Coastguard Worker     // The blocks are sorted by group id. Determine the segment of group and
845*76559068SAndroid Build Coastguard Worker     // push them to their group together.
846*76559068SAndroid Build Coastguard Worker     u32 Count = 1;
847*76559068SAndroid Build Coastguard Worker     for (u32 I = 1; I < Size; ++I) {
848*76559068SAndroid Build Coastguard Worker       if (compactPtrGroup(Array[I - 1]) != compactPtrGroup(Array[I])) {
849*76559068SAndroid Build Coastguard Worker         DCHECK_EQ(compactPtrGroup(Array[I - 1]), Cur->CompactPtrGroupBase);
850*76559068SAndroid Build Coastguard Worker         InsertBlocks(Cur, Array + I - Count, Count);
851*76559068SAndroid Build Coastguard Worker 
852*76559068SAndroid Build Coastguard Worker         while (Cur != nullptr &&
853*76559068SAndroid Build Coastguard Worker                compactPtrGroup(Array[I]) > Cur->CompactPtrGroupBase) {
854*76559068SAndroid Build Coastguard Worker           Prev = Cur;
855*76559068SAndroid Build Coastguard Worker           Cur = Cur->Next;
856*76559068SAndroid Build Coastguard Worker         }
857*76559068SAndroid Build Coastguard Worker 
858*76559068SAndroid Build Coastguard Worker         if (Cur == nullptr ||
859*76559068SAndroid Build Coastguard Worker             compactPtrGroup(Array[I]) != Cur->CompactPtrGroupBase) {
860*76559068SAndroid Build Coastguard Worker           Cur = CreateGroup(compactPtrGroup(Array[I]));
861*76559068SAndroid Build Coastguard Worker           DCHECK_NE(Prev, nullptr);
862*76559068SAndroid Build Coastguard Worker           Region->FreeListInfo.BlockList.insert(Prev, Cur);
863*76559068SAndroid Build Coastguard Worker         }
864*76559068SAndroid Build Coastguard Worker 
865*76559068SAndroid Build Coastguard Worker         Count = 1;
866*76559068SAndroid Build Coastguard Worker       } else {
867*76559068SAndroid Build Coastguard Worker         ++Count;
868*76559068SAndroid Build Coastguard Worker       }
869*76559068SAndroid Build Coastguard Worker     }
870*76559068SAndroid Build Coastguard Worker 
871*76559068SAndroid Build Coastguard Worker     InsertBlocks(Cur, Array + Size - Count, Count);
872*76559068SAndroid Build Coastguard Worker   }
873*76559068SAndroid Build Coastguard Worker 
popBlocksWithCV(CacheT * C,uptr ClassId,RegionInfo * Region,CompactPtrT * ToArray,const u16 MaxBlockCount,bool & ReportRegionExhausted)874*76559068SAndroid Build Coastguard Worker   u16 popBlocksWithCV(CacheT *C, uptr ClassId, RegionInfo *Region,
875*76559068SAndroid Build Coastguard Worker                       CompactPtrT *ToArray, const u16 MaxBlockCount,
876*76559068SAndroid Build Coastguard Worker                       bool &ReportRegionExhausted) {
877*76559068SAndroid Build Coastguard Worker     u16 PopCount = 0;
878*76559068SAndroid Build Coastguard Worker 
879*76559068SAndroid Build Coastguard Worker     while (true) {
880*76559068SAndroid Build Coastguard Worker       // We only expect one thread doing the freelist refillment and other
881*76559068SAndroid Build Coastguard Worker       // threads will be waiting for either the completion of the
882*76559068SAndroid Build Coastguard Worker       // `populateFreeListAndPopBlocks()` or `pushBlocks()` called by other
883*76559068SAndroid Build Coastguard Worker       // threads.
884*76559068SAndroid Build Coastguard Worker       bool PopulateFreeList = false;
885*76559068SAndroid Build Coastguard Worker       {
886*76559068SAndroid Build Coastguard Worker         ScopedLock FL(Region->FLLock);
887*76559068SAndroid Build Coastguard Worker         if (!Region->isPopulatingFreeList) {
888*76559068SAndroid Build Coastguard Worker           Region->isPopulatingFreeList = true;
889*76559068SAndroid Build Coastguard Worker           PopulateFreeList = true;
890*76559068SAndroid Build Coastguard Worker         }
891*76559068SAndroid Build Coastguard Worker       }
892*76559068SAndroid Build Coastguard Worker 
893*76559068SAndroid Build Coastguard Worker       if (PopulateFreeList) {
894*76559068SAndroid Build Coastguard Worker         ScopedLock ML(Region->MMLock);
895*76559068SAndroid Build Coastguard Worker 
896*76559068SAndroid Build Coastguard Worker         const bool RegionIsExhausted = Region->Exhausted;
897*76559068SAndroid Build Coastguard Worker         if (!RegionIsExhausted) {
898*76559068SAndroid Build Coastguard Worker           PopCount = populateFreeListAndPopBlocks(C, ClassId, Region, ToArray,
899*76559068SAndroid Build Coastguard Worker                                                   MaxBlockCount);
900*76559068SAndroid Build Coastguard Worker         }
901*76559068SAndroid Build Coastguard Worker         ReportRegionExhausted = !RegionIsExhausted && Region->Exhausted;
902*76559068SAndroid Build Coastguard Worker 
903*76559068SAndroid Build Coastguard Worker         {
904*76559068SAndroid Build Coastguard Worker           // Before reacquiring the `FLLock`, the freelist may be used up again
905*76559068SAndroid Build Coastguard Worker           // and some threads are waiting for the freelist refillment by the
906*76559068SAndroid Build Coastguard Worker           // current thread. It's important to set
907*76559068SAndroid Build Coastguard Worker           // `Region->isPopulatingFreeList` to false so the threads about to
908*76559068SAndroid Build Coastguard Worker           // sleep will notice the status change.
909*76559068SAndroid Build Coastguard Worker           ScopedLock FL(Region->FLLock);
910*76559068SAndroid Build Coastguard Worker           Region->isPopulatingFreeList = false;
911*76559068SAndroid Build Coastguard Worker           Region->FLLockCV.notifyAll(Region->FLLock);
912*76559068SAndroid Build Coastguard Worker         }
913*76559068SAndroid Build Coastguard Worker 
914*76559068SAndroid Build Coastguard Worker         break;
915*76559068SAndroid Build Coastguard Worker       }
916*76559068SAndroid Build Coastguard Worker 
917*76559068SAndroid Build Coastguard Worker       // At here, there are two preconditions to be met before waiting,
918*76559068SAndroid Build Coastguard Worker       //   1. The freelist is empty.
919*76559068SAndroid Build Coastguard Worker       //   2. Region->isPopulatingFreeList == true, i.e, someone is still doing
920*76559068SAndroid Build Coastguard Worker       //   `populateFreeListAndPopBlocks()`.
921*76559068SAndroid Build Coastguard Worker       //
922*76559068SAndroid Build Coastguard Worker       // Note that it has the chance that freelist is empty but
923*76559068SAndroid Build Coastguard Worker       // Region->isPopulatingFreeList == false because all the new populated
924*76559068SAndroid Build Coastguard Worker       // blocks were used up right after the refillment. Therefore, we have to
925*76559068SAndroid Build Coastguard Worker       // check if someone is still populating the freelist.
926*76559068SAndroid Build Coastguard Worker       ScopedLock FL(Region->FLLock);
927*76559068SAndroid Build Coastguard Worker       PopCount = popBlocksImpl(C, ClassId, Region, ToArray, MaxBlockCount);
928*76559068SAndroid Build Coastguard Worker       if (PopCount != 0U)
929*76559068SAndroid Build Coastguard Worker         break;
930*76559068SAndroid Build Coastguard Worker 
931*76559068SAndroid Build Coastguard Worker       if (!Region->isPopulatingFreeList)
932*76559068SAndroid Build Coastguard Worker         continue;
933*76559068SAndroid Build Coastguard Worker 
934*76559068SAndroid Build Coastguard Worker       // Now the freelist is empty and someone's doing the refillment. We will
935*76559068SAndroid Build Coastguard Worker       // wait until anyone refills the freelist or someone finishes doing
936*76559068SAndroid Build Coastguard Worker       // `populateFreeListAndPopBlocks()`. The refillment can be done by
937*76559068SAndroid Build Coastguard Worker       // `populateFreeListAndPopBlocks()`, `pushBlocks()`,
938*76559068SAndroid Build Coastguard Worker       // `pushBatchClassBlocks()` and `mergeGroupsToReleaseBack()`.
939*76559068SAndroid Build Coastguard Worker       Region->FLLockCV.wait(Region->FLLock);
940*76559068SAndroid Build Coastguard Worker 
941*76559068SAndroid Build Coastguard Worker       PopCount = popBlocksImpl(C, ClassId, Region, ToArray, MaxBlockCount);
942*76559068SAndroid Build Coastguard Worker       if (PopCount != 0U)
943*76559068SAndroid Build Coastguard Worker         break;
944*76559068SAndroid Build Coastguard Worker     }
945*76559068SAndroid Build Coastguard Worker 
946*76559068SAndroid Build Coastguard Worker     return PopCount;
947*76559068SAndroid Build Coastguard Worker   }
948*76559068SAndroid Build Coastguard Worker 
popBlocksImpl(CacheT * C,uptr ClassId,RegionInfo * Region,CompactPtrT * ToArray,const u16 MaxBlockCount)949*76559068SAndroid Build Coastguard Worker   u16 popBlocksImpl(CacheT *C, uptr ClassId, RegionInfo *Region,
950*76559068SAndroid Build Coastguard Worker                     CompactPtrT *ToArray, const u16 MaxBlockCount)
951*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->FLLock) {
952*76559068SAndroid Build Coastguard Worker     if (Region->FreeListInfo.BlockList.empty())
953*76559068SAndroid Build Coastguard Worker       return 0U;
954*76559068SAndroid Build Coastguard Worker 
955*76559068SAndroid Build Coastguard Worker     SinglyLinkedList<TransferBatchT> &Batches =
956*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.front()->Batches;
957*76559068SAndroid Build Coastguard Worker 
958*76559068SAndroid Build Coastguard Worker     if (Batches.empty()) {
959*76559068SAndroid Build Coastguard Worker       DCHECK_EQ(ClassId, SizeClassMap::BatchClassId);
960*76559068SAndroid Build Coastguard Worker       BatchGroupT *BG = Region->FreeListInfo.BlockList.front();
961*76559068SAndroid Build Coastguard Worker       Region->FreeListInfo.BlockList.pop_front();
962*76559068SAndroid Build Coastguard Worker 
963*76559068SAndroid Build Coastguard Worker       // Block used by `BatchGroup` is from BatchClassId. Turn the block into
964*76559068SAndroid Build Coastguard Worker       // `TransferBatch` with single block.
965*76559068SAndroid Build Coastguard Worker       TransferBatchT *TB = reinterpret_cast<TransferBatchT *>(BG);
966*76559068SAndroid Build Coastguard Worker       ToArray[0] =
967*76559068SAndroid Build Coastguard Worker           compactPtr(SizeClassMap::BatchClassId, reinterpret_cast<uptr>(TB));
968*76559068SAndroid Build Coastguard Worker       Region->FreeListInfo.PoppedBlocks += 1;
969*76559068SAndroid Build Coastguard Worker       return 1U;
970*76559068SAndroid Build Coastguard Worker     }
971*76559068SAndroid Build Coastguard Worker 
972*76559068SAndroid Build Coastguard Worker     // So far, instead of always filling blocks to `MaxBlockCount`, we only
973*76559068SAndroid Build Coastguard Worker     // examine single `TransferBatch` to minimize the time spent in the primary
974*76559068SAndroid Build Coastguard Worker     // allocator. Besides, the sizes of `TransferBatch` and
975*76559068SAndroid Build Coastguard Worker     // `CacheT::getMaxCached()` may also impact the time spent on accessing the
976*76559068SAndroid Build Coastguard Worker     // primary allocator.
977*76559068SAndroid Build Coastguard Worker     // TODO(chiahungduan): Evaluate if we want to always prepare `MaxBlockCount`
978*76559068SAndroid Build Coastguard Worker     // blocks and/or adjust the size of `TransferBatch` according to
979*76559068SAndroid Build Coastguard Worker     // `CacheT::getMaxCached()`.
980*76559068SAndroid Build Coastguard Worker     TransferBatchT *B = Batches.front();
981*76559068SAndroid Build Coastguard Worker     DCHECK_NE(B, nullptr);
982*76559068SAndroid Build Coastguard Worker     DCHECK_GT(B->getCount(), 0U);
983*76559068SAndroid Build Coastguard Worker 
984*76559068SAndroid Build Coastguard Worker     // BachClassId should always take all blocks in the TransferBatch. Read the
985*76559068SAndroid Build Coastguard Worker     // comment in `pushBatchClassBlocks()` for more details.
986*76559068SAndroid Build Coastguard Worker     const u16 PopCount = ClassId == SizeClassMap::BatchClassId
987*76559068SAndroid Build Coastguard Worker                              ? B->getCount()
988*76559068SAndroid Build Coastguard Worker                              : Min(MaxBlockCount, B->getCount());
989*76559068SAndroid Build Coastguard Worker     B->moveNToArray(ToArray, PopCount);
990*76559068SAndroid Build Coastguard Worker 
991*76559068SAndroid Build Coastguard Worker     // TODO(chiahungduan): The deallocation of unused BatchClassId blocks can be
992*76559068SAndroid Build Coastguard Worker     // done without holding `FLLock`.
993*76559068SAndroid Build Coastguard Worker     if (B->empty()) {
994*76559068SAndroid Build Coastguard Worker       Batches.pop_front();
995*76559068SAndroid Build Coastguard Worker       // `TransferBatch` of BatchClassId is self-contained, no need to
996*76559068SAndroid Build Coastguard Worker       // deallocate. Read the comment in `pushBatchClassBlocks()` for more
997*76559068SAndroid Build Coastguard Worker       // details.
998*76559068SAndroid Build Coastguard Worker       if (ClassId != SizeClassMap::BatchClassId)
999*76559068SAndroid Build Coastguard Worker         C->deallocate(SizeClassMap::BatchClassId, B);
1000*76559068SAndroid Build Coastguard Worker 
1001*76559068SAndroid Build Coastguard Worker       if (Batches.empty()) {
1002*76559068SAndroid Build Coastguard Worker         BatchGroupT *BG = Region->FreeListInfo.BlockList.front();
1003*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.pop_front();
1004*76559068SAndroid Build Coastguard Worker 
1005*76559068SAndroid Build Coastguard Worker         // We don't keep BatchGroup with zero blocks to avoid empty-checking
1006*76559068SAndroid Build Coastguard Worker         // while allocating. Note that block used for constructing BatchGroup is
1007*76559068SAndroid Build Coastguard Worker         // recorded as free blocks in the last element of BatchGroup::Batches.
1008*76559068SAndroid Build Coastguard Worker         // Which means, once we pop the last TransferBatch, the block is
1009*76559068SAndroid Build Coastguard Worker         // implicitly deallocated.
1010*76559068SAndroid Build Coastguard Worker         if (ClassId != SizeClassMap::BatchClassId)
1011*76559068SAndroid Build Coastguard Worker           C->deallocate(SizeClassMap::BatchClassId, BG);
1012*76559068SAndroid Build Coastguard Worker       }
1013*76559068SAndroid Build Coastguard Worker     }
1014*76559068SAndroid Build Coastguard Worker 
1015*76559068SAndroid Build Coastguard Worker     Region->FreeListInfo.PoppedBlocks += PopCount;
1016*76559068SAndroid Build Coastguard Worker 
1017*76559068SAndroid Build Coastguard Worker     return PopCount;
1018*76559068SAndroid Build Coastguard Worker   }
1019*76559068SAndroid Build Coastguard Worker 
populateFreeListAndPopBlocks(CacheT * C,uptr ClassId,RegionInfo * Region,CompactPtrT * ToArray,const u16 MaxBlockCount)1020*76559068SAndroid Build Coastguard Worker   NOINLINE u16 populateFreeListAndPopBlocks(CacheT *C, uptr ClassId,
1021*76559068SAndroid Build Coastguard Worker                                             RegionInfo *Region,
1022*76559068SAndroid Build Coastguard Worker                                             CompactPtrT *ToArray,
1023*76559068SAndroid Build Coastguard Worker                                             const u16 MaxBlockCount)
1024*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock) EXCLUDES(Region->FLLock) {
1025*76559068SAndroid Build Coastguard Worker     if (!Config::getEnableContiguousRegions() &&
1026*76559068SAndroid Build Coastguard Worker         !Region->MemMapInfo.MemMap.isAllocated()) {
1027*76559068SAndroid Build Coastguard Worker       ReservedMemoryT ReservedMemory;
1028*76559068SAndroid Build Coastguard Worker       if (UNLIKELY(!ReservedMemory.create(/*Addr=*/0U, RegionSize,
1029*76559068SAndroid Build Coastguard Worker                                           "scudo:primary_reserve",
1030*76559068SAndroid Build Coastguard Worker                                           MAP_ALLOWNOMEM))) {
1031*76559068SAndroid Build Coastguard Worker         Printf("Can't reserve pages for size class %zu.\n",
1032*76559068SAndroid Build Coastguard Worker                getSizeByClassId(ClassId));
1033*76559068SAndroid Build Coastguard Worker         return 0U;
1034*76559068SAndroid Build Coastguard Worker       }
1035*76559068SAndroid Build Coastguard Worker       initRegion(Region, ClassId,
1036*76559068SAndroid Build Coastguard Worker                  ReservedMemory.dispatch(ReservedMemory.getBase(),
1037*76559068SAndroid Build Coastguard Worker                                          ReservedMemory.getCapacity()),
1038*76559068SAndroid Build Coastguard Worker                  /*EnableRandomOffset=*/false);
1039*76559068SAndroid Build Coastguard Worker     }
1040*76559068SAndroid Build Coastguard Worker 
1041*76559068SAndroid Build Coastguard Worker     DCHECK(Region->MemMapInfo.MemMap.isAllocated());
1042*76559068SAndroid Build Coastguard Worker     const uptr Size = getSizeByClassId(ClassId);
1043*76559068SAndroid Build Coastguard Worker     const u16 MaxCount = CacheT::getMaxCached(Size);
1044*76559068SAndroid Build Coastguard Worker     const uptr RegionBeg = Region->RegionBeg;
1045*76559068SAndroid Build Coastguard Worker     const uptr MappedUser = Region->MemMapInfo.MappedUser;
1046*76559068SAndroid Build Coastguard Worker     const uptr TotalUserBytes =
1047*76559068SAndroid Build Coastguard Worker         Region->MemMapInfo.AllocatedUser + MaxCount * Size;
1048*76559068SAndroid Build Coastguard Worker     // Map more space for blocks, if necessary.
1049*76559068SAndroid Build Coastguard Worker     if (TotalUserBytes > MappedUser) {
1050*76559068SAndroid Build Coastguard Worker       // Do the mmap for the user memory.
1051*76559068SAndroid Build Coastguard Worker       const uptr MapSize =
1052*76559068SAndroid Build Coastguard Worker           roundUp(TotalUserBytes - MappedUser, MapSizeIncrement);
1053*76559068SAndroid Build Coastguard Worker       const uptr RegionBase = RegionBeg - getRegionBaseByClassId(ClassId);
1054*76559068SAndroid Build Coastguard Worker       if (UNLIKELY(RegionBase + MappedUser + MapSize > RegionSize)) {
1055*76559068SAndroid Build Coastguard Worker         Region->Exhausted = true;
1056*76559068SAndroid Build Coastguard Worker         return 0U;
1057*76559068SAndroid Build Coastguard Worker       }
1058*76559068SAndroid Build Coastguard Worker 
1059*76559068SAndroid Build Coastguard Worker       if (UNLIKELY(!Region->MemMapInfo.MemMap.remap(
1060*76559068SAndroid Build Coastguard Worker               RegionBeg + MappedUser, MapSize, "scudo:primary",
1061*76559068SAndroid Build Coastguard Worker               MAP_ALLOWNOMEM | MAP_RESIZABLE |
1062*76559068SAndroid Build Coastguard Worker                   (useMemoryTagging<Config>(Options.load()) ? MAP_MEMTAG
1063*76559068SAndroid Build Coastguard Worker                                                             : 0)))) {
1064*76559068SAndroid Build Coastguard Worker         return 0U;
1065*76559068SAndroid Build Coastguard Worker       }
1066*76559068SAndroid Build Coastguard Worker       Region->MemMapInfo.MappedUser += MapSize;
1067*76559068SAndroid Build Coastguard Worker       C->getStats().add(StatMapped, MapSize);
1068*76559068SAndroid Build Coastguard Worker     }
1069*76559068SAndroid Build Coastguard Worker 
1070*76559068SAndroid Build Coastguard Worker     const u32 NumberOfBlocks =
1071*76559068SAndroid Build Coastguard Worker         Min(MaxNumBatches * MaxCount,
1072*76559068SAndroid Build Coastguard Worker             static_cast<u32>((Region->MemMapInfo.MappedUser -
1073*76559068SAndroid Build Coastguard Worker                               Region->MemMapInfo.AllocatedUser) /
1074*76559068SAndroid Build Coastguard Worker                              Size));
1075*76559068SAndroid Build Coastguard Worker     DCHECK_GT(NumberOfBlocks, 0);
1076*76559068SAndroid Build Coastguard Worker 
1077*76559068SAndroid Build Coastguard Worker     constexpr u32 ShuffleArraySize =
1078*76559068SAndroid Build Coastguard Worker         MaxNumBatches * TransferBatchT::MaxNumCached;
1079*76559068SAndroid Build Coastguard Worker     CompactPtrT ShuffleArray[ShuffleArraySize];
1080*76559068SAndroid Build Coastguard Worker     DCHECK_LE(NumberOfBlocks, ShuffleArraySize);
1081*76559068SAndroid Build Coastguard Worker 
1082*76559068SAndroid Build Coastguard Worker     const uptr CompactPtrBase = getCompactPtrBaseByClassId(ClassId);
1083*76559068SAndroid Build Coastguard Worker     uptr P = RegionBeg + Region->MemMapInfo.AllocatedUser;
1084*76559068SAndroid Build Coastguard Worker     for (u32 I = 0; I < NumberOfBlocks; I++, P += Size)
1085*76559068SAndroid Build Coastguard Worker       ShuffleArray[I] = compactPtrInternal(CompactPtrBase, P);
1086*76559068SAndroid Build Coastguard Worker 
1087*76559068SAndroid Build Coastguard Worker     ScopedLock L(Region->FLLock);
1088*76559068SAndroid Build Coastguard Worker 
1089*76559068SAndroid Build Coastguard Worker     if (ClassId != SizeClassMap::BatchClassId) {
1090*76559068SAndroid Build Coastguard Worker       u32 N = 1;
1091*76559068SAndroid Build Coastguard Worker       uptr CurGroup = compactPtrGroup(ShuffleArray[0]);
1092*76559068SAndroid Build Coastguard Worker       for (u32 I = 1; I < NumberOfBlocks; I++) {
1093*76559068SAndroid Build Coastguard Worker         if (UNLIKELY(compactPtrGroup(ShuffleArray[I]) != CurGroup)) {
1094*76559068SAndroid Build Coastguard Worker           shuffle(ShuffleArray + I - N, N, &Region->RandState);
1095*76559068SAndroid Build Coastguard Worker           pushBlocksImpl(C, ClassId, Region, ShuffleArray + I - N, N,
1096*76559068SAndroid Build Coastguard Worker                          /*SameGroup=*/true);
1097*76559068SAndroid Build Coastguard Worker           N = 1;
1098*76559068SAndroid Build Coastguard Worker           CurGroup = compactPtrGroup(ShuffleArray[I]);
1099*76559068SAndroid Build Coastguard Worker         } else {
1100*76559068SAndroid Build Coastguard Worker           ++N;
1101*76559068SAndroid Build Coastguard Worker         }
1102*76559068SAndroid Build Coastguard Worker       }
1103*76559068SAndroid Build Coastguard Worker 
1104*76559068SAndroid Build Coastguard Worker       shuffle(ShuffleArray + NumberOfBlocks - N, N, &Region->RandState);
1105*76559068SAndroid Build Coastguard Worker       pushBlocksImpl(C, ClassId, Region, &ShuffleArray[NumberOfBlocks - N], N,
1106*76559068SAndroid Build Coastguard Worker                      /*SameGroup=*/true);
1107*76559068SAndroid Build Coastguard Worker     } else {
1108*76559068SAndroid Build Coastguard Worker       pushBatchClassBlocks(Region, ShuffleArray, NumberOfBlocks);
1109*76559068SAndroid Build Coastguard Worker     }
1110*76559068SAndroid Build Coastguard Worker 
1111*76559068SAndroid Build Coastguard Worker     const u16 PopCount =
1112*76559068SAndroid Build Coastguard Worker         popBlocksImpl(C, ClassId, Region, ToArray, MaxBlockCount);
1113*76559068SAndroid Build Coastguard Worker     DCHECK_NE(PopCount, 0U);
1114*76559068SAndroid Build Coastguard Worker 
1115*76559068SAndroid Build Coastguard Worker     // Note that `PushedBlocks` and `PoppedBlocks` are supposed to only record
1116*76559068SAndroid Build Coastguard Worker     // the requests from `PushBlocks` and `PopBatch` which are external
1117*76559068SAndroid Build Coastguard Worker     // interfaces. `populateFreeListAndPopBlocks` is the internal interface so
1118*76559068SAndroid Build Coastguard Worker     // we should set the values back to avoid incorrectly setting the stats.
1119*76559068SAndroid Build Coastguard Worker     Region->FreeListInfo.PushedBlocks -= NumberOfBlocks;
1120*76559068SAndroid Build Coastguard Worker 
1121*76559068SAndroid Build Coastguard Worker     const uptr AllocatedUser = Size * NumberOfBlocks;
1122*76559068SAndroid Build Coastguard Worker     C->getStats().add(StatFree, AllocatedUser);
1123*76559068SAndroid Build Coastguard Worker     Region->MemMapInfo.AllocatedUser += AllocatedUser;
1124*76559068SAndroid Build Coastguard Worker 
1125*76559068SAndroid Build Coastguard Worker     return PopCount;
1126*76559068SAndroid Build Coastguard Worker   }
1127*76559068SAndroid Build Coastguard Worker 
getStats(ScopedString * Str,uptr ClassId,RegionInfo * Region)1128*76559068SAndroid Build Coastguard Worker   void getStats(ScopedString *Str, uptr ClassId, RegionInfo *Region)
1129*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock, Region->FLLock) {
1130*76559068SAndroid Build Coastguard Worker     if (Region->MemMapInfo.MappedUser == 0)
1131*76559068SAndroid Build Coastguard Worker       return;
1132*76559068SAndroid Build Coastguard Worker     const uptr BlockSize = getSizeByClassId(ClassId);
1133*76559068SAndroid Build Coastguard Worker     const uptr InUseBlocks =
1134*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.PoppedBlocks - Region->FreeListInfo.PushedBlocks;
1135*76559068SAndroid Build Coastguard Worker     const uptr BytesInFreeList =
1136*76559068SAndroid Build Coastguard Worker         Region->MemMapInfo.AllocatedUser - InUseBlocks * BlockSize;
1137*76559068SAndroid Build Coastguard Worker     uptr RegionPushedBytesDelta = 0;
1138*76559068SAndroid Build Coastguard Worker     if (BytesInFreeList >=
1139*76559068SAndroid Build Coastguard Worker         Region->ReleaseInfo.BytesInFreeListAtLastCheckpoint) {
1140*76559068SAndroid Build Coastguard Worker       RegionPushedBytesDelta =
1141*76559068SAndroid Build Coastguard Worker           BytesInFreeList - Region->ReleaseInfo.BytesInFreeListAtLastCheckpoint;
1142*76559068SAndroid Build Coastguard Worker     }
1143*76559068SAndroid Build Coastguard Worker     const uptr TotalChunks = Region->MemMapInfo.AllocatedUser / BlockSize;
1144*76559068SAndroid Build Coastguard Worker     Str->append(
1145*76559068SAndroid Build Coastguard Worker         "%s %02zu (%6zu): mapped: %6zuK popped: %7zu pushed: %7zu "
1146*76559068SAndroid Build Coastguard Worker         "inuse: %6zu total: %6zu releases: %6zu last "
1147*76559068SAndroid Build Coastguard Worker         "released: %6zuK latest pushed bytes: %6zuK region: 0x%zx (0x%zx)\n",
1148*76559068SAndroid Build Coastguard Worker         Region->Exhausted ? "E" : " ", ClassId, getSizeByClassId(ClassId),
1149*76559068SAndroid Build Coastguard Worker         Region->MemMapInfo.MappedUser >> 10, Region->FreeListInfo.PoppedBlocks,
1150*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.PushedBlocks, InUseBlocks, TotalChunks,
1151*76559068SAndroid Build Coastguard Worker         Region->ReleaseInfo.RangesReleased,
1152*76559068SAndroid Build Coastguard Worker         Region->ReleaseInfo.LastReleasedBytes >> 10,
1153*76559068SAndroid Build Coastguard Worker         RegionPushedBytesDelta >> 10, Region->RegionBeg,
1154*76559068SAndroid Build Coastguard Worker         getRegionBaseByClassId(ClassId));
1155*76559068SAndroid Build Coastguard Worker   }
1156*76559068SAndroid Build Coastguard Worker 
getRegionFragmentationInfo(RegionInfo * Region,uptr ClassId,ScopedString * Str)1157*76559068SAndroid Build Coastguard Worker   void getRegionFragmentationInfo(RegionInfo *Region, uptr ClassId,
1158*76559068SAndroid Build Coastguard Worker                                   ScopedString *Str) REQUIRES(Region->MMLock) {
1159*76559068SAndroid Build Coastguard Worker     const uptr BlockSize = getSizeByClassId(ClassId);
1160*76559068SAndroid Build Coastguard Worker     const uptr AllocatedUserEnd =
1161*76559068SAndroid Build Coastguard Worker         Region->MemMapInfo.AllocatedUser + Region->RegionBeg;
1162*76559068SAndroid Build Coastguard Worker 
1163*76559068SAndroid Build Coastguard Worker     SinglyLinkedList<BatchGroupT> GroupsToRelease;
1164*76559068SAndroid Build Coastguard Worker     {
1165*76559068SAndroid Build Coastguard Worker       ScopedLock L(Region->FLLock);
1166*76559068SAndroid Build Coastguard Worker       GroupsToRelease = Region->FreeListInfo.BlockList;
1167*76559068SAndroid Build Coastguard Worker       Region->FreeListInfo.BlockList.clear();
1168*76559068SAndroid Build Coastguard Worker     }
1169*76559068SAndroid Build Coastguard Worker 
1170*76559068SAndroid Build Coastguard Worker     FragmentationRecorder Recorder;
1171*76559068SAndroid Build Coastguard Worker     if (!GroupsToRelease.empty()) {
1172*76559068SAndroid Build Coastguard Worker       PageReleaseContext Context =
1173*76559068SAndroid Build Coastguard Worker           markFreeBlocks(Region, BlockSize, AllocatedUserEnd,
1174*76559068SAndroid Build Coastguard Worker                          getCompactPtrBaseByClassId(ClassId), GroupsToRelease);
1175*76559068SAndroid Build Coastguard Worker       auto SkipRegion = [](UNUSED uptr RegionIndex) { return false; };
1176*76559068SAndroid Build Coastguard Worker       releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
1177*76559068SAndroid Build Coastguard Worker 
1178*76559068SAndroid Build Coastguard Worker       mergeGroupsToReleaseBack(Region, GroupsToRelease);
1179*76559068SAndroid Build Coastguard Worker     }
1180*76559068SAndroid Build Coastguard Worker 
1181*76559068SAndroid Build Coastguard Worker     ScopedLock L(Region->FLLock);
1182*76559068SAndroid Build Coastguard Worker     const uptr PageSize = getPageSizeCached();
1183*76559068SAndroid Build Coastguard Worker     const uptr TotalBlocks = Region->MemMapInfo.AllocatedUser / BlockSize;
1184*76559068SAndroid Build Coastguard Worker     const uptr InUseBlocks =
1185*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.PoppedBlocks - Region->FreeListInfo.PushedBlocks;
1186*76559068SAndroid Build Coastguard Worker     const uptr AllocatedPagesCount =
1187*76559068SAndroid Build Coastguard Worker         roundUp(Region->MemMapInfo.AllocatedUser, PageSize) / PageSize;
1188*76559068SAndroid Build Coastguard Worker     DCHECK_GE(AllocatedPagesCount, Recorder.getReleasedPagesCount());
1189*76559068SAndroid Build Coastguard Worker     const uptr InUsePages =
1190*76559068SAndroid Build Coastguard Worker         AllocatedPagesCount - Recorder.getReleasedPagesCount();
1191*76559068SAndroid Build Coastguard Worker     const uptr InUseBytes = InUsePages * PageSize;
1192*76559068SAndroid Build Coastguard Worker 
1193*76559068SAndroid Build Coastguard Worker     uptr Integral;
1194*76559068SAndroid Build Coastguard Worker     uptr Fractional;
1195*76559068SAndroid Build Coastguard Worker     computePercentage(BlockSize * InUseBlocks, InUseBytes, &Integral,
1196*76559068SAndroid Build Coastguard Worker                       &Fractional);
1197*76559068SAndroid Build Coastguard Worker     Str->append("  %02zu (%6zu): inuse/total blocks: %6zu/%6zu inuse/total "
1198*76559068SAndroid Build Coastguard Worker                 "pages: %6zu/%6zu inuse bytes: %6zuK util: %3zu.%02zu%%\n",
1199*76559068SAndroid Build Coastguard Worker                 ClassId, BlockSize, InUseBlocks, TotalBlocks, InUsePages,
1200*76559068SAndroid Build Coastguard Worker                 AllocatedPagesCount, InUseBytes >> 10, Integral, Fractional);
1201*76559068SAndroid Build Coastguard Worker   }
1202*76559068SAndroid Build Coastguard Worker 
getMemoryGroupFragmentationInfoInRegion(RegionInfo * Region,uptr ClassId,ScopedString * Str)1203*76559068SAndroid Build Coastguard Worker   void getMemoryGroupFragmentationInfoInRegion(RegionInfo *Region, uptr ClassId,
1204*76559068SAndroid Build Coastguard Worker                                                ScopedString *Str)
1205*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock) EXCLUDES(Region->FLLock) {
1206*76559068SAndroid Build Coastguard Worker     const uptr BlockSize = getSizeByClassId(ClassId);
1207*76559068SAndroid Build Coastguard Worker     const uptr AllocatedUserEnd =
1208*76559068SAndroid Build Coastguard Worker         Region->MemMapInfo.AllocatedUser + Region->RegionBeg;
1209*76559068SAndroid Build Coastguard Worker 
1210*76559068SAndroid Build Coastguard Worker     SinglyLinkedList<BatchGroupT> GroupsToRelease;
1211*76559068SAndroid Build Coastguard Worker     {
1212*76559068SAndroid Build Coastguard Worker       ScopedLock L(Region->FLLock);
1213*76559068SAndroid Build Coastguard Worker       GroupsToRelease = Region->FreeListInfo.BlockList;
1214*76559068SAndroid Build Coastguard Worker       Region->FreeListInfo.BlockList.clear();
1215*76559068SAndroid Build Coastguard Worker     }
1216*76559068SAndroid Build Coastguard Worker 
1217*76559068SAndroid Build Coastguard Worker     constexpr uptr GroupSize = (1UL << GroupSizeLog);
1218*76559068SAndroid Build Coastguard Worker     constexpr uptr MaxNumGroups = RegionSize / GroupSize;
1219*76559068SAndroid Build Coastguard Worker 
1220*76559068SAndroid Build Coastguard Worker     MemoryGroupFragmentationRecorder<GroupSize, MaxNumGroups> Recorder;
1221*76559068SAndroid Build Coastguard Worker     if (!GroupsToRelease.empty()) {
1222*76559068SAndroid Build Coastguard Worker       PageReleaseContext Context =
1223*76559068SAndroid Build Coastguard Worker           markFreeBlocks(Region, BlockSize, AllocatedUserEnd,
1224*76559068SAndroid Build Coastguard Worker                          getCompactPtrBaseByClassId(ClassId), GroupsToRelease);
1225*76559068SAndroid Build Coastguard Worker       auto SkipRegion = [](UNUSED uptr RegionIndex) { return false; };
1226*76559068SAndroid Build Coastguard Worker       releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
1227*76559068SAndroid Build Coastguard Worker 
1228*76559068SAndroid Build Coastguard Worker       mergeGroupsToReleaseBack(Region, GroupsToRelease);
1229*76559068SAndroid Build Coastguard Worker     }
1230*76559068SAndroid Build Coastguard Worker 
1231*76559068SAndroid Build Coastguard Worker     Str->append("MemoryGroupFragmentationInfo in Region %zu (%zu)\n", ClassId,
1232*76559068SAndroid Build Coastguard Worker                 BlockSize);
1233*76559068SAndroid Build Coastguard Worker 
1234*76559068SAndroid Build Coastguard Worker     const uptr MaxNumGroupsInUse =
1235*76559068SAndroid Build Coastguard Worker         roundUp(Region->MemMapInfo.AllocatedUser, GroupSize) / GroupSize;
1236*76559068SAndroid Build Coastguard Worker     for (uptr I = 0; I < MaxNumGroupsInUse; ++I) {
1237*76559068SAndroid Build Coastguard Worker       uptr Integral;
1238*76559068SAndroid Build Coastguard Worker       uptr Fractional;
1239*76559068SAndroid Build Coastguard Worker       computePercentage(Recorder.NumPagesInOneGroup -
1240*76559068SAndroid Build Coastguard Worker                             Recorder.getNumFreePages(I),
1241*76559068SAndroid Build Coastguard Worker                         Recorder.NumPagesInOneGroup, &Integral, &Fractional);
1242*76559068SAndroid Build Coastguard Worker       Str->append("MemoryGroup #%zu (0x%zx): util: %3zu.%02zu%%\n", I,
1243*76559068SAndroid Build Coastguard Worker                   Region->RegionBeg + I * GroupSize, Integral, Fractional);
1244*76559068SAndroid Build Coastguard Worker     }
1245*76559068SAndroid Build Coastguard Worker   }
1246*76559068SAndroid Build Coastguard Worker 
1247*76559068SAndroid Build Coastguard Worker   NOINLINE uptr releaseToOSMaybe(RegionInfo *Region, uptr ClassId,
1248*76559068SAndroid Build Coastguard Worker                                  ReleaseToOS ReleaseType = ReleaseToOS::Normal)
1249*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock) EXCLUDES(Region->FLLock) {
1250*76559068SAndroid Build Coastguard Worker     const uptr BlockSize = getSizeByClassId(ClassId);
1251*76559068SAndroid Build Coastguard Worker     uptr BytesInFreeList;
1252*76559068SAndroid Build Coastguard Worker     const uptr AllocatedUserEnd =
1253*76559068SAndroid Build Coastguard Worker         Region->MemMapInfo.AllocatedUser + Region->RegionBeg;
1254*76559068SAndroid Build Coastguard Worker     uptr RegionPushedBytesDelta = 0;
1255*76559068SAndroid Build Coastguard Worker     SinglyLinkedList<BatchGroupT> GroupsToRelease;
1256*76559068SAndroid Build Coastguard Worker 
1257*76559068SAndroid Build Coastguard Worker     {
1258*76559068SAndroid Build Coastguard Worker       ScopedLock L(Region->FLLock);
1259*76559068SAndroid Build Coastguard Worker 
1260*76559068SAndroid Build Coastguard Worker       BytesInFreeList = Region->MemMapInfo.AllocatedUser -
1261*76559068SAndroid Build Coastguard Worker                         (Region->FreeListInfo.PoppedBlocks -
1262*76559068SAndroid Build Coastguard Worker                          Region->FreeListInfo.PushedBlocks) *
1263*76559068SAndroid Build Coastguard Worker                             BlockSize;
1264*76559068SAndroid Build Coastguard Worker       if (UNLIKELY(BytesInFreeList == 0))
1265*76559068SAndroid Build Coastguard Worker         return false;
1266*76559068SAndroid Build Coastguard Worker 
1267*76559068SAndroid Build Coastguard Worker       // ==================================================================== //
1268*76559068SAndroid Build Coastguard Worker       // 1. Check if we have enough free blocks and if it's worth doing a page
1269*76559068SAndroid Build Coastguard Worker       //    release.
1270*76559068SAndroid Build Coastguard Worker       // ==================================================================== //
1271*76559068SAndroid Build Coastguard Worker       if (ReleaseType != ReleaseToOS::ForceAll &&
1272*76559068SAndroid Build Coastguard Worker           !hasChanceToReleasePages(Region, BlockSize, BytesInFreeList,
1273*76559068SAndroid Build Coastguard Worker                                    ReleaseType)) {
1274*76559068SAndroid Build Coastguard Worker         return 0;
1275*76559068SAndroid Build Coastguard Worker       }
1276*76559068SAndroid Build Coastguard Worker 
1277*76559068SAndroid Build Coastguard Worker       // Given that we will unlock the freelist for block operations, cache the
1278*76559068SAndroid Build Coastguard Worker       // value here so that when we are adapting the `TryReleaseThreshold`
1279*76559068SAndroid Build Coastguard Worker       // later, we are using the right metric.
1280*76559068SAndroid Build Coastguard Worker       RegionPushedBytesDelta =
1281*76559068SAndroid Build Coastguard Worker           BytesInFreeList - Region->ReleaseInfo.BytesInFreeListAtLastCheckpoint;
1282*76559068SAndroid Build Coastguard Worker 
1283*76559068SAndroid Build Coastguard Worker       // ==================================================================== //
1284*76559068SAndroid Build Coastguard Worker       // 2. Determine which groups can release the pages. Use a heuristic to
1285*76559068SAndroid Build Coastguard Worker       //    gather groups that are candidates for doing a release.
1286*76559068SAndroid Build Coastguard Worker       // ==================================================================== //
1287*76559068SAndroid Build Coastguard Worker       if (ReleaseType == ReleaseToOS::ForceAll) {
1288*76559068SAndroid Build Coastguard Worker         GroupsToRelease = Region->FreeListInfo.BlockList;
1289*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.clear();
1290*76559068SAndroid Build Coastguard Worker       } else {
1291*76559068SAndroid Build Coastguard Worker         GroupsToRelease =
1292*76559068SAndroid Build Coastguard Worker             collectGroupsToRelease(Region, BlockSize, AllocatedUserEnd,
1293*76559068SAndroid Build Coastguard Worker                                    getCompactPtrBaseByClassId(ClassId));
1294*76559068SAndroid Build Coastguard Worker       }
1295*76559068SAndroid Build Coastguard Worker       if (GroupsToRelease.empty())
1296*76559068SAndroid Build Coastguard Worker         return 0;
1297*76559068SAndroid Build Coastguard Worker     }
1298*76559068SAndroid Build Coastguard Worker 
1299*76559068SAndroid Build Coastguard Worker     // Note that we have extracted the `GroupsToRelease` from region freelist.
1300*76559068SAndroid Build Coastguard Worker     // It's safe to let pushBlocks()/popBlocks() access the remaining region
1301*76559068SAndroid Build Coastguard Worker     // freelist. In the steps 3 and 4, we will temporarily release the FLLock
1302*76559068SAndroid Build Coastguard Worker     // and lock it again before step 5.
1303*76559068SAndroid Build Coastguard Worker 
1304*76559068SAndroid Build Coastguard Worker     // ==================================================================== //
1305*76559068SAndroid Build Coastguard Worker     // 3. Mark the free blocks in `GroupsToRelease` in the `PageReleaseContext`.
1306*76559068SAndroid Build Coastguard Worker     //    Then we can tell which pages are in-use by querying
1307*76559068SAndroid Build Coastguard Worker     //    `PageReleaseContext`.
1308*76559068SAndroid Build Coastguard Worker     // ==================================================================== //
1309*76559068SAndroid Build Coastguard Worker     PageReleaseContext Context =
1310*76559068SAndroid Build Coastguard Worker         markFreeBlocks(Region, BlockSize, AllocatedUserEnd,
1311*76559068SAndroid Build Coastguard Worker                        getCompactPtrBaseByClassId(ClassId), GroupsToRelease);
1312*76559068SAndroid Build Coastguard Worker     if (UNLIKELY(!Context.hasBlockMarked())) {
1313*76559068SAndroid Build Coastguard Worker       mergeGroupsToReleaseBack(Region, GroupsToRelease);
1314*76559068SAndroid Build Coastguard Worker       return 0;
1315*76559068SAndroid Build Coastguard Worker     }
1316*76559068SAndroid Build Coastguard Worker 
1317*76559068SAndroid Build Coastguard Worker     // ==================================================================== //
1318*76559068SAndroid Build Coastguard Worker     // 4. Release the unused physical pages back to the OS.
1319*76559068SAndroid Build Coastguard Worker     // ==================================================================== //
1320*76559068SAndroid Build Coastguard Worker     RegionReleaseRecorder<MemMapT> Recorder(&Region->MemMapInfo.MemMap,
1321*76559068SAndroid Build Coastguard Worker                                             Region->RegionBeg,
1322*76559068SAndroid Build Coastguard Worker                                             Context.getReleaseOffset());
1323*76559068SAndroid Build Coastguard Worker     auto SkipRegion = [](UNUSED uptr RegionIndex) { return false; };
1324*76559068SAndroid Build Coastguard Worker     releaseFreeMemoryToOS(Context, Recorder, SkipRegion);
1325*76559068SAndroid Build Coastguard Worker     if (Recorder.getReleasedRangesCount() > 0) {
1326*76559068SAndroid Build Coastguard Worker       // This is the case that we didn't hit the release threshold but it has
1327*76559068SAndroid Build Coastguard Worker       // been past a certain period of time. Thus we try to release some pages
1328*76559068SAndroid Build Coastguard Worker       // and if it does release some additional pages, it's hint that we are
1329*76559068SAndroid Build Coastguard Worker       // able to lower the threshold. Currently, this case happens when the
1330*76559068SAndroid Build Coastguard Worker       // `RegionPushedBytesDelta` is over half of the `TryReleaseThreshold`. As
1331*76559068SAndroid Build Coastguard Worker       // a result, we shrink the threshold to half accordingly.
1332*76559068SAndroid Build Coastguard Worker       // TODO(chiahungduan): Apply the same adjustment strategy to small blocks.
1333*76559068SAndroid Build Coastguard Worker       if (!isSmallBlock(BlockSize)) {
1334*76559068SAndroid Build Coastguard Worker         if (RegionPushedBytesDelta < Region->ReleaseInfo.TryReleaseThreshold &&
1335*76559068SAndroid Build Coastguard Worker             Recorder.getReleasedBytes() >
1336*76559068SAndroid Build Coastguard Worker                 Region->ReleaseInfo.LastReleasedBytes +
1337*76559068SAndroid Build Coastguard Worker                     getMinReleaseAttemptSize(BlockSize)) {
1338*76559068SAndroid Build Coastguard Worker           Region->ReleaseInfo.TryReleaseThreshold =
1339*76559068SAndroid Build Coastguard Worker               Max(Region->ReleaseInfo.TryReleaseThreshold / 2,
1340*76559068SAndroid Build Coastguard Worker                   getMinReleaseAttemptSize(BlockSize));
1341*76559068SAndroid Build Coastguard Worker         }
1342*76559068SAndroid Build Coastguard Worker       }
1343*76559068SAndroid Build Coastguard Worker 
1344*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.BytesInFreeListAtLastCheckpoint = BytesInFreeList;
1345*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.RangesReleased += Recorder.getReleasedRangesCount();
1346*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.LastReleasedBytes = Recorder.getReleasedBytes();
1347*76559068SAndroid Build Coastguard Worker     }
1348*76559068SAndroid Build Coastguard Worker     Region->ReleaseInfo.LastReleaseAtNs = getMonotonicTimeFast();
1349*76559068SAndroid Build Coastguard Worker 
1350*76559068SAndroid Build Coastguard Worker     if (Region->ReleaseInfo.PendingPushedBytesDelta > 0) {
1351*76559068SAndroid Build Coastguard Worker       // Instead of increasing the threshold by the amount of
1352*76559068SAndroid Build Coastguard Worker       // `PendingPushedBytesDelta`, we only increase half of the amount so that
1353*76559068SAndroid Build Coastguard Worker       // it won't be a leap (which may lead to higher memory pressure) because
1354*76559068SAndroid Build Coastguard Worker       // of certain memory usage bursts which don't happen frequently.
1355*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.TryReleaseThreshold +=
1356*76559068SAndroid Build Coastguard Worker           Region->ReleaseInfo.PendingPushedBytesDelta / 2;
1357*76559068SAndroid Build Coastguard Worker       // This is another guard of avoiding the growth of threshold indefinitely.
1358*76559068SAndroid Build Coastguard Worker       // Note that we may consider to make this configurable if we have a better
1359*76559068SAndroid Build Coastguard Worker       // way to model this.
1360*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.TryReleaseThreshold = Min<uptr>(
1361*76559068SAndroid Build Coastguard Worker           Region->ReleaseInfo.TryReleaseThreshold, (1UL << GroupSizeLog) / 2);
1362*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.PendingPushedBytesDelta = 0;
1363*76559068SAndroid Build Coastguard Worker     }
1364*76559068SAndroid Build Coastguard Worker 
1365*76559068SAndroid Build Coastguard Worker     // ====================================================================== //
1366*76559068SAndroid Build Coastguard Worker     // 5. Merge the `GroupsToRelease` back to the freelist.
1367*76559068SAndroid Build Coastguard Worker     // ====================================================================== //
1368*76559068SAndroid Build Coastguard Worker     mergeGroupsToReleaseBack(Region, GroupsToRelease);
1369*76559068SAndroid Build Coastguard Worker 
1370*76559068SAndroid Build Coastguard Worker     return Recorder.getReleasedBytes();
1371*76559068SAndroid Build Coastguard Worker   }
1372*76559068SAndroid Build Coastguard Worker 
hasChanceToReleasePages(RegionInfo * Region,uptr BlockSize,uptr BytesInFreeList,ReleaseToOS ReleaseType)1373*76559068SAndroid Build Coastguard Worker   bool hasChanceToReleasePages(RegionInfo *Region, uptr BlockSize,
1374*76559068SAndroid Build Coastguard Worker                                uptr BytesInFreeList, ReleaseToOS ReleaseType)
1375*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock, Region->FLLock) {
1376*76559068SAndroid Build Coastguard Worker     DCHECK_GE(Region->FreeListInfo.PoppedBlocks,
1377*76559068SAndroid Build Coastguard Worker               Region->FreeListInfo.PushedBlocks);
1378*76559068SAndroid Build Coastguard Worker     // Always update `BytesInFreeListAtLastCheckpoint` with the smallest value
1379*76559068SAndroid Build Coastguard Worker     // so that we won't underestimate the releasable pages. For example, the
1380*76559068SAndroid Build Coastguard Worker     // following is the region usage,
1381*76559068SAndroid Build Coastguard Worker     //
1382*76559068SAndroid Build Coastguard Worker     //  BytesInFreeListAtLastCheckpoint   AllocatedUser
1383*76559068SAndroid Build Coastguard Worker     //                v                         v
1384*76559068SAndroid Build Coastguard Worker     //  |--------------------------------------->
1385*76559068SAndroid Build Coastguard Worker     //         ^                   ^
1386*76559068SAndroid Build Coastguard Worker     //  BytesInFreeList     ReleaseThreshold
1387*76559068SAndroid Build Coastguard Worker     //
1388*76559068SAndroid Build Coastguard Worker     // In general, if we have collected enough bytes and the amount of free
1389*76559068SAndroid Build Coastguard Worker     // bytes meets the ReleaseThreshold, we will try to do page release. If we
1390*76559068SAndroid Build Coastguard Worker     // don't update `BytesInFreeListAtLastCheckpoint` when the current
1391*76559068SAndroid Build Coastguard Worker     // `BytesInFreeList` is smaller, we may take longer time to wait for enough
1392*76559068SAndroid Build Coastguard Worker     // freed blocks because we miss the bytes between
1393*76559068SAndroid Build Coastguard Worker     // (BytesInFreeListAtLastCheckpoint - BytesInFreeList).
1394*76559068SAndroid Build Coastguard Worker     if (BytesInFreeList <=
1395*76559068SAndroid Build Coastguard Worker         Region->ReleaseInfo.BytesInFreeListAtLastCheckpoint) {
1396*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.BytesInFreeListAtLastCheckpoint = BytesInFreeList;
1397*76559068SAndroid Build Coastguard Worker     }
1398*76559068SAndroid Build Coastguard Worker 
1399*76559068SAndroid Build Coastguard Worker     const uptr RegionPushedBytesDelta =
1400*76559068SAndroid Build Coastguard Worker         BytesInFreeList - Region->ReleaseInfo.BytesInFreeListAtLastCheckpoint;
1401*76559068SAndroid Build Coastguard Worker 
1402*76559068SAndroid Build Coastguard Worker     if (ReleaseType == ReleaseToOS::Normal) {
1403*76559068SAndroid Build Coastguard Worker       if (RegionPushedBytesDelta < Region->ReleaseInfo.TryReleaseThreshold / 2)
1404*76559068SAndroid Build Coastguard Worker         return false;
1405*76559068SAndroid Build Coastguard Worker 
1406*76559068SAndroid Build Coastguard Worker       const s64 IntervalMs = atomic_load_relaxed(&ReleaseToOsIntervalMs);
1407*76559068SAndroid Build Coastguard Worker       if (IntervalMs < 0)
1408*76559068SAndroid Build Coastguard Worker         return false;
1409*76559068SAndroid Build Coastguard Worker 
1410*76559068SAndroid Build Coastguard Worker       const u64 IntervalNs = static_cast<u64>(IntervalMs) * 1000000;
1411*76559068SAndroid Build Coastguard Worker       const u64 CurTimeNs = getMonotonicTimeFast();
1412*76559068SAndroid Build Coastguard Worker       const u64 DiffSinceLastReleaseNs =
1413*76559068SAndroid Build Coastguard Worker           CurTimeNs - Region->ReleaseInfo.LastReleaseAtNs;
1414*76559068SAndroid Build Coastguard Worker 
1415*76559068SAndroid Build Coastguard Worker       // At here, `RegionPushedBytesDelta` is more than half of
1416*76559068SAndroid Build Coastguard Worker       // `TryReleaseThreshold`. If the last release happened 2 release interval
1417*76559068SAndroid Build Coastguard Worker       // before, we will still try to see if there's any chance to release some
1418*76559068SAndroid Build Coastguard Worker       // memory even it doesn't exceed the threshold.
1419*76559068SAndroid Build Coastguard Worker       if (RegionPushedBytesDelta < Region->ReleaseInfo.TryReleaseThreshold) {
1420*76559068SAndroid Build Coastguard Worker         // We want the threshold to have a shorter response time to the variant
1421*76559068SAndroid Build Coastguard Worker         // memory usage patterns. According to data collected during experiments
1422*76559068SAndroid Build Coastguard Worker         // (which were done with 1, 2, 4, 8 intervals), `2` strikes the better
1423*76559068SAndroid Build Coastguard Worker         // balance between the memory usage and number of page release attempts.
1424*76559068SAndroid Build Coastguard Worker         if (DiffSinceLastReleaseNs < 2 * IntervalNs)
1425*76559068SAndroid Build Coastguard Worker           return false;
1426*76559068SAndroid Build Coastguard Worker       } else if (DiffSinceLastReleaseNs < IntervalNs) {
1427*76559068SAndroid Build Coastguard Worker         // In this case, we are over the threshold but we just did some page
1428*76559068SAndroid Build Coastguard Worker         // release in the same release interval. This is a hint that we may want
1429*76559068SAndroid Build Coastguard Worker         // a higher threshold so that we can release more memory at once.
1430*76559068SAndroid Build Coastguard Worker         // `TryReleaseThreshold` will be adjusted according to how many bytes
1431*76559068SAndroid Build Coastguard Worker         // are not released, i.e., the `PendingPushedBytesdelta` here.
1432*76559068SAndroid Build Coastguard Worker         // TODO(chiahungduan): Apply the same adjustment strategy to small
1433*76559068SAndroid Build Coastguard Worker         // blocks.
1434*76559068SAndroid Build Coastguard Worker         if (!isSmallBlock(BlockSize))
1435*76559068SAndroid Build Coastguard Worker           Region->ReleaseInfo.PendingPushedBytesDelta = RegionPushedBytesDelta;
1436*76559068SAndroid Build Coastguard Worker 
1437*76559068SAndroid Build Coastguard Worker         // Memory was returned recently.
1438*76559068SAndroid Build Coastguard Worker         return false;
1439*76559068SAndroid Build Coastguard Worker       }
1440*76559068SAndroid Build Coastguard Worker     } // if (ReleaseType == ReleaseToOS::Normal)
1441*76559068SAndroid Build Coastguard Worker 
1442*76559068SAndroid Build Coastguard Worker     return true;
1443*76559068SAndroid Build Coastguard Worker   }
1444*76559068SAndroid Build Coastguard Worker 
1445*76559068SAndroid Build Coastguard Worker   SinglyLinkedList<BatchGroupT>
collectGroupsToRelease(RegionInfo * Region,const uptr BlockSize,const uptr AllocatedUserEnd,const uptr CompactPtrBase)1446*76559068SAndroid Build Coastguard Worker   collectGroupsToRelease(RegionInfo *Region, const uptr BlockSize,
1447*76559068SAndroid Build Coastguard Worker                          const uptr AllocatedUserEnd, const uptr CompactPtrBase)
1448*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock, Region->FLLock) {
1449*76559068SAndroid Build Coastguard Worker     const uptr GroupSize = (1UL << GroupSizeLog);
1450*76559068SAndroid Build Coastguard Worker     const uptr PageSize = getPageSizeCached();
1451*76559068SAndroid Build Coastguard Worker     SinglyLinkedList<BatchGroupT> GroupsToRelease;
1452*76559068SAndroid Build Coastguard Worker 
1453*76559068SAndroid Build Coastguard Worker     // We are examining each group and will take the minimum distance to the
1454*76559068SAndroid Build Coastguard Worker     // release threshold as the next `TryReleaseThreshold`. Note that if the
1455*76559068SAndroid Build Coastguard Worker     // size of free blocks has reached the release threshold, the distance to
1456*76559068SAndroid Build Coastguard Worker     // the next release will be PageSize * SmallerBlockReleasePageDelta. See the
1457*76559068SAndroid Build Coastguard Worker     // comment on `SmallerBlockReleasePageDelta` for more details.
1458*76559068SAndroid Build Coastguard Worker     uptr MinDistToThreshold = GroupSize;
1459*76559068SAndroid Build Coastguard Worker 
1460*76559068SAndroid Build Coastguard Worker     for (BatchGroupT *BG = Region->FreeListInfo.BlockList.front(),
1461*76559068SAndroid Build Coastguard Worker                      *Prev = nullptr;
1462*76559068SAndroid Build Coastguard Worker          BG != nullptr;) {
1463*76559068SAndroid Build Coastguard Worker       // Group boundary is always GroupSize-aligned from CompactPtr base. The
1464*76559068SAndroid Build Coastguard Worker       // layout of memory groups is like,
1465*76559068SAndroid Build Coastguard Worker       //
1466*76559068SAndroid Build Coastguard Worker       //     (CompactPtrBase)
1467*76559068SAndroid Build Coastguard Worker       // #1 CompactPtrGroupBase   #2 CompactPtrGroupBase            ...
1468*76559068SAndroid Build Coastguard Worker       //           |                       |                       |
1469*76559068SAndroid Build Coastguard Worker       //           v                       v                       v
1470*76559068SAndroid Build Coastguard Worker       //           +-----------------------+-----------------------+
1471*76559068SAndroid Build Coastguard Worker       //            \                     / \                     /
1472*76559068SAndroid Build Coastguard Worker       //             ---   GroupSize   ---   ---   GroupSize   ---
1473*76559068SAndroid Build Coastguard Worker       //
1474*76559068SAndroid Build Coastguard Worker       // After decompacting the CompactPtrGroupBase, we expect the alignment
1475*76559068SAndroid Build Coastguard Worker       // property is held as well.
1476*76559068SAndroid Build Coastguard Worker       const uptr BatchGroupBase =
1477*76559068SAndroid Build Coastguard Worker           decompactGroupBase(CompactPtrBase, BG->CompactPtrGroupBase);
1478*76559068SAndroid Build Coastguard Worker       DCHECK_LE(Region->RegionBeg, BatchGroupBase);
1479*76559068SAndroid Build Coastguard Worker       DCHECK_GE(AllocatedUserEnd, BatchGroupBase);
1480*76559068SAndroid Build Coastguard Worker       DCHECK_EQ((Region->RegionBeg - BatchGroupBase) % GroupSize, 0U);
1481*76559068SAndroid Build Coastguard Worker       // TransferBatches are pushed in front of BG.Batches. The first one may
1482*76559068SAndroid Build Coastguard Worker       // not have all caches used.
1483*76559068SAndroid Build Coastguard Worker       const uptr NumBlocks = (BG->Batches.size() - 1) * BG->MaxCachedPerBatch +
1484*76559068SAndroid Build Coastguard Worker                              BG->Batches.front()->getCount();
1485*76559068SAndroid Build Coastguard Worker       const uptr BytesInBG = NumBlocks * BlockSize;
1486*76559068SAndroid Build Coastguard Worker 
1487*76559068SAndroid Build Coastguard Worker       if (BytesInBG <= BG->BytesInBGAtLastCheckpoint) {
1488*76559068SAndroid Build Coastguard Worker         BG->BytesInBGAtLastCheckpoint = BytesInBG;
1489*76559068SAndroid Build Coastguard Worker         Prev = BG;
1490*76559068SAndroid Build Coastguard Worker         BG = BG->Next;
1491*76559068SAndroid Build Coastguard Worker         continue;
1492*76559068SAndroid Build Coastguard Worker       }
1493*76559068SAndroid Build Coastguard Worker 
1494*76559068SAndroid Build Coastguard Worker       const uptr PushedBytesDelta = BytesInBG - BG->BytesInBGAtLastCheckpoint;
1495*76559068SAndroid Build Coastguard Worker       if (PushedBytesDelta < getMinReleaseAttemptSize(BlockSize)) {
1496*76559068SAndroid Build Coastguard Worker         Prev = BG;
1497*76559068SAndroid Build Coastguard Worker         BG = BG->Next;
1498*76559068SAndroid Build Coastguard Worker         continue;
1499*76559068SAndroid Build Coastguard Worker       }
1500*76559068SAndroid Build Coastguard Worker 
1501*76559068SAndroid Build Coastguard Worker       // Given the randomness property, we try to release the pages only if the
1502*76559068SAndroid Build Coastguard Worker       // bytes used by free blocks exceed certain proportion of group size. Note
1503*76559068SAndroid Build Coastguard Worker       // that this heuristic only applies when all the spaces in a BatchGroup
1504*76559068SAndroid Build Coastguard Worker       // are allocated.
1505*76559068SAndroid Build Coastguard Worker       if (isSmallBlock(BlockSize)) {
1506*76559068SAndroid Build Coastguard Worker         const uptr BatchGroupEnd = BatchGroupBase + GroupSize;
1507*76559068SAndroid Build Coastguard Worker         const uptr AllocatedGroupSize = AllocatedUserEnd >= BatchGroupEnd
1508*76559068SAndroid Build Coastguard Worker                                             ? GroupSize
1509*76559068SAndroid Build Coastguard Worker                                             : AllocatedUserEnd - BatchGroupBase;
1510*76559068SAndroid Build Coastguard Worker         const uptr ReleaseThreshold =
1511*76559068SAndroid Build Coastguard Worker             (AllocatedGroupSize * (100 - 1U - BlockSize / 16U)) / 100U;
1512*76559068SAndroid Build Coastguard Worker         const bool HighDensity = BytesInBG >= ReleaseThreshold;
1513*76559068SAndroid Build Coastguard Worker         const bool MayHaveReleasedAll = NumBlocks >= (GroupSize / BlockSize);
1514*76559068SAndroid Build Coastguard Worker         // If all blocks in the group are released, we will do range marking
1515*76559068SAndroid Build Coastguard Worker         // which is fast. Otherwise, we will wait until we have accumulated
1516*76559068SAndroid Build Coastguard Worker         // a certain amount of free memory.
1517*76559068SAndroid Build Coastguard Worker         const bool ReachReleaseDelta =
1518*76559068SAndroid Build Coastguard Worker             MayHaveReleasedAll
1519*76559068SAndroid Build Coastguard Worker                 ? true
1520*76559068SAndroid Build Coastguard Worker                 : PushedBytesDelta >= PageSize * SmallerBlockReleasePageDelta;
1521*76559068SAndroid Build Coastguard Worker 
1522*76559068SAndroid Build Coastguard Worker         if (!HighDensity) {
1523*76559068SAndroid Build Coastguard Worker           DCHECK_LE(BytesInBG, ReleaseThreshold);
1524*76559068SAndroid Build Coastguard Worker           // The following is the usage of a memroy group,
1525*76559068SAndroid Build Coastguard Worker           //
1526*76559068SAndroid Build Coastguard Worker           //     BytesInBG             ReleaseThreshold
1527*76559068SAndroid Build Coastguard Worker           //  /             \                 v
1528*76559068SAndroid Build Coastguard Worker           //  +---+---------------------------+-----+
1529*76559068SAndroid Build Coastguard Worker           //  |   |         |                 |     |
1530*76559068SAndroid Build Coastguard Worker           //  +---+---------------------------+-----+
1531*76559068SAndroid Build Coastguard Worker           //       \        /                       ^
1532*76559068SAndroid Build Coastguard Worker           //    PushedBytesDelta                 GroupEnd
1533*76559068SAndroid Build Coastguard Worker           MinDistToThreshold =
1534*76559068SAndroid Build Coastguard Worker               Min(MinDistToThreshold,
1535*76559068SAndroid Build Coastguard Worker                   ReleaseThreshold - BytesInBG + PushedBytesDelta);
1536*76559068SAndroid Build Coastguard Worker         } else {
1537*76559068SAndroid Build Coastguard Worker           // If it reaches high density at this round, the next time we will try
1538*76559068SAndroid Build Coastguard Worker           // to release is based on SmallerBlockReleasePageDelta
1539*76559068SAndroid Build Coastguard Worker           MinDistToThreshold =
1540*76559068SAndroid Build Coastguard Worker               Min(MinDistToThreshold, PageSize * SmallerBlockReleasePageDelta);
1541*76559068SAndroid Build Coastguard Worker         }
1542*76559068SAndroid Build Coastguard Worker 
1543*76559068SAndroid Build Coastguard Worker         if (!HighDensity || !ReachReleaseDelta) {
1544*76559068SAndroid Build Coastguard Worker           Prev = BG;
1545*76559068SAndroid Build Coastguard Worker           BG = BG->Next;
1546*76559068SAndroid Build Coastguard Worker           continue;
1547*76559068SAndroid Build Coastguard Worker         }
1548*76559068SAndroid Build Coastguard Worker       }
1549*76559068SAndroid Build Coastguard Worker 
1550*76559068SAndroid Build Coastguard Worker       // If `BG` is the first BatchGroupT in the list, we only need to advance
1551*76559068SAndroid Build Coastguard Worker       // `BG` and call FreeListInfo.BlockList::pop_front(). No update is needed
1552*76559068SAndroid Build Coastguard Worker       // for `Prev`.
1553*76559068SAndroid Build Coastguard Worker       //
1554*76559068SAndroid Build Coastguard Worker       //         (BG)   (BG->Next)
1555*76559068SAndroid Build Coastguard Worker       // Prev     Cur      BG
1556*76559068SAndroid Build Coastguard Worker       //   |       |       |
1557*76559068SAndroid Build Coastguard Worker       //   v       v       v
1558*76559068SAndroid Build Coastguard Worker       //  nil     +--+    +--+
1559*76559068SAndroid Build Coastguard Worker       //          |X | -> |  | -> ...
1560*76559068SAndroid Build Coastguard Worker       //          +--+    +--+
1561*76559068SAndroid Build Coastguard Worker       //
1562*76559068SAndroid Build Coastguard Worker       // Otherwise, `Prev` will be used to extract the `Cur` from the
1563*76559068SAndroid Build Coastguard Worker       // `FreeListInfo.BlockList`.
1564*76559068SAndroid Build Coastguard Worker       //
1565*76559068SAndroid Build Coastguard Worker       //         (BG)   (BG->Next)
1566*76559068SAndroid Build Coastguard Worker       // Prev     Cur      BG
1567*76559068SAndroid Build Coastguard Worker       //   |       |       |
1568*76559068SAndroid Build Coastguard Worker       //   v       v       v
1569*76559068SAndroid Build Coastguard Worker       //  +--+    +--+    +--+
1570*76559068SAndroid Build Coastguard Worker       //  |  | -> |X | -> |  | -> ...
1571*76559068SAndroid Build Coastguard Worker       //  +--+    +--+    +--+
1572*76559068SAndroid Build Coastguard Worker       //
1573*76559068SAndroid Build Coastguard Worker       // After FreeListInfo.BlockList::extract(),
1574*76559068SAndroid Build Coastguard Worker       //
1575*76559068SAndroid Build Coastguard Worker       // Prev     Cur       BG
1576*76559068SAndroid Build Coastguard Worker       //   |       |        |
1577*76559068SAndroid Build Coastguard Worker       //   v       v        v
1578*76559068SAndroid Build Coastguard Worker       //  +--+    +--+     +--+
1579*76559068SAndroid Build Coastguard Worker       //  |  |-+  |X |  +->|  | -> ...
1580*76559068SAndroid Build Coastguard Worker       //  +--+ |  +--+  |  +--+
1581*76559068SAndroid Build Coastguard Worker       //       +--------+
1582*76559068SAndroid Build Coastguard Worker       //
1583*76559068SAndroid Build Coastguard Worker       // Note that we need to advance before pushing this BatchGroup to
1584*76559068SAndroid Build Coastguard Worker       // GroupsToRelease because it's a destructive operation.
1585*76559068SAndroid Build Coastguard Worker 
1586*76559068SAndroid Build Coastguard Worker       BatchGroupT *Cur = BG;
1587*76559068SAndroid Build Coastguard Worker       BG = BG->Next;
1588*76559068SAndroid Build Coastguard Worker 
1589*76559068SAndroid Build Coastguard Worker       // Ideally, we may want to update this only after successful release.
1590*76559068SAndroid Build Coastguard Worker       // However, for smaller blocks, each block marking is a costly operation.
1591*76559068SAndroid Build Coastguard Worker       // Therefore, we update it earlier.
1592*76559068SAndroid Build Coastguard Worker       // TODO: Consider updating this after releasing pages if `ReleaseRecorder`
1593*76559068SAndroid Build Coastguard Worker       // can tell the released bytes in each group.
1594*76559068SAndroid Build Coastguard Worker       Cur->BytesInBGAtLastCheckpoint = BytesInBG;
1595*76559068SAndroid Build Coastguard Worker 
1596*76559068SAndroid Build Coastguard Worker       if (Prev != nullptr)
1597*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.extract(Prev, Cur);
1598*76559068SAndroid Build Coastguard Worker       else
1599*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.pop_front();
1600*76559068SAndroid Build Coastguard Worker       GroupsToRelease.push_back(Cur);
1601*76559068SAndroid Build Coastguard Worker     }
1602*76559068SAndroid Build Coastguard Worker 
1603*76559068SAndroid Build Coastguard Worker     // Only small blocks have the adaptive `TryReleaseThreshold`.
1604*76559068SAndroid Build Coastguard Worker     if (isSmallBlock(BlockSize)) {
1605*76559068SAndroid Build Coastguard Worker       // If the MinDistToThreshold is not updated, that means each memory group
1606*76559068SAndroid Build Coastguard Worker       // may have only pushed less than a page size. In that case, just set it
1607*76559068SAndroid Build Coastguard Worker       // back to normal.
1608*76559068SAndroid Build Coastguard Worker       if (MinDistToThreshold == GroupSize)
1609*76559068SAndroid Build Coastguard Worker         MinDistToThreshold = PageSize * SmallerBlockReleasePageDelta;
1610*76559068SAndroid Build Coastguard Worker       Region->ReleaseInfo.TryReleaseThreshold = MinDistToThreshold;
1611*76559068SAndroid Build Coastguard Worker     }
1612*76559068SAndroid Build Coastguard Worker 
1613*76559068SAndroid Build Coastguard Worker     return GroupsToRelease;
1614*76559068SAndroid Build Coastguard Worker   }
1615*76559068SAndroid Build Coastguard Worker 
1616*76559068SAndroid Build Coastguard Worker   PageReleaseContext
markFreeBlocks(RegionInfo * Region,const uptr BlockSize,const uptr AllocatedUserEnd,const uptr CompactPtrBase,SinglyLinkedList<BatchGroupT> & GroupsToRelease)1617*76559068SAndroid Build Coastguard Worker   markFreeBlocks(RegionInfo *Region, const uptr BlockSize,
1618*76559068SAndroid Build Coastguard Worker                  const uptr AllocatedUserEnd, const uptr CompactPtrBase,
1619*76559068SAndroid Build Coastguard Worker                  SinglyLinkedList<BatchGroupT> &GroupsToRelease)
1620*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock) EXCLUDES(Region->FLLock) {
1621*76559068SAndroid Build Coastguard Worker     const uptr GroupSize = (1UL << GroupSizeLog);
1622*76559068SAndroid Build Coastguard Worker     auto DecompactPtr = [CompactPtrBase](CompactPtrT CompactPtr) {
1623*76559068SAndroid Build Coastguard Worker       return decompactPtrInternal(CompactPtrBase, CompactPtr);
1624*76559068SAndroid Build Coastguard Worker     };
1625*76559068SAndroid Build Coastguard Worker 
1626*76559068SAndroid Build Coastguard Worker     const uptr ReleaseBase = decompactGroupBase(
1627*76559068SAndroid Build Coastguard Worker         CompactPtrBase, GroupsToRelease.front()->CompactPtrGroupBase);
1628*76559068SAndroid Build Coastguard Worker     const uptr LastGroupEnd =
1629*76559068SAndroid Build Coastguard Worker         Min(decompactGroupBase(CompactPtrBase,
1630*76559068SAndroid Build Coastguard Worker                                GroupsToRelease.back()->CompactPtrGroupBase) +
1631*76559068SAndroid Build Coastguard Worker                 GroupSize,
1632*76559068SAndroid Build Coastguard Worker             AllocatedUserEnd);
1633*76559068SAndroid Build Coastguard Worker     // The last block may straddle the group boundary. Rounding up to BlockSize
1634*76559068SAndroid Build Coastguard Worker     // to get the exact range.
1635*76559068SAndroid Build Coastguard Worker     const uptr ReleaseEnd =
1636*76559068SAndroid Build Coastguard Worker         roundUpSlow(LastGroupEnd - Region->RegionBeg, BlockSize) +
1637*76559068SAndroid Build Coastguard Worker         Region->RegionBeg;
1638*76559068SAndroid Build Coastguard Worker     const uptr ReleaseRangeSize = ReleaseEnd - ReleaseBase;
1639*76559068SAndroid Build Coastguard Worker     const uptr ReleaseOffset = ReleaseBase - Region->RegionBeg;
1640*76559068SAndroid Build Coastguard Worker 
1641*76559068SAndroid Build Coastguard Worker     PageReleaseContext Context(BlockSize, /*NumberOfRegions=*/1U,
1642*76559068SAndroid Build Coastguard Worker                                ReleaseRangeSize, ReleaseOffset);
1643*76559068SAndroid Build Coastguard Worker     // We may not be able to do the page release in a rare case that we may
1644*76559068SAndroid Build Coastguard Worker     // fail on PageMap allocation.
1645*76559068SAndroid Build Coastguard Worker     if (UNLIKELY(!Context.ensurePageMapAllocated()))
1646*76559068SAndroid Build Coastguard Worker       return Context;
1647*76559068SAndroid Build Coastguard Worker 
1648*76559068SAndroid Build Coastguard Worker     for (BatchGroupT &BG : GroupsToRelease) {
1649*76559068SAndroid Build Coastguard Worker       const uptr BatchGroupBase =
1650*76559068SAndroid Build Coastguard Worker           decompactGroupBase(CompactPtrBase, BG.CompactPtrGroupBase);
1651*76559068SAndroid Build Coastguard Worker       const uptr BatchGroupEnd = BatchGroupBase + GroupSize;
1652*76559068SAndroid Build Coastguard Worker       const uptr AllocatedGroupSize = AllocatedUserEnd >= BatchGroupEnd
1653*76559068SAndroid Build Coastguard Worker                                           ? GroupSize
1654*76559068SAndroid Build Coastguard Worker                                           : AllocatedUserEnd - BatchGroupBase;
1655*76559068SAndroid Build Coastguard Worker       const uptr BatchGroupUsedEnd = BatchGroupBase + AllocatedGroupSize;
1656*76559068SAndroid Build Coastguard Worker       const bool MayContainLastBlockInRegion =
1657*76559068SAndroid Build Coastguard Worker           BatchGroupUsedEnd == AllocatedUserEnd;
1658*76559068SAndroid Build Coastguard Worker       const bool BlockAlignedWithUsedEnd =
1659*76559068SAndroid Build Coastguard Worker           (BatchGroupUsedEnd - Region->RegionBeg) % BlockSize == 0;
1660*76559068SAndroid Build Coastguard Worker 
1661*76559068SAndroid Build Coastguard Worker       uptr MaxContainedBlocks = AllocatedGroupSize / BlockSize;
1662*76559068SAndroid Build Coastguard Worker       if (!BlockAlignedWithUsedEnd)
1663*76559068SAndroid Build Coastguard Worker         ++MaxContainedBlocks;
1664*76559068SAndroid Build Coastguard Worker 
1665*76559068SAndroid Build Coastguard Worker       const uptr NumBlocks = (BG.Batches.size() - 1) * BG.MaxCachedPerBatch +
1666*76559068SAndroid Build Coastguard Worker                              BG.Batches.front()->getCount();
1667*76559068SAndroid Build Coastguard Worker 
1668*76559068SAndroid Build Coastguard Worker       if (NumBlocks == MaxContainedBlocks) {
1669*76559068SAndroid Build Coastguard Worker         for (const auto &It : BG.Batches) {
1670*76559068SAndroid Build Coastguard Worker           if (&It != BG.Batches.front())
1671*76559068SAndroid Build Coastguard Worker             DCHECK_EQ(It.getCount(), BG.MaxCachedPerBatch);
1672*76559068SAndroid Build Coastguard Worker           for (u16 I = 0; I < It.getCount(); ++I)
1673*76559068SAndroid Build Coastguard Worker             DCHECK_EQ(compactPtrGroup(It.get(I)), BG.CompactPtrGroupBase);
1674*76559068SAndroid Build Coastguard Worker         }
1675*76559068SAndroid Build Coastguard Worker 
1676*76559068SAndroid Build Coastguard Worker         Context.markRangeAsAllCounted(BatchGroupBase, BatchGroupUsedEnd,
1677*76559068SAndroid Build Coastguard Worker                                       Region->RegionBeg, /*RegionIndex=*/0,
1678*76559068SAndroid Build Coastguard Worker                                       Region->MemMapInfo.AllocatedUser);
1679*76559068SAndroid Build Coastguard Worker       } else {
1680*76559068SAndroid Build Coastguard Worker         DCHECK_LT(NumBlocks, MaxContainedBlocks);
1681*76559068SAndroid Build Coastguard Worker         // Note that we don't always visit blocks in each BatchGroup so that we
1682*76559068SAndroid Build Coastguard Worker         // may miss the chance of releasing certain pages that cross
1683*76559068SAndroid Build Coastguard Worker         // BatchGroups.
1684*76559068SAndroid Build Coastguard Worker         Context.markFreeBlocksInRegion(
1685*76559068SAndroid Build Coastguard Worker             BG.Batches, DecompactPtr, Region->RegionBeg, /*RegionIndex=*/0,
1686*76559068SAndroid Build Coastguard Worker             Region->MemMapInfo.AllocatedUser, MayContainLastBlockInRegion);
1687*76559068SAndroid Build Coastguard Worker       }
1688*76559068SAndroid Build Coastguard Worker     }
1689*76559068SAndroid Build Coastguard Worker 
1690*76559068SAndroid Build Coastguard Worker     DCHECK(Context.hasBlockMarked());
1691*76559068SAndroid Build Coastguard Worker 
1692*76559068SAndroid Build Coastguard Worker     return Context;
1693*76559068SAndroid Build Coastguard Worker   }
1694*76559068SAndroid Build Coastguard Worker 
mergeGroupsToReleaseBack(RegionInfo * Region,SinglyLinkedList<BatchGroupT> & GroupsToRelease)1695*76559068SAndroid Build Coastguard Worker   void mergeGroupsToReleaseBack(RegionInfo *Region,
1696*76559068SAndroid Build Coastguard Worker                                 SinglyLinkedList<BatchGroupT> &GroupsToRelease)
1697*76559068SAndroid Build Coastguard Worker       REQUIRES(Region->MMLock) EXCLUDES(Region->FLLock) {
1698*76559068SAndroid Build Coastguard Worker     ScopedLock L(Region->FLLock);
1699*76559068SAndroid Build Coastguard Worker 
1700*76559068SAndroid Build Coastguard Worker     // After merging two freelists, we may have redundant `BatchGroup`s that
1701*76559068SAndroid Build Coastguard Worker     // need to be recycled. The number of unused `BatchGroup`s is expected to be
1702*76559068SAndroid Build Coastguard Worker     // small. Pick a constant which is inferred from real programs.
1703*76559068SAndroid Build Coastguard Worker     constexpr uptr MaxUnusedSize = 8;
1704*76559068SAndroid Build Coastguard Worker     CompactPtrT Blocks[MaxUnusedSize];
1705*76559068SAndroid Build Coastguard Worker     u32 Idx = 0;
1706*76559068SAndroid Build Coastguard Worker     RegionInfo *BatchClassRegion = getRegionInfo(SizeClassMap::BatchClassId);
1707*76559068SAndroid Build Coastguard Worker     // We can't call pushBatchClassBlocks() to recycle the unused `BatchGroup`s
1708*76559068SAndroid Build Coastguard Worker     // when we are manipulating the freelist of `BatchClassRegion`. Instead, we
1709*76559068SAndroid Build Coastguard Worker     // should just push it back to the freelist when we merge two `BatchGroup`s.
1710*76559068SAndroid Build Coastguard Worker     // This logic hasn't been implemented because we haven't supported releasing
1711*76559068SAndroid Build Coastguard Worker     // pages in `BatchClassRegion`.
1712*76559068SAndroid Build Coastguard Worker     DCHECK_NE(BatchClassRegion, Region);
1713*76559068SAndroid Build Coastguard Worker 
1714*76559068SAndroid Build Coastguard Worker     // Merge GroupsToRelease back to the Region::FreeListInfo.BlockList. Note
1715*76559068SAndroid Build Coastguard Worker     // that both `Region->FreeListInfo.BlockList` and `GroupsToRelease` are
1716*76559068SAndroid Build Coastguard Worker     // sorted.
1717*76559068SAndroid Build Coastguard Worker     for (BatchGroupT *BG = Region->FreeListInfo.BlockList.front(),
1718*76559068SAndroid Build Coastguard Worker                      *Prev = nullptr;
1719*76559068SAndroid Build Coastguard Worker          ;) {
1720*76559068SAndroid Build Coastguard Worker       if (BG == nullptr || GroupsToRelease.empty()) {
1721*76559068SAndroid Build Coastguard Worker         if (!GroupsToRelease.empty())
1722*76559068SAndroid Build Coastguard Worker           Region->FreeListInfo.BlockList.append_back(&GroupsToRelease);
1723*76559068SAndroid Build Coastguard Worker         break;
1724*76559068SAndroid Build Coastguard Worker       }
1725*76559068SAndroid Build Coastguard Worker 
1726*76559068SAndroid Build Coastguard Worker       DCHECK(!BG->Batches.empty());
1727*76559068SAndroid Build Coastguard Worker 
1728*76559068SAndroid Build Coastguard Worker       if (BG->CompactPtrGroupBase <
1729*76559068SAndroid Build Coastguard Worker           GroupsToRelease.front()->CompactPtrGroupBase) {
1730*76559068SAndroid Build Coastguard Worker         Prev = BG;
1731*76559068SAndroid Build Coastguard Worker         BG = BG->Next;
1732*76559068SAndroid Build Coastguard Worker         continue;
1733*76559068SAndroid Build Coastguard Worker       }
1734*76559068SAndroid Build Coastguard Worker 
1735*76559068SAndroid Build Coastguard Worker       BatchGroupT *Cur = GroupsToRelease.front();
1736*76559068SAndroid Build Coastguard Worker       TransferBatchT *UnusedTransferBatch = nullptr;
1737*76559068SAndroid Build Coastguard Worker       GroupsToRelease.pop_front();
1738*76559068SAndroid Build Coastguard Worker 
1739*76559068SAndroid Build Coastguard Worker       if (BG->CompactPtrGroupBase == Cur->CompactPtrGroupBase) {
1740*76559068SAndroid Build Coastguard Worker         // We have updated `BatchGroup::BytesInBGAtLastCheckpoint` while
1741*76559068SAndroid Build Coastguard Worker         // collecting the `GroupsToRelease`.
1742*76559068SAndroid Build Coastguard Worker         BG->BytesInBGAtLastCheckpoint = Cur->BytesInBGAtLastCheckpoint;
1743*76559068SAndroid Build Coastguard Worker         const uptr MaxCachedPerBatch = BG->MaxCachedPerBatch;
1744*76559068SAndroid Build Coastguard Worker 
1745*76559068SAndroid Build Coastguard Worker         // Note that the first TransferBatches in both `Batches` may not be
1746*76559068SAndroid Build Coastguard Worker         // full and only the first TransferBatch can have non-full blocks. Thus
1747*76559068SAndroid Build Coastguard Worker         // we have to merge them before appending one to another.
1748*76559068SAndroid Build Coastguard Worker         if (Cur->Batches.front()->getCount() == MaxCachedPerBatch) {
1749*76559068SAndroid Build Coastguard Worker           BG->Batches.append_back(&Cur->Batches);
1750*76559068SAndroid Build Coastguard Worker         } else {
1751*76559068SAndroid Build Coastguard Worker           TransferBatchT *NonFullBatch = Cur->Batches.front();
1752*76559068SAndroid Build Coastguard Worker           Cur->Batches.pop_front();
1753*76559068SAndroid Build Coastguard Worker           const u16 NonFullBatchCount = NonFullBatch->getCount();
1754*76559068SAndroid Build Coastguard Worker           // The remaining Batches in `Cur` are full.
1755*76559068SAndroid Build Coastguard Worker           BG->Batches.append_back(&Cur->Batches);
1756*76559068SAndroid Build Coastguard Worker 
1757*76559068SAndroid Build Coastguard Worker           if (BG->Batches.front()->getCount() == MaxCachedPerBatch) {
1758*76559068SAndroid Build Coastguard Worker             // Only 1 non-full TransferBatch, push it to the front.
1759*76559068SAndroid Build Coastguard Worker             BG->Batches.push_front(NonFullBatch);
1760*76559068SAndroid Build Coastguard Worker           } else {
1761*76559068SAndroid Build Coastguard Worker             const u16 NumBlocksToMove = static_cast<u16>(
1762*76559068SAndroid Build Coastguard Worker                 Min(static_cast<u16>(MaxCachedPerBatch -
1763*76559068SAndroid Build Coastguard Worker                                      BG->Batches.front()->getCount()),
1764*76559068SAndroid Build Coastguard Worker                     NonFullBatchCount));
1765*76559068SAndroid Build Coastguard Worker             BG->Batches.front()->appendFromTransferBatch(NonFullBatch,
1766*76559068SAndroid Build Coastguard Worker                                                          NumBlocksToMove);
1767*76559068SAndroid Build Coastguard Worker             if (NonFullBatch->isEmpty())
1768*76559068SAndroid Build Coastguard Worker               UnusedTransferBatch = NonFullBatch;
1769*76559068SAndroid Build Coastguard Worker             else
1770*76559068SAndroid Build Coastguard Worker               BG->Batches.push_front(NonFullBatch);
1771*76559068SAndroid Build Coastguard Worker           }
1772*76559068SAndroid Build Coastguard Worker         }
1773*76559068SAndroid Build Coastguard Worker 
1774*76559068SAndroid Build Coastguard Worker         const u32 NeededSlots = UnusedTransferBatch == nullptr ? 1U : 2U;
1775*76559068SAndroid Build Coastguard Worker         if (UNLIKELY(Idx + NeededSlots > MaxUnusedSize)) {
1776*76559068SAndroid Build Coastguard Worker           ScopedLock L(BatchClassRegion->FLLock);
1777*76559068SAndroid Build Coastguard Worker           pushBatchClassBlocks(BatchClassRegion, Blocks, Idx);
1778*76559068SAndroid Build Coastguard Worker           if (conditionVariableEnabled())
1779*76559068SAndroid Build Coastguard Worker             BatchClassRegion->FLLockCV.notifyAll(BatchClassRegion->FLLock);
1780*76559068SAndroid Build Coastguard Worker           Idx = 0;
1781*76559068SAndroid Build Coastguard Worker         }
1782*76559068SAndroid Build Coastguard Worker         Blocks[Idx++] =
1783*76559068SAndroid Build Coastguard Worker             compactPtr(SizeClassMap::BatchClassId, reinterpret_cast<uptr>(Cur));
1784*76559068SAndroid Build Coastguard Worker         if (UnusedTransferBatch) {
1785*76559068SAndroid Build Coastguard Worker           Blocks[Idx++] =
1786*76559068SAndroid Build Coastguard Worker               compactPtr(SizeClassMap::BatchClassId,
1787*76559068SAndroid Build Coastguard Worker                          reinterpret_cast<uptr>(UnusedTransferBatch));
1788*76559068SAndroid Build Coastguard Worker         }
1789*76559068SAndroid Build Coastguard Worker         Prev = BG;
1790*76559068SAndroid Build Coastguard Worker         BG = BG->Next;
1791*76559068SAndroid Build Coastguard Worker         continue;
1792*76559068SAndroid Build Coastguard Worker       }
1793*76559068SAndroid Build Coastguard Worker 
1794*76559068SAndroid Build Coastguard Worker       // At here, the `BG` is the first BatchGroup with CompactPtrGroupBase
1795*76559068SAndroid Build Coastguard Worker       // larger than the first element in `GroupsToRelease`. We need to insert
1796*76559068SAndroid Build Coastguard Worker       // `GroupsToRelease::front()` (which is `Cur` below)  before `BG`.
1797*76559068SAndroid Build Coastguard Worker       //
1798*76559068SAndroid Build Coastguard Worker       //   1. If `Prev` is nullptr, we simply push `Cur` to the front of
1799*76559068SAndroid Build Coastguard Worker       //      FreeListInfo.BlockList.
1800*76559068SAndroid Build Coastguard Worker       //   2. Otherwise, use `insert()` which inserts an element next to `Prev`.
1801*76559068SAndroid Build Coastguard Worker       //
1802*76559068SAndroid Build Coastguard Worker       // Afterwards, we don't need to advance `BG` because the order between
1803*76559068SAndroid Build Coastguard Worker       // `BG` and the new `GroupsToRelease::front()` hasn't been checked.
1804*76559068SAndroid Build Coastguard Worker       if (Prev == nullptr)
1805*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.push_front(Cur);
1806*76559068SAndroid Build Coastguard Worker       else
1807*76559068SAndroid Build Coastguard Worker         Region->FreeListInfo.BlockList.insert(Prev, Cur);
1808*76559068SAndroid Build Coastguard Worker       DCHECK_EQ(Cur->Next, BG);
1809*76559068SAndroid Build Coastguard Worker       Prev = Cur;
1810*76559068SAndroid Build Coastguard Worker     }
1811*76559068SAndroid Build Coastguard Worker 
1812*76559068SAndroid Build Coastguard Worker     if (Idx != 0) {
1813*76559068SAndroid Build Coastguard Worker       ScopedLock L(BatchClassRegion->FLLock);
1814*76559068SAndroid Build Coastguard Worker       pushBatchClassBlocks(BatchClassRegion, Blocks, Idx);
1815*76559068SAndroid Build Coastguard Worker       if (conditionVariableEnabled())
1816*76559068SAndroid Build Coastguard Worker         BatchClassRegion->FLLockCV.notifyAll(BatchClassRegion->FLLock);
1817*76559068SAndroid Build Coastguard Worker     }
1818*76559068SAndroid Build Coastguard Worker 
1819*76559068SAndroid Build Coastguard Worker     if (SCUDO_DEBUG) {
1820*76559068SAndroid Build Coastguard Worker       BatchGroupT *Prev = Region->FreeListInfo.BlockList.front();
1821*76559068SAndroid Build Coastguard Worker       for (BatchGroupT *Cur = Prev->Next; Cur != nullptr;
1822*76559068SAndroid Build Coastguard Worker            Prev = Cur, Cur = Cur->Next) {
1823*76559068SAndroid Build Coastguard Worker         CHECK_LT(Prev->CompactPtrGroupBase, Cur->CompactPtrGroupBase);
1824*76559068SAndroid Build Coastguard Worker       }
1825*76559068SAndroid Build Coastguard Worker     }
1826*76559068SAndroid Build Coastguard Worker 
1827*76559068SAndroid Build Coastguard Worker     if (conditionVariableEnabled())
1828*76559068SAndroid Build Coastguard Worker       Region->FLLockCV.notifyAll(Region->FLLock);
1829*76559068SAndroid Build Coastguard Worker   }
1830*76559068SAndroid Build Coastguard Worker 
1831*76559068SAndroid Build Coastguard Worker   // The minimum size of pushed blocks that we will try to release the pages in
1832*76559068SAndroid Build Coastguard Worker   // that size class.
1833*76559068SAndroid Build Coastguard Worker   uptr SmallerBlockReleasePageDelta = 0;
1834*76559068SAndroid Build Coastguard Worker   atomic_s32 ReleaseToOsIntervalMs = {};
1835*76559068SAndroid Build Coastguard Worker   alignas(SCUDO_CACHE_LINE_SIZE) RegionInfo RegionInfoArray[NumClasses];
1836*76559068SAndroid Build Coastguard Worker };
1837*76559068SAndroid Build Coastguard Worker 
1838*76559068SAndroid Build Coastguard Worker } // namespace scudo
1839*76559068SAndroid Build Coastguard Worker 
1840*76559068SAndroid Build Coastguard Worker #endif // SCUDO_PRIMARY64_H_
1841