xref: /oneTBB/src/tbbmalloc/backend.cpp (revision 51c0b2f7)
1*51c0b2f7Stbbdev /*
2*51c0b2f7Stbbdev     Copyright (c) 2005-2020 Intel Corporation
3*51c0b2f7Stbbdev 
4*51c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
5*51c0b2f7Stbbdev     you may not use this file except in compliance with the License.
6*51c0b2f7Stbbdev     You may obtain a copy of the License at
7*51c0b2f7Stbbdev 
8*51c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
9*51c0b2f7Stbbdev 
10*51c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
11*51c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
12*51c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*51c0b2f7Stbbdev     See the License for the specific language governing permissions and
14*51c0b2f7Stbbdev     limitations under the License.
15*51c0b2f7Stbbdev */
16*51c0b2f7Stbbdev 
17*51c0b2f7Stbbdev #include <string.h>   /* for memset */
18*51c0b2f7Stbbdev #include <errno.h>
19*51c0b2f7Stbbdev #include "tbbmalloc_internal.h"
20*51c0b2f7Stbbdev 
21*51c0b2f7Stbbdev namespace rml {
22*51c0b2f7Stbbdev namespace internal {
23*51c0b2f7Stbbdev 
24*51c0b2f7Stbbdev /*********** Code to acquire memory from the OS or other executive ****************/
25*51c0b2f7Stbbdev 
26*51c0b2f7Stbbdev /*
27*51c0b2f7Stbbdev   syscall/malloc can set non-zero errno in case of failure,
28*51c0b2f7Stbbdev   but later allocator might be able to find memory to fulfill the request.
29*51c0b2f7Stbbdev   And we do not want changing of errno by successful scalable_malloc call.
30*51c0b2f7Stbbdev   To support this, restore old errno in (get|free)RawMemory, and set errno
31*51c0b2f7Stbbdev   in frontend just before returning to user code.
32*51c0b2f7Stbbdev   Please note: every syscall/libc call used inside scalable_malloc that
33*51c0b2f7Stbbdev   sets errno must be protected this way, not just memory allocation per se.
34*51c0b2f7Stbbdev */
35*51c0b2f7Stbbdev 
36*51c0b2f7Stbbdev #if USE_DEFAULT_MEMORY_MAPPING
37*51c0b2f7Stbbdev #include "MapMemory.h"
38*51c0b2f7Stbbdev #else
39*51c0b2f7Stbbdev /* assume MapMemory and UnmapMemory are customized */
40*51c0b2f7Stbbdev #endif
41*51c0b2f7Stbbdev 
42*51c0b2f7Stbbdev void* getRawMemory (size_t size, PageType pageType) {
43*51c0b2f7Stbbdev     return MapMemory(size, pageType);
44*51c0b2f7Stbbdev }
45*51c0b2f7Stbbdev 
46*51c0b2f7Stbbdev int freeRawMemory (void *object, size_t size) {
47*51c0b2f7Stbbdev     return UnmapMemory(object, size);
48*51c0b2f7Stbbdev }
49*51c0b2f7Stbbdev 
50*51c0b2f7Stbbdev #if CHECK_ALLOCATION_RANGE
51*51c0b2f7Stbbdev 
52*51c0b2f7Stbbdev void Backend::UsedAddressRange::registerAlloc(uintptr_t left, uintptr_t right)
53*51c0b2f7Stbbdev {
54*51c0b2f7Stbbdev     MallocMutex::scoped_lock lock(mutex);
55*51c0b2f7Stbbdev     if (left < leftBound)
56*51c0b2f7Stbbdev         leftBound = left;
57*51c0b2f7Stbbdev     if (right > rightBound)
58*51c0b2f7Stbbdev         rightBound = right;
59*51c0b2f7Stbbdev     MALLOC_ASSERT(leftBound, ASSERT_TEXT);
60*51c0b2f7Stbbdev     MALLOC_ASSERT(leftBound < rightBound, ASSERT_TEXT);
61*51c0b2f7Stbbdev     MALLOC_ASSERT(leftBound <= left && right <= rightBound, ASSERT_TEXT);
62*51c0b2f7Stbbdev }
63*51c0b2f7Stbbdev 
64*51c0b2f7Stbbdev void Backend::UsedAddressRange::registerFree(uintptr_t left, uintptr_t right)
65*51c0b2f7Stbbdev {
66*51c0b2f7Stbbdev     MallocMutex::scoped_lock lock(mutex);
67*51c0b2f7Stbbdev     if (leftBound == left) {
68*51c0b2f7Stbbdev         if (rightBound == right) {
69*51c0b2f7Stbbdev             leftBound = ADDRESS_UPPER_BOUND;
70*51c0b2f7Stbbdev             rightBound = 0;
71*51c0b2f7Stbbdev         } else
72*51c0b2f7Stbbdev             leftBound = right;
73*51c0b2f7Stbbdev     } else if (rightBound == right)
74*51c0b2f7Stbbdev         rightBound = left;
75*51c0b2f7Stbbdev     MALLOC_ASSERT((!rightBound && leftBound == ADDRESS_UPPER_BOUND)
76*51c0b2f7Stbbdev                   || leftBound < rightBound, ASSERT_TEXT);
77*51c0b2f7Stbbdev }
78*51c0b2f7Stbbdev #endif // CHECK_ALLOCATION_RANGE
79*51c0b2f7Stbbdev 
80*51c0b2f7Stbbdev // Initialized in frontend inside defaultMemPool
81*51c0b2f7Stbbdev extern HugePagesStatus hugePages;
82*51c0b2f7Stbbdev 
83*51c0b2f7Stbbdev void *Backend::allocRawMem(size_t &size)
84*51c0b2f7Stbbdev {
85*51c0b2f7Stbbdev     void *res = NULL;
86*51c0b2f7Stbbdev     size_t allocSize = 0;
87*51c0b2f7Stbbdev 
88*51c0b2f7Stbbdev     if (extMemPool->userPool()) {
89*51c0b2f7Stbbdev         if (extMemPool->fixedPool && bootsrapMemDone == bootsrapMemStatus.load(std::memory_order_acquire))
90*51c0b2f7Stbbdev             return NULL;
91*51c0b2f7Stbbdev         MALLOC_ASSERT(bootsrapMemStatus != bootsrapMemNotDone,
92*51c0b2f7Stbbdev                       "Backend::allocRawMem() called prematurely?");
93*51c0b2f7Stbbdev         // TODO: support for raw mem not aligned at sizeof(uintptr_t)
94*51c0b2f7Stbbdev         // memory from fixed pool is asked once and only once
95*51c0b2f7Stbbdev         allocSize = alignUpGeneric(size, extMemPool->granularity);
96*51c0b2f7Stbbdev         res = (*extMemPool->rawAlloc)(extMemPool->poolId, allocSize);
97*51c0b2f7Stbbdev     } else {
98*51c0b2f7Stbbdev         // Align allocation on page size
99*51c0b2f7Stbbdev         size_t pageSize = hugePages.isEnabled ? hugePages.getGranularity() : extMemPool->granularity;
100*51c0b2f7Stbbdev         MALLOC_ASSERT(pageSize, "Page size cannot be zero.");
101*51c0b2f7Stbbdev         allocSize = alignUpGeneric(size, pageSize);
102*51c0b2f7Stbbdev 
103*51c0b2f7Stbbdev         // If user requested huge pages and they are available, try to use preallocated ones firstly.
104*51c0b2f7Stbbdev         // If there are none, lets check transparent huge pages support and use them instead.
105*51c0b2f7Stbbdev         if (hugePages.isEnabled) {
106*51c0b2f7Stbbdev             if (hugePages.isHPAvailable) {
107*51c0b2f7Stbbdev                 res = getRawMemory(allocSize, PREALLOCATED_HUGE_PAGE);
108*51c0b2f7Stbbdev             }
109*51c0b2f7Stbbdev             if (!res && hugePages.isTHPAvailable) {
110*51c0b2f7Stbbdev                 res = getRawMemory(allocSize, TRANSPARENT_HUGE_PAGE);
111*51c0b2f7Stbbdev             }
112*51c0b2f7Stbbdev         }
113*51c0b2f7Stbbdev 
114*51c0b2f7Stbbdev         if (!res) {
115*51c0b2f7Stbbdev             res = getRawMemory(allocSize, REGULAR);
116*51c0b2f7Stbbdev         }
117*51c0b2f7Stbbdev     }
118*51c0b2f7Stbbdev 
119*51c0b2f7Stbbdev     if (res) {
120*51c0b2f7Stbbdev         MALLOC_ASSERT(allocSize > 0, "Invalid size of an allocated region.");
121*51c0b2f7Stbbdev         size = allocSize;
122*51c0b2f7Stbbdev         if (!extMemPool->userPool())
123*51c0b2f7Stbbdev             usedAddrRange.registerAlloc((uintptr_t)res, (uintptr_t)res+size);
124*51c0b2f7Stbbdev #if MALLOC_DEBUG
125*51c0b2f7Stbbdev         volatile size_t curTotalSize = totalMemSize; // to read global value once
126*51c0b2f7Stbbdev         MALLOC_ASSERT(curTotalSize+size > curTotalSize, "Overflow allocation size.");
127*51c0b2f7Stbbdev #endif
128*51c0b2f7Stbbdev         totalMemSize.fetch_add(size);
129*51c0b2f7Stbbdev     }
130*51c0b2f7Stbbdev 
131*51c0b2f7Stbbdev     return res;
132*51c0b2f7Stbbdev }
133*51c0b2f7Stbbdev 
134*51c0b2f7Stbbdev bool Backend::freeRawMem(void *object, size_t size)
135*51c0b2f7Stbbdev {
136*51c0b2f7Stbbdev     bool fail;
137*51c0b2f7Stbbdev #if MALLOC_DEBUG
138*51c0b2f7Stbbdev     volatile size_t curTotalSize = totalMemSize; // to read global value once
139*51c0b2f7Stbbdev     MALLOC_ASSERT(curTotalSize-size < curTotalSize, "Negative allocation size.");
140*51c0b2f7Stbbdev #endif
141*51c0b2f7Stbbdev     totalMemSize.fetch_sub(size);
142*51c0b2f7Stbbdev     if (extMemPool->userPool()) {
143*51c0b2f7Stbbdev         MALLOC_ASSERT(!extMemPool->fixedPool, "No free for fixed-size pools.");
144*51c0b2f7Stbbdev         fail = (*extMemPool->rawFree)(extMemPool->poolId, object, size);
145*51c0b2f7Stbbdev     } else {
146*51c0b2f7Stbbdev         usedAddrRange.registerFree((uintptr_t)object, (uintptr_t)object + size);
147*51c0b2f7Stbbdev         fail = freeRawMemory(object, size);
148*51c0b2f7Stbbdev     }
149*51c0b2f7Stbbdev     // TODO: use result in all freeRawMem() callers
150*51c0b2f7Stbbdev     return !fail;
151*51c0b2f7Stbbdev }
152*51c0b2f7Stbbdev 
153*51c0b2f7Stbbdev /********* End memory acquisition code ********************************/
154*51c0b2f7Stbbdev 
155*51c0b2f7Stbbdev // Protected object size. After successful locking returns size of locked block,
156*51c0b2f7Stbbdev // and releasing requires setting block size.
157*51c0b2f7Stbbdev class GuardedSize : tbb::detail::no_copy {
158*51c0b2f7Stbbdev     std::atomic<uintptr_t> value;
159*51c0b2f7Stbbdev public:
160*51c0b2f7Stbbdev     enum State {
161*51c0b2f7Stbbdev         LOCKED,
162*51c0b2f7Stbbdev         COAL_BLOCK,        // block is coalescing now
163*51c0b2f7Stbbdev         MAX_LOCKED_VAL = COAL_BLOCK,
164*51c0b2f7Stbbdev         LAST_REGION_BLOCK, // used to mark last block in region
165*51c0b2f7Stbbdev         // values after this are "normal" block sizes
166*51c0b2f7Stbbdev         MAX_SPEC_VAL = LAST_REGION_BLOCK
167*51c0b2f7Stbbdev     };
168*51c0b2f7Stbbdev 
169*51c0b2f7Stbbdev     void initLocked() { value.store(LOCKED, std::memory_order_release); } // TBB_REVAMP_TODO: was relaxed
170*51c0b2f7Stbbdev     void makeCoalscing() {
171*51c0b2f7Stbbdev         MALLOC_ASSERT(value.load(std::memory_order_relaxed) == LOCKED, ASSERT_TEXT);
172*51c0b2f7Stbbdev         value.store(COAL_BLOCK, std::memory_order_release); // TBB_REVAMP_TODO: was relaxed
173*51c0b2f7Stbbdev     }
174*51c0b2f7Stbbdev     size_t tryLock(State state) {
175*51c0b2f7Stbbdev         MALLOC_ASSERT(state <= MAX_LOCKED_VAL, ASSERT_TEXT);
176*51c0b2f7Stbbdev         size_t sz = value.load(std::memory_order_acquire);
177*51c0b2f7Stbbdev         for (;;) {
178*51c0b2f7Stbbdev             if (sz <= MAX_LOCKED_VAL) {
179*51c0b2f7Stbbdev                 break;
180*51c0b2f7Stbbdev             }
181*51c0b2f7Stbbdev             if (value.compare_exchange_strong(sz, state)) {
182*51c0b2f7Stbbdev                 break;
183*51c0b2f7Stbbdev             }
184*51c0b2f7Stbbdev         }
185*51c0b2f7Stbbdev         return sz;
186*51c0b2f7Stbbdev     }
187*51c0b2f7Stbbdev     void unlock(size_t size) {
188*51c0b2f7Stbbdev         MALLOC_ASSERT(value.load(std::memory_order_relaxed) <= MAX_LOCKED_VAL, "The lock is not locked");
189*51c0b2f7Stbbdev         MALLOC_ASSERT(size > MAX_LOCKED_VAL, ASSERT_TEXT);
190*51c0b2f7Stbbdev         value.store(size, std::memory_order_release);
191*51c0b2f7Stbbdev     }
192*51c0b2f7Stbbdev     bool isLastRegionBlock() const { return value.load(std::memory_order_relaxed) == LAST_REGION_BLOCK; }
193*51c0b2f7Stbbdev     friend void Backend::IndexedBins::verify();
194*51c0b2f7Stbbdev };
195*51c0b2f7Stbbdev 
196*51c0b2f7Stbbdev struct MemRegion {
197*51c0b2f7Stbbdev     MemRegion *next,      // keep all regions in any pool to release all them on
198*51c0b2f7Stbbdev               *prev;      // pool destroying, 2-linked list to release individual
199*51c0b2f7Stbbdev                           // regions.
200*51c0b2f7Stbbdev     size_t     allocSz,   // got from pool callback
201*51c0b2f7Stbbdev                blockSz;   // initial and maximal inner block size
202*51c0b2f7Stbbdev     MemRegionType type;
203*51c0b2f7Stbbdev };
204*51c0b2f7Stbbdev 
205*51c0b2f7Stbbdev // this data must be unmodified while block is in use, so separate it
206*51c0b2f7Stbbdev class BlockMutexes {
207*51c0b2f7Stbbdev protected:
208*51c0b2f7Stbbdev     GuardedSize myL,   // lock for me
209*51c0b2f7Stbbdev                 leftL; // lock for left neighbor
210*51c0b2f7Stbbdev };
211*51c0b2f7Stbbdev 
212*51c0b2f7Stbbdev class FreeBlock : BlockMutexes {
213*51c0b2f7Stbbdev public:
214*51c0b2f7Stbbdev     static const size_t minBlockSize;
215*51c0b2f7Stbbdev     friend void Backend::IndexedBins::verify();
216*51c0b2f7Stbbdev 
217*51c0b2f7Stbbdev     FreeBlock    *prev,       // in 2-linked list related to bin
218*51c0b2f7Stbbdev                  *next,
219*51c0b2f7Stbbdev                  *nextToFree; // used to form a queue during coalescing
220*51c0b2f7Stbbdev     // valid only when block is in processing, i.e. one is not free and not
221*51c0b2f7Stbbdev     size_t        sizeTmp;    // used outside of backend
222*51c0b2f7Stbbdev     int           myBin;      // bin that is owner of the block
223*51c0b2f7Stbbdev     bool          slabAligned;
224*51c0b2f7Stbbdev     bool          blockInBin; // this block in myBin already
225*51c0b2f7Stbbdev 
226*51c0b2f7Stbbdev     FreeBlock *rightNeig(size_t sz) const {
227*51c0b2f7Stbbdev         MALLOC_ASSERT(sz, ASSERT_TEXT);
228*51c0b2f7Stbbdev         return (FreeBlock*)((uintptr_t)this+sz);
229*51c0b2f7Stbbdev     }
230*51c0b2f7Stbbdev     FreeBlock *leftNeig(size_t sz) const {
231*51c0b2f7Stbbdev         MALLOC_ASSERT(sz, ASSERT_TEXT);
232*51c0b2f7Stbbdev         return (FreeBlock*)((uintptr_t)this - sz);
233*51c0b2f7Stbbdev     }
234*51c0b2f7Stbbdev 
235*51c0b2f7Stbbdev     void initHeader() { myL.initLocked(); leftL.initLocked(); }
236*51c0b2f7Stbbdev     void setMeFree(size_t size) { myL.unlock(size); }
237*51c0b2f7Stbbdev     size_t trySetMeUsed(GuardedSize::State s) { return myL.tryLock(s); }
238*51c0b2f7Stbbdev     bool isLastRegionBlock() const { return myL.isLastRegionBlock(); }
239*51c0b2f7Stbbdev 
240*51c0b2f7Stbbdev     void setLeftFree(size_t sz) { leftL.unlock(sz); }
241*51c0b2f7Stbbdev     size_t trySetLeftUsed(GuardedSize::State s) { return leftL.tryLock(s); }
242*51c0b2f7Stbbdev 
243*51c0b2f7Stbbdev     size_t tryLockBlock() {
244*51c0b2f7Stbbdev         size_t rSz, sz = trySetMeUsed(GuardedSize::LOCKED);
245*51c0b2f7Stbbdev 
246*51c0b2f7Stbbdev         if (sz <= GuardedSize::MAX_LOCKED_VAL)
247*51c0b2f7Stbbdev             return false;
248*51c0b2f7Stbbdev         rSz = rightNeig(sz)->trySetLeftUsed(GuardedSize::LOCKED);
249*51c0b2f7Stbbdev         if (rSz <= GuardedSize::MAX_LOCKED_VAL) {
250*51c0b2f7Stbbdev             setMeFree(sz);
251*51c0b2f7Stbbdev             return false;
252*51c0b2f7Stbbdev         }
253*51c0b2f7Stbbdev         MALLOC_ASSERT(rSz == sz, ASSERT_TEXT);
254*51c0b2f7Stbbdev         return sz;
255*51c0b2f7Stbbdev     }
256*51c0b2f7Stbbdev     void markCoalescing(size_t blockSz) {
257*51c0b2f7Stbbdev         myL.makeCoalscing();
258*51c0b2f7Stbbdev         rightNeig(blockSz)->leftL.makeCoalscing();
259*51c0b2f7Stbbdev         sizeTmp = blockSz;
260*51c0b2f7Stbbdev         nextToFree = NULL;
261*51c0b2f7Stbbdev     }
262*51c0b2f7Stbbdev     void markUsed() {
263*51c0b2f7Stbbdev         myL.initLocked();
264*51c0b2f7Stbbdev         rightNeig(sizeTmp)->leftL.initLocked();
265*51c0b2f7Stbbdev         nextToFree = NULL;
266*51c0b2f7Stbbdev     }
267*51c0b2f7Stbbdev     static void markBlocks(FreeBlock *fBlock, int num, size_t size) {
268*51c0b2f7Stbbdev         for (int i=1; i<num; i++) {
269*51c0b2f7Stbbdev             fBlock = (FreeBlock*)((uintptr_t)fBlock + size);
270*51c0b2f7Stbbdev             fBlock->initHeader();
271*51c0b2f7Stbbdev         }
272*51c0b2f7Stbbdev     }
273*51c0b2f7Stbbdev };
274*51c0b2f7Stbbdev 
275*51c0b2f7Stbbdev // Last block in any region. Its "size" field is GuardedSize::LAST_REGION_BLOCK,
276*51c0b2f7Stbbdev // This kind of blocks used to find region header
277*51c0b2f7Stbbdev // and have a possibility to return region back to OS
278*51c0b2f7Stbbdev struct LastFreeBlock : public FreeBlock {
279*51c0b2f7Stbbdev     MemRegion *memRegion;
280*51c0b2f7Stbbdev };
281*51c0b2f7Stbbdev 
282*51c0b2f7Stbbdev const size_t FreeBlock::minBlockSize = sizeof(FreeBlock);
283*51c0b2f7Stbbdev 
284*51c0b2f7Stbbdev inline bool BackendSync::waitTillBlockReleased(intptr_t startModifiedCnt)
285*51c0b2f7Stbbdev {
286*51c0b2f7Stbbdev     AtomicBackoff backoff;
287*51c0b2f7Stbbdev #if __TBB_MALLOC_BACKEND_STAT
288*51c0b2f7Stbbdev     class ITT_Guard {
289*51c0b2f7Stbbdev         void *ptr;
290*51c0b2f7Stbbdev     public:
291*51c0b2f7Stbbdev         ITT_Guard(void *p) : ptr(p) {
292*51c0b2f7Stbbdev             MALLOC_ITT_SYNC_PREPARE(ptr);
293*51c0b2f7Stbbdev         }
294*51c0b2f7Stbbdev         ~ITT_Guard() {
295*51c0b2f7Stbbdev             MALLOC_ITT_SYNC_ACQUIRED(ptr);
296*51c0b2f7Stbbdev         }
297*51c0b2f7Stbbdev     };
298*51c0b2f7Stbbdev     ITT_Guard ittGuard(&inFlyBlocks);
299*51c0b2f7Stbbdev #endif
300*51c0b2f7Stbbdev     for (intptr_t myBinsInFlyBlocks = inFlyBlocks.load(std::memory_order_acquire),
301*51c0b2f7Stbbdev              myCoalescQInFlyBlocks = backend->blocksInCoalescing(); ; backoff.pause()) {
302*51c0b2f7Stbbdev         MALLOC_ASSERT(myBinsInFlyBlocks>=0 && myCoalescQInFlyBlocks>=0, NULL);
303*51c0b2f7Stbbdev         intptr_t currBinsInFlyBlocks = inFlyBlocks.load(std::memory_order_acquire),
304*51c0b2f7Stbbdev             currCoalescQInFlyBlocks = backend->blocksInCoalescing();
305*51c0b2f7Stbbdev         WhiteboxTestingYield();
306*51c0b2f7Stbbdev         // Stop waiting iff:
307*51c0b2f7Stbbdev 
308*51c0b2f7Stbbdev         // 1) blocks were removed from processing, not added
309*51c0b2f7Stbbdev         if (myBinsInFlyBlocks > currBinsInFlyBlocks
310*51c0b2f7Stbbdev         // 2) released during delayed coalescing queue
311*51c0b2f7Stbbdev             || myCoalescQInFlyBlocks > currCoalescQInFlyBlocks)
312*51c0b2f7Stbbdev             break;
313*51c0b2f7Stbbdev         // 3) if there are blocks in coalescing, and no progress in its processing,
314*51c0b2f7Stbbdev         // try to scan coalescing queue and stop waiting, if changes were made
315*51c0b2f7Stbbdev         // (if there are no changes and in-fly blocks exist, we continue
316*51c0b2f7Stbbdev         //  waiting to not increase load on coalescQ)
317*51c0b2f7Stbbdev         if (currCoalescQInFlyBlocks > 0 && backend->scanCoalescQ(/*forceCoalescQDrop=*/false))
318*51c0b2f7Stbbdev             break;
319*51c0b2f7Stbbdev         // 4) when there are no blocks
320*51c0b2f7Stbbdev         if (!currBinsInFlyBlocks && !currCoalescQInFlyBlocks)
321*51c0b2f7Stbbdev             // re-scan make sense only if bins were modified since scanned
322*51c0b2f7Stbbdev             return startModifiedCnt != getNumOfMods();
323*51c0b2f7Stbbdev         myBinsInFlyBlocks = currBinsInFlyBlocks;
324*51c0b2f7Stbbdev         myCoalescQInFlyBlocks = currCoalescQInFlyBlocks;
325*51c0b2f7Stbbdev     }
326*51c0b2f7Stbbdev     return true;
327*51c0b2f7Stbbdev }
328*51c0b2f7Stbbdev 
329*51c0b2f7Stbbdev void CoalRequestQ::putBlock(FreeBlock *fBlock)
330*51c0b2f7Stbbdev {
331*51c0b2f7Stbbdev     MALLOC_ASSERT(fBlock->sizeTmp >= FreeBlock::minBlockSize, ASSERT_TEXT);
332*51c0b2f7Stbbdev     fBlock->markUsed();
333*51c0b2f7Stbbdev     // the block is in the queue, do not forget that it's here
334*51c0b2f7Stbbdev     inFlyBlocks++;
335*51c0b2f7Stbbdev 
336*51c0b2f7Stbbdev     FreeBlock *myBlToFree = blocksToFree.load(std::memory_order_acquire);
337*51c0b2f7Stbbdev     for (;;) {
338*51c0b2f7Stbbdev         fBlock->nextToFree = myBlToFree;
339*51c0b2f7Stbbdev         if (blocksToFree.compare_exchange_strong(myBlToFree, fBlock)) {
340*51c0b2f7Stbbdev             return;
341*51c0b2f7Stbbdev         }
342*51c0b2f7Stbbdev     }
343*51c0b2f7Stbbdev }
344*51c0b2f7Stbbdev 
345*51c0b2f7Stbbdev FreeBlock *CoalRequestQ::getAll()
346*51c0b2f7Stbbdev {
347*51c0b2f7Stbbdev     for (;;) {
348*51c0b2f7Stbbdev         FreeBlock *myBlToFree = blocksToFree.load(std::memory_order_acquire);
349*51c0b2f7Stbbdev 
350*51c0b2f7Stbbdev         if (!myBlToFree) {
351*51c0b2f7Stbbdev             return NULL;
352*51c0b2f7Stbbdev         } else {
353*51c0b2f7Stbbdev             if (blocksToFree.compare_exchange_strong(myBlToFree, 0)) {
354*51c0b2f7Stbbdev                 return myBlToFree;
355*51c0b2f7Stbbdev             } else {
356*51c0b2f7Stbbdev                 continue;
357*51c0b2f7Stbbdev             }
358*51c0b2f7Stbbdev         }
359*51c0b2f7Stbbdev     }
360*51c0b2f7Stbbdev }
361*51c0b2f7Stbbdev 
362*51c0b2f7Stbbdev inline void CoalRequestQ::blockWasProcessed()
363*51c0b2f7Stbbdev {
364*51c0b2f7Stbbdev     bkndSync->binsModified();
365*51c0b2f7Stbbdev     int prev = inFlyBlocks.fetch_sub(1);
366*51c0b2f7Stbbdev     MALLOC_ASSERT(prev > 0, ASSERT_TEXT);
367*51c0b2f7Stbbdev }
368*51c0b2f7Stbbdev 
369*51c0b2f7Stbbdev // Try to get a block from a bin.
370*51c0b2f7Stbbdev // If the remaining free space would stay in the same bin,
371*51c0b2f7Stbbdev //     split the block without removing it.
372*51c0b2f7Stbbdev // If the free space should go to other bin(s), remove the block.
373*51c0b2f7Stbbdev // alignedBin is true, if all blocks in the bin have slab-aligned right side.
374*51c0b2f7Stbbdev FreeBlock *Backend::IndexedBins::getFromBin(int binIdx, BackendSync *sync, size_t size,
375*51c0b2f7Stbbdev         bool needAlignedRes, bool alignedBin,  bool wait, int *binLocked)
376*51c0b2f7Stbbdev {
377*51c0b2f7Stbbdev     Bin *b = &freeBins[binIdx];
378*51c0b2f7Stbbdev try_next:
379*51c0b2f7Stbbdev     FreeBlock *fBlock = NULL;
380*51c0b2f7Stbbdev     if (b->head) {
381*51c0b2f7Stbbdev         bool locked;
382*51c0b2f7Stbbdev         MallocMutex::scoped_lock scopedLock(b->tLock, wait, &locked);
383*51c0b2f7Stbbdev 
384*51c0b2f7Stbbdev         if (!locked) {
385*51c0b2f7Stbbdev             if (binLocked) (*binLocked)++;
386*51c0b2f7Stbbdev             return NULL;
387*51c0b2f7Stbbdev         }
388*51c0b2f7Stbbdev 
389*51c0b2f7Stbbdev         for (FreeBlock *curr = b->head; curr; curr = curr->next) {
390*51c0b2f7Stbbdev             size_t szBlock = curr->tryLockBlock();
391*51c0b2f7Stbbdev             if (!szBlock) {
392*51c0b2f7Stbbdev                 // block is locked, re-do bin lock, as there is no place to spin
393*51c0b2f7Stbbdev                 // while block coalescing
394*51c0b2f7Stbbdev                 goto try_next;
395*51c0b2f7Stbbdev             }
396*51c0b2f7Stbbdev 
397*51c0b2f7Stbbdev             // GENERAL CASE
398*51c0b2f7Stbbdev             if (alignedBin || !needAlignedRes) {
399*51c0b2f7Stbbdev                 size_t splitSz = szBlock - size;
400*51c0b2f7Stbbdev                 // If we got a block as split result, it must have a room for control structures.
401*51c0b2f7Stbbdev                 if (szBlock >= size && (splitSz >= FreeBlock::minBlockSize || !splitSz))
402*51c0b2f7Stbbdev                     fBlock = curr;
403*51c0b2f7Stbbdev             } else {
404*51c0b2f7Stbbdev                 // SPECIAL CASE, to get aligned block from unaligned bin we have to cut the middle of a block
405*51c0b2f7Stbbdev                 // and return remaining left and right part. Possible only in fixed pool scenario, assert for this
406*51c0b2f7Stbbdev                 // is set inside splitBlock() function.
407*51c0b2f7Stbbdev 
408*51c0b2f7Stbbdev                 void *newB = alignUp(curr, slabSize);
409*51c0b2f7Stbbdev                 uintptr_t rightNew = (uintptr_t)newB + size;
410*51c0b2f7Stbbdev                 uintptr_t rightCurr = (uintptr_t)curr + szBlock;
411*51c0b2f7Stbbdev                 // Check if the block size is sufficient,
412*51c0b2f7Stbbdev                 // and also left and right split results are either big enough or non-existent
413*51c0b2f7Stbbdev                 if (rightNew <= rightCurr
414*51c0b2f7Stbbdev                         && (newB == curr || ((uintptr_t)newB - (uintptr_t)curr) >= FreeBlock::minBlockSize)
415*51c0b2f7Stbbdev                         && (rightNew == rightCurr || (rightCurr - rightNew) >= FreeBlock::minBlockSize))
416*51c0b2f7Stbbdev                     fBlock = curr;
417*51c0b2f7Stbbdev             }
418*51c0b2f7Stbbdev 
419*51c0b2f7Stbbdev             if (fBlock) {
420*51c0b2f7Stbbdev                 // consume must be called before result of removing from a bin is visible externally.
421*51c0b2f7Stbbdev                 sync->blockConsumed();
422*51c0b2f7Stbbdev                 // TODO: think about cases when block stays in the same bin
423*51c0b2f7Stbbdev                 b->removeBlock(fBlock);
424*51c0b2f7Stbbdev                 if (freeBins[binIdx].empty())
425*51c0b2f7Stbbdev                     bitMask.set(binIdx, false);
426*51c0b2f7Stbbdev                 fBlock->sizeTmp = szBlock;
427*51c0b2f7Stbbdev                 break;
428*51c0b2f7Stbbdev             } else { // block size is not valid, search for next block in the bin
429*51c0b2f7Stbbdev                 curr->setMeFree(szBlock);
430*51c0b2f7Stbbdev                 curr->rightNeig(szBlock)->setLeftFree(szBlock);
431*51c0b2f7Stbbdev             }
432*51c0b2f7Stbbdev         }
433*51c0b2f7Stbbdev     }
434*51c0b2f7Stbbdev     return fBlock;
435*51c0b2f7Stbbdev }
436*51c0b2f7Stbbdev 
437*51c0b2f7Stbbdev bool Backend::IndexedBins::tryReleaseRegions(int binIdx, Backend *backend)
438*51c0b2f7Stbbdev {
439*51c0b2f7Stbbdev     Bin *b = &freeBins[binIdx];
440*51c0b2f7Stbbdev     FreeBlock *fBlockList = NULL;
441*51c0b2f7Stbbdev 
442*51c0b2f7Stbbdev     // got all blocks from the bin and re-do coalesce on them
443*51c0b2f7Stbbdev     // to release single-block regions
444*51c0b2f7Stbbdev try_next:
445*51c0b2f7Stbbdev     if (b->head) {
446*51c0b2f7Stbbdev         MallocMutex::scoped_lock binLock(b->tLock);
447*51c0b2f7Stbbdev         for (FreeBlock *curr = b->head; curr; ) {
448*51c0b2f7Stbbdev             size_t szBlock = curr->tryLockBlock();
449*51c0b2f7Stbbdev             if (!szBlock)
450*51c0b2f7Stbbdev                 goto try_next;
451*51c0b2f7Stbbdev 
452*51c0b2f7Stbbdev             FreeBlock *next = curr->next;
453*51c0b2f7Stbbdev 
454*51c0b2f7Stbbdev             b->removeBlock(curr);
455*51c0b2f7Stbbdev             curr->sizeTmp = szBlock;
456*51c0b2f7Stbbdev             curr->nextToFree = fBlockList;
457*51c0b2f7Stbbdev             fBlockList = curr;
458*51c0b2f7Stbbdev             curr = next;
459*51c0b2f7Stbbdev         }
460*51c0b2f7Stbbdev     }
461*51c0b2f7Stbbdev     return backend->coalescAndPutList(fBlockList, /*forceCoalescQDrop=*/true,
462*51c0b2f7Stbbdev                                       /*reportBlocksProcessed=*/false);
463*51c0b2f7Stbbdev }
464*51c0b2f7Stbbdev 
465*51c0b2f7Stbbdev void Backend::Bin::removeBlock(FreeBlock *fBlock)
466*51c0b2f7Stbbdev {
467*51c0b2f7Stbbdev     MALLOC_ASSERT(fBlock->next||fBlock->prev||fBlock==head,
468*51c0b2f7Stbbdev                   "Detected that a block is not in the bin.");
469*51c0b2f7Stbbdev     if (head == fBlock)
470*51c0b2f7Stbbdev         head = fBlock->next;
471*51c0b2f7Stbbdev     if (tail == fBlock)
472*51c0b2f7Stbbdev         tail = fBlock->prev;
473*51c0b2f7Stbbdev     if (fBlock->prev)
474*51c0b2f7Stbbdev         fBlock->prev->next = fBlock->next;
475*51c0b2f7Stbbdev     if (fBlock->next)
476*51c0b2f7Stbbdev         fBlock->next->prev = fBlock->prev;
477*51c0b2f7Stbbdev }
478*51c0b2f7Stbbdev 
479*51c0b2f7Stbbdev void Backend::IndexedBins::addBlock(int binIdx, FreeBlock *fBlock, size_t /* blockSz */, bool addToTail)
480*51c0b2f7Stbbdev {
481*51c0b2f7Stbbdev     Bin *b = &freeBins[binIdx];
482*51c0b2f7Stbbdev     fBlock->myBin = binIdx;
483*51c0b2f7Stbbdev     fBlock->next = fBlock->prev = NULL;
484*51c0b2f7Stbbdev     {
485*51c0b2f7Stbbdev         MallocMutex::scoped_lock scopedLock(b->tLock);
486*51c0b2f7Stbbdev         if (addToTail) {
487*51c0b2f7Stbbdev             fBlock->prev = b->tail;
488*51c0b2f7Stbbdev             b->tail = fBlock;
489*51c0b2f7Stbbdev             if (fBlock->prev)
490*51c0b2f7Stbbdev                 fBlock->prev->next = fBlock;
491*51c0b2f7Stbbdev             if (!b->head)
492*51c0b2f7Stbbdev                 b->head = fBlock;
493*51c0b2f7Stbbdev         } else {
494*51c0b2f7Stbbdev             fBlock->next = b->head;
495*51c0b2f7Stbbdev             b->head = fBlock;
496*51c0b2f7Stbbdev             if (fBlock->next)
497*51c0b2f7Stbbdev                 fBlock->next->prev = fBlock;
498*51c0b2f7Stbbdev             if (!b->tail)
499*51c0b2f7Stbbdev                 b->tail = fBlock;
500*51c0b2f7Stbbdev         }
501*51c0b2f7Stbbdev     }
502*51c0b2f7Stbbdev     bitMask.set(binIdx, true);
503*51c0b2f7Stbbdev }
504*51c0b2f7Stbbdev 
505*51c0b2f7Stbbdev bool Backend::IndexedBins::tryAddBlock(int binIdx, FreeBlock *fBlock, bool addToTail)
506*51c0b2f7Stbbdev {
507*51c0b2f7Stbbdev     bool locked;
508*51c0b2f7Stbbdev     Bin *b = &freeBins[binIdx];
509*51c0b2f7Stbbdev     fBlock->myBin = binIdx;
510*51c0b2f7Stbbdev     if (addToTail) {
511*51c0b2f7Stbbdev         fBlock->next = NULL;
512*51c0b2f7Stbbdev         {
513*51c0b2f7Stbbdev             MallocMutex::scoped_lock scopedLock(b->tLock, /*wait=*/false, &locked);
514*51c0b2f7Stbbdev             if (!locked)
515*51c0b2f7Stbbdev                 return false;
516*51c0b2f7Stbbdev             fBlock->prev = b->tail;
517*51c0b2f7Stbbdev             b->tail = fBlock;
518*51c0b2f7Stbbdev             if (fBlock->prev)
519*51c0b2f7Stbbdev                 fBlock->prev->next = fBlock;
520*51c0b2f7Stbbdev             if (!b->head)
521*51c0b2f7Stbbdev                 b->head = fBlock;
522*51c0b2f7Stbbdev         }
523*51c0b2f7Stbbdev     } else {
524*51c0b2f7Stbbdev         fBlock->prev = NULL;
525*51c0b2f7Stbbdev         {
526*51c0b2f7Stbbdev             MallocMutex::scoped_lock scopedLock(b->tLock, /*wait=*/false, &locked);
527*51c0b2f7Stbbdev             if (!locked)
528*51c0b2f7Stbbdev                 return false;
529*51c0b2f7Stbbdev             fBlock->next = b->head;
530*51c0b2f7Stbbdev             b->head = fBlock;
531*51c0b2f7Stbbdev             if (fBlock->next)
532*51c0b2f7Stbbdev                 fBlock->next->prev = fBlock;
533*51c0b2f7Stbbdev             if (!b->tail)
534*51c0b2f7Stbbdev                 b->tail = fBlock;
535*51c0b2f7Stbbdev         }
536*51c0b2f7Stbbdev     }
537*51c0b2f7Stbbdev     bitMask.set(binIdx, true);
538*51c0b2f7Stbbdev     return true;
539*51c0b2f7Stbbdev }
540*51c0b2f7Stbbdev 
541*51c0b2f7Stbbdev void Backend::IndexedBins::reset()
542*51c0b2f7Stbbdev {
543*51c0b2f7Stbbdev     for (unsigned i=0; i<Backend::freeBinsNum; i++)
544*51c0b2f7Stbbdev         freeBins[i].reset();
545*51c0b2f7Stbbdev     bitMask.reset();
546*51c0b2f7Stbbdev }
547*51c0b2f7Stbbdev 
548*51c0b2f7Stbbdev void Backend::IndexedBins::lockRemoveBlock(int binIdx, FreeBlock *fBlock)
549*51c0b2f7Stbbdev {
550*51c0b2f7Stbbdev     MallocMutex::scoped_lock scopedLock(freeBins[binIdx].tLock);
551*51c0b2f7Stbbdev     freeBins[binIdx].removeBlock(fBlock);
552*51c0b2f7Stbbdev     if (freeBins[binIdx].empty())
553*51c0b2f7Stbbdev         bitMask.set(binIdx, false);
554*51c0b2f7Stbbdev }
555*51c0b2f7Stbbdev 
556*51c0b2f7Stbbdev bool ExtMemoryPool::regionsAreReleaseable() const
557*51c0b2f7Stbbdev {
558*51c0b2f7Stbbdev     return !keepAllMemory && !delayRegsReleasing;
559*51c0b2f7Stbbdev }
560*51c0b2f7Stbbdev 
561*51c0b2f7Stbbdev FreeBlock *Backend::splitBlock(FreeBlock *fBlock, int num, size_t size, bool blockIsAligned, bool needAlignedBlock)
562*51c0b2f7Stbbdev {
563*51c0b2f7Stbbdev     const size_t totalSize = num * size;
564*51c0b2f7Stbbdev 
565*51c0b2f7Stbbdev     // SPECIAL CASE, for unaligned block we have to cut the middle of a block
566*51c0b2f7Stbbdev     // and return remaining left and right part. Possible only in a fixed pool scenario.
567*51c0b2f7Stbbdev     if (needAlignedBlock && !blockIsAligned) {
568*51c0b2f7Stbbdev         MALLOC_ASSERT(extMemPool->fixedPool,
569*51c0b2f7Stbbdev                 "Aligned block request from unaligned bin possible only in fixed pool scenario.");
570*51c0b2f7Stbbdev 
571*51c0b2f7Stbbdev         // Space to use is in the middle
572*51c0b2f7Stbbdev         FreeBlock *newBlock = alignUp(fBlock, slabSize);
573*51c0b2f7Stbbdev         FreeBlock *rightPart = (FreeBlock*)((uintptr_t)newBlock + totalSize);
574*51c0b2f7Stbbdev         uintptr_t fBlockEnd = (uintptr_t)fBlock + fBlock->sizeTmp;
575*51c0b2f7Stbbdev 
576*51c0b2f7Stbbdev         // Return free right part
577*51c0b2f7Stbbdev         if ((uintptr_t)rightPart != fBlockEnd) {
578*51c0b2f7Stbbdev             rightPart->initHeader();  // to prevent coalescing rightPart with fBlock
579*51c0b2f7Stbbdev             size_t rightSize = fBlockEnd - (uintptr_t)rightPart;
580*51c0b2f7Stbbdev             coalescAndPut(rightPart, rightSize, toAlignedBin(rightPart, rightSize));
581*51c0b2f7Stbbdev         }
582*51c0b2f7Stbbdev         // And free left part
583*51c0b2f7Stbbdev         if (newBlock != fBlock) {
584*51c0b2f7Stbbdev             newBlock->initHeader(); // to prevent coalescing fBlock with newB
585*51c0b2f7Stbbdev             size_t leftSize = (uintptr_t)newBlock - (uintptr_t)fBlock;
586*51c0b2f7Stbbdev             coalescAndPut(fBlock, leftSize, toAlignedBin(fBlock, leftSize));
587*51c0b2f7Stbbdev         }
588*51c0b2f7Stbbdev         fBlock = newBlock;
589*51c0b2f7Stbbdev     } else if (size_t splitSize = fBlock->sizeTmp - totalSize) { // need to split the block
590*51c0b2f7Stbbdev         // GENERAL CASE, cut the left or right part of the block
591*51c0b2f7Stbbdev         FreeBlock *splitBlock = NULL;
592*51c0b2f7Stbbdev         if (needAlignedBlock) {
593*51c0b2f7Stbbdev             // For slab aligned blocks cut the right side of the block
594*51c0b2f7Stbbdev             // and return it to a requester, original block returns to backend
595*51c0b2f7Stbbdev             splitBlock = fBlock;
596*51c0b2f7Stbbdev             fBlock = (FreeBlock*)((uintptr_t)splitBlock + splitSize);
597*51c0b2f7Stbbdev             fBlock->initHeader();
598*51c0b2f7Stbbdev         } else {
599*51c0b2f7Stbbdev             // For large object blocks cut original block and put free righ part to backend
600*51c0b2f7Stbbdev             splitBlock = (FreeBlock*)((uintptr_t)fBlock + totalSize);
601*51c0b2f7Stbbdev             splitBlock->initHeader();
602*51c0b2f7Stbbdev         }
603*51c0b2f7Stbbdev         // Mark free block as it`s parent only when the requested type (needAlignedBlock)
604*51c0b2f7Stbbdev         // and returned from Bins/OS block (isAligned) are equal (XOR operation used)
605*51c0b2f7Stbbdev         bool markAligned = (blockIsAligned ^ needAlignedBlock) ? toAlignedBin(splitBlock, splitSize) : blockIsAligned;
606*51c0b2f7Stbbdev         coalescAndPut(splitBlock, splitSize, markAligned);
607*51c0b2f7Stbbdev     }
608*51c0b2f7Stbbdev     MALLOC_ASSERT(!needAlignedBlock || isAligned(fBlock, slabSize), "Expect to get aligned block, if one was requested.");
609*51c0b2f7Stbbdev     FreeBlock::markBlocks(fBlock, num, size);
610*51c0b2f7Stbbdev     return fBlock;
611*51c0b2f7Stbbdev }
612*51c0b2f7Stbbdev 
613*51c0b2f7Stbbdev size_t Backend::getMaxBinnedSize() const
614*51c0b2f7Stbbdev {
615*51c0b2f7Stbbdev     return hugePages.isEnabled && !inUserPool() ?
616*51c0b2f7Stbbdev         maxBinned_HugePage : maxBinned_SmallPage;
617*51c0b2f7Stbbdev }
618*51c0b2f7Stbbdev 
619*51c0b2f7Stbbdev inline bool Backend::MaxRequestComparator::operator()(size_t oldMaxReq, size_t requestSize) const
620*51c0b2f7Stbbdev {
621*51c0b2f7Stbbdev     return requestSize > oldMaxReq && requestSize < backend->getMaxBinnedSize();
622*51c0b2f7Stbbdev }
623*51c0b2f7Stbbdev 
624*51c0b2f7Stbbdev // last chance to get memory
625*51c0b2f7Stbbdev FreeBlock *Backend::releaseMemInCaches(intptr_t startModifiedCnt,
626*51c0b2f7Stbbdev                                     int *lockedBinsThreshold, int numOfLockedBins)
627*51c0b2f7Stbbdev {
628*51c0b2f7Stbbdev     // something released from caches
629*51c0b2f7Stbbdev     if (extMemPool->hardCachesCleanup()
630*51c0b2f7Stbbdev         // ..or can use blocks that are in processing now
631*51c0b2f7Stbbdev         || bkndSync.waitTillBlockReleased(startModifiedCnt))
632*51c0b2f7Stbbdev         return (FreeBlock*)VALID_BLOCK_IN_BIN;
633*51c0b2f7Stbbdev     // OS can't give us more memory, but we have some in locked bins
634*51c0b2f7Stbbdev     if (*lockedBinsThreshold && numOfLockedBins) {
635*51c0b2f7Stbbdev         *lockedBinsThreshold = 0;
636*51c0b2f7Stbbdev         return (FreeBlock*)VALID_BLOCK_IN_BIN;
637*51c0b2f7Stbbdev     }
638*51c0b2f7Stbbdev     return NULL; // nothing found, give up
639*51c0b2f7Stbbdev }
640*51c0b2f7Stbbdev 
641*51c0b2f7Stbbdev FreeBlock *Backend::askMemFromOS(size_t blockSize, intptr_t startModifiedCnt,
642*51c0b2f7Stbbdev                                  int *lockedBinsThreshold, int numOfLockedBins,
643*51c0b2f7Stbbdev                                  bool *splittableRet, bool needSlabRegion)
644*51c0b2f7Stbbdev {
645*51c0b2f7Stbbdev     FreeBlock *block;
646*51c0b2f7Stbbdev     // The block sizes can be divided into 3 groups:
647*51c0b2f7Stbbdev     //   1. "quite small": popular object size, we are in bootstarp or something
648*51c0b2f7Stbbdev     //      like; request several regions.
649*51c0b2f7Stbbdev     //   2. "quite large": we want to have several such blocks in the region
650*51c0b2f7Stbbdev     //      but not want several pre-allocated regions.
651*51c0b2f7Stbbdev     //   3. "huge": exact fit, we allocate only one block and do not allow
652*51c0b2f7Stbbdev     //       any other allocations to placed in a region.
653*51c0b2f7Stbbdev     // Dividing the block sizes in these groups we are trying to balance between
654*51c0b2f7Stbbdev     // too small regions (that leads to fragmentation) and too large ones (that
655*51c0b2f7Stbbdev     // leads to excessive address space consumption). If a region is "too
656*51c0b2f7Stbbdev     // large", allocate only one, to prevent fragmentation. It supposedly
657*51c0b2f7Stbbdev     // doesn't hurt performance, because the object requested by user is large.
658*51c0b2f7Stbbdev     // Bounds for the groups are:
659*51c0b2f7Stbbdev     const size_t maxBinned = getMaxBinnedSize();
660*51c0b2f7Stbbdev     const size_t quiteSmall = maxBinned / 8;
661*51c0b2f7Stbbdev     const size_t quiteLarge = maxBinned;
662*51c0b2f7Stbbdev 
663*51c0b2f7Stbbdev     if (blockSize >= quiteLarge) {
664*51c0b2f7Stbbdev         // Do not interact with other threads via semaphores, as for exact fit
665*51c0b2f7Stbbdev         // we can't share regions with them, memory requesting is individual.
666*51c0b2f7Stbbdev         block = addNewRegion(blockSize, MEMREG_ONE_BLOCK, /*addToBin=*/false);
667*51c0b2f7Stbbdev         if (!block)
668*51c0b2f7Stbbdev             return releaseMemInCaches(startModifiedCnt, lockedBinsThreshold, numOfLockedBins);
669*51c0b2f7Stbbdev         *splittableRet = false;
670*51c0b2f7Stbbdev     } else {
671*51c0b2f7Stbbdev         const size_t regSz_sizeBased = alignUp(4*maxRequestedSize, 1024*1024);
672*51c0b2f7Stbbdev         // Another thread is modifying backend while we can't get the block.
673*51c0b2f7Stbbdev         // Wait while it leaves and re-do the scan
674*51c0b2f7Stbbdev         // before trying other ways to extend the backend.
675*51c0b2f7Stbbdev         if (bkndSync.waitTillBlockReleased(startModifiedCnt)
676*51c0b2f7Stbbdev             // semaphore is protecting adding more more memory from OS
677*51c0b2f7Stbbdev             || memExtendingSema.wait())
678*51c0b2f7Stbbdev             return (FreeBlock*)VALID_BLOCK_IN_BIN;
679*51c0b2f7Stbbdev 
680*51c0b2f7Stbbdev         if (startModifiedCnt != bkndSync.getNumOfMods()) {
681*51c0b2f7Stbbdev             memExtendingSema.signal();
682*51c0b2f7Stbbdev             return (FreeBlock*)VALID_BLOCK_IN_BIN;
683*51c0b2f7Stbbdev         }
684*51c0b2f7Stbbdev 
685*51c0b2f7Stbbdev         if (blockSize < quiteSmall) {
686*51c0b2f7Stbbdev             // For this size of blocks, add NUM_OF_REG "advance" regions in bin,
687*51c0b2f7Stbbdev             // and return one as a result.
688*51c0b2f7Stbbdev             // TODO: add to bin first, because other threads can use them right away.
689*51c0b2f7Stbbdev             // This must be done carefully, because blocks in bins can be released
690*51c0b2f7Stbbdev             // in releaseCachesToLimit().
691*51c0b2f7Stbbdev             const unsigned NUM_OF_REG = 3;
692*51c0b2f7Stbbdev             MemRegionType regType = needSlabRegion ? MEMREG_SLAB_BLOCKS : MEMREG_LARGE_BLOCKS;
693*51c0b2f7Stbbdev             block = addNewRegion(regSz_sizeBased, regType, /*addToBin=*/false);
694*51c0b2f7Stbbdev             if (block)
695*51c0b2f7Stbbdev                 for (unsigned idx=0; idx<NUM_OF_REG; idx++)
696*51c0b2f7Stbbdev                     if (! addNewRegion(regSz_sizeBased, regType, /*addToBin=*/true))
697*51c0b2f7Stbbdev                         break;
698*51c0b2f7Stbbdev         } else {
699*51c0b2f7Stbbdev             block = addNewRegion(regSz_sizeBased, MEMREG_LARGE_BLOCKS, /*addToBin=*/false);
700*51c0b2f7Stbbdev         }
701*51c0b2f7Stbbdev         memExtendingSema.signal();
702*51c0b2f7Stbbdev 
703*51c0b2f7Stbbdev         // no regions found, try to clean cache
704*51c0b2f7Stbbdev         if (!block || block == (FreeBlock*)VALID_BLOCK_IN_BIN)
705*51c0b2f7Stbbdev             return releaseMemInCaches(startModifiedCnt, lockedBinsThreshold, numOfLockedBins);
706*51c0b2f7Stbbdev         // Since a region can hold more than one block it can be split.
707*51c0b2f7Stbbdev         *splittableRet = true;
708*51c0b2f7Stbbdev     }
709*51c0b2f7Stbbdev     // after asking memory from OS, release caches if we above the memory limits
710*51c0b2f7Stbbdev     releaseCachesToLimit();
711*51c0b2f7Stbbdev 
712*51c0b2f7Stbbdev     return block;
713*51c0b2f7Stbbdev }
714*51c0b2f7Stbbdev 
715*51c0b2f7Stbbdev void Backend::releaseCachesToLimit()
716*51c0b2f7Stbbdev {
717*51c0b2f7Stbbdev     if (!memSoftLimit.load(std::memory_order_relaxed)
718*51c0b2f7Stbbdev             || totalMemSize.load(std::memory_order_relaxed) <= memSoftLimit.load(std::memory_order_relaxed)) {
719*51c0b2f7Stbbdev         return;
720*51c0b2f7Stbbdev     }
721*51c0b2f7Stbbdev     size_t locTotalMemSize, locMemSoftLimit;
722*51c0b2f7Stbbdev 
723*51c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
724*51c0b2f7Stbbdev     if (extMemPool->softCachesCleanup() &&
725*51c0b2f7Stbbdev         (locTotalMemSize = totalMemSize.load(std::memory_order_acquire)) <=
726*51c0b2f7Stbbdev         (locMemSoftLimit = memSoftLimit.load(std::memory_order_acquire)))
727*51c0b2f7Stbbdev         return;
728*51c0b2f7Stbbdev     // clean global large-object cache, if this is not enough, clean local caches
729*51c0b2f7Stbbdev     // do this in several tries, because backend fragmentation can prevent
730*51c0b2f7Stbbdev     // region from releasing
731*51c0b2f7Stbbdev     for (int cleanLocal = 0; cleanLocal<2; cleanLocal++)
732*51c0b2f7Stbbdev         while (cleanLocal ?
733*51c0b2f7Stbbdev                  extMemPool->allLocalCaches.cleanup(/*cleanOnlyUnused=*/true) :
734*51c0b2f7Stbbdev                  extMemPool->loc.decreasingCleanup())
735*51c0b2f7Stbbdev             if ((locTotalMemSize = totalMemSize.load(std::memory_order_acquire)) <=
736*51c0b2f7Stbbdev                 (locMemSoftLimit = memSoftLimit.load(std::memory_order_acquire)))
737*51c0b2f7Stbbdev                 return;
738*51c0b2f7Stbbdev     // last chance to match memSoftLimit
739*51c0b2f7Stbbdev     extMemPool->hardCachesCleanup();
740*51c0b2f7Stbbdev }
741*51c0b2f7Stbbdev 
742*51c0b2f7Stbbdev int Backend::IndexedBins::getMinNonemptyBin(unsigned startBin) const
743*51c0b2f7Stbbdev {
744*51c0b2f7Stbbdev     int p = bitMask.getMinTrue(startBin);
745*51c0b2f7Stbbdev     return p == -1 ? Backend::freeBinsNum : p;
746*51c0b2f7Stbbdev }
747*51c0b2f7Stbbdev 
748*51c0b2f7Stbbdev FreeBlock *Backend::IndexedBins::findBlock(int nativeBin, BackendSync *sync, size_t size,
749*51c0b2f7Stbbdev         bool needAlignedBlock, bool alignedBin, int *numOfLockedBins)
750*51c0b2f7Stbbdev {
751*51c0b2f7Stbbdev     for (int i=getMinNonemptyBin(nativeBin); i<freeBinsNum; i=getMinNonemptyBin(i+1))
752*51c0b2f7Stbbdev         if (FreeBlock *block = getFromBin(i, sync, size, needAlignedBlock, alignedBin, /*wait=*/false, numOfLockedBins))
753*51c0b2f7Stbbdev             return block;
754*51c0b2f7Stbbdev 
755*51c0b2f7Stbbdev     return NULL;
756*51c0b2f7Stbbdev }
757*51c0b2f7Stbbdev 
758*51c0b2f7Stbbdev void Backend::requestBootstrapMem()
759*51c0b2f7Stbbdev {
760*51c0b2f7Stbbdev     if (bootsrapMemDone == bootsrapMemStatus.load(std::memory_order_acquire))
761*51c0b2f7Stbbdev         return;
762*51c0b2f7Stbbdev     MallocMutex::scoped_lock lock( bootsrapMemStatusMutex );
763*51c0b2f7Stbbdev     if (bootsrapMemDone == bootsrapMemStatus)
764*51c0b2f7Stbbdev         return;
765*51c0b2f7Stbbdev     MALLOC_ASSERT(bootsrapMemNotDone == bootsrapMemStatus, ASSERT_TEXT);
766*51c0b2f7Stbbdev     bootsrapMemStatus = bootsrapMemInitializing;
767*51c0b2f7Stbbdev     // request some rather big region during bootstrap in advance
768*51c0b2f7Stbbdev     // ok to get NULL here, as later we re-do a request with more modest size
769*51c0b2f7Stbbdev     addNewRegion(2*1024*1024, MEMREG_SLAB_BLOCKS, /*addToBin=*/true);
770*51c0b2f7Stbbdev     bootsrapMemStatus = bootsrapMemDone;
771*51c0b2f7Stbbdev }
772*51c0b2f7Stbbdev 
773*51c0b2f7Stbbdev // try to allocate size Byte block in available bins
774*51c0b2f7Stbbdev // needAlignedRes is true if result must be slab-aligned
775*51c0b2f7Stbbdev FreeBlock *Backend::genericGetBlock(int num, size_t size, bool needAlignedBlock)
776*51c0b2f7Stbbdev {
777*51c0b2f7Stbbdev     FreeBlock *block = NULL;
778*51c0b2f7Stbbdev     const size_t totalReqSize = num*size;
779*51c0b2f7Stbbdev     // no splitting after requesting new region, asks exact size
780*51c0b2f7Stbbdev     const int nativeBin = sizeToBin(totalReqSize);
781*51c0b2f7Stbbdev 
782*51c0b2f7Stbbdev     requestBootstrapMem();
783*51c0b2f7Stbbdev     // If we found 2 or less locked bins, it's time to ask more memory from OS.
784*51c0b2f7Stbbdev     // But nothing can be asked from fixed pool. And we prefer wait, not ask
785*51c0b2f7Stbbdev     // for more memory, if block is quite large.
786*51c0b2f7Stbbdev     int lockedBinsThreshold = extMemPool->fixedPool || size>=maxBinned_SmallPage? 0 : 2;
787*51c0b2f7Stbbdev 
788*51c0b2f7Stbbdev     // Find maximal requested size limited by getMaxBinnedSize()
789*51c0b2f7Stbbdev     AtomicUpdate(maxRequestedSize, totalReqSize, MaxRequestComparator(this));
790*51c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
791*51c0b2f7Stbbdev 
792*51c0b2f7Stbbdev     bool splittable = true;
793*51c0b2f7Stbbdev     for (;;) {
794*51c0b2f7Stbbdev         const intptr_t startModifiedCnt = bkndSync.getNumOfMods();
795*51c0b2f7Stbbdev         int numOfLockedBins;
796*51c0b2f7Stbbdev 
797*51c0b2f7Stbbdev         do {
798*51c0b2f7Stbbdev             numOfLockedBins = 0;
799*51c0b2f7Stbbdev             if (needAlignedBlock) {
800*51c0b2f7Stbbdev                 block = freeSlabAlignedBins.findBlock(nativeBin, &bkndSync, num*size, needAlignedBlock,
801*51c0b2f7Stbbdev                                                         /*alignedBin=*/true, &numOfLockedBins);
802*51c0b2f7Stbbdev                 if (!block && extMemPool->fixedPool)
803*51c0b2f7Stbbdev                     block = freeLargeBlockBins.findBlock(nativeBin, &bkndSync, num*size, needAlignedBlock,
804*51c0b2f7Stbbdev                                                         /*alignedBin=*/false, &numOfLockedBins);
805*51c0b2f7Stbbdev             } else {
806*51c0b2f7Stbbdev                 block = freeLargeBlockBins.findBlock(nativeBin, &bkndSync, num*size, needAlignedBlock,
807*51c0b2f7Stbbdev                                                         /*alignedBin=*/false, &numOfLockedBins);
808*51c0b2f7Stbbdev                 if (!block && extMemPool->fixedPool)
809*51c0b2f7Stbbdev                     block = freeSlabAlignedBins.findBlock(nativeBin, &bkndSync, num*size, needAlignedBlock,
810*51c0b2f7Stbbdev                                                         /*alignedBin=*/true, &numOfLockedBins);
811*51c0b2f7Stbbdev             }
812*51c0b2f7Stbbdev         } while (!block && numOfLockedBins>lockedBinsThreshold);
813*51c0b2f7Stbbdev 
814*51c0b2f7Stbbdev         if (block)
815*51c0b2f7Stbbdev             break;
816*51c0b2f7Stbbdev 
817*51c0b2f7Stbbdev         if (!(scanCoalescQ(/*forceCoalescQDrop=*/true) | extMemPool->softCachesCleanup())) {
818*51c0b2f7Stbbdev             // bins are not updated,
819*51c0b2f7Stbbdev             // only remaining possibility is to ask for more memory
820*51c0b2f7Stbbdev             block = askMemFromOS(totalReqSize, startModifiedCnt, &lockedBinsThreshold,
821*51c0b2f7Stbbdev                         numOfLockedBins, &splittable, needAlignedBlock);
822*51c0b2f7Stbbdev             if (!block)
823*51c0b2f7Stbbdev                 return NULL;
824*51c0b2f7Stbbdev             if (block != (FreeBlock*)VALID_BLOCK_IN_BIN) {
825*51c0b2f7Stbbdev                 // size can be increased in askMemFromOS, that's why >=
826*51c0b2f7Stbbdev                 MALLOC_ASSERT(block->sizeTmp >= size, ASSERT_TEXT);
827*51c0b2f7Stbbdev                 break;
828*51c0b2f7Stbbdev             }
829*51c0b2f7Stbbdev             // valid block somewhere in bins, let's find it
830*51c0b2f7Stbbdev             block = NULL;
831*51c0b2f7Stbbdev         }
832*51c0b2f7Stbbdev     }
833*51c0b2f7Stbbdev     MALLOC_ASSERT(block, ASSERT_TEXT);
834*51c0b2f7Stbbdev     if (splittable) {
835*51c0b2f7Stbbdev         // At this point we have to be sure that slabAligned attribute describes the right block state
836*51c0b2f7Stbbdev         block = splitBlock(block, num, size, block->slabAligned, needAlignedBlock);
837*51c0b2f7Stbbdev     }
838*51c0b2f7Stbbdev     // matched blockConsumed() from startUseBlock()
839*51c0b2f7Stbbdev     bkndSync.blockReleased();
840*51c0b2f7Stbbdev 
841*51c0b2f7Stbbdev     return block;
842*51c0b2f7Stbbdev }
843*51c0b2f7Stbbdev 
844*51c0b2f7Stbbdev LargeMemoryBlock *Backend::getLargeBlock(size_t size)
845*51c0b2f7Stbbdev {
846*51c0b2f7Stbbdev     LargeMemoryBlock *lmb =
847*51c0b2f7Stbbdev         (LargeMemoryBlock*)genericGetBlock(1, size, /*needAlignedRes=*/false);
848*51c0b2f7Stbbdev     if (lmb) {
849*51c0b2f7Stbbdev         lmb->unalignedSize = size;
850*51c0b2f7Stbbdev         if (extMemPool->userPool())
851*51c0b2f7Stbbdev             extMemPool->lmbList.add(lmb);
852*51c0b2f7Stbbdev     }
853*51c0b2f7Stbbdev     return lmb;
854*51c0b2f7Stbbdev }
855*51c0b2f7Stbbdev 
856*51c0b2f7Stbbdev BlockI *Backend::getSlabBlock(int num) {
857*51c0b2f7Stbbdev     BlockI *b = (BlockI*)genericGetBlock(num, slabSize, /*slabAligned=*/true);
858*51c0b2f7Stbbdev     MALLOC_ASSERT(isAligned(b, slabSize), ASSERT_TEXT);
859*51c0b2f7Stbbdev     return b;
860*51c0b2f7Stbbdev }
861*51c0b2f7Stbbdev 
862*51c0b2f7Stbbdev void Backend::putSlabBlock(BlockI *block) {
863*51c0b2f7Stbbdev     genericPutBlock((FreeBlock *)block, slabSize, /*slabAligned=*/true);
864*51c0b2f7Stbbdev }
865*51c0b2f7Stbbdev 
866*51c0b2f7Stbbdev void *Backend::getBackRefSpace(size_t size, bool *rawMemUsed)
867*51c0b2f7Stbbdev {
868*51c0b2f7Stbbdev     // This block is released only at shutdown, so it can prevent
869*51c0b2f7Stbbdev     // a entire region releasing when it's received from the backend,
870*51c0b2f7Stbbdev     // so prefer getRawMemory using.
871*51c0b2f7Stbbdev     if (void *ret = getRawMemory(size, REGULAR)) {
872*51c0b2f7Stbbdev         *rawMemUsed = true;
873*51c0b2f7Stbbdev         return ret;
874*51c0b2f7Stbbdev     }
875*51c0b2f7Stbbdev     void *ret = genericGetBlock(1, size, /*needAlignedRes=*/false);
876*51c0b2f7Stbbdev     if (ret) *rawMemUsed = false;
877*51c0b2f7Stbbdev     return ret;
878*51c0b2f7Stbbdev }
879*51c0b2f7Stbbdev 
880*51c0b2f7Stbbdev void Backend::putBackRefSpace(void *b, size_t size, bool rawMemUsed)
881*51c0b2f7Stbbdev {
882*51c0b2f7Stbbdev     if (rawMemUsed)
883*51c0b2f7Stbbdev         freeRawMemory(b, size);
884*51c0b2f7Stbbdev     // ignore not raw mem, as it released on region releasing
885*51c0b2f7Stbbdev }
886*51c0b2f7Stbbdev 
887*51c0b2f7Stbbdev void Backend::removeBlockFromBin(FreeBlock *fBlock)
888*51c0b2f7Stbbdev {
889*51c0b2f7Stbbdev     if (fBlock->myBin != Backend::NO_BIN) {
890*51c0b2f7Stbbdev         if (fBlock->slabAligned)
891*51c0b2f7Stbbdev             freeSlabAlignedBins.lockRemoveBlock(fBlock->myBin, fBlock);
892*51c0b2f7Stbbdev         else
893*51c0b2f7Stbbdev             freeLargeBlockBins.lockRemoveBlock(fBlock->myBin, fBlock);
894*51c0b2f7Stbbdev     }
895*51c0b2f7Stbbdev }
896*51c0b2f7Stbbdev 
897*51c0b2f7Stbbdev void Backend::genericPutBlock(FreeBlock *fBlock, size_t blockSz, bool slabAligned)
898*51c0b2f7Stbbdev {
899*51c0b2f7Stbbdev     bkndSync.blockConsumed();
900*51c0b2f7Stbbdev     coalescAndPut(fBlock, blockSz, slabAligned);
901*51c0b2f7Stbbdev     bkndSync.blockReleased();
902*51c0b2f7Stbbdev }
903*51c0b2f7Stbbdev 
904*51c0b2f7Stbbdev void AllLargeBlocksList::add(LargeMemoryBlock *lmb)
905*51c0b2f7Stbbdev {
906*51c0b2f7Stbbdev     MallocMutex::scoped_lock scoped_cs(largeObjLock);
907*51c0b2f7Stbbdev     lmb->gPrev = NULL;
908*51c0b2f7Stbbdev     lmb->gNext = loHead;
909*51c0b2f7Stbbdev     if (lmb->gNext)
910*51c0b2f7Stbbdev         lmb->gNext->gPrev = lmb;
911*51c0b2f7Stbbdev     loHead = lmb;
912*51c0b2f7Stbbdev }
913*51c0b2f7Stbbdev 
914*51c0b2f7Stbbdev void AllLargeBlocksList::remove(LargeMemoryBlock *lmb)
915*51c0b2f7Stbbdev {
916*51c0b2f7Stbbdev     MallocMutex::scoped_lock scoped_cs(largeObjLock);
917*51c0b2f7Stbbdev     if (loHead == lmb)
918*51c0b2f7Stbbdev         loHead = lmb->gNext;
919*51c0b2f7Stbbdev     if (lmb->gNext)
920*51c0b2f7Stbbdev         lmb->gNext->gPrev = lmb->gPrev;
921*51c0b2f7Stbbdev     if (lmb->gPrev)
922*51c0b2f7Stbbdev         lmb->gPrev->gNext = lmb->gNext;
923*51c0b2f7Stbbdev }
924*51c0b2f7Stbbdev 
925*51c0b2f7Stbbdev void Backend::putLargeBlock(LargeMemoryBlock *lmb)
926*51c0b2f7Stbbdev {
927*51c0b2f7Stbbdev     if (extMemPool->userPool())
928*51c0b2f7Stbbdev         extMemPool->lmbList.remove(lmb);
929*51c0b2f7Stbbdev     genericPutBlock((FreeBlock *)lmb, lmb->unalignedSize, false);
930*51c0b2f7Stbbdev }
931*51c0b2f7Stbbdev 
932*51c0b2f7Stbbdev void Backend::returnLargeObject(LargeMemoryBlock *lmb)
933*51c0b2f7Stbbdev {
934*51c0b2f7Stbbdev     removeBackRef(lmb->backRefIdx);
935*51c0b2f7Stbbdev     putLargeBlock(lmb);
936*51c0b2f7Stbbdev     STAT_increment(getThreadId(), ThreadCommonCounters, freeLargeObj);
937*51c0b2f7Stbbdev }
938*51c0b2f7Stbbdev 
939*51c0b2f7Stbbdev #if BACKEND_HAS_MREMAP
940*51c0b2f7Stbbdev void *Backend::remap(void *ptr, size_t oldSize, size_t newSize, size_t alignment)
941*51c0b2f7Stbbdev {
942*51c0b2f7Stbbdev     // no remap for user pools and for object too small that living in bins
943*51c0b2f7Stbbdev     if (inUserPool() || min(oldSize, newSize)<maxBinned_SmallPage
944*51c0b2f7Stbbdev         // during remap, can't guarantee alignment more strict than current or
945*51c0b2f7Stbbdev         // more strict than page alignment
946*51c0b2f7Stbbdev         || !isAligned(ptr, alignment) || alignment>extMemPool->granularity)
947*51c0b2f7Stbbdev         return NULL;
948*51c0b2f7Stbbdev     const LargeMemoryBlock* lmbOld = ((LargeObjectHdr *)ptr - 1)->memoryBlock;
949*51c0b2f7Stbbdev     const size_t oldUnalignedSize = lmbOld->unalignedSize;
950*51c0b2f7Stbbdev     FreeBlock *oldFBlock = (FreeBlock *)lmbOld;
951*51c0b2f7Stbbdev     FreeBlock *right = oldFBlock->rightNeig(oldUnalignedSize);
952*51c0b2f7Stbbdev     // in every region only one block can have LAST_REGION_BLOCK on right,
953*51c0b2f7Stbbdev     // so don't need no synchronization
954*51c0b2f7Stbbdev     if (!right->isLastRegionBlock())
955*51c0b2f7Stbbdev         return NULL;
956*51c0b2f7Stbbdev 
957*51c0b2f7Stbbdev     MemRegion *oldRegion = static_cast<LastFreeBlock*>(right)->memRegion;
958*51c0b2f7Stbbdev     MALLOC_ASSERT( oldRegion < ptr, ASSERT_TEXT );
959*51c0b2f7Stbbdev     const size_t oldRegionSize = oldRegion->allocSz;
960*51c0b2f7Stbbdev     if (oldRegion->type != MEMREG_ONE_BLOCK)
961*51c0b2f7Stbbdev         return NULL;  // we are not single in the region
962*51c0b2f7Stbbdev     const size_t userOffset = (uintptr_t)ptr - (uintptr_t)oldRegion;
963*51c0b2f7Stbbdev     const size_t alignedSize = LargeObjectCache::alignToBin(newSize + userOffset);
964*51c0b2f7Stbbdev     const size_t requestSize =
965*51c0b2f7Stbbdev         alignUp(sizeof(MemRegion) + alignedSize + sizeof(LastFreeBlock), extMemPool->granularity);
966*51c0b2f7Stbbdev     if (requestSize < alignedSize) // is wrapped around?
967*51c0b2f7Stbbdev         return NULL;
968*51c0b2f7Stbbdev     regionList.remove(oldRegion);
969*51c0b2f7Stbbdev 
970*51c0b2f7Stbbdev     void *ret = mremap(oldRegion, oldRegion->allocSz, requestSize, MREMAP_MAYMOVE);
971*51c0b2f7Stbbdev     if (MAP_FAILED == ret) { // can't remap, revert and leave
972*51c0b2f7Stbbdev         regionList.add(oldRegion);
973*51c0b2f7Stbbdev         return NULL;
974*51c0b2f7Stbbdev     }
975*51c0b2f7Stbbdev     MemRegion *region = (MemRegion*)ret;
976*51c0b2f7Stbbdev     MALLOC_ASSERT(region->type == MEMREG_ONE_BLOCK, ASSERT_TEXT);
977*51c0b2f7Stbbdev     region->allocSz = requestSize;
978*51c0b2f7Stbbdev     region->blockSz = alignedSize;
979*51c0b2f7Stbbdev 
980*51c0b2f7Stbbdev     FreeBlock *fBlock = (FreeBlock *)alignUp((uintptr_t)region + sizeof(MemRegion),
981*51c0b2f7Stbbdev                                              largeObjectAlignment);
982*51c0b2f7Stbbdev 
983*51c0b2f7Stbbdev     regionList.add(region);
984*51c0b2f7Stbbdev     startUseBlock(region, fBlock, /*addToBin=*/false);
985*51c0b2f7Stbbdev     MALLOC_ASSERT(fBlock->sizeTmp == region->blockSz, ASSERT_TEXT);
986*51c0b2f7Stbbdev     // matched blockConsumed() in startUseBlock().
987*51c0b2f7Stbbdev     // TODO: get rid of useless pair blockConsumed()/blockReleased()
988*51c0b2f7Stbbdev     bkndSync.blockReleased();
989*51c0b2f7Stbbdev 
990*51c0b2f7Stbbdev     // object must start at same offset from region's start
991*51c0b2f7Stbbdev     void *object = (void*)((uintptr_t)region + userOffset);
992*51c0b2f7Stbbdev     MALLOC_ASSERT(isAligned(object, alignment), ASSERT_TEXT);
993*51c0b2f7Stbbdev     LargeObjectHdr *header = (LargeObjectHdr*)object - 1;
994*51c0b2f7Stbbdev     setBackRef(header->backRefIdx, header);
995*51c0b2f7Stbbdev 
996*51c0b2f7Stbbdev     LargeMemoryBlock *lmb = (LargeMemoryBlock*)fBlock;
997*51c0b2f7Stbbdev     lmb->unalignedSize = region->blockSz;
998*51c0b2f7Stbbdev     lmb->objectSize = newSize;
999*51c0b2f7Stbbdev     lmb->backRefIdx = header->backRefIdx;
1000*51c0b2f7Stbbdev     header->memoryBlock = lmb;
1001*51c0b2f7Stbbdev     MALLOC_ASSERT((uintptr_t)lmb + lmb->unalignedSize >=
1002*51c0b2f7Stbbdev                   (uintptr_t)object + lmb->objectSize, "An object must fit to the block.");
1003*51c0b2f7Stbbdev 
1004*51c0b2f7Stbbdev     usedAddrRange.registerFree((uintptr_t)oldRegion, (uintptr_t)oldRegion + oldRegionSize);
1005*51c0b2f7Stbbdev     usedAddrRange.registerAlloc((uintptr_t)region, (uintptr_t)region + requestSize);
1006*51c0b2f7Stbbdev     totalMemSize.fetch_add(region->allocSz - oldRegionSize);
1007*51c0b2f7Stbbdev 
1008*51c0b2f7Stbbdev     return object;
1009*51c0b2f7Stbbdev }
1010*51c0b2f7Stbbdev #endif /* BACKEND_HAS_MREMAP */
1011*51c0b2f7Stbbdev 
1012*51c0b2f7Stbbdev void Backend::releaseRegion(MemRegion *memRegion)
1013*51c0b2f7Stbbdev {
1014*51c0b2f7Stbbdev     regionList.remove(memRegion);
1015*51c0b2f7Stbbdev     freeRawMem(memRegion, memRegion->allocSz);
1016*51c0b2f7Stbbdev }
1017*51c0b2f7Stbbdev 
1018*51c0b2f7Stbbdev // coalesce fBlock with its neighborhood
1019*51c0b2f7Stbbdev FreeBlock *Backend::doCoalesc(FreeBlock *fBlock, MemRegion **mRegion)
1020*51c0b2f7Stbbdev {
1021*51c0b2f7Stbbdev     FreeBlock *resBlock = fBlock;
1022*51c0b2f7Stbbdev     size_t resSize = fBlock->sizeTmp;
1023*51c0b2f7Stbbdev     MemRegion *memRegion = NULL;
1024*51c0b2f7Stbbdev 
1025*51c0b2f7Stbbdev     fBlock->markCoalescing(resSize);
1026*51c0b2f7Stbbdev     resBlock->blockInBin = false;
1027*51c0b2f7Stbbdev 
1028*51c0b2f7Stbbdev     // coalescing with left neighbor
1029*51c0b2f7Stbbdev     size_t leftSz = fBlock->trySetLeftUsed(GuardedSize::COAL_BLOCK);
1030*51c0b2f7Stbbdev     if (leftSz != GuardedSize::LOCKED) {
1031*51c0b2f7Stbbdev         if (leftSz == GuardedSize::COAL_BLOCK) {
1032*51c0b2f7Stbbdev             coalescQ.putBlock(fBlock);
1033*51c0b2f7Stbbdev             return NULL;
1034*51c0b2f7Stbbdev         } else {
1035*51c0b2f7Stbbdev             FreeBlock *left = fBlock->leftNeig(leftSz);
1036*51c0b2f7Stbbdev             size_t lSz = left->trySetMeUsed(GuardedSize::COAL_BLOCK);
1037*51c0b2f7Stbbdev             if (lSz <= GuardedSize::MAX_LOCKED_VAL) {
1038*51c0b2f7Stbbdev                 fBlock->setLeftFree(leftSz); // rollback
1039*51c0b2f7Stbbdev                 coalescQ.putBlock(fBlock);
1040*51c0b2f7Stbbdev                 return NULL;
1041*51c0b2f7Stbbdev             } else {
1042*51c0b2f7Stbbdev                 MALLOC_ASSERT(lSz == leftSz, "Invalid header");
1043*51c0b2f7Stbbdev                 left->blockInBin = true;
1044*51c0b2f7Stbbdev                 resBlock = left;
1045*51c0b2f7Stbbdev                 resSize += leftSz;
1046*51c0b2f7Stbbdev                 resBlock->sizeTmp = resSize;
1047*51c0b2f7Stbbdev             }
1048*51c0b2f7Stbbdev         }
1049*51c0b2f7Stbbdev     }
1050*51c0b2f7Stbbdev     // coalescing with right neighbor
1051*51c0b2f7Stbbdev     FreeBlock *right = fBlock->rightNeig(fBlock->sizeTmp);
1052*51c0b2f7Stbbdev     size_t rightSz = right->trySetMeUsed(GuardedSize::COAL_BLOCK);
1053*51c0b2f7Stbbdev     if (rightSz != GuardedSize::LOCKED) {
1054*51c0b2f7Stbbdev         // LastFreeBlock is on the right side
1055*51c0b2f7Stbbdev         if (GuardedSize::LAST_REGION_BLOCK == rightSz) {
1056*51c0b2f7Stbbdev             right->setMeFree(GuardedSize::LAST_REGION_BLOCK);
1057*51c0b2f7Stbbdev             memRegion = static_cast<LastFreeBlock*>(right)->memRegion;
1058*51c0b2f7Stbbdev         } else if (GuardedSize::COAL_BLOCK == rightSz) {
1059*51c0b2f7Stbbdev             if (resBlock->blockInBin) {
1060*51c0b2f7Stbbdev                 resBlock->blockInBin = false;
1061*51c0b2f7Stbbdev                 removeBlockFromBin(resBlock);
1062*51c0b2f7Stbbdev             }
1063*51c0b2f7Stbbdev             coalescQ.putBlock(resBlock);
1064*51c0b2f7Stbbdev             return NULL;
1065*51c0b2f7Stbbdev         } else {
1066*51c0b2f7Stbbdev             size_t rSz = right->rightNeig(rightSz)->
1067*51c0b2f7Stbbdev                 trySetLeftUsed(GuardedSize::COAL_BLOCK);
1068*51c0b2f7Stbbdev             if (rSz <= GuardedSize::MAX_LOCKED_VAL) {
1069*51c0b2f7Stbbdev                 right->setMeFree(rightSz);  // rollback
1070*51c0b2f7Stbbdev                 if (resBlock->blockInBin) {
1071*51c0b2f7Stbbdev                     resBlock->blockInBin = false;
1072*51c0b2f7Stbbdev                     removeBlockFromBin(resBlock);
1073*51c0b2f7Stbbdev                 }
1074*51c0b2f7Stbbdev                 coalescQ.putBlock(resBlock);
1075*51c0b2f7Stbbdev                 return NULL;
1076*51c0b2f7Stbbdev             } else {
1077*51c0b2f7Stbbdev                 MALLOC_ASSERT(rSz == rightSz, "Invalid header");
1078*51c0b2f7Stbbdev                 removeBlockFromBin(right);
1079*51c0b2f7Stbbdev                 resSize += rightSz;
1080*51c0b2f7Stbbdev 
1081*51c0b2f7Stbbdev                 // Is LastFreeBlock on the right side of right?
1082*51c0b2f7Stbbdev                 FreeBlock *nextRight = right->rightNeig(rightSz);
1083*51c0b2f7Stbbdev                 size_t nextRightSz = nextRight->
1084*51c0b2f7Stbbdev                     trySetMeUsed(GuardedSize::COAL_BLOCK);
1085*51c0b2f7Stbbdev                 if (nextRightSz > GuardedSize::MAX_LOCKED_VAL) {
1086*51c0b2f7Stbbdev                     if (nextRightSz == GuardedSize::LAST_REGION_BLOCK)
1087*51c0b2f7Stbbdev                         memRegion = static_cast<LastFreeBlock*>(nextRight)->memRegion;
1088*51c0b2f7Stbbdev 
1089*51c0b2f7Stbbdev                     nextRight->setMeFree(nextRightSz);
1090*51c0b2f7Stbbdev                 }
1091*51c0b2f7Stbbdev             }
1092*51c0b2f7Stbbdev         }
1093*51c0b2f7Stbbdev     }
1094*51c0b2f7Stbbdev     if (memRegion) {
1095*51c0b2f7Stbbdev         MALLOC_ASSERT((uintptr_t)memRegion + memRegion->allocSz >=
1096*51c0b2f7Stbbdev                       (uintptr_t)right + sizeof(LastFreeBlock), ASSERT_TEXT);
1097*51c0b2f7Stbbdev         MALLOC_ASSERT((uintptr_t)memRegion < (uintptr_t)resBlock, ASSERT_TEXT);
1098*51c0b2f7Stbbdev         *mRegion = memRegion;
1099*51c0b2f7Stbbdev     } else
1100*51c0b2f7Stbbdev         *mRegion = NULL;
1101*51c0b2f7Stbbdev     resBlock->sizeTmp = resSize;
1102*51c0b2f7Stbbdev     return resBlock;
1103*51c0b2f7Stbbdev }
1104*51c0b2f7Stbbdev 
1105*51c0b2f7Stbbdev bool Backend::coalescAndPutList(FreeBlock *list, bool forceCoalescQDrop, bool reportBlocksProcessed)
1106*51c0b2f7Stbbdev {
1107*51c0b2f7Stbbdev     bool regionReleased = false;
1108*51c0b2f7Stbbdev 
1109*51c0b2f7Stbbdev     for (FreeBlock *helper; list;
1110*51c0b2f7Stbbdev          list = helper,
1111*51c0b2f7Stbbdev              // matches block enqueue in CoalRequestQ::putBlock()
1112*51c0b2f7Stbbdev              reportBlocksProcessed? coalescQ.blockWasProcessed() : (void)0) {
1113*51c0b2f7Stbbdev         MemRegion *memRegion;
1114*51c0b2f7Stbbdev         bool addToTail = false;
1115*51c0b2f7Stbbdev 
1116*51c0b2f7Stbbdev         helper = list->nextToFree;
1117*51c0b2f7Stbbdev         FreeBlock *toRet = doCoalesc(list, &memRegion);
1118*51c0b2f7Stbbdev         if (!toRet)
1119*51c0b2f7Stbbdev             continue;
1120*51c0b2f7Stbbdev 
1121*51c0b2f7Stbbdev         if (memRegion && memRegion->blockSz == toRet->sizeTmp
1122*51c0b2f7Stbbdev             && !extMemPool->fixedPool) {
1123*51c0b2f7Stbbdev             if (extMemPool->regionsAreReleaseable()) {
1124*51c0b2f7Stbbdev                 // release the region, because there is no used blocks in it
1125*51c0b2f7Stbbdev                 if (toRet->blockInBin)
1126*51c0b2f7Stbbdev                     removeBlockFromBin(toRet);
1127*51c0b2f7Stbbdev                 releaseRegion(memRegion);
1128*51c0b2f7Stbbdev                 regionReleased = true;
1129*51c0b2f7Stbbdev                 continue;
1130*51c0b2f7Stbbdev             } else // add block from empty region to end of bin,
1131*51c0b2f7Stbbdev                 addToTail = true; // preserving for exact fit
1132*51c0b2f7Stbbdev         }
1133*51c0b2f7Stbbdev         size_t currSz = toRet->sizeTmp;
1134*51c0b2f7Stbbdev         int bin = sizeToBin(currSz);
1135*51c0b2f7Stbbdev         bool toAligned = extMemPool->fixedPool ? toAlignedBin(toRet, currSz) : toRet->slabAligned;
1136*51c0b2f7Stbbdev         bool needAddToBin = true;
1137*51c0b2f7Stbbdev 
1138*51c0b2f7Stbbdev         if (toRet->blockInBin) {
1139*51c0b2f7Stbbdev             // Does it stay in same bin?
1140*51c0b2f7Stbbdev             if (toRet->myBin == bin && toRet->slabAligned == toAligned)
1141*51c0b2f7Stbbdev                 needAddToBin = false;
1142*51c0b2f7Stbbdev             else {
1143*51c0b2f7Stbbdev                 toRet->blockInBin = false;
1144*51c0b2f7Stbbdev                 removeBlockFromBin(toRet);
1145*51c0b2f7Stbbdev             }
1146*51c0b2f7Stbbdev         }
1147*51c0b2f7Stbbdev 
1148*51c0b2f7Stbbdev         // Does not stay in same bin, or bin-less; add it
1149*51c0b2f7Stbbdev         if (needAddToBin) {
1150*51c0b2f7Stbbdev             toRet->prev = toRet->next = toRet->nextToFree = NULL;
1151*51c0b2f7Stbbdev             toRet->myBin = NO_BIN;
1152*51c0b2f7Stbbdev             toRet->slabAligned = toAligned;
1153*51c0b2f7Stbbdev 
1154*51c0b2f7Stbbdev             // If the block is too small to fit in any bin, keep it bin-less.
1155*51c0b2f7Stbbdev             // It's not a leak because the block later can be coalesced.
1156*51c0b2f7Stbbdev             if (currSz >= minBinnedSize) {
1157*51c0b2f7Stbbdev                 toRet->sizeTmp = currSz;
1158*51c0b2f7Stbbdev                 IndexedBins *target = toRet->slabAligned ? &freeSlabAlignedBins : &freeLargeBlockBins;
1159*51c0b2f7Stbbdev                 if (forceCoalescQDrop) {
1160*51c0b2f7Stbbdev                     target->addBlock(bin, toRet, toRet->sizeTmp, addToTail);
1161*51c0b2f7Stbbdev                 } else if (!target->tryAddBlock(bin, toRet, addToTail)) {
1162*51c0b2f7Stbbdev                     coalescQ.putBlock(toRet);
1163*51c0b2f7Stbbdev                     continue;
1164*51c0b2f7Stbbdev                 }
1165*51c0b2f7Stbbdev             }
1166*51c0b2f7Stbbdev             toRet->sizeTmp = 0;
1167*51c0b2f7Stbbdev         }
1168*51c0b2f7Stbbdev         // Free (possibly coalesced) free block.
1169*51c0b2f7Stbbdev         // Adding to bin must be done before this point,
1170*51c0b2f7Stbbdev         // because after a block is free it can be coalesced, and
1171*51c0b2f7Stbbdev         // using its pointer became unsafe.
1172*51c0b2f7Stbbdev         // Remember that coalescing is not done under any global lock.
1173*51c0b2f7Stbbdev         toRet->setMeFree(currSz);
1174*51c0b2f7Stbbdev         toRet->rightNeig(currSz)->setLeftFree(currSz);
1175*51c0b2f7Stbbdev     }
1176*51c0b2f7Stbbdev     return regionReleased;
1177*51c0b2f7Stbbdev }
1178*51c0b2f7Stbbdev 
1179*51c0b2f7Stbbdev // Coalesce fBlock and add it back to a bin;
1180*51c0b2f7Stbbdev // processing delayed coalescing requests.
1181*51c0b2f7Stbbdev void Backend::coalescAndPut(FreeBlock *fBlock, size_t blockSz, bool slabAligned)
1182*51c0b2f7Stbbdev {
1183*51c0b2f7Stbbdev     fBlock->sizeTmp = blockSz;
1184*51c0b2f7Stbbdev     fBlock->nextToFree = NULL;
1185*51c0b2f7Stbbdev     fBlock->slabAligned = slabAligned;
1186*51c0b2f7Stbbdev 
1187*51c0b2f7Stbbdev     coalescAndPutList(fBlock, /*forceCoalescQDrop=*/false, /*reportBlocksProcessed=*/false);
1188*51c0b2f7Stbbdev }
1189*51c0b2f7Stbbdev 
1190*51c0b2f7Stbbdev bool Backend::scanCoalescQ(bool forceCoalescQDrop)
1191*51c0b2f7Stbbdev {
1192*51c0b2f7Stbbdev     FreeBlock *currCoalescList = coalescQ.getAll();
1193*51c0b2f7Stbbdev 
1194*51c0b2f7Stbbdev     if (currCoalescList)
1195*51c0b2f7Stbbdev         // reportBlocksProcessed=true informs that the blocks leave coalescQ,
1196*51c0b2f7Stbbdev         // matches blockConsumed() from CoalRequestQ::putBlock()
1197*51c0b2f7Stbbdev         coalescAndPutList(currCoalescList, forceCoalescQDrop,
1198*51c0b2f7Stbbdev                           /*reportBlocksProcessed=*/true);
1199*51c0b2f7Stbbdev     // returns status of coalescQ.getAll(), as an indication of possible changes in backend
1200*51c0b2f7Stbbdev     // TODO: coalescAndPutList() may report is some new free blocks became available or not
1201*51c0b2f7Stbbdev     return currCoalescList;
1202*51c0b2f7Stbbdev }
1203*51c0b2f7Stbbdev 
1204*51c0b2f7Stbbdev FreeBlock *Backend::findBlockInRegion(MemRegion *region, size_t exactBlockSize)
1205*51c0b2f7Stbbdev {
1206*51c0b2f7Stbbdev     FreeBlock *fBlock;
1207*51c0b2f7Stbbdev     size_t blockSz;
1208*51c0b2f7Stbbdev     uintptr_t fBlockEnd,
1209*51c0b2f7Stbbdev         lastFreeBlock = (uintptr_t)region + region->allocSz - sizeof(LastFreeBlock);
1210*51c0b2f7Stbbdev 
1211*51c0b2f7Stbbdev     static_assert(sizeof(LastFreeBlock) % sizeof(uintptr_t) == 0,
1212*51c0b2f7Stbbdev         "Atomic applied on LastFreeBlock, and we put it at the end of region, that"
1213*51c0b2f7Stbbdev         " is uintptr_t-aligned, so no unaligned atomic operations are possible.");
1214*51c0b2f7Stbbdev      // right bound is slab-aligned, keep LastFreeBlock after it
1215*51c0b2f7Stbbdev     if (region->type == MEMREG_SLAB_BLOCKS) {
1216*51c0b2f7Stbbdev         fBlock = (FreeBlock *)alignUp((uintptr_t)region + sizeof(MemRegion), sizeof(uintptr_t));
1217*51c0b2f7Stbbdev         fBlockEnd = alignDown(lastFreeBlock, slabSize);
1218*51c0b2f7Stbbdev     } else {
1219*51c0b2f7Stbbdev         fBlock = (FreeBlock *)alignUp((uintptr_t)region + sizeof(MemRegion), largeObjectAlignment);
1220*51c0b2f7Stbbdev         fBlockEnd = (uintptr_t)fBlock + exactBlockSize;
1221*51c0b2f7Stbbdev         MALLOC_ASSERT(fBlockEnd <= lastFreeBlock, ASSERT_TEXT);
1222*51c0b2f7Stbbdev     }
1223*51c0b2f7Stbbdev     if (fBlockEnd <= (uintptr_t)fBlock)
1224*51c0b2f7Stbbdev         return NULL; // allocSz is too small
1225*51c0b2f7Stbbdev     blockSz = fBlockEnd - (uintptr_t)fBlock;
1226*51c0b2f7Stbbdev     // TODO: extend getSlabBlock to support degradation, i.e. getting less blocks
1227*51c0b2f7Stbbdev     // then requested, and then relax this check
1228*51c0b2f7Stbbdev     // (now all or nothing is implemented, check according to this)
1229*51c0b2f7Stbbdev     if (blockSz < numOfSlabAllocOnMiss*slabSize)
1230*51c0b2f7Stbbdev         return NULL;
1231*51c0b2f7Stbbdev 
1232*51c0b2f7Stbbdev     region->blockSz = blockSz;
1233*51c0b2f7Stbbdev     return fBlock;
1234*51c0b2f7Stbbdev }
1235*51c0b2f7Stbbdev 
1236*51c0b2f7Stbbdev // startUseBlock may add the free block to a bin, the block can be used and
1237*51c0b2f7Stbbdev // even released after this, so the region must be added to regionList already
1238*51c0b2f7Stbbdev void Backend::startUseBlock(MemRegion *region, FreeBlock *fBlock, bool addToBin)
1239*51c0b2f7Stbbdev {
1240*51c0b2f7Stbbdev     size_t blockSz = region->blockSz;
1241*51c0b2f7Stbbdev     fBlock->initHeader();
1242*51c0b2f7Stbbdev     fBlock->setMeFree(blockSz);
1243*51c0b2f7Stbbdev 
1244*51c0b2f7Stbbdev     LastFreeBlock *lastBl = static_cast<LastFreeBlock*>(fBlock->rightNeig(blockSz));
1245*51c0b2f7Stbbdev     // to not get unaligned atomics during LastFreeBlock access
1246*51c0b2f7Stbbdev     MALLOC_ASSERT(isAligned(lastBl, sizeof(uintptr_t)), NULL);
1247*51c0b2f7Stbbdev     lastBl->initHeader();
1248*51c0b2f7Stbbdev     lastBl->setMeFree(GuardedSize::LAST_REGION_BLOCK);
1249*51c0b2f7Stbbdev     lastBl->setLeftFree(blockSz);
1250*51c0b2f7Stbbdev     lastBl->myBin = NO_BIN;
1251*51c0b2f7Stbbdev     lastBl->memRegion = region;
1252*51c0b2f7Stbbdev 
1253*51c0b2f7Stbbdev     if (addToBin) {
1254*51c0b2f7Stbbdev         unsigned targetBin = sizeToBin(blockSz);
1255*51c0b2f7Stbbdev         // during adding advance regions, register bin for a largest block in region
1256*51c0b2f7Stbbdev         advRegBins.registerBin(targetBin);
1257*51c0b2f7Stbbdev         if (region->type == MEMREG_SLAB_BLOCKS) {
1258*51c0b2f7Stbbdev             fBlock->slabAligned = true;
1259*51c0b2f7Stbbdev             freeSlabAlignedBins.addBlock(targetBin, fBlock, blockSz, /*addToTail=*/false);
1260*51c0b2f7Stbbdev         } else {
1261*51c0b2f7Stbbdev             fBlock->slabAligned = false;
1262*51c0b2f7Stbbdev             freeLargeBlockBins.addBlock(targetBin, fBlock, blockSz, /*addToTail=*/false);
1263*51c0b2f7Stbbdev         }
1264*51c0b2f7Stbbdev     } else {
1265*51c0b2f7Stbbdev         // to match with blockReleased() in genericGetBlock
1266*51c0b2f7Stbbdev         bkndSync.blockConsumed();
1267*51c0b2f7Stbbdev         // Understand our alignment for correct splitBlock operation
1268*51c0b2f7Stbbdev         fBlock->slabAligned = region->type == MEMREG_SLAB_BLOCKS ? true : false;
1269*51c0b2f7Stbbdev         fBlock->sizeTmp = fBlock->tryLockBlock();
1270*51c0b2f7Stbbdev         MALLOC_ASSERT(fBlock->sizeTmp >= FreeBlock::minBlockSize, "Locking must be successful");
1271*51c0b2f7Stbbdev     }
1272*51c0b2f7Stbbdev }
1273*51c0b2f7Stbbdev 
1274*51c0b2f7Stbbdev void MemRegionList::add(MemRegion *r)
1275*51c0b2f7Stbbdev {
1276*51c0b2f7Stbbdev     r->prev = NULL;
1277*51c0b2f7Stbbdev     MallocMutex::scoped_lock lock(regionListLock);
1278*51c0b2f7Stbbdev     r->next = head;
1279*51c0b2f7Stbbdev     head = r;
1280*51c0b2f7Stbbdev     if (head->next)
1281*51c0b2f7Stbbdev         head->next->prev = head;
1282*51c0b2f7Stbbdev }
1283*51c0b2f7Stbbdev 
1284*51c0b2f7Stbbdev void MemRegionList::remove(MemRegion *r)
1285*51c0b2f7Stbbdev {
1286*51c0b2f7Stbbdev     MallocMutex::scoped_lock lock(regionListLock);
1287*51c0b2f7Stbbdev     if (head == r)
1288*51c0b2f7Stbbdev         head = head->next;
1289*51c0b2f7Stbbdev     if (r->next)
1290*51c0b2f7Stbbdev         r->next->prev = r->prev;
1291*51c0b2f7Stbbdev     if (r->prev)
1292*51c0b2f7Stbbdev         r->prev->next = r->next;
1293*51c0b2f7Stbbdev }
1294*51c0b2f7Stbbdev 
1295*51c0b2f7Stbbdev #if __TBB_MALLOC_BACKEND_STAT
1296*51c0b2f7Stbbdev int MemRegionList::reportStat(FILE *f)
1297*51c0b2f7Stbbdev {
1298*51c0b2f7Stbbdev     int regNum = 0;
1299*51c0b2f7Stbbdev     MallocMutex::scoped_lock lock(regionListLock);
1300*51c0b2f7Stbbdev     for (MemRegion *curr = head; curr; curr = curr->next) {
1301*51c0b2f7Stbbdev         fprintf(f, "%p: max block %lu B, ", curr, curr->blockSz);
1302*51c0b2f7Stbbdev         regNum++;
1303*51c0b2f7Stbbdev     }
1304*51c0b2f7Stbbdev     return regNum;
1305*51c0b2f7Stbbdev }
1306*51c0b2f7Stbbdev #endif
1307*51c0b2f7Stbbdev 
1308*51c0b2f7Stbbdev FreeBlock *Backend::addNewRegion(size_t size, MemRegionType memRegType, bool addToBin)
1309*51c0b2f7Stbbdev {
1310*51c0b2f7Stbbdev     static_assert(sizeof(BlockMutexes) <= sizeof(BlockI), "Header must be not overwritten in used blocks");
1311*51c0b2f7Stbbdev     MALLOC_ASSERT(FreeBlock::minBlockSize > GuardedSize::MAX_SPEC_VAL,
1312*51c0b2f7Stbbdev           "Block length must not conflict with special values of GuardedSize");
1313*51c0b2f7Stbbdev     // If the region is not "for slabs" we should reserve some space for
1314*51c0b2f7Stbbdev     // a region header, the worst case alignment and the last block mark.
1315*51c0b2f7Stbbdev     const size_t requestSize = memRegType == MEMREG_SLAB_BLOCKS ? size :
1316*51c0b2f7Stbbdev         size + sizeof(MemRegion) + largeObjectAlignment
1317*51c0b2f7Stbbdev              +  FreeBlock::minBlockSize + sizeof(LastFreeBlock);
1318*51c0b2f7Stbbdev 
1319*51c0b2f7Stbbdev     size_t rawSize = requestSize;
1320*51c0b2f7Stbbdev     MemRegion *region = (MemRegion*)allocRawMem(rawSize);
1321*51c0b2f7Stbbdev     if (!region) {
1322*51c0b2f7Stbbdev         MALLOC_ASSERT(rawSize==requestSize, "getRawMem has not allocated memory but changed the allocated size.");
1323*51c0b2f7Stbbdev         return NULL;
1324*51c0b2f7Stbbdev     }
1325*51c0b2f7Stbbdev     if (rawSize < sizeof(MemRegion)) {
1326*51c0b2f7Stbbdev         if (!extMemPool->fixedPool)
1327*51c0b2f7Stbbdev             freeRawMem(region, rawSize);
1328*51c0b2f7Stbbdev         return NULL;
1329*51c0b2f7Stbbdev     }
1330*51c0b2f7Stbbdev 
1331*51c0b2f7Stbbdev     region->type = memRegType;
1332*51c0b2f7Stbbdev     region->allocSz = rawSize;
1333*51c0b2f7Stbbdev     FreeBlock *fBlock = findBlockInRegion(region, size);
1334*51c0b2f7Stbbdev     if (!fBlock) {
1335*51c0b2f7Stbbdev         if (!extMemPool->fixedPool)
1336*51c0b2f7Stbbdev             freeRawMem(region, rawSize);
1337*51c0b2f7Stbbdev         return NULL;
1338*51c0b2f7Stbbdev     }
1339*51c0b2f7Stbbdev     regionList.add(region);
1340*51c0b2f7Stbbdev     startUseBlock(region, fBlock, addToBin);
1341*51c0b2f7Stbbdev     bkndSync.binsModified();
1342*51c0b2f7Stbbdev     return addToBin? (FreeBlock*)VALID_BLOCK_IN_BIN : fBlock;
1343*51c0b2f7Stbbdev }
1344*51c0b2f7Stbbdev 
1345*51c0b2f7Stbbdev void Backend::init(ExtMemoryPool *extMemoryPool)
1346*51c0b2f7Stbbdev {
1347*51c0b2f7Stbbdev     extMemPool = extMemoryPool;
1348*51c0b2f7Stbbdev     usedAddrRange.init();
1349*51c0b2f7Stbbdev     coalescQ.init(&bkndSync);
1350*51c0b2f7Stbbdev     bkndSync.init(this);
1351*51c0b2f7Stbbdev }
1352*51c0b2f7Stbbdev 
1353*51c0b2f7Stbbdev void Backend::reset()
1354*51c0b2f7Stbbdev {
1355*51c0b2f7Stbbdev     MALLOC_ASSERT(extMemPool->userPool(), "Only user pool can be reset.");
1356*51c0b2f7Stbbdev     // no active threads are allowed in backend while reset() called
1357*51c0b2f7Stbbdev     verify();
1358*51c0b2f7Stbbdev 
1359*51c0b2f7Stbbdev     freeLargeBlockBins.reset();
1360*51c0b2f7Stbbdev     freeSlabAlignedBins.reset();
1361*51c0b2f7Stbbdev     advRegBins.reset();
1362*51c0b2f7Stbbdev 
1363*51c0b2f7Stbbdev     for (MemRegion *curr = regionList.head; curr; curr = curr->next) {
1364*51c0b2f7Stbbdev         FreeBlock *fBlock = findBlockInRegion(curr, curr->blockSz);
1365*51c0b2f7Stbbdev         MALLOC_ASSERT(fBlock, "A memory region unexpectedly got smaller");
1366*51c0b2f7Stbbdev         startUseBlock(curr, fBlock, /*addToBin=*/true);
1367*51c0b2f7Stbbdev     }
1368*51c0b2f7Stbbdev }
1369*51c0b2f7Stbbdev 
1370*51c0b2f7Stbbdev bool Backend::destroy()
1371*51c0b2f7Stbbdev {
1372*51c0b2f7Stbbdev     bool noError = true;
1373*51c0b2f7Stbbdev     // no active threads are allowed in backend while destroy() called
1374*51c0b2f7Stbbdev     verify();
1375*51c0b2f7Stbbdev     if (!inUserPool()) {
1376*51c0b2f7Stbbdev         freeLargeBlockBins.reset();
1377*51c0b2f7Stbbdev         freeSlabAlignedBins.reset();
1378*51c0b2f7Stbbdev     }
1379*51c0b2f7Stbbdev     while (regionList.head) {
1380*51c0b2f7Stbbdev         MemRegion *helper = regionList.head->next;
1381*51c0b2f7Stbbdev         noError &= freeRawMem(regionList.head, regionList.head->allocSz);
1382*51c0b2f7Stbbdev         regionList.head = helper;
1383*51c0b2f7Stbbdev     }
1384*51c0b2f7Stbbdev     return noError;
1385*51c0b2f7Stbbdev }
1386*51c0b2f7Stbbdev 
1387*51c0b2f7Stbbdev bool Backend::clean()
1388*51c0b2f7Stbbdev {
1389*51c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
1390*51c0b2f7Stbbdev 
1391*51c0b2f7Stbbdev     bool res = false;
1392*51c0b2f7Stbbdev     // We can have several blocks occupying a whole region,
1393*51c0b2f7Stbbdev     // because such regions are added in advance (see askMemFromOS() and reset()),
1394*51c0b2f7Stbbdev     // and never used. Release them all.
1395*51c0b2f7Stbbdev     for (int i = advRegBins.getMinUsedBin(0); i != -1; i = advRegBins.getMinUsedBin(i+1)) {
1396*51c0b2f7Stbbdev         if (i == freeSlabAlignedBins.getMinNonemptyBin(i))
1397*51c0b2f7Stbbdev             res |= freeSlabAlignedBins.tryReleaseRegions(i, this);
1398*51c0b2f7Stbbdev         if (i == freeLargeBlockBins.getMinNonemptyBin(i))
1399*51c0b2f7Stbbdev             res |= freeLargeBlockBins.tryReleaseRegions(i, this);
1400*51c0b2f7Stbbdev     }
1401*51c0b2f7Stbbdev 
1402*51c0b2f7Stbbdev     return res;
1403*51c0b2f7Stbbdev }
1404*51c0b2f7Stbbdev 
1405*51c0b2f7Stbbdev void Backend::IndexedBins::verify()
1406*51c0b2f7Stbbdev {
1407*51c0b2f7Stbbdev     for (int i=0; i<freeBinsNum; i++) {
1408*51c0b2f7Stbbdev         for (FreeBlock *fb = freeBins[i].head; fb; fb=fb->next) {
1409*51c0b2f7Stbbdev             uintptr_t mySz = fb->myL.value;
1410*51c0b2f7Stbbdev             MALLOC_ASSERT(mySz>GuardedSize::MAX_SPEC_VAL, ASSERT_TEXT);
1411*51c0b2f7Stbbdev             FreeBlock *right = (FreeBlock*)((uintptr_t)fb + mySz);
1412*51c0b2f7Stbbdev             suppress_unused_warning(right);
1413*51c0b2f7Stbbdev             MALLOC_ASSERT(right->myL.value<=GuardedSize::MAX_SPEC_VAL, ASSERT_TEXT);
1414*51c0b2f7Stbbdev             MALLOC_ASSERT(right->leftL.value==mySz, ASSERT_TEXT);
1415*51c0b2f7Stbbdev             MALLOC_ASSERT(fb->leftL.value<=GuardedSize::MAX_SPEC_VAL, ASSERT_TEXT);
1416*51c0b2f7Stbbdev         }
1417*51c0b2f7Stbbdev     }
1418*51c0b2f7Stbbdev }
1419*51c0b2f7Stbbdev 
1420*51c0b2f7Stbbdev // For correct operation, it must be called when no other threads
1421*51c0b2f7Stbbdev // is changing backend.
1422*51c0b2f7Stbbdev void Backend::verify()
1423*51c0b2f7Stbbdev {
1424*51c0b2f7Stbbdev #if MALLOC_DEBUG
1425*51c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
1426*51c0b2f7Stbbdev 
1427*51c0b2f7Stbbdev     freeLargeBlockBins.verify();
1428*51c0b2f7Stbbdev     freeSlabAlignedBins.verify();
1429*51c0b2f7Stbbdev #endif // MALLOC_DEBUG
1430*51c0b2f7Stbbdev }
1431*51c0b2f7Stbbdev 
1432*51c0b2f7Stbbdev #if __TBB_MALLOC_BACKEND_STAT
1433*51c0b2f7Stbbdev size_t Backend::Bin::countFreeBlocks()
1434*51c0b2f7Stbbdev {
1435*51c0b2f7Stbbdev     size_t cnt = 0;
1436*51c0b2f7Stbbdev     {
1437*51c0b2f7Stbbdev         MallocMutex::scoped_lock lock(tLock);
1438*51c0b2f7Stbbdev         for (FreeBlock *fb = head; fb; fb = fb->next)
1439*51c0b2f7Stbbdev             cnt++;
1440*51c0b2f7Stbbdev     }
1441*51c0b2f7Stbbdev     return cnt;
1442*51c0b2f7Stbbdev }
1443*51c0b2f7Stbbdev 
1444*51c0b2f7Stbbdev size_t Backend::Bin::reportFreeBlocks(FILE *f)
1445*51c0b2f7Stbbdev {
1446*51c0b2f7Stbbdev     size_t totalSz = 0;
1447*51c0b2f7Stbbdev     MallocMutex::scoped_lock lock(tLock);
1448*51c0b2f7Stbbdev     for (FreeBlock *fb = head; fb; fb = fb->next) {
1449*51c0b2f7Stbbdev         size_t sz = fb->tryLockBlock();
1450*51c0b2f7Stbbdev         fb->setMeFree(sz);
1451*51c0b2f7Stbbdev         fprintf(f, " [%p;%p]", fb, (void*)((uintptr_t)fb+sz));
1452*51c0b2f7Stbbdev         totalSz += sz;
1453*51c0b2f7Stbbdev     }
1454*51c0b2f7Stbbdev     return totalSz;
1455*51c0b2f7Stbbdev }
1456*51c0b2f7Stbbdev 
1457*51c0b2f7Stbbdev void Backend::IndexedBins::reportStat(FILE *f)
1458*51c0b2f7Stbbdev {
1459*51c0b2f7Stbbdev     size_t totalSize = 0;
1460*51c0b2f7Stbbdev 
1461*51c0b2f7Stbbdev     for (int i=0; i<Backend::freeBinsNum; i++)
1462*51c0b2f7Stbbdev         if (size_t cnt = freeBins[i].countFreeBlocks()) {
1463*51c0b2f7Stbbdev             totalSize += freeBins[i].reportFreeBlocks(f);
1464*51c0b2f7Stbbdev             fprintf(f, " %d:%lu, ", i, cnt);
1465*51c0b2f7Stbbdev         }
1466*51c0b2f7Stbbdev     fprintf(f, "\ttotal size %lu KB", totalSize/1024);
1467*51c0b2f7Stbbdev }
1468*51c0b2f7Stbbdev 
1469*51c0b2f7Stbbdev void Backend::reportStat(FILE *f)
1470*51c0b2f7Stbbdev {
1471*51c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
1472*51c0b2f7Stbbdev 
1473*51c0b2f7Stbbdev     fprintf(f, "\n  regions:\n");
1474*51c0b2f7Stbbdev     int regNum = regionList.reportStat(f);
1475*51c0b2f7Stbbdev     fprintf(f, "\n%d regions, %lu KB in all regions\n  free bins:\nlarge bins: ",
1476*51c0b2f7Stbbdev             regNum, totalMemSize/1024);
1477*51c0b2f7Stbbdev     freeLargeBlockBins.reportStat(f);
1478*51c0b2f7Stbbdev     fprintf(f, "\naligned bins: ");
1479*51c0b2f7Stbbdev     freeSlabAlignedBins.reportStat(f);
1480*51c0b2f7Stbbdev     fprintf(f, "\n");
1481*51c0b2f7Stbbdev }
1482*51c0b2f7Stbbdev #endif // __TBB_MALLOC_BACKEND_STAT
1483*51c0b2f7Stbbdev 
1484*51c0b2f7Stbbdev } } // namespaces
1485