xref: /oneTBB/src/tbbmalloc/backend.cpp (revision b15aabb3)
151c0b2f7Stbbdev /*
2*b15aabb3Stbbdev     Copyright (c) 2005-2021 Intel Corporation
351c0b2f7Stbbdev 
451c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
551c0b2f7Stbbdev     you may not use this file except in compliance with the License.
651c0b2f7Stbbdev     You may obtain a copy of the License at
751c0b2f7Stbbdev 
851c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
951c0b2f7Stbbdev 
1051c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
1151c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
1251c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1351c0b2f7Stbbdev     See the License for the specific language governing permissions and
1451c0b2f7Stbbdev     limitations under the License.
1551c0b2f7Stbbdev */
1651c0b2f7Stbbdev 
1751c0b2f7Stbbdev #include <string.h>   /* for memset */
1851c0b2f7Stbbdev #include <errno.h>
1951c0b2f7Stbbdev #include "tbbmalloc_internal.h"
2051c0b2f7Stbbdev 
2151c0b2f7Stbbdev namespace rml {
2251c0b2f7Stbbdev namespace internal {
2351c0b2f7Stbbdev 
2451c0b2f7Stbbdev /*********** Code to acquire memory from the OS or other executive ****************/
2551c0b2f7Stbbdev 
2651c0b2f7Stbbdev /*
2751c0b2f7Stbbdev   syscall/malloc can set non-zero errno in case of failure,
2851c0b2f7Stbbdev   but later allocator might be able to find memory to fulfill the request.
2951c0b2f7Stbbdev   And we do not want changing of errno by successful scalable_malloc call.
3051c0b2f7Stbbdev   To support this, restore old errno in (get|free)RawMemory, and set errno
3151c0b2f7Stbbdev   in frontend just before returning to user code.
3251c0b2f7Stbbdev   Please note: every syscall/libc call used inside scalable_malloc that
3351c0b2f7Stbbdev   sets errno must be protected this way, not just memory allocation per se.
3451c0b2f7Stbbdev */
3551c0b2f7Stbbdev 
3651c0b2f7Stbbdev #if USE_DEFAULT_MEMORY_MAPPING
3751c0b2f7Stbbdev #include "MapMemory.h"
3851c0b2f7Stbbdev #else
3951c0b2f7Stbbdev /* assume MapMemory and UnmapMemory are customized */
4051c0b2f7Stbbdev #endif
4151c0b2f7Stbbdev 
4251c0b2f7Stbbdev void* getRawMemory (size_t size, PageType pageType) {
4351c0b2f7Stbbdev     return MapMemory(size, pageType);
4451c0b2f7Stbbdev }
4551c0b2f7Stbbdev 
4651c0b2f7Stbbdev int freeRawMemory (void *object, size_t size) {
4751c0b2f7Stbbdev     return UnmapMemory(object, size);
4851c0b2f7Stbbdev }
4951c0b2f7Stbbdev 
5051c0b2f7Stbbdev #if CHECK_ALLOCATION_RANGE
5151c0b2f7Stbbdev 
5251c0b2f7Stbbdev void Backend::UsedAddressRange::registerAlloc(uintptr_t left, uintptr_t right)
5351c0b2f7Stbbdev {
5451c0b2f7Stbbdev     MallocMutex::scoped_lock lock(mutex);
5551c0b2f7Stbbdev     if (left < leftBound)
5651c0b2f7Stbbdev         leftBound = left;
5751c0b2f7Stbbdev     if (right > rightBound)
5851c0b2f7Stbbdev         rightBound = right;
5951c0b2f7Stbbdev     MALLOC_ASSERT(leftBound, ASSERT_TEXT);
6051c0b2f7Stbbdev     MALLOC_ASSERT(leftBound < rightBound, ASSERT_TEXT);
6151c0b2f7Stbbdev     MALLOC_ASSERT(leftBound <= left && right <= rightBound, ASSERT_TEXT);
6251c0b2f7Stbbdev }
6351c0b2f7Stbbdev 
6451c0b2f7Stbbdev void Backend::UsedAddressRange::registerFree(uintptr_t left, uintptr_t right)
6551c0b2f7Stbbdev {
6651c0b2f7Stbbdev     MallocMutex::scoped_lock lock(mutex);
6751c0b2f7Stbbdev     if (leftBound == left) {
6851c0b2f7Stbbdev         if (rightBound == right) {
6951c0b2f7Stbbdev             leftBound = ADDRESS_UPPER_BOUND;
7051c0b2f7Stbbdev             rightBound = 0;
7151c0b2f7Stbbdev         } else
7251c0b2f7Stbbdev             leftBound = right;
7351c0b2f7Stbbdev     } else if (rightBound == right)
7451c0b2f7Stbbdev         rightBound = left;
7551c0b2f7Stbbdev     MALLOC_ASSERT((!rightBound && leftBound == ADDRESS_UPPER_BOUND)
7651c0b2f7Stbbdev                   || leftBound < rightBound, ASSERT_TEXT);
7751c0b2f7Stbbdev }
7851c0b2f7Stbbdev #endif // CHECK_ALLOCATION_RANGE
7951c0b2f7Stbbdev 
8051c0b2f7Stbbdev // Initialized in frontend inside defaultMemPool
8151c0b2f7Stbbdev extern HugePagesStatus hugePages;
8251c0b2f7Stbbdev 
8351c0b2f7Stbbdev void *Backend::allocRawMem(size_t &size)
8451c0b2f7Stbbdev {
8551c0b2f7Stbbdev     void *res = NULL;
8651c0b2f7Stbbdev     size_t allocSize = 0;
8751c0b2f7Stbbdev 
8851c0b2f7Stbbdev     if (extMemPool->userPool()) {
8951c0b2f7Stbbdev         if (extMemPool->fixedPool && bootsrapMemDone == bootsrapMemStatus.load(std::memory_order_acquire))
9051c0b2f7Stbbdev             return NULL;
9151c0b2f7Stbbdev         MALLOC_ASSERT(bootsrapMemStatus != bootsrapMemNotDone,
9251c0b2f7Stbbdev                       "Backend::allocRawMem() called prematurely?");
9351c0b2f7Stbbdev         // TODO: support for raw mem not aligned at sizeof(uintptr_t)
9451c0b2f7Stbbdev         // memory from fixed pool is asked once and only once
9551c0b2f7Stbbdev         allocSize = alignUpGeneric(size, extMemPool->granularity);
9651c0b2f7Stbbdev         res = (*extMemPool->rawAlloc)(extMemPool->poolId, allocSize);
9751c0b2f7Stbbdev     } else {
9851c0b2f7Stbbdev         // Align allocation on page size
9951c0b2f7Stbbdev         size_t pageSize = hugePages.isEnabled ? hugePages.getGranularity() : extMemPool->granularity;
10051c0b2f7Stbbdev         MALLOC_ASSERT(pageSize, "Page size cannot be zero.");
10151c0b2f7Stbbdev         allocSize = alignUpGeneric(size, pageSize);
10251c0b2f7Stbbdev 
10351c0b2f7Stbbdev         // If user requested huge pages and they are available, try to use preallocated ones firstly.
10451c0b2f7Stbbdev         // If there are none, lets check transparent huge pages support and use them instead.
10551c0b2f7Stbbdev         if (hugePages.isEnabled) {
10651c0b2f7Stbbdev             if (hugePages.isHPAvailable) {
10751c0b2f7Stbbdev                 res = getRawMemory(allocSize, PREALLOCATED_HUGE_PAGE);
10851c0b2f7Stbbdev             }
10951c0b2f7Stbbdev             if (!res && hugePages.isTHPAvailable) {
11051c0b2f7Stbbdev                 res = getRawMemory(allocSize, TRANSPARENT_HUGE_PAGE);
11151c0b2f7Stbbdev             }
11251c0b2f7Stbbdev         }
11351c0b2f7Stbbdev 
11451c0b2f7Stbbdev         if (!res) {
11551c0b2f7Stbbdev             res = getRawMemory(allocSize, REGULAR);
11651c0b2f7Stbbdev         }
11751c0b2f7Stbbdev     }
11851c0b2f7Stbbdev 
11951c0b2f7Stbbdev     if (res) {
12051c0b2f7Stbbdev         MALLOC_ASSERT(allocSize > 0, "Invalid size of an allocated region.");
12151c0b2f7Stbbdev         size = allocSize;
12251c0b2f7Stbbdev         if (!extMemPool->userPool())
12351c0b2f7Stbbdev             usedAddrRange.registerAlloc((uintptr_t)res, (uintptr_t)res+size);
12451c0b2f7Stbbdev #if MALLOC_DEBUG
12551c0b2f7Stbbdev         volatile size_t curTotalSize = totalMemSize; // to read global value once
12651c0b2f7Stbbdev         MALLOC_ASSERT(curTotalSize+size > curTotalSize, "Overflow allocation size.");
12751c0b2f7Stbbdev #endif
12851c0b2f7Stbbdev         totalMemSize.fetch_add(size);
12951c0b2f7Stbbdev     }
13051c0b2f7Stbbdev 
13151c0b2f7Stbbdev     return res;
13251c0b2f7Stbbdev }
13351c0b2f7Stbbdev 
13451c0b2f7Stbbdev bool Backend::freeRawMem(void *object, size_t size)
13551c0b2f7Stbbdev {
13651c0b2f7Stbbdev     bool fail;
13751c0b2f7Stbbdev #if MALLOC_DEBUG
13851c0b2f7Stbbdev     volatile size_t curTotalSize = totalMemSize; // to read global value once
13951c0b2f7Stbbdev     MALLOC_ASSERT(curTotalSize-size < curTotalSize, "Negative allocation size.");
14051c0b2f7Stbbdev #endif
14151c0b2f7Stbbdev     totalMemSize.fetch_sub(size);
14251c0b2f7Stbbdev     if (extMemPool->userPool()) {
14351c0b2f7Stbbdev         MALLOC_ASSERT(!extMemPool->fixedPool, "No free for fixed-size pools.");
14451c0b2f7Stbbdev         fail = (*extMemPool->rawFree)(extMemPool->poolId, object, size);
14551c0b2f7Stbbdev     } else {
14651c0b2f7Stbbdev         usedAddrRange.registerFree((uintptr_t)object, (uintptr_t)object + size);
14751c0b2f7Stbbdev         fail = freeRawMemory(object, size);
14851c0b2f7Stbbdev     }
14951c0b2f7Stbbdev     // TODO: use result in all freeRawMem() callers
15051c0b2f7Stbbdev     return !fail;
15151c0b2f7Stbbdev }
15251c0b2f7Stbbdev 
15351c0b2f7Stbbdev /********* End memory acquisition code ********************************/
15451c0b2f7Stbbdev 
15551c0b2f7Stbbdev // Protected object size. After successful locking returns size of locked block,
15651c0b2f7Stbbdev // and releasing requires setting block size.
15751c0b2f7Stbbdev class GuardedSize : tbb::detail::no_copy {
15851c0b2f7Stbbdev     std::atomic<uintptr_t> value;
15951c0b2f7Stbbdev public:
16051c0b2f7Stbbdev     enum State {
16151c0b2f7Stbbdev         LOCKED,
16251c0b2f7Stbbdev         COAL_BLOCK,        // block is coalescing now
16351c0b2f7Stbbdev         MAX_LOCKED_VAL = COAL_BLOCK,
16451c0b2f7Stbbdev         LAST_REGION_BLOCK, // used to mark last block in region
16551c0b2f7Stbbdev         // values after this are "normal" block sizes
16651c0b2f7Stbbdev         MAX_SPEC_VAL = LAST_REGION_BLOCK
16751c0b2f7Stbbdev     };
16851c0b2f7Stbbdev 
16951c0b2f7Stbbdev     void initLocked() { value.store(LOCKED, std::memory_order_release); } // TBB_REVAMP_TODO: was relaxed
17051c0b2f7Stbbdev     void makeCoalscing() {
17151c0b2f7Stbbdev         MALLOC_ASSERT(value.load(std::memory_order_relaxed) == LOCKED, ASSERT_TEXT);
17251c0b2f7Stbbdev         value.store(COAL_BLOCK, std::memory_order_release); // TBB_REVAMP_TODO: was relaxed
17351c0b2f7Stbbdev     }
17451c0b2f7Stbbdev     size_t tryLock(State state) {
17551c0b2f7Stbbdev         MALLOC_ASSERT(state <= MAX_LOCKED_VAL, ASSERT_TEXT);
17651c0b2f7Stbbdev         size_t sz = value.load(std::memory_order_acquire);
17751c0b2f7Stbbdev         for (;;) {
17851c0b2f7Stbbdev             if (sz <= MAX_LOCKED_VAL) {
17951c0b2f7Stbbdev                 break;
18051c0b2f7Stbbdev             }
18151c0b2f7Stbbdev             if (value.compare_exchange_strong(sz, state)) {
18251c0b2f7Stbbdev                 break;
18351c0b2f7Stbbdev             }
18451c0b2f7Stbbdev         }
18551c0b2f7Stbbdev         return sz;
18651c0b2f7Stbbdev     }
18751c0b2f7Stbbdev     void unlock(size_t size) {
18851c0b2f7Stbbdev         MALLOC_ASSERT(value.load(std::memory_order_relaxed) <= MAX_LOCKED_VAL, "The lock is not locked");
18951c0b2f7Stbbdev         MALLOC_ASSERT(size > MAX_LOCKED_VAL, ASSERT_TEXT);
19051c0b2f7Stbbdev         value.store(size, std::memory_order_release);
19151c0b2f7Stbbdev     }
19251c0b2f7Stbbdev     bool isLastRegionBlock() const { return value.load(std::memory_order_relaxed) == LAST_REGION_BLOCK; }
19351c0b2f7Stbbdev     friend void Backend::IndexedBins::verify();
19451c0b2f7Stbbdev };
19551c0b2f7Stbbdev 
19651c0b2f7Stbbdev struct MemRegion {
19751c0b2f7Stbbdev     MemRegion *next,      // keep all regions in any pool to release all them on
19851c0b2f7Stbbdev               *prev;      // pool destroying, 2-linked list to release individual
19951c0b2f7Stbbdev                           // regions.
20051c0b2f7Stbbdev     size_t     allocSz,   // got from pool callback
20151c0b2f7Stbbdev                blockSz;   // initial and maximal inner block size
20251c0b2f7Stbbdev     MemRegionType type;
20351c0b2f7Stbbdev };
20451c0b2f7Stbbdev 
20551c0b2f7Stbbdev // this data must be unmodified while block is in use, so separate it
20651c0b2f7Stbbdev class BlockMutexes {
20751c0b2f7Stbbdev protected:
20851c0b2f7Stbbdev     GuardedSize myL,   // lock for me
20951c0b2f7Stbbdev                 leftL; // lock for left neighbor
21051c0b2f7Stbbdev };
21151c0b2f7Stbbdev 
21251c0b2f7Stbbdev class FreeBlock : BlockMutexes {
21351c0b2f7Stbbdev public:
21451c0b2f7Stbbdev     static const size_t minBlockSize;
21551c0b2f7Stbbdev     friend void Backend::IndexedBins::verify();
21651c0b2f7Stbbdev 
21751c0b2f7Stbbdev     FreeBlock    *prev,       // in 2-linked list related to bin
21851c0b2f7Stbbdev                  *next,
21951c0b2f7Stbbdev                  *nextToFree; // used to form a queue during coalescing
22051c0b2f7Stbbdev     // valid only when block is in processing, i.e. one is not free and not
22151c0b2f7Stbbdev     size_t        sizeTmp;    // used outside of backend
22251c0b2f7Stbbdev     int           myBin;      // bin that is owner of the block
22351c0b2f7Stbbdev     bool          slabAligned;
22451c0b2f7Stbbdev     bool          blockInBin; // this block in myBin already
22551c0b2f7Stbbdev 
22651c0b2f7Stbbdev     FreeBlock *rightNeig(size_t sz) const {
22751c0b2f7Stbbdev         MALLOC_ASSERT(sz, ASSERT_TEXT);
22851c0b2f7Stbbdev         return (FreeBlock*)((uintptr_t)this+sz);
22951c0b2f7Stbbdev     }
23051c0b2f7Stbbdev     FreeBlock *leftNeig(size_t sz) const {
23151c0b2f7Stbbdev         MALLOC_ASSERT(sz, ASSERT_TEXT);
23251c0b2f7Stbbdev         return (FreeBlock*)((uintptr_t)this - sz);
23351c0b2f7Stbbdev     }
23451c0b2f7Stbbdev 
23551c0b2f7Stbbdev     void initHeader() { myL.initLocked(); leftL.initLocked(); }
23651c0b2f7Stbbdev     void setMeFree(size_t size) { myL.unlock(size); }
23751c0b2f7Stbbdev     size_t trySetMeUsed(GuardedSize::State s) { return myL.tryLock(s); }
23851c0b2f7Stbbdev     bool isLastRegionBlock() const { return myL.isLastRegionBlock(); }
23951c0b2f7Stbbdev 
24051c0b2f7Stbbdev     void setLeftFree(size_t sz) { leftL.unlock(sz); }
24151c0b2f7Stbbdev     size_t trySetLeftUsed(GuardedSize::State s) { return leftL.tryLock(s); }
24251c0b2f7Stbbdev 
24351c0b2f7Stbbdev     size_t tryLockBlock() {
24451c0b2f7Stbbdev         size_t rSz, sz = trySetMeUsed(GuardedSize::LOCKED);
24551c0b2f7Stbbdev 
24651c0b2f7Stbbdev         if (sz <= GuardedSize::MAX_LOCKED_VAL)
24751c0b2f7Stbbdev             return false;
24851c0b2f7Stbbdev         rSz = rightNeig(sz)->trySetLeftUsed(GuardedSize::LOCKED);
24951c0b2f7Stbbdev         if (rSz <= GuardedSize::MAX_LOCKED_VAL) {
25051c0b2f7Stbbdev             setMeFree(sz);
25151c0b2f7Stbbdev             return false;
25251c0b2f7Stbbdev         }
25351c0b2f7Stbbdev         MALLOC_ASSERT(rSz == sz, ASSERT_TEXT);
25451c0b2f7Stbbdev         return sz;
25551c0b2f7Stbbdev     }
25651c0b2f7Stbbdev     void markCoalescing(size_t blockSz) {
25751c0b2f7Stbbdev         myL.makeCoalscing();
25851c0b2f7Stbbdev         rightNeig(blockSz)->leftL.makeCoalscing();
25951c0b2f7Stbbdev         sizeTmp = blockSz;
26051c0b2f7Stbbdev         nextToFree = NULL;
26151c0b2f7Stbbdev     }
26251c0b2f7Stbbdev     void markUsed() {
26351c0b2f7Stbbdev         myL.initLocked();
26451c0b2f7Stbbdev         rightNeig(sizeTmp)->leftL.initLocked();
26551c0b2f7Stbbdev         nextToFree = NULL;
26651c0b2f7Stbbdev     }
26751c0b2f7Stbbdev     static void markBlocks(FreeBlock *fBlock, int num, size_t size) {
26851c0b2f7Stbbdev         for (int i=1; i<num; i++) {
26951c0b2f7Stbbdev             fBlock = (FreeBlock*)((uintptr_t)fBlock + size);
27051c0b2f7Stbbdev             fBlock->initHeader();
27151c0b2f7Stbbdev         }
27251c0b2f7Stbbdev     }
27351c0b2f7Stbbdev };
27451c0b2f7Stbbdev 
27551c0b2f7Stbbdev // Last block in any region. Its "size" field is GuardedSize::LAST_REGION_BLOCK,
27651c0b2f7Stbbdev // This kind of blocks used to find region header
27751c0b2f7Stbbdev // and have a possibility to return region back to OS
27851c0b2f7Stbbdev struct LastFreeBlock : public FreeBlock {
27951c0b2f7Stbbdev     MemRegion *memRegion;
28051c0b2f7Stbbdev };
28151c0b2f7Stbbdev 
28251c0b2f7Stbbdev const size_t FreeBlock::minBlockSize = sizeof(FreeBlock);
28351c0b2f7Stbbdev 
28451c0b2f7Stbbdev inline bool BackendSync::waitTillBlockReleased(intptr_t startModifiedCnt)
28551c0b2f7Stbbdev {
28651c0b2f7Stbbdev     AtomicBackoff backoff;
28751c0b2f7Stbbdev #if __TBB_MALLOC_BACKEND_STAT
28851c0b2f7Stbbdev     class ITT_Guard {
28951c0b2f7Stbbdev         void *ptr;
29051c0b2f7Stbbdev     public:
29151c0b2f7Stbbdev         ITT_Guard(void *p) : ptr(p) {
29251c0b2f7Stbbdev             MALLOC_ITT_SYNC_PREPARE(ptr);
29351c0b2f7Stbbdev         }
29451c0b2f7Stbbdev         ~ITT_Guard() {
29551c0b2f7Stbbdev             MALLOC_ITT_SYNC_ACQUIRED(ptr);
29651c0b2f7Stbbdev         }
29751c0b2f7Stbbdev     };
29851c0b2f7Stbbdev     ITT_Guard ittGuard(&inFlyBlocks);
29951c0b2f7Stbbdev #endif
30051c0b2f7Stbbdev     for (intptr_t myBinsInFlyBlocks = inFlyBlocks.load(std::memory_order_acquire),
30151c0b2f7Stbbdev              myCoalescQInFlyBlocks = backend->blocksInCoalescing(); ; backoff.pause()) {
30251c0b2f7Stbbdev         MALLOC_ASSERT(myBinsInFlyBlocks>=0 && myCoalescQInFlyBlocks>=0, NULL);
30351c0b2f7Stbbdev         intptr_t currBinsInFlyBlocks = inFlyBlocks.load(std::memory_order_acquire),
30451c0b2f7Stbbdev             currCoalescQInFlyBlocks = backend->blocksInCoalescing();
30551c0b2f7Stbbdev         WhiteboxTestingYield();
30651c0b2f7Stbbdev         // Stop waiting iff:
30751c0b2f7Stbbdev 
30851c0b2f7Stbbdev         // 1) blocks were removed from processing, not added
30951c0b2f7Stbbdev         if (myBinsInFlyBlocks > currBinsInFlyBlocks
31051c0b2f7Stbbdev         // 2) released during delayed coalescing queue
31151c0b2f7Stbbdev             || myCoalescQInFlyBlocks > currCoalescQInFlyBlocks)
31251c0b2f7Stbbdev             break;
31351c0b2f7Stbbdev         // 3) if there are blocks in coalescing, and no progress in its processing,
31451c0b2f7Stbbdev         // try to scan coalescing queue and stop waiting, if changes were made
31551c0b2f7Stbbdev         // (if there are no changes and in-fly blocks exist, we continue
31651c0b2f7Stbbdev         //  waiting to not increase load on coalescQ)
31751c0b2f7Stbbdev         if (currCoalescQInFlyBlocks > 0 && backend->scanCoalescQ(/*forceCoalescQDrop=*/false))
31851c0b2f7Stbbdev             break;
31951c0b2f7Stbbdev         // 4) when there are no blocks
32051c0b2f7Stbbdev         if (!currBinsInFlyBlocks && !currCoalescQInFlyBlocks)
32151c0b2f7Stbbdev             // re-scan make sense only if bins were modified since scanned
32251c0b2f7Stbbdev             return startModifiedCnt != getNumOfMods();
32351c0b2f7Stbbdev         myBinsInFlyBlocks = currBinsInFlyBlocks;
32451c0b2f7Stbbdev         myCoalescQInFlyBlocks = currCoalescQInFlyBlocks;
32551c0b2f7Stbbdev     }
32651c0b2f7Stbbdev     return true;
32751c0b2f7Stbbdev }
32851c0b2f7Stbbdev 
32951c0b2f7Stbbdev void CoalRequestQ::putBlock(FreeBlock *fBlock)
33051c0b2f7Stbbdev {
33151c0b2f7Stbbdev     MALLOC_ASSERT(fBlock->sizeTmp >= FreeBlock::minBlockSize, ASSERT_TEXT);
33251c0b2f7Stbbdev     fBlock->markUsed();
33351c0b2f7Stbbdev     // the block is in the queue, do not forget that it's here
33451c0b2f7Stbbdev     inFlyBlocks++;
33551c0b2f7Stbbdev 
33651c0b2f7Stbbdev     FreeBlock *myBlToFree = blocksToFree.load(std::memory_order_acquire);
33751c0b2f7Stbbdev     for (;;) {
33851c0b2f7Stbbdev         fBlock->nextToFree = myBlToFree;
33951c0b2f7Stbbdev         if (blocksToFree.compare_exchange_strong(myBlToFree, fBlock)) {
34051c0b2f7Stbbdev             return;
34151c0b2f7Stbbdev         }
34251c0b2f7Stbbdev     }
34351c0b2f7Stbbdev }
34451c0b2f7Stbbdev 
34551c0b2f7Stbbdev FreeBlock *CoalRequestQ::getAll()
34651c0b2f7Stbbdev {
34751c0b2f7Stbbdev     for (;;) {
34851c0b2f7Stbbdev         FreeBlock *myBlToFree = blocksToFree.load(std::memory_order_acquire);
34951c0b2f7Stbbdev 
35051c0b2f7Stbbdev         if (!myBlToFree) {
35151c0b2f7Stbbdev             return NULL;
35251c0b2f7Stbbdev         } else {
35351c0b2f7Stbbdev             if (blocksToFree.compare_exchange_strong(myBlToFree, 0)) {
35451c0b2f7Stbbdev                 return myBlToFree;
35551c0b2f7Stbbdev             } else {
35651c0b2f7Stbbdev                 continue;
35751c0b2f7Stbbdev             }
35851c0b2f7Stbbdev         }
35951c0b2f7Stbbdev     }
36051c0b2f7Stbbdev }
36151c0b2f7Stbbdev 
36251c0b2f7Stbbdev inline void CoalRequestQ::blockWasProcessed()
36351c0b2f7Stbbdev {
36451c0b2f7Stbbdev     bkndSync->binsModified();
36551c0b2f7Stbbdev     int prev = inFlyBlocks.fetch_sub(1);
36651c0b2f7Stbbdev     MALLOC_ASSERT(prev > 0, ASSERT_TEXT);
36751c0b2f7Stbbdev }
36851c0b2f7Stbbdev 
36951c0b2f7Stbbdev // Try to get a block from a bin.
37051c0b2f7Stbbdev // If the remaining free space would stay in the same bin,
37151c0b2f7Stbbdev //     split the block without removing it.
37251c0b2f7Stbbdev // If the free space should go to other bin(s), remove the block.
37351c0b2f7Stbbdev // alignedBin is true, if all blocks in the bin have slab-aligned right side.
37451c0b2f7Stbbdev FreeBlock *Backend::IndexedBins::getFromBin(int binIdx, BackendSync *sync, size_t size,
37551c0b2f7Stbbdev         bool needAlignedRes, bool alignedBin,  bool wait, int *binLocked)
37651c0b2f7Stbbdev {
37751c0b2f7Stbbdev     Bin *b = &freeBins[binIdx];
37851c0b2f7Stbbdev try_next:
37951c0b2f7Stbbdev     FreeBlock *fBlock = NULL;
38051c0b2f7Stbbdev     if (b->head) {
38151c0b2f7Stbbdev         bool locked;
38251c0b2f7Stbbdev         MallocMutex::scoped_lock scopedLock(b->tLock, wait, &locked);
38351c0b2f7Stbbdev 
38451c0b2f7Stbbdev         if (!locked) {
38551c0b2f7Stbbdev             if (binLocked) (*binLocked)++;
38651c0b2f7Stbbdev             return NULL;
38751c0b2f7Stbbdev         }
38851c0b2f7Stbbdev 
38951c0b2f7Stbbdev         for (FreeBlock *curr = b->head; curr; curr = curr->next) {
39051c0b2f7Stbbdev             size_t szBlock = curr->tryLockBlock();
39151c0b2f7Stbbdev             if (!szBlock) {
39251c0b2f7Stbbdev                 // block is locked, re-do bin lock, as there is no place to spin
39351c0b2f7Stbbdev                 // while block coalescing
39451c0b2f7Stbbdev                 goto try_next;
39551c0b2f7Stbbdev             }
39651c0b2f7Stbbdev 
39751c0b2f7Stbbdev             // GENERAL CASE
39851c0b2f7Stbbdev             if (alignedBin || !needAlignedRes) {
39951c0b2f7Stbbdev                 size_t splitSz = szBlock - size;
40051c0b2f7Stbbdev                 // If we got a block as split result, it must have a room for control structures.
40151c0b2f7Stbbdev                 if (szBlock >= size && (splitSz >= FreeBlock::minBlockSize || !splitSz))
40251c0b2f7Stbbdev                     fBlock = curr;
40351c0b2f7Stbbdev             } else {
40451c0b2f7Stbbdev                 // SPECIAL CASE, to get aligned block from unaligned bin we have to cut the middle of a block
40551c0b2f7Stbbdev                 // and return remaining left and right part. Possible only in fixed pool scenario, assert for this
40651c0b2f7Stbbdev                 // is set inside splitBlock() function.
40751c0b2f7Stbbdev 
40851c0b2f7Stbbdev                 void *newB = alignUp(curr, slabSize);
40951c0b2f7Stbbdev                 uintptr_t rightNew = (uintptr_t)newB + size;
41051c0b2f7Stbbdev                 uintptr_t rightCurr = (uintptr_t)curr + szBlock;
41151c0b2f7Stbbdev                 // Check if the block size is sufficient,
41251c0b2f7Stbbdev                 // and also left and right split results are either big enough or non-existent
41351c0b2f7Stbbdev                 if (rightNew <= rightCurr
41451c0b2f7Stbbdev                         && (newB == curr || ((uintptr_t)newB - (uintptr_t)curr) >= FreeBlock::minBlockSize)
41551c0b2f7Stbbdev                         && (rightNew == rightCurr || (rightCurr - rightNew) >= FreeBlock::minBlockSize))
41651c0b2f7Stbbdev                     fBlock = curr;
41751c0b2f7Stbbdev             }
41851c0b2f7Stbbdev 
41951c0b2f7Stbbdev             if (fBlock) {
42051c0b2f7Stbbdev                 // consume must be called before result of removing from a bin is visible externally.
42151c0b2f7Stbbdev                 sync->blockConsumed();
42251c0b2f7Stbbdev                 // TODO: think about cases when block stays in the same bin
42351c0b2f7Stbbdev                 b->removeBlock(fBlock);
42451c0b2f7Stbbdev                 if (freeBins[binIdx].empty())
42551c0b2f7Stbbdev                     bitMask.set(binIdx, false);
42651c0b2f7Stbbdev                 fBlock->sizeTmp = szBlock;
42751c0b2f7Stbbdev                 break;
42851c0b2f7Stbbdev             } else { // block size is not valid, search for next block in the bin
42951c0b2f7Stbbdev                 curr->setMeFree(szBlock);
43051c0b2f7Stbbdev                 curr->rightNeig(szBlock)->setLeftFree(szBlock);
43151c0b2f7Stbbdev             }
43251c0b2f7Stbbdev         }
43351c0b2f7Stbbdev     }
43451c0b2f7Stbbdev     return fBlock;
43551c0b2f7Stbbdev }
43651c0b2f7Stbbdev 
43751c0b2f7Stbbdev bool Backend::IndexedBins::tryReleaseRegions(int binIdx, Backend *backend)
43851c0b2f7Stbbdev {
43951c0b2f7Stbbdev     Bin *b = &freeBins[binIdx];
44051c0b2f7Stbbdev     FreeBlock *fBlockList = NULL;
44151c0b2f7Stbbdev 
44251c0b2f7Stbbdev     // got all blocks from the bin and re-do coalesce on them
44351c0b2f7Stbbdev     // to release single-block regions
44451c0b2f7Stbbdev try_next:
44551c0b2f7Stbbdev     if (b->head) {
44651c0b2f7Stbbdev         MallocMutex::scoped_lock binLock(b->tLock);
44751c0b2f7Stbbdev         for (FreeBlock *curr = b->head; curr; ) {
44851c0b2f7Stbbdev             size_t szBlock = curr->tryLockBlock();
44951c0b2f7Stbbdev             if (!szBlock)
45051c0b2f7Stbbdev                 goto try_next;
45151c0b2f7Stbbdev 
45251c0b2f7Stbbdev             FreeBlock *next = curr->next;
45351c0b2f7Stbbdev 
45451c0b2f7Stbbdev             b->removeBlock(curr);
45551c0b2f7Stbbdev             curr->sizeTmp = szBlock;
45651c0b2f7Stbbdev             curr->nextToFree = fBlockList;
45751c0b2f7Stbbdev             fBlockList = curr;
45851c0b2f7Stbbdev             curr = next;
45951c0b2f7Stbbdev         }
46051c0b2f7Stbbdev     }
46151c0b2f7Stbbdev     return backend->coalescAndPutList(fBlockList, /*forceCoalescQDrop=*/true,
46251c0b2f7Stbbdev                                       /*reportBlocksProcessed=*/false);
46351c0b2f7Stbbdev }
46451c0b2f7Stbbdev 
46551c0b2f7Stbbdev void Backend::Bin::removeBlock(FreeBlock *fBlock)
46651c0b2f7Stbbdev {
46751c0b2f7Stbbdev     MALLOC_ASSERT(fBlock->next||fBlock->prev||fBlock==head,
46851c0b2f7Stbbdev                   "Detected that a block is not in the bin.");
46951c0b2f7Stbbdev     if (head == fBlock)
47051c0b2f7Stbbdev         head = fBlock->next;
47151c0b2f7Stbbdev     if (tail == fBlock)
47251c0b2f7Stbbdev         tail = fBlock->prev;
47351c0b2f7Stbbdev     if (fBlock->prev)
47451c0b2f7Stbbdev         fBlock->prev->next = fBlock->next;
47551c0b2f7Stbbdev     if (fBlock->next)
47651c0b2f7Stbbdev         fBlock->next->prev = fBlock->prev;
47751c0b2f7Stbbdev }
47851c0b2f7Stbbdev 
47951c0b2f7Stbbdev void Backend::IndexedBins::addBlock(int binIdx, FreeBlock *fBlock, size_t /* blockSz */, bool addToTail)
48051c0b2f7Stbbdev {
48151c0b2f7Stbbdev     Bin *b = &freeBins[binIdx];
48251c0b2f7Stbbdev     fBlock->myBin = binIdx;
48351c0b2f7Stbbdev     fBlock->next = fBlock->prev = NULL;
48451c0b2f7Stbbdev     {
48551c0b2f7Stbbdev         MallocMutex::scoped_lock scopedLock(b->tLock);
48651c0b2f7Stbbdev         if (addToTail) {
48751c0b2f7Stbbdev             fBlock->prev = b->tail;
48851c0b2f7Stbbdev             b->tail = fBlock;
48951c0b2f7Stbbdev             if (fBlock->prev)
49051c0b2f7Stbbdev                 fBlock->prev->next = fBlock;
49151c0b2f7Stbbdev             if (!b->head)
49251c0b2f7Stbbdev                 b->head = fBlock;
49351c0b2f7Stbbdev         } else {
49451c0b2f7Stbbdev             fBlock->next = b->head;
49551c0b2f7Stbbdev             b->head = fBlock;
49651c0b2f7Stbbdev             if (fBlock->next)
49751c0b2f7Stbbdev                 fBlock->next->prev = fBlock;
49851c0b2f7Stbbdev             if (!b->tail)
49951c0b2f7Stbbdev                 b->tail = fBlock;
50051c0b2f7Stbbdev         }
50151c0b2f7Stbbdev     }
50251c0b2f7Stbbdev     bitMask.set(binIdx, true);
50351c0b2f7Stbbdev }
50451c0b2f7Stbbdev 
50551c0b2f7Stbbdev bool Backend::IndexedBins::tryAddBlock(int binIdx, FreeBlock *fBlock, bool addToTail)
50651c0b2f7Stbbdev {
50751c0b2f7Stbbdev     bool locked;
50851c0b2f7Stbbdev     Bin *b = &freeBins[binIdx];
50951c0b2f7Stbbdev     fBlock->myBin = binIdx;
51051c0b2f7Stbbdev     if (addToTail) {
51151c0b2f7Stbbdev         fBlock->next = NULL;
51251c0b2f7Stbbdev         {
51351c0b2f7Stbbdev             MallocMutex::scoped_lock scopedLock(b->tLock, /*wait=*/false, &locked);
51451c0b2f7Stbbdev             if (!locked)
51551c0b2f7Stbbdev                 return false;
51651c0b2f7Stbbdev             fBlock->prev = b->tail;
51751c0b2f7Stbbdev             b->tail = fBlock;
51851c0b2f7Stbbdev             if (fBlock->prev)
51951c0b2f7Stbbdev                 fBlock->prev->next = fBlock;
52051c0b2f7Stbbdev             if (!b->head)
52151c0b2f7Stbbdev                 b->head = fBlock;
52251c0b2f7Stbbdev         }
52351c0b2f7Stbbdev     } else {
52451c0b2f7Stbbdev         fBlock->prev = NULL;
52551c0b2f7Stbbdev         {
52651c0b2f7Stbbdev             MallocMutex::scoped_lock scopedLock(b->tLock, /*wait=*/false, &locked);
52751c0b2f7Stbbdev             if (!locked)
52851c0b2f7Stbbdev                 return false;
52951c0b2f7Stbbdev             fBlock->next = b->head;
53051c0b2f7Stbbdev             b->head = fBlock;
53151c0b2f7Stbbdev             if (fBlock->next)
53251c0b2f7Stbbdev                 fBlock->next->prev = fBlock;
53351c0b2f7Stbbdev             if (!b->tail)
53451c0b2f7Stbbdev                 b->tail = fBlock;
53551c0b2f7Stbbdev         }
53651c0b2f7Stbbdev     }
53751c0b2f7Stbbdev     bitMask.set(binIdx, true);
53851c0b2f7Stbbdev     return true;
53951c0b2f7Stbbdev }
54051c0b2f7Stbbdev 
54151c0b2f7Stbbdev void Backend::IndexedBins::reset()
54251c0b2f7Stbbdev {
54351c0b2f7Stbbdev     for (unsigned i=0; i<Backend::freeBinsNum; i++)
54451c0b2f7Stbbdev         freeBins[i].reset();
54551c0b2f7Stbbdev     bitMask.reset();
54651c0b2f7Stbbdev }
54751c0b2f7Stbbdev 
54851c0b2f7Stbbdev void Backend::IndexedBins::lockRemoveBlock(int binIdx, FreeBlock *fBlock)
54951c0b2f7Stbbdev {
55051c0b2f7Stbbdev     MallocMutex::scoped_lock scopedLock(freeBins[binIdx].tLock);
55151c0b2f7Stbbdev     freeBins[binIdx].removeBlock(fBlock);
55251c0b2f7Stbbdev     if (freeBins[binIdx].empty())
55351c0b2f7Stbbdev         bitMask.set(binIdx, false);
55451c0b2f7Stbbdev }
55551c0b2f7Stbbdev 
55651c0b2f7Stbbdev bool ExtMemoryPool::regionsAreReleaseable() const
55751c0b2f7Stbbdev {
55851c0b2f7Stbbdev     return !keepAllMemory && !delayRegsReleasing;
55951c0b2f7Stbbdev }
56051c0b2f7Stbbdev 
56151c0b2f7Stbbdev FreeBlock *Backend::splitBlock(FreeBlock *fBlock, int num, size_t size, bool blockIsAligned, bool needAlignedBlock)
56251c0b2f7Stbbdev {
56351c0b2f7Stbbdev     const size_t totalSize = num * size;
56451c0b2f7Stbbdev 
56551c0b2f7Stbbdev     // SPECIAL CASE, for unaligned block we have to cut the middle of a block
56651c0b2f7Stbbdev     // and return remaining left and right part. Possible only in a fixed pool scenario.
56751c0b2f7Stbbdev     if (needAlignedBlock && !blockIsAligned) {
56851c0b2f7Stbbdev         MALLOC_ASSERT(extMemPool->fixedPool,
56951c0b2f7Stbbdev                 "Aligned block request from unaligned bin possible only in fixed pool scenario.");
57051c0b2f7Stbbdev 
57151c0b2f7Stbbdev         // Space to use is in the middle
57251c0b2f7Stbbdev         FreeBlock *newBlock = alignUp(fBlock, slabSize);
57351c0b2f7Stbbdev         FreeBlock *rightPart = (FreeBlock*)((uintptr_t)newBlock + totalSize);
57451c0b2f7Stbbdev         uintptr_t fBlockEnd = (uintptr_t)fBlock + fBlock->sizeTmp;
57551c0b2f7Stbbdev 
57651c0b2f7Stbbdev         // Return free right part
57751c0b2f7Stbbdev         if ((uintptr_t)rightPart != fBlockEnd) {
57851c0b2f7Stbbdev             rightPart->initHeader();  // to prevent coalescing rightPart with fBlock
57951c0b2f7Stbbdev             size_t rightSize = fBlockEnd - (uintptr_t)rightPart;
58051c0b2f7Stbbdev             coalescAndPut(rightPart, rightSize, toAlignedBin(rightPart, rightSize));
58151c0b2f7Stbbdev         }
58251c0b2f7Stbbdev         // And free left part
58351c0b2f7Stbbdev         if (newBlock != fBlock) {
58451c0b2f7Stbbdev             newBlock->initHeader(); // to prevent coalescing fBlock with newB
58551c0b2f7Stbbdev             size_t leftSize = (uintptr_t)newBlock - (uintptr_t)fBlock;
58651c0b2f7Stbbdev             coalescAndPut(fBlock, leftSize, toAlignedBin(fBlock, leftSize));
58751c0b2f7Stbbdev         }
58851c0b2f7Stbbdev         fBlock = newBlock;
58951c0b2f7Stbbdev     } else if (size_t splitSize = fBlock->sizeTmp - totalSize) { // need to split the block
59051c0b2f7Stbbdev         // GENERAL CASE, cut the left or right part of the block
59151c0b2f7Stbbdev         FreeBlock *splitBlock = NULL;
59251c0b2f7Stbbdev         if (needAlignedBlock) {
59351c0b2f7Stbbdev             // For slab aligned blocks cut the right side of the block
59451c0b2f7Stbbdev             // and return it to a requester, original block returns to backend
59551c0b2f7Stbbdev             splitBlock = fBlock;
59651c0b2f7Stbbdev             fBlock = (FreeBlock*)((uintptr_t)splitBlock + splitSize);
59751c0b2f7Stbbdev             fBlock->initHeader();
59851c0b2f7Stbbdev         } else {
59951c0b2f7Stbbdev             // For large object blocks cut original block and put free righ part to backend
60051c0b2f7Stbbdev             splitBlock = (FreeBlock*)((uintptr_t)fBlock + totalSize);
60151c0b2f7Stbbdev             splitBlock->initHeader();
60251c0b2f7Stbbdev         }
60351c0b2f7Stbbdev         // Mark free block as it`s parent only when the requested type (needAlignedBlock)
60451c0b2f7Stbbdev         // and returned from Bins/OS block (isAligned) are equal (XOR operation used)
60551c0b2f7Stbbdev         bool markAligned = (blockIsAligned ^ needAlignedBlock) ? toAlignedBin(splitBlock, splitSize) : blockIsAligned;
60651c0b2f7Stbbdev         coalescAndPut(splitBlock, splitSize, markAligned);
60751c0b2f7Stbbdev     }
60851c0b2f7Stbbdev     MALLOC_ASSERT(!needAlignedBlock || isAligned(fBlock, slabSize), "Expect to get aligned block, if one was requested.");
60951c0b2f7Stbbdev     FreeBlock::markBlocks(fBlock, num, size);
61051c0b2f7Stbbdev     return fBlock;
61151c0b2f7Stbbdev }
61251c0b2f7Stbbdev 
61351c0b2f7Stbbdev size_t Backend::getMaxBinnedSize() const
61451c0b2f7Stbbdev {
61551c0b2f7Stbbdev     return hugePages.isEnabled && !inUserPool() ?
61651c0b2f7Stbbdev         maxBinned_HugePage : maxBinned_SmallPage;
61751c0b2f7Stbbdev }
61851c0b2f7Stbbdev 
61951c0b2f7Stbbdev inline bool Backend::MaxRequestComparator::operator()(size_t oldMaxReq, size_t requestSize) const
62051c0b2f7Stbbdev {
62151c0b2f7Stbbdev     return requestSize > oldMaxReq && requestSize < backend->getMaxBinnedSize();
62251c0b2f7Stbbdev }
62351c0b2f7Stbbdev 
62451c0b2f7Stbbdev // last chance to get memory
62551c0b2f7Stbbdev FreeBlock *Backend::releaseMemInCaches(intptr_t startModifiedCnt,
62651c0b2f7Stbbdev                                     int *lockedBinsThreshold, int numOfLockedBins)
62751c0b2f7Stbbdev {
62851c0b2f7Stbbdev     // something released from caches
62951c0b2f7Stbbdev     if (extMemPool->hardCachesCleanup()
63051c0b2f7Stbbdev         // ..or can use blocks that are in processing now
63151c0b2f7Stbbdev         || bkndSync.waitTillBlockReleased(startModifiedCnt))
63251c0b2f7Stbbdev         return (FreeBlock*)VALID_BLOCK_IN_BIN;
63351c0b2f7Stbbdev     // OS can't give us more memory, but we have some in locked bins
63451c0b2f7Stbbdev     if (*lockedBinsThreshold && numOfLockedBins) {
63551c0b2f7Stbbdev         *lockedBinsThreshold = 0;
63651c0b2f7Stbbdev         return (FreeBlock*)VALID_BLOCK_IN_BIN;
63751c0b2f7Stbbdev     }
63851c0b2f7Stbbdev     return NULL; // nothing found, give up
63951c0b2f7Stbbdev }
64051c0b2f7Stbbdev 
64151c0b2f7Stbbdev FreeBlock *Backend::askMemFromOS(size_t blockSize, intptr_t startModifiedCnt,
64251c0b2f7Stbbdev                                  int *lockedBinsThreshold, int numOfLockedBins,
64351c0b2f7Stbbdev                                  bool *splittableRet, bool needSlabRegion)
64451c0b2f7Stbbdev {
64551c0b2f7Stbbdev     FreeBlock *block;
64651c0b2f7Stbbdev     // The block sizes can be divided into 3 groups:
64751c0b2f7Stbbdev     //   1. "quite small": popular object size, we are in bootstarp or something
64851c0b2f7Stbbdev     //      like; request several regions.
64951c0b2f7Stbbdev     //   2. "quite large": we want to have several such blocks in the region
65051c0b2f7Stbbdev     //      but not want several pre-allocated regions.
65151c0b2f7Stbbdev     //   3. "huge": exact fit, we allocate only one block and do not allow
65251c0b2f7Stbbdev     //       any other allocations to placed in a region.
65351c0b2f7Stbbdev     // Dividing the block sizes in these groups we are trying to balance between
65451c0b2f7Stbbdev     // too small regions (that leads to fragmentation) and too large ones (that
65551c0b2f7Stbbdev     // leads to excessive address space consumption). If a region is "too
65651c0b2f7Stbbdev     // large", allocate only one, to prevent fragmentation. It supposedly
65751c0b2f7Stbbdev     // doesn't hurt performance, because the object requested by user is large.
65851c0b2f7Stbbdev     // Bounds for the groups are:
65951c0b2f7Stbbdev     const size_t maxBinned = getMaxBinnedSize();
66051c0b2f7Stbbdev     const size_t quiteSmall = maxBinned / 8;
66151c0b2f7Stbbdev     const size_t quiteLarge = maxBinned;
66251c0b2f7Stbbdev 
66351c0b2f7Stbbdev     if (blockSize >= quiteLarge) {
66451c0b2f7Stbbdev         // Do not interact with other threads via semaphores, as for exact fit
66551c0b2f7Stbbdev         // we can't share regions with them, memory requesting is individual.
66651c0b2f7Stbbdev         block = addNewRegion(blockSize, MEMREG_ONE_BLOCK, /*addToBin=*/false);
66751c0b2f7Stbbdev         if (!block)
66851c0b2f7Stbbdev             return releaseMemInCaches(startModifiedCnt, lockedBinsThreshold, numOfLockedBins);
66951c0b2f7Stbbdev         *splittableRet = false;
67051c0b2f7Stbbdev     } else {
67151c0b2f7Stbbdev         const size_t regSz_sizeBased = alignUp(4*maxRequestedSize, 1024*1024);
67251c0b2f7Stbbdev         // Another thread is modifying backend while we can't get the block.
67351c0b2f7Stbbdev         // Wait while it leaves and re-do the scan
67451c0b2f7Stbbdev         // before trying other ways to extend the backend.
67551c0b2f7Stbbdev         if (bkndSync.waitTillBlockReleased(startModifiedCnt)
67651c0b2f7Stbbdev             // semaphore is protecting adding more more memory from OS
67751c0b2f7Stbbdev             || memExtendingSema.wait())
67851c0b2f7Stbbdev             return (FreeBlock*)VALID_BLOCK_IN_BIN;
67951c0b2f7Stbbdev 
68051c0b2f7Stbbdev         if (startModifiedCnt != bkndSync.getNumOfMods()) {
68151c0b2f7Stbbdev             memExtendingSema.signal();
68251c0b2f7Stbbdev             return (FreeBlock*)VALID_BLOCK_IN_BIN;
68351c0b2f7Stbbdev         }
68451c0b2f7Stbbdev 
68551c0b2f7Stbbdev         if (blockSize < quiteSmall) {
68651c0b2f7Stbbdev             // For this size of blocks, add NUM_OF_REG "advance" regions in bin,
68751c0b2f7Stbbdev             // and return one as a result.
68851c0b2f7Stbbdev             // TODO: add to bin first, because other threads can use them right away.
68951c0b2f7Stbbdev             // This must be done carefully, because blocks in bins can be released
69051c0b2f7Stbbdev             // in releaseCachesToLimit().
69151c0b2f7Stbbdev             const unsigned NUM_OF_REG = 3;
69251c0b2f7Stbbdev             MemRegionType regType = needSlabRegion ? MEMREG_SLAB_BLOCKS : MEMREG_LARGE_BLOCKS;
69351c0b2f7Stbbdev             block = addNewRegion(regSz_sizeBased, regType, /*addToBin=*/false);
69451c0b2f7Stbbdev             if (block)
69551c0b2f7Stbbdev                 for (unsigned idx=0; idx<NUM_OF_REG; idx++)
69651c0b2f7Stbbdev                     if (! addNewRegion(regSz_sizeBased, regType, /*addToBin=*/true))
69751c0b2f7Stbbdev                         break;
69851c0b2f7Stbbdev         } else {
69951c0b2f7Stbbdev             block = addNewRegion(regSz_sizeBased, MEMREG_LARGE_BLOCKS, /*addToBin=*/false);
70051c0b2f7Stbbdev         }
70151c0b2f7Stbbdev         memExtendingSema.signal();
70251c0b2f7Stbbdev 
70351c0b2f7Stbbdev         // no regions found, try to clean cache
70451c0b2f7Stbbdev         if (!block || block == (FreeBlock*)VALID_BLOCK_IN_BIN)
70551c0b2f7Stbbdev             return releaseMemInCaches(startModifiedCnt, lockedBinsThreshold, numOfLockedBins);
70651c0b2f7Stbbdev         // Since a region can hold more than one block it can be split.
70751c0b2f7Stbbdev         *splittableRet = true;
70851c0b2f7Stbbdev     }
70951c0b2f7Stbbdev     // after asking memory from OS, release caches if we above the memory limits
71051c0b2f7Stbbdev     releaseCachesToLimit();
71151c0b2f7Stbbdev 
71251c0b2f7Stbbdev     return block;
71351c0b2f7Stbbdev }
71451c0b2f7Stbbdev 
71551c0b2f7Stbbdev void Backend::releaseCachesToLimit()
71651c0b2f7Stbbdev {
71751c0b2f7Stbbdev     if (!memSoftLimit.load(std::memory_order_relaxed)
71851c0b2f7Stbbdev             || totalMemSize.load(std::memory_order_relaxed) <= memSoftLimit.load(std::memory_order_relaxed)) {
71951c0b2f7Stbbdev         return;
72051c0b2f7Stbbdev     }
72151c0b2f7Stbbdev     size_t locTotalMemSize, locMemSoftLimit;
72251c0b2f7Stbbdev 
72351c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
72451c0b2f7Stbbdev     if (extMemPool->softCachesCleanup() &&
72551c0b2f7Stbbdev         (locTotalMemSize = totalMemSize.load(std::memory_order_acquire)) <=
72651c0b2f7Stbbdev         (locMemSoftLimit = memSoftLimit.load(std::memory_order_acquire)))
72751c0b2f7Stbbdev         return;
72851c0b2f7Stbbdev     // clean global large-object cache, if this is not enough, clean local caches
72951c0b2f7Stbbdev     // do this in several tries, because backend fragmentation can prevent
73051c0b2f7Stbbdev     // region from releasing
73151c0b2f7Stbbdev     for (int cleanLocal = 0; cleanLocal<2; cleanLocal++)
73251c0b2f7Stbbdev         while (cleanLocal ?
73351c0b2f7Stbbdev                  extMemPool->allLocalCaches.cleanup(/*cleanOnlyUnused=*/true) :
73451c0b2f7Stbbdev                  extMemPool->loc.decreasingCleanup())
73551c0b2f7Stbbdev             if ((locTotalMemSize = totalMemSize.load(std::memory_order_acquire)) <=
73651c0b2f7Stbbdev                 (locMemSoftLimit = memSoftLimit.load(std::memory_order_acquire)))
73751c0b2f7Stbbdev                 return;
73851c0b2f7Stbbdev     // last chance to match memSoftLimit
73951c0b2f7Stbbdev     extMemPool->hardCachesCleanup();
74051c0b2f7Stbbdev }
74151c0b2f7Stbbdev 
74251c0b2f7Stbbdev int Backend::IndexedBins::getMinNonemptyBin(unsigned startBin) const
74351c0b2f7Stbbdev {
74451c0b2f7Stbbdev     int p = bitMask.getMinTrue(startBin);
74551c0b2f7Stbbdev     return p == -1 ? Backend::freeBinsNum : p;
74651c0b2f7Stbbdev }
74751c0b2f7Stbbdev 
74851c0b2f7Stbbdev FreeBlock *Backend::IndexedBins::findBlock(int nativeBin, BackendSync *sync, size_t size,
74951c0b2f7Stbbdev         bool needAlignedBlock, bool alignedBin, int *numOfLockedBins)
75051c0b2f7Stbbdev {
75151c0b2f7Stbbdev     for (int i=getMinNonemptyBin(nativeBin); i<freeBinsNum; i=getMinNonemptyBin(i+1))
75251c0b2f7Stbbdev         if (FreeBlock *block = getFromBin(i, sync, size, needAlignedBlock, alignedBin, /*wait=*/false, numOfLockedBins))
75351c0b2f7Stbbdev             return block;
75451c0b2f7Stbbdev 
75551c0b2f7Stbbdev     return NULL;
75651c0b2f7Stbbdev }
75751c0b2f7Stbbdev 
75851c0b2f7Stbbdev void Backend::requestBootstrapMem()
75951c0b2f7Stbbdev {
76051c0b2f7Stbbdev     if (bootsrapMemDone == bootsrapMemStatus.load(std::memory_order_acquire))
76151c0b2f7Stbbdev         return;
76251c0b2f7Stbbdev     MallocMutex::scoped_lock lock( bootsrapMemStatusMutex );
76351c0b2f7Stbbdev     if (bootsrapMemDone == bootsrapMemStatus)
76451c0b2f7Stbbdev         return;
76551c0b2f7Stbbdev     MALLOC_ASSERT(bootsrapMemNotDone == bootsrapMemStatus, ASSERT_TEXT);
76651c0b2f7Stbbdev     bootsrapMemStatus = bootsrapMemInitializing;
76751c0b2f7Stbbdev     // request some rather big region during bootstrap in advance
76851c0b2f7Stbbdev     // ok to get NULL here, as later we re-do a request with more modest size
76951c0b2f7Stbbdev     addNewRegion(2*1024*1024, MEMREG_SLAB_BLOCKS, /*addToBin=*/true);
77051c0b2f7Stbbdev     bootsrapMemStatus = bootsrapMemDone;
77151c0b2f7Stbbdev }
77251c0b2f7Stbbdev 
77351c0b2f7Stbbdev // try to allocate size Byte block in available bins
77451c0b2f7Stbbdev // needAlignedRes is true if result must be slab-aligned
77551c0b2f7Stbbdev FreeBlock *Backend::genericGetBlock(int num, size_t size, bool needAlignedBlock)
77651c0b2f7Stbbdev {
77751c0b2f7Stbbdev     FreeBlock *block = NULL;
77851c0b2f7Stbbdev     const size_t totalReqSize = num*size;
77951c0b2f7Stbbdev     // no splitting after requesting new region, asks exact size
78051c0b2f7Stbbdev     const int nativeBin = sizeToBin(totalReqSize);
78151c0b2f7Stbbdev 
78251c0b2f7Stbbdev     requestBootstrapMem();
78351c0b2f7Stbbdev     // If we found 2 or less locked bins, it's time to ask more memory from OS.
78451c0b2f7Stbbdev     // But nothing can be asked from fixed pool. And we prefer wait, not ask
78551c0b2f7Stbbdev     // for more memory, if block is quite large.
78651c0b2f7Stbbdev     int lockedBinsThreshold = extMemPool->fixedPool || size>=maxBinned_SmallPage? 0 : 2;
78751c0b2f7Stbbdev 
78851c0b2f7Stbbdev     // Find maximal requested size limited by getMaxBinnedSize()
78951c0b2f7Stbbdev     AtomicUpdate(maxRequestedSize, totalReqSize, MaxRequestComparator(this));
79051c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
79151c0b2f7Stbbdev 
79251c0b2f7Stbbdev     bool splittable = true;
79351c0b2f7Stbbdev     for (;;) {
79451c0b2f7Stbbdev         const intptr_t startModifiedCnt = bkndSync.getNumOfMods();
79551c0b2f7Stbbdev         int numOfLockedBins;
79651c0b2f7Stbbdev 
79751c0b2f7Stbbdev         do {
79851c0b2f7Stbbdev             numOfLockedBins = 0;
79951c0b2f7Stbbdev             if (needAlignedBlock) {
80051c0b2f7Stbbdev                 block = freeSlabAlignedBins.findBlock(nativeBin, &bkndSync, num*size, needAlignedBlock,
80151c0b2f7Stbbdev                                                         /*alignedBin=*/true, &numOfLockedBins);
80251c0b2f7Stbbdev                 if (!block && extMemPool->fixedPool)
80351c0b2f7Stbbdev                     block = freeLargeBlockBins.findBlock(nativeBin, &bkndSync, num*size, needAlignedBlock,
80451c0b2f7Stbbdev                                                         /*alignedBin=*/false, &numOfLockedBins);
80551c0b2f7Stbbdev             } else {
80651c0b2f7Stbbdev                 block = freeLargeBlockBins.findBlock(nativeBin, &bkndSync, num*size, needAlignedBlock,
80751c0b2f7Stbbdev                                                         /*alignedBin=*/false, &numOfLockedBins);
80851c0b2f7Stbbdev                 if (!block && extMemPool->fixedPool)
80951c0b2f7Stbbdev                     block = freeSlabAlignedBins.findBlock(nativeBin, &bkndSync, num*size, needAlignedBlock,
81051c0b2f7Stbbdev                                                         /*alignedBin=*/true, &numOfLockedBins);
81151c0b2f7Stbbdev             }
81251c0b2f7Stbbdev         } while (!block && numOfLockedBins>lockedBinsThreshold);
81351c0b2f7Stbbdev 
81451c0b2f7Stbbdev         if (block)
81551c0b2f7Stbbdev             break;
81651c0b2f7Stbbdev 
81751c0b2f7Stbbdev         if (!(scanCoalescQ(/*forceCoalescQDrop=*/true) | extMemPool->softCachesCleanup())) {
81851c0b2f7Stbbdev             // bins are not updated,
81951c0b2f7Stbbdev             // only remaining possibility is to ask for more memory
82051c0b2f7Stbbdev             block = askMemFromOS(totalReqSize, startModifiedCnt, &lockedBinsThreshold,
82151c0b2f7Stbbdev                         numOfLockedBins, &splittable, needAlignedBlock);
82251c0b2f7Stbbdev             if (!block)
82351c0b2f7Stbbdev                 return NULL;
82451c0b2f7Stbbdev             if (block != (FreeBlock*)VALID_BLOCK_IN_BIN) {
82551c0b2f7Stbbdev                 // size can be increased in askMemFromOS, that's why >=
82651c0b2f7Stbbdev                 MALLOC_ASSERT(block->sizeTmp >= size, ASSERT_TEXT);
82751c0b2f7Stbbdev                 break;
82851c0b2f7Stbbdev             }
82951c0b2f7Stbbdev             // valid block somewhere in bins, let's find it
83051c0b2f7Stbbdev             block = NULL;
83151c0b2f7Stbbdev         }
83251c0b2f7Stbbdev     }
83351c0b2f7Stbbdev     MALLOC_ASSERT(block, ASSERT_TEXT);
83451c0b2f7Stbbdev     if (splittable) {
83551c0b2f7Stbbdev         // At this point we have to be sure that slabAligned attribute describes the right block state
83651c0b2f7Stbbdev         block = splitBlock(block, num, size, block->slabAligned, needAlignedBlock);
83751c0b2f7Stbbdev     }
83851c0b2f7Stbbdev     // matched blockConsumed() from startUseBlock()
83951c0b2f7Stbbdev     bkndSync.blockReleased();
84051c0b2f7Stbbdev 
84151c0b2f7Stbbdev     return block;
84251c0b2f7Stbbdev }
84351c0b2f7Stbbdev 
84451c0b2f7Stbbdev LargeMemoryBlock *Backend::getLargeBlock(size_t size)
84551c0b2f7Stbbdev {
84651c0b2f7Stbbdev     LargeMemoryBlock *lmb =
84751c0b2f7Stbbdev         (LargeMemoryBlock*)genericGetBlock(1, size, /*needAlignedRes=*/false);
84851c0b2f7Stbbdev     if (lmb) {
84951c0b2f7Stbbdev         lmb->unalignedSize = size;
85051c0b2f7Stbbdev         if (extMemPool->userPool())
85151c0b2f7Stbbdev             extMemPool->lmbList.add(lmb);
85251c0b2f7Stbbdev     }
85351c0b2f7Stbbdev     return lmb;
85451c0b2f7Stbbdev }
85551c0b2f7Stbbdev 
85651c0b2f7Stbbdev BlockI *Backend::getSlabBlock(int num) {
85751c0b2f7Stbbdev     BlockI *b = (BlockI*)genericGetBlock(num, slabSize, /*slabAligned=*/true);
85851c0b2f7Stbbdev     MALLOC_ASSERT(isAligned(b, slabSize), ASSERT_TEXT);
85951c0b2f7Stbbdev     return b;
86051c0b2f7Stbbdev }
86151c0b2f7Stbbdev 
86251c0b2f7Stbbdev void Backend::putSlabBlock(BlockI *block) {
86351c0b2f7Stbbdev     genericPutBlock((FreeBlock *)block, slabSize, /*slabAligned=*/true);
86451c0b2f7Stbbdev }
86551c0b2f7Stbbdev 
86651c0b2f7Stbbdev void *Backend::getBackRefSpace(size_t size, bool *rawMemUsed)
86751c0b2f7Stbbdev {
86851c0b2f7Stbbdev     // This block is released only at shutdown, so it can prevent
86951c0b2f7Stbbdev     // a entire region releasing when it's received from the backend,
87051c0b2f7Stbbdev     // so prefer getRawMemory using.
87151c0b2f7Stbbdev     if (void *ret = getRawMemory(size, REGULAR)) {
87251c0b2f7Stbbdev         *rawMemUsed = true;
87351c0b2f7Stbbdev         return ret;
87451c0b2f7Stbbdev     }
87551c0b2f7Stbbdev     void *ret = genericGetBlock(1, size, /*needAlignedRes=*/false);
87651c0b2f7Stbbdev     if (ret) *rawMemUsed = false;
87751c0b2f7Stbbdev     return ret;
87851c0b2f7Stbbdev }
87951c0b2f7Stbbdev 
88051c0b2f7Stbbdev void Backend::putBackRefSpace(void *b, size_t size, bool rawMemUsed)
88151c0b2f7Stbbdev {
88251c0b2f7Stbbdev     if (rawMemUsed)
88351c0b2f7Stbbdev         freeRawMemory(b, size);
88451c0b2f7Stbbdev     // ignore not raw mem, as it released on region releasing
88551c0b2f7Stbbdev }
88651c0b2f7Stbbdev 
88751c0b2f7Stbbdev void Backend::removeBlockFromBin(FreeBlock *fBlock)
88851c0b2f7Stbbdev {
88951c0b2f7Stbbdev     if (fBlock->myBin != Backend::NO_BIN) {
89051c0b2f7Stbbdev         if (fBlock->slabAligned)
89151c0b2f7Stbbdev             freeSlabAlignedBins.lockRemoveBlock(fBlock->myBin, fBlock);
89251c0b2f7Stbbdev         else
89351c0b2f7Stbbdev             freeLargeBlockBins.lockRemoveBlock(fBlock->myBin, fBlock);
89451c0b2f7Stbbdev     }
89551c0b2f7Stbbdev }
89651c0b2f7Stbbdev 
89751c0b2f7Stbbdev void Backend::genericPutBlock(FreeBlock *fBlock, size_t blockSz, bool slabAligned)
89851c0b2f7Stbbdev {
89951c0b2f7Stbbdev     bkndSync.blockConsumed();
90051c0b2f7Stbbdev     coalescAndPut(fBlock, blockSz, slabAligned);
90151c0b2f7Stbbdev     bkndSync.blockReleased();
90251c0b2f7Stbbdev }
90351c0b2f7Stbbdev 
90451c0b2f7Stbbdev void AllLargeBlocksList::add(LargeMemoryBlock *lmb)
90551c0b2f7Stbbdev {
90651c0b2f7Stbbdev     MallocMutex::scoped_lock scoped_cs(largeObjLock);
90751c0b2f7Stbbdev     lmb->gPrev = NULL;
90851c0b2f7Stbbdev     lmb->gNext = loHead;
90951c0b2f7Stbbdev     if (lmb->gNext)
91051c0b2f7Stbbdev         lmb->gNext->gPrev = lmb;
91151c0b2f7Stbbdev     loHead = lmb;
91251c0b2f7Stbbdev }
91351c0b2f7Stbbdev 
91451c0b2f7Stbbdev void AllLargeBlocksList::remove(LargeMemoryBlock *lmb)
91551c0b2f7Stbbdev {
91651c0b2f7Stbbdev     MallocMutex::scoped_lock scoped_cs(largeObjLock);
91751c0b2f7Stbbdev     if (loHead == lmb)
91851c0b2f7Stbbdev         loHead = lmb->gNext;
91951c0b2f7Stbbdev     if (lmb->gNext)
92051c0b2f7Stbbdev         lmb->gNext->gPrev = lmb->gPrev;
92151c0b2f7Stbbdev     if (lmb->gPrev)
92251c0b2f7Stbbdev         lmb->gPrev->gNext = lmb->gNext;
92351c0b2f7Stbbdev }
92451c0b2f7Stbbdev 
92551c0b2f7Stbbdev void Backend::putLargeBlock(LargeMemoryBlock *lmb)
92651c0b2f7Stbbdev {
92751c0b2f7Stbbdev     if (extMemPool->userPool())
92851c0b2f7Stbbdev         extMemPool->lmbList.remove(lmb);
92951c0b2f7Stbbdev     genericPutBlock((FreeBlock *)lmb, lmb->unalignedSize, false);
93051c0b2f7Stbbdev }
93151c0b2f7Stbbdev 
93251c0b2f7Stbbdev void Backend::returnLargeObject(LargeMemoryBlock *lmb)
93351c0b2f7Stbbdev {
93451c0b2f7Stbbdev     removeBackRef(lmb->backRefIdx);
93551c0b2f7Stbbdev     putLargeBlock(lmb);
93651c0b2f7Stbbdev     STAT_increment(getThreadId(), ThreadCommonCounters, freeLargeObj);
93751c0b2f7Stbbdev }
93851c0b2f7Stbbdev 
93951c0b2f7Stbbdev #if BACKEND_HAS_MREMAP
94051c0b2f7Stbbdev void *Backend::remap(void *ptr, size_t oldSize, size_t newSize, size_t alignment)
94151c0b2f7Stbbdev {
94251c0b2f7Stbbdev     // no remap for user pools and for object too small that living in bins
94351c0b2f7Stbbdev     if (inUserPool() || min(oldSize, newSize)<maxBinned_SmallPage
94451c0b2f7Stbbdev         // during remap, can't guarantee alignment more strict than current or
94551c0b2f7Stbbdev         // more strict than page alignment
94651c0b2f7Stbbdev         || !isAligned(ptr, alignment) || alignment>extMemPool->granularity)
94751c0b2f7Stbbdev         return NULL;
94851c0b2f7Stbbdev     const LargeMemoryBlock* lmbOld = ((LargeObjectHdr *)ptr - 1)->memoryBlock;
94951c0b2f7Stbbdev     const size_t oldUnalignedSize = lmbOld->unalignedSize;
95051c0b2f7Stbbdev     FreeBlock *oldFBlock = (FreeBlock *)lmbOld;
95151c0b2f7Stbbdev     FreeBlock *right = oldFBlock->rightNeig(oldUnalignedSize);
95251c0b2f7Stbbdev     // in every region only one block can have LAST_REGION_BLOCK on right,
95351c0b2f7Stbbdev     // so don't need no synchronization
95451c0b2f7Stbbdev     if (!right->isLastRegionBlock())
95551c0b2f7Stbbdev         return NULL;
95651c0b2f7Stbbdev 
95751c0b2f7Stbbdev     MemRegion *oldRegion = static_cast<LastFreeBlock*>(right)->memRegion;
95851c0b2f7Stbbdev     MALLOC_ASSERT( oldRegion < ptr, ASSERT_TEXT );
95951c0b2f7Stbbdev     const size_t oldRegionSize = oldRegion->allocSz;
96051c0b2f7Stbbdev     if (oldRegion->type != MEMREG_ONE_BLOCK)
96151c0b2f7Stbbdev         return NULL;  // we are not single in the region
96251c0b2f7Stbbdev     const size_t userOffset = (uintptr_t)ptr - (uintptr_t)oldRegion;
96351c0b2f7Stbbdev     const size_t alignedSize = LargeObjectCache::alignToBin(newSize + userOffset);
96451c0b2f7Stbbdev     const size_t requestSize =
96551c0b2f7Stbbdev         alignUp(sizeof(MemRegion) + alignedSize + sizeof(LastFreeBlock), extMemPool->granularity);
96651c0b2f7Stbbdev     if (requestSize < alignedSize) // is wrapped around?
96751c0b2f7Stbbdev         return NULL;
96851c0b2f7Stbbdev     regionList.remove(oldRegion);
96951c0b2f7Stbbdev 
97051c0b2f7Stbbdev     void *ret = mremap(oldRegion, oldRegion->allocSz, requestSize, MREMAP_MAYMOVE);
97151c0b2f7Stbbdev     if (MAP_FAILED == ret) { // can't remap, revert and leave
97251c0b2f7Stbbdev         regionList.add(oldRegion);
97351c0b2f7Stbbdev         return NULL;
97451c0b2f7Stbbdev     }
97551c0b2f7Stbbdev     MemRegion *region = (MemRegion*)ret;
97651c0b2f7Stbbdev     MALLOC_ASSERT(region->type == MEMREG_ONE_BLOCK, ASSERT_TEXT);
97751c0b2f7Stbbdev     region->allocSz = requestSize;
97851c0b2f7Stbbdev     region->blockSz = alignedSize;
97951c0b2f7Stbbdev 
98051c0b2f7Stbbdev     FreeBlock *fBlock = (FreeBlock *)alignUp((uintptr_t)region + sizeof(MemRegion),
98151c0b2f7Stbbdev                                              largeObjectAlignment);
98251c0b2f7Stbbdev 
98351c0b2f7Stbbdev     regionList.add(region);
98451c0b2f7Stbbdev     startUseBlock(region, fBlock, /*addToBin=*/false);
98551c0b2f7Stbbdev     MALLOC_ASSERT(fBlock->sizeTmp == region->blockSz, ASSERT_TEXT);
98651c0b2f7Stbbdev     // matched blockConsumed() in startUseBlock().
98751c0b2f7Stbbdev     // TODO: get rid of useless pair blockConsumed()/blockReleased()
98851c0b2f7Stbbdev     bkndSync.blockReleased();
98951c0b2f7Stbbdev 
99051c0b2f7Stbbdev     // object must start at same offset from region's start
99151c0b2f7Stbbdev     void *object = (void*)((uintptr_t)region + userOffset);
99251c0b2f7Stbbdev     MALLOC_ASSERT(isAligned(object, alignment), ASSERT_TEXT);
99351c0b2f7Stbbdev     LargeObjectHdr *header = (LargeObjectHdr*)object - 1;
99451c0b2f7Stbbdev     setBackRef(header->backRefIdx, header);
99551c0b2f7Stbbdev 
99651c0b2f7Stbbdev     LargeMemoryBlock *lmb = (LargeMemoryBlock*)fBlock;
99751c0b2f7Stbbdev     lmb->unalignedSize = region->blockSz;
99851c0b2f7Stbbdev     lmb->objectSize = newSize;
99951c0b2f7Stbbdev     lmb->backRefIdx = header->backRefIdx;
100051c0b2f7Stbbdev     header->memoryBlock = lmb;
100151c0b2f7Stbbdev     MALLOC_ASSERT((uintptr_t)lmb + lmb->unalignedSize >=
100251c0b2f7Stbbdev                   (uintptr_t)object + lmb->objectSize, "An object must fit to the block.");
100351c0b2f7Stbbdev 
100451c0b2f7Stbbdev     usedAddrRange.registerFree((uintptr_t)oldRegion, (uintptr_t)oldRegion + oldRegionSize);
100551c0b2f7Stbbdev     usedAddrRange.registerAlloc((uintptr_t)region, (uintptr_t)region + requestSize);
100651c0b2f7Stbbdev     totalMemSize.fetch_add(region->allocSz - oldRegionSize);
100751c0b2f7Stbbdev 
100851c0b2f7Stbbdev     return object;
100951c0b2f7Stbbdev }
101051c0b2f7Stbbdev #endif /* BACKEND_HAS_MREMAP */
101151c0b2f7Stbbdev 
101251c0b2f7Stbbdev void Backend::releaseRegion(MemRegion *memRegion)
101351c0b2f7Stbbdev {
101451c0b2f7Stbbdev     regionList.remove(memRegion);
101551c0b2f7Stbbdev     freeRawMem(memRegion, memRegion->allocSz);
101651c0b2f7Stbbdev }
101751c0b2f7Stbbdev 
101851c0b2f7Stbbdev // coalesce fBlock with its neighborhood
101951c0b2f7Stbbdev FreeBlock *Backend::doCoalesc(FreeBlock *fBlock, MemRegion **mRegion)
102051c0b2f7Stbbdev {
102151c0b2f7Stbbdev     FreeBlock *resBlock = fBlock;
102251c0b2f7Stbbdev     size_t resSize = fBlock->sizeTmp;
102351c0b2f7Stbbdev     MemRegion *memRegion = NULL;
102451c0b2f7Stbbdev 
102551c0b2f7Stbbdev     fBlock->markCoalescing(resSize);
102651c0b2f7Stbbdev     resBlock->blockInBin = false;
102751c0b2f7Stbbdev 
102851c0b2f7Stbbdev     // coalescing with left neighbor
102951c0b2f7Stbbdev     size_t leftSz = fBlock->trySetLeftUsed(GuardedSize::COAL_BLOCK);
103051c0b2f7Stbbdev     if (leftSz != GuardedSize::LOCKED) {
103151c0b2f7Stbbdev         if (leftSz == GuardedSize::COAL_BLOCK) {
103251c0b2f7Stbbdev             coalescQ.putBlock(fBlock);
103351c0b2f7Stbbdev             return NULL;
103451c0b2f7Stbbdev         } else {
103551c0b2f7Stbbdev             FreeBlock *left = fBlock->leftNeig(leftSz);
103651c0b2f7Stbbdev             size_t lSz = left->trySetMeUsed(GuardedSize::COAL_BLOCK);
103751c0b2f7Stbbdev             if (lSz <= GuardedSize::MAX_LOCKED_VAL) {
103851c0b2f7Stbbdev                 fBlock->setLeftFree(leftSz); // rollback
103951c0b2f7Stbbdev                 coalescQ.putBlock(fBlock);
104051c0b2f7Stbbdev                 return NULL;
104151c0b2f7Stbbdev             } else {
104251c0b2f7Stbbdev                 MALLOC_ASSERT(lSz == leftSz, "Invalid header");
104351c0b2f7Stbbdev                 left->blockInBin = true;
104451c0b2f7Stbbdev                 resBlock = left;
104551c0b2f7Stbbdev                 resSize += leftSz;
104651c0b2f7Stbbdev                 resBlock->sizeTmp = resSize;
104751c0b2f7Stbbdev             }
104851c0b2f7Stbbdev         }
104951c0b2f7Stbbdev     }
105051c0b2f7Stbbdev     // coalescing with right neighbor
105151c0b2f7Stbbdev     FreeBlock *right = fBlock->rightNeig(fBlock->sizeTmp);
105251c0b2f7Stbbdev     size_t rightSz = right->trySetMeUsed(GuardedSize::COAL_BLOCK);
105351c0b2f7Stbbdev     if (rightSz != GuardedSize::LOCKED) {
105451c0b2f7Stbbdev         // LastFreeBlock is on the right side
105551c0b2f7Stbbdev         if (GuardedSize::LAST_REGION_BLOCK == rightSz) {
105651c0b2f7Stbbdev             right->setMeFree(GuardedSize::LAST_REGION_BLOCK);
105751c0b2f7Stbbdev             memRegion = static_cast<LastFreeBlock*>(right)->memRegion;
105851c0b2f7Stbbdev         } else if (GuardedSize::COAL_BLOCK == rightSz) {
105951c0b2f7Stbbdev             if (resBlock->blockInBin) {
106051c0b2f7Stbbdev                 resBlock->blockInBin = false;
106151c0b2f7Stbbdev                 removeBlockFromBin(resBlock);
106251c0b2f7Stbbdev             }
106351c0b2f7Stbbdev             coalescQ.putBlock(resBlock);
106451c0b2f7Stbbdev             return NULL;
106551c0b2f7Stbbdev         } else {
106651c0b2f7Stbbdev             size_t rSz = right->rightNeig(rightSz)->
106751c0b2f7Stbbdev                 trySetLeftUsed(GuardedSize::COAL_BLOCK);
106851c0b2f7Stbbdev             if (rSz <= GuardedSize::MAX_LOCKED_VAL) {
106951c0b2f7Stbbdev                 right->setMeFree(rightSz);  // rollback
107051c0b2f7Stbbdev                 if (resBlock->blockInBin) {
107151c0b2f7Stbbdev                     resBlock->blockInBin = false;
107251c0b2f7Stbbdev                     removeBlockFromBin(resBlock);
107351c0b2f7Stbbdev                 }
107451c0b2f7Stbbdev                 coalescQ.putBlock(resBlock);
107551c0b2f7Stbbdev                 return NULL;
107651c0b2f7Stbbdev             } else {
107751c0b2f7Stbbdev                 MALLOC_ASSERT(rSz == rightSz, "Invalid header");
107851c0b2f7Stbbdev                 removeBlockFromBin(right);
107951c0b2f7Stbbdev                 resSize += rightSz;
108051c0b2f7Stbbdev 
108151c0b2f7Stbbdev                 // Is LastFreeBlock on the right side of right?
108251c0b2f7Stbbdev                 FreeBlock *nextRight = right->rightNeig(rightSz);
108351c0b2f7Stbbdev                 size_t nextRightSz = nextRight->
108451c0b2f7Stbbdev                     trySetMeUsed(GuardedSize::COAL_BLOCK);
108551c0b2f7Stbbdev                 if (nextRightSz > GuardedSize::MAX_LOCKED_VAL) {
108651c0b2f7Stbbdev                     if (nextRightSz == GuardedSize::LAST_REGION_BLOCK)
108751c0b2f7Stbbdev                         memRegion = static_cast<LastFreeBlock*>(nextRight)->memRegion;
108851c0b2f7Stbbdev 
108951c0b2f7Stbbdev                     nextRight->setMeFree(nextRightSz);
109051c0b2f7Stbbdev                 }
109151c0b2f7Stbbdev             }
109251c0b2f7Stbbdev         }
109351c0b2f7Stbbdev     }
109451c0b2f7Stbbdev     if (memRegion) {
109551c0b2f7Stbbdev         MALLOC_ASSERT((uintptr_t)memRegion + memRegion->allocSz >=
109651c0b2f7Stbbdev                       (uintptr_t)right + sizeof(LastFreeBlock), ASSERT_TEXT);
109751c0b2f7Stbbdev         MALLOC_ASSERT((uintptr_t)memRegion < (uintptr_t)resBlock, ASSERT_TEXT);
109851c0b2f7Stbbdev         *mRegion = memRegion;
109951c0b2f7Stbbdev     } else
110051c0b2f7Stbbdev         *mRegion = NULL;
110151c0b2f7Stbbdev     resBlock->sizeTmp = resSize;
110251c0b2f7Stbbdev     return resBlock;
110351c0b2f7Stbbdev }
110451c0b2f7Stbbdev 
110551c0b2f7Stbbdev bool Backend::coalescAndPutList(FreeBlock *list, bool forceCoalescQDrop, bool reportBlocksProcessed)
110651c0b2f7Stbbdev {
110751c0b2f7Stbbdev     bool regionReleased = false;
110851c0b2f7Stbbdev 
110951c0b2f7Stbbdev     for (FreeBlock *helper; list;
111051c0b2f7Stbbdev          list = helper,
111151c0b2f7Stbbdev              // matches block enqueue in CoalRequestQ::putBlock()
111251c0b2f7Stbbdev              reportBlocksProcessed? coalescQ.blockWasProcessed() : (void)0) {
111351c0b2f7Stbbdev         MemRegion *memRegion;
111451c0b2f7Stbbdev         bool addToTail = false;
111551c0b2f7Stbbdev 
111651c0b2f7Stbbdev         helper = list->nextToFree;
111751c0b2f7Stbbdev         FreeBlock *toRet = doCoalesc(list, &memRegion);
111851c0b2f7Stbbdev         if (!toRet)
111951c0b2f7Stbbdev             continue;
112051c0b2f7Stbbdev 
112151c0b2f7Stbbdev         if (memRegion && memRegion->blockSz == toRet->sizeTmp
112251c0b2f7Stbbdev             && !extMemPool->fixedPool) {
112351c0b2f7Stbbdev             if (extMemPool->regionsAreReleaseable()) {
112451c0b2f7Stbbdev                 // release the region, because there is no used blocks in it
112551c0b2f7Stbbdev                 if (toRet->blockInBin)
112651c0b2f7Stbbdev                     removeBlockFromBin(toRet);
112751c0b2f7Stbbdev                 releaseRegion(memRegion);
112851c0b2f7Stbbdev                 regionReleased = true;
112951c0b2f7Stbbdev                 continue;
113051c0b2f7Stbbdev             } else // add block from empty region to end of bin,
113151c0b2f7Stbbdev                 addToTail = true; // preserving for exact fit
113251c0b2f7Stbbdev         }
113351c0b2f7Stbbdev         size_t currSz = toRet->sizeTmp;
113451c0b2f7Stbbdev         int bin = sizeToBin(currSz);
113551c0b2f7Stbbdev         bool toAligned = extMemPool->fixedPool ? toAlignedBin(toRet, currSz) : toRet->slabAligned;
113651c0b2f7Stbbdev         bool needAddToBin = true;
113751c0b2f7Stbbdev 
113851c0b2f7Stbbdev         if (toRet->blockInBin) {
113951c0b2f7Stbbdev             // Does it stay in same bin?
114051c0b2f7Stbbdev             if (toRet->myBin == bin && toRet->slabAligned == toAligned)
114151c0b2f7Stbbdev                 needAddToBin = false;
114251c0b2f7Stbbdev             else {
114351c0b2f7Stbbdev                 toRet->blockInBin = false;
114451c0b2f7Stbbdev                 removeBlockFromBin(toRet);
114551c0b2f7Stbbdev             }
114651c0b2f7Stbbdev         }
114751c0b2f7Stbbdev 
114851c0b2f7Stbbdev         // Does not stay in same bin, or bin-less; add it
114951c0b2f7Stbbdev         if (needAddToBin) {
115051c0b2f7Stbbdev             toRet->prev = toRet->next = toRet->nextToFree = NULL;
115151c0b2f7Stbbdev             toRet->myBin = NO_BIN;
115251c0b2f7Stbbdev             toRet->slabAligned = toAligned;
115351c0b2f7Stbbdev 
115451c0b2f7Stbbdev             // If the block is too small to fit in any bin, keep it bin-less.
115551c0b2f7Stbbdev             // It's not a leak because the block later can be coalesced.
115651c0b2f7Stbbdev             if (currSz >= minBinnedSize) {
115751c0b2f7Stbbdev                 toRet->sizeTmp = currSz;
115851c0b2f7Stbbdev                 IndexedBins *target = toRet->slabAligned ? &freeSlabAlignedBins : &freeLargeBlockBins;
115951c0b2f7Stbbdev                 if (forceCoalescQDrop) {
116051c0b2f7Stbbdev                     target->addBlock(bin, toRet, toRet->sizeTmp, addToTail);
116151c0b2f7Stbbdev                 } else if (!target->tryAddBlock(bin, toRet, addToTail)) {
116251c0b2f7Stbbdev                     coalescQ.putBlock(toRet);
116351c0b2f7Stbbdev                     continue;
116451c0b2f7Stbbdev                 }
116551c0b2f7Stbbdev             }
116651c0b2f7Stbbdev             toRet->sizeTmp = 0;
116751c0b2f7Stbbdev         }
116851c0b2f7Stbbdev         // Free (possibly coalesced) free block.
116951c0b2f7Stbbdev         // Adding to bin must be done before this point,
117051c0b2f7Stbbdev         // because after a block is free it can be coalesced, and
117151c0b2f7Stbbdev         // using its pointer became unsafe.
117251c0b2f7Stbbdev         // Remember that coalescing is not done under any global lock.
117351c0b2f7Stbbdev         toRet->setMeFree(currSz);
117451c0b2f7Stbbdev         toRet->rightNeig(currSz)->setLeftFree(currSz);
117551c0b2f7Stbbdev     }
117651c0b2f7Stbbdev     return regionReleased;
117751c0b2f7Stbbdev }
117851c0b2f7Stbbdev 
117951c0b2f7Stbbdev // Coalesce fBlock and add it back to a bin;
118051c0b2f7Stbbdev // processing delayed coalescing requests.
118151c0b2f7Stbbdev void Backend::coalescAndPut(FreeBlock *fBlock, size_t blockSz, bool slabAligned)
118251c0b2f7Stbbdev {
118351c0b2f7Stbbdev     fBlock->sizeTmp = blockSz;
118451c0b2f7Stbbdev     fBlock->nextToFree = NULL;
118551c0b2f7Stbbdev     fBlock->slabAligned = slabAligned;
118651c0b2f7Stbbdev 
118751c0b2f7Stbbdev     coalescAndPutList(fBlock, /*forceCoalescQDrop=*/false, /*reportBlocksProcessed=*/false);
118851c0b2f7Stbbdev }
118951c0b2f7Stbbdev 
119051c0b2f7Stbbdev bool Backend::scanCoalescQ(bool forceCoalescQDrop)
119151c0b2f7Stbbdev {
119251c0b2f7Stbbdev     FreeBlock *currCoalescList = coalescQ.getAll();
119351c0b2f7Stbbdev 
119451c0b2f7Stbbdev     if (currCoalescList)
119551c0b2f7Stbbdev         // reportBlocksProcessed=true informs that the blocks leave coalescQ,
119651c0b2f7Stbbdev         // matches blockConsumed() from CoalRequestQ::putBlock()
119751c0b2f7Stbbdev         coalescAndPutList(currCoalescList, forceCoalescQDrop,
119851c0b2f7Stbbdev                           /*reportBlocksProcessed=*/true);
119951c0b2f7Stbbdev     // returns status of coalescQ.getAll(), as an indication of possible changes in backend
120051c0b2f7Stbbdev     // TODO: coalescAndPutList() may report is some new free blocks became available or not
120151c0b2f7Stbbdev     return currCoalescList;
120251c0b2f7Stbbdev }
120351c0b2f7Stbbdev 
120451c0b2f7Stbbdev FreeBlock *Backend::findBlockInRegion(MemRegion *region, size_t exactBlockSize)
120551c0b2f7Stbbdev {
120651c0b2f7Stbbdev     FreeBlock *fBlock;
120751c0b2f7Stbbdev     size_t blockSz;
120851c0b2f7Stbbdev     uintptr_t fBlockEnd,
120951c0b2f7Stbbdev         lastFreeBlock = (uintptr_t)region + region->allocSz - sizeof(LastFreeBlock);
121051c0b2f7Stbbdev 
121151c0b2f7Stbbdev     static_assert(sizeof(LastFreeBlock) % sizeof(uintptr_t) == 0,
121251c0b2f7Stbbdev         "Atomic applied on LastFreeBlock, and we put it at the end of region, that"
121351c0b2f7Stbbdev         " is uintptr_t-aligned, so no unaligned atomic operations are possible.");
121451c0b2f7Stbbdev      // right bound is slab-aligned, keep LastFreeBlock after it
121551c0b2f7Stbbdev     if (region->type == MEMREG_SLAB_BLOCKS) {
121651c0b2f7Stbbdev         fBlock = (FreeBlock *)alignUp((uintptr_t)region + sizeof(MemRegion), sizeof(uintptr_t));
121751c0b2f7Stbbdev         fBlockEnd = alignDown(lastFreeBlock, slabSize);
121851c0b2f7Stbbdev     } else {
121951c0b2f7Stbbdev         fBlock = (FreeBlock *)alignUp((uintptr_t)region + sizeof(MemRegion), largeObjectAlignment);
122051c0b2f7Stbbdev         fBlockEnd = (uintptr_t)fBlock + exactBlockSize;
122151c0b2f7Stbbdev         MALLOC_ASSERT(fBlockEnd <= lastFreeBlock, ASSERT_TEXT);
122251c0b2f7Stbbdev     }
122351c0b2f7Stbbdev     if (fBlockEnd <= (uintptr_t)fBlock)
122451c0b2f7Stbbdev         return NULL; // allocSz is too small
122551c0b2f7Stbbdev     blockSz = fBlockEnd - (uintptr_t)fBlock;
122651c0b2f7Stbbdev     // TODO: extend getSlabBlock to support degradation, i.e. getting less blocks
122751c0b2f7Stbbdev     // then requested, and then relax this check
122851c0b2f7Stbbdev     // (now all or nothing is implemented, check according to this)
122951c0b2f7Stbbdev     if (blockSz < numOfSlabAllocOnMiss*slabSize)
123051c0b2f7Stbbdev         return NULL;
123151c0b2f7Stbbdev 
123251c0b2f7Stbbdev     region->blockSz = blockSz;
123351c0b2f7Stbbdev     return fBlock;
123451c0b2f7Stbbdev }
123551c0b2f7Stbbdev 
123651c0b2f7Stbbdev // startUseBlock may add the free block to a bin, the block can be used and
123751c0b2f7Stbbdev // even released after this, so the region must be added to regionList already
123851c0b2f7Stbbdev void Backend::startUseBlock(MemRegion *region, FreeBlock *fBlock, bool addToBin)
123951c0b2f7Stbbdev {
124051c0b2f7Stbbdev     size_t blockSz = region->blockSz;
124151c0b2f7Stbbdev     fBlock->initHeader();
124251c0b2f7Stbbdev     fBlock->setMeFree(blockSz);
124351c0b2f7Stbbdev 
124451c0b2f7Stbbdev     LastFreeBlock *lastBl = static_cast<LastFreeBlock*>(fBlock->rightNeig(blockSz));
124551c0b2f7Stbbdev     // to not get unaligned atomics during LastFreeBlock access
124651c0b2f7Stbbdev     MALLOC_ASSERT(isAligned(lastBl, sizeof(uintptr_t)), NULL);
124751c0b2f7Stbbdev     lastBl->initHeader();
124851c0b2f7Stbbdev     lastBl->setMeFree(GuardedSize::LAST_REGION_BLOCK);
124951c0b2f7Stbbdev     lastBl->setLeftFree(blockSz);
125051c0b2f7Stbbdev     lastBl->myBin = NO_BIN;
125151c0b2f7Stbbdev     lastBl->memRegion = region;
125251c0b2f7Stbbdev 
125351c0b2f7Stbbdev     if (addToBin) {
125451c0b2f7Stbbdev         unsigned targetBin = sizeToBin(blockSz);
125551c0b2f7Stbbdev         // during adding advance regions, register bin for a largest block in region
125651c0b2f7Stbbdev         advRegBins.registerBin(targetBin);
125751c0b2f7Stbbdev         if (region->type == MEMREG_SLAB_BLOCKS) {
125851c0b2f7Stbbdev             fBlock->slabAligned = true;
125951c0b2f7Stbbdev             freeSlabAlignedBins.addBlock(targetBin, fBlock, blockSz, /*addToTail=*/false);
126051c0b2f7Stbbdev         } else {
126151c0b2f7Stbbdev             fBlock->slabAligned = false;
126251c0b2f7Stbbdev             freeLargeBlockBins.addBlock(targetBin, fBlock, blockSz, /*addToTail=*/false);
126351c0b2f7Stbbdev         }
126451c0b2f7Stbbdev     } else {
126551c0b2f7Stbbdev         // to match with blockReleased() in genericGetBlock
126651c0b2f7Stbbdev         bkndSync.blockConsumed();
126751c0b2f7Stbbdev         // Understand our alignment for correct splitBlock operation
126851c0b2f7Stbbdev         fBlock->slabAligned = region->type == MEMREG_SLAB_BLOCKS ? true : false;
126951c0b2f7Stbbdev         fBlock->sizeTmp = fBlock->tryLockBlock();
127051c0b2f7Stbbdev         MALLOC_ASSERT(fBlock->sizeTmp >= FreeBlock::minBlockSize, "Locking must be successful");
127151c0b2f7Stbbdev     }
127251c0b2f7Stbbdev }
127351c0b2f7Stbbdev 
127451c0b2f7Stbbdev void MemRegionList::add(MemRegion *r)
127551c0b2f7Stbbdev {
127651c0b2f7Stbbdev     r->prev = NULL;
127751c0b2f7Stbbdev     MallocMutex::scoped_lock lock(regionListLock);
127851c0b2f7Stbbdev     r->next = head;
127951c0b2f7Stbbdev     head = r;
128051c0b2f7Stbbdev     if (head->next)
128151c0b2f7Stbbdev         head->next->prev = head;
128251c0b2f7Stbbdev }
128351c0b2f7Stbbdev 
128451c0b2f7Stbbdev void MemRegionList::remove(MemRegion *r)
128551c0b2f7Stbbdev {
128651c0b2f7Stbbdev     MallocMutex::scoped_lock lock(regionListLock);
128751c0b2f7Stbbdev     if (head == r)
128851c0b2f7Stbbdev         head = head->next;
128951c0b2f7Stbbdev     if (r->next)
129051c0b2f7Stbbdev         r->next->prev = r->prev;
129151c0b2f7Stbbdev     if (r->prev)
129251c0b2f7Stbbdev         r->prev->next = r->next;
129351c0b2f7Stbbdev }
129451c0b2f7Stbbdev 
129551c0b2f7Stbbdev #if __TBB_MALLOC_BACKEND_STAT
129651c0b2f7Stbbdev int MemRegionList::reportStat(FILE *f)
129751c0b2f7Stbbdev {
129851c0b2f7Stbbdev     int regNum = 0;
129951c0b2f7Stbbdev     MallocMutex::scoped_lock lock(regionListLock);
130051c0b2f7Stbbdev     for (MemRegion *curr = head; curr; curr = curr->next) {
130151c0b2f7Stbbdev         fprintf(f, "%p: max block %lu B, ", curr, curr->blockSz);
130251c0b2f7Stbbdev         regNum++;
130351c0b2f7Stbbdev     }
130451c0b2f7Stbbdev     return regNum;
130551c0b2f7Stbbdev }
130651c0b2f7Stbbdev #endif
130751c0b2f7Stbbdev 
130851c0b2f7Stbbdev FreeBlock *Backend::addNewRegion(size_t size, MemRegionType memRegType, bool addToBin)
130951c0b2f7Stbbdev {
131051c0b2f7Stbbdev     static_assert(sizeof(BlockMutexes) <= sizeof(BlockI), "Header must be not overwritten in used blocks");
131151c0b2f7Stbbdev     MALLOC_ASSERT(FreeBlock::minBlockSize > GuardedSize::MAX_SPEC_VAL,
131251c0b2f7Stbbdev           "Block length must not conflict with special values of GuardedSize");
131351c0b2f7Stbbdev     // If the region is not "for slabs" we should reserve some space for
131451c0b2f7Stbbdev     // a region header, the worst case alignment and the last block mark.
131551c0b2f7Stbbdev     const size_t requestSize = memRegType == MEMREG_SLAB_BLOCKS ? size :
131651c0b2f7Stbbdev         size + sizeof(MemRegion) + largeObjectAlignment
131751c0b2f7Stbbdev              +  FreeBlock::minBlockSize + sizeof(LastFreeBlock);
131851c0b2f7Stbbdev 
131951c0b2f7Stbbdev     size_t rawSize = requestSize;
132051c0b2f7Stbbdev     MemRegion *region = (MemRegion*)allocRawMem(rawSize);
132151c0b2f7Stbbdev     if (!region) {
132251c0b2f7Stbbdev         MALLOC_ASSERT(rawSize==requestSize, "getRawMem has not allocated memory but changed the allocated size.");
132351c0b2f7Stbbdev         return NULL;
132451c0b2f7Stbbdev     }
132551c0b2f7Stbbdev     if (rawSize < sizeof(MemRegion)) {
132651c0b2f7Stbbdev         if (!extMemPool->fixedPool)
132751c0b2f7Stbbdev             freeRawMem(region, rawSize);
132851c0b2f7Stbbdev         return NULL;
132951c0b2f7Stbbdev     }
133051c0b2f7Stbbdev 
133151c0b2f7Stbbdev     region->type = memRegType;
133251c0b2f7Stbbdev     region->allocSz = rawSize;
133351c0b2f7Stbbdev     FreeBlock *fBlock = findBlockInRegion(region, size);
133451c0b2f7Stbbdev     if (!fBlock) {
133551c0b2f7Stbbdev         if (!extMemPool->fixedPool)
133651c0b2f7Stbbdev             freeRawMem(region, rawSize);
133751c0b2f7Stbbdev         return NULL;
133851c0b2f7Stbbdev     }
133951c0b2f7Stbbdev     regionList.add(region);
134051c0b2f7Stbbdev     startUseBlock(region, fBlock, addToBin);
134151c0b2f7Stbbdev     bkndSync.binsModified();
134251c0b2f7Stbbdev     return addToBin? (FreeBlock*)VALID_BLOCK_IN_BIN : fBlock;
134351c0b2f7Stbbdev }
134451c0b2f7Stbbdev 
134551c0b2f7Stbbdev void Backend::init(ExtMemoryPool *extMemoryPool)
134651c0b2f7Stbbdev {
134751c0b2f7Stbbdev     extMemPool = extMemoryPool;
134851c0b2f7Stbbdev     usedAddrRange.init();
134951c0b2f7Stbbdev     coalescQ.init(&bkndSync);
135051c0b2f7Stbbdev     bkndSync.init(this);
135151c0b2f7Stbbdev }
135251c0b2f7Stbbdev 
135351c0b2f7Stbbdev void Backend::reset()
135451c0b2f7Stbbdev {
135551c0b2f7Stbbdev     MALLOC_ASSERT(extMemPool->userPool(), "Only user pool can be reset.");
135651c0b2f7Stbbdev     // no active threads are allowed in backend while reset() called
135751c0b2f7Stbbdev     verify();
135851c0b2f7Stbbdev 
135951c0b2f7Stbbdev     freeLargeBlockBins.reset();
136051c0b2f7Stbbdev     freeSlabAlignedBins.reset();
136151c0b2f7Stbbdev     advRegBins.reset();
136251c0b2f7Stbbdev 
136351c0b2f7Stbbdev     for (MemRegion *curr = regionList.head; curr; curr = curr->next) {
136451c0b2f7Stbbdev         FreeBlock *fBlock = findBlockInRegion(curr, curr->blockSz);
136551c0b2f7Stbbdev         MALLOC_ASSERT(fBlock, "A memory region unexpectedly got smaller");
136651c0b2f7Stbbdev         startUseBlock(curr, fBlock, /*addToBin=*/true);
136751c0b2f7Stbbdev     }
136851c0b2f7Stbbdev }
136951c0b2f7Stbbdev 
137051c0b2f7Stbbdev bool Backend::destroy()
137151c0b2f7Stbbdev {
137251c0b2f7Stbbdev     bool noError = true;
137351c0b2f7Stbbdev     // no active threads are allowed in backend while destroy() called
137451c0b2f7Stbbdev     verify();
137551c0b2f7Stbbdev     if (!inUserPool()) {
137651c0b2f7Stbbdev         freeLargeBlockBins.reset();
137751c0b2f7Stbbdev         freeSlabAlignedBins.reset();
137851c0b2f7Stbbdev     }
137951c0b2f7Stbbdev     while (regionList.head) {
138051c0b2f7Stbbdev         MemRegion *helper = regionList.head->next;
138151c0b2f7Stbbdev         noError &= freeRawMem(regionList.head, regionList.head->allocSz);
138251c0b2f7Stbbdev         regionList.head = helper;
138351c0b2f7Stbbdev     }
138451c0b2f7Stbbdev     return noError;
138551c0b2f7Stbbdev }
138651c0b2f7Stbbdev 
138751c0b2f7Stbbdev bool Backend::clean()
138851c0b2f7Stbbdev {
138951c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
139051c0b2f7Stbbdev 
139151c0b2f7Stbbdev     bool res = false;
139251c0b2f7Stbbdev     // We can have several blocks occupying a whole region,
139351c0b2f7Stbbdev     // because such regions are added in advance (see askMemFromOS() and reset()),
139451c0b2f7Stbbdev     // and never used. Release them all.
139551c0b2f7Stbbdev     for (int i = advRegBins.getMinUsedBin(0); i != -1; i = advRegBins.getMinUsedBin(i+1)) {
139651c0b2f7Stbbdev         if (i == freeSlabAlignedBins.getMinNonemptyBin(i))
139751c0b2f7Stbbdev             res |= freeSlabAlignedBins.tryReleaseRegions(i, this);
139851c0b2f7Stbbdev         if (i == freeLargeBlockBins.getMinNonemptyBin(i))
139951c0b2f7Stbbdev             res |= freeLargeBlockBins.tryReleaseRegions(i, this);
140051c0b2f7Stbbdev     }
140151c0b2f7Stbbdev 
140251c0b2f7Stbbdev     return res;
140351c0b2f7Stbbdev }
140451c0b2f7Stbbdev 
140551c0b2f7Stbbdev void Backend::IndexedBins::verify()
140651c0b2f7Stbbdev {
140751c0b2f7Stbbdev     for (int i=0; i<freeBinsNum; i++) {
140851c0b2f7Stbbdev         for (FreeBlock *fb = freeBins[i].head; fb; fb=fb->next) {
140951c0b2f7Stbbdev             uintptr_t mySz = fb->myL.value;
141051c0b2f7Stbbdev             MALLOC_ASSERT(mySz>GuardedSize::MAX_SPEC_VAL, ASSERT_TEXT);
141151c0b2f7Stbbdev             FreeBlock *right = (FreeBlock*)((uintptr_t)fb + mySz);
141251c0b2f7Stbbdev             suppress_unused_warning(right);
141351c0b2f7Stbbdev             MALLOC_ASSERT(right->myL.value<=GuardedSize::MAX_SPEC_VAL, ASSERT_TEXT);
141451c0b2f7Stbbdev             MALLOC_ASSERT(right->leftL.value==mySz, ASSERT_TEXT);
141551c0b2f7Stbbdev             MALLOC_ASSERT(fb->leftL.value<=GuardedSize::MAX_SPEC_VAL, ASSERT_TEXT);
141651c0b2f7Stbbdev         }
141751c0b2f7Stbbdev     }
141851c0b2f7Stbbdev }
141951c0b2f7Stbbdev 
142051c0b2f7Stbbdev // For correct operation, it must be called when no other threads
142151c0b2f7Stbbdev // is changing backend.
142251c0b2f7Stbbdev void Backend::verify()
142351c0b2f7Stbbdev {
142451c0b2f7Stbbdev #if MALLOC_DEBUG
142551c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
142651c0b2f7Stbbdev 
142751c0b2f7Stbbdev     freeLargeBlockBins.verify();
142851c0b2f7Stbbdev     freeSlabAlignedBins.verify();
142951c0b2f7Stbbdev #endif // MALLOC_DEBUG
143051c0b2f7Stbbdev }
143151c0b2f7Stbbdev 
143251c0b2f7Stbbdev #if __TBB_MALLOC_BACKEND_STAT
143351c0b2f7Stbbdev size_t Backend::Bin::countFreeBlocks()
143451c0b2f7Stbbdev {
143551c0b2f7Stbbdev     size_t cnt = 0;
143651c0b2f7Stbbdev     {
143751c0b2f7Stbbdev         MallocMutex::scoped_lock lock(tLock);
143851c0b2f7Stbbdev         for (FreeBlock *fb = head; fb; fb = fb->next)
143951c0b2f7Stbbdev             cnt++;
144051c0b2f7Stbbdev     }
144151c0b2f7Stbbdev     return cnt;
144251c0b2f7Stbbdev }
144351c0b2f7Stbbdev 
144451c0b2f7Stbbdev size_t Backend::Bin::reportFreeBlocks(FILE *f)
144551c0b2f7Stbbdev {
144651c0b2f7Stbbdev     size_t totalSz = 0;
144751c0b2f7Stbbdev     MallocMutex::scoped_lock lock(tLock);
144851c0b2f7Stbbdev     for (FreeBlock *fb = head; fb; fb = fb->next) {
144951c0b2f7Stbbdev         size_t sz = fb->tryLockBlock();
145051c0b2f7Stbbdev         fb->setMeFree(sz);
145151c0b2f7Stbbdev         fprintf(f, " [%p;%p]", fb, (void*)((uintptr_t)fb+sz));
145251c0b2f7Stbbdev         totalSz += sz;
145351c0b2f7Stbbdev     }
145451c0b2f7Stbbdev     return totalSz;
145551c0b2f7Stbbdev }
145651c0b2f7Stbbdev 
145751c0b2f7Stbbdev void Backend::IndexedBins::reportStat(FILE *f)
145851c0b2f7Stbbdev {
145951c0b2f7Stbbdev     size_t totalSize = 0;
146051c0b2f7Stbbdev 
146151c0b2f7Stbbdev     for (int i=0; i<Backend::freeBinsNum; i++)
146251c0b2f7Stbbdev         if (size_t cnt = freeBins[i].countFreeBlocks()) {
146351c0b2f7Stbbdev             totalSize += freeBins[i].reportFreeBlocks(f);
146451c0b2f7Stbbdev             fprintf(f, " %d:%lu, ", i, cnt);
146551c0b2f7Stbbdev         }
146651c0b2f7Stbbdev     fprintf(f, "\ttotal size %lu KB", totalSize/1024);
146751c0b2f7Stbbdev }
146851c0b2f7Stbbdev 
146951c0b2f7Stbbdev void Backend::reportStat(FILE *f)
147051c0b2f7Stbbdev {
147151c0b2f7Stbbdev     scanCoalescQ(/*forceCoalescQDrop=*/false);
147251c0b2f7Stbbdev 
147351c0b2f7Stbbdev     fprintf(f, "\n  regions:\n");
147451c0b2f7Stbbdev     int regNum = regionList.reportStat(f);
147551c0b2f7Stbbdev     fprintf(f, "\n%d regions, %lu KB in all regions\n  free bins:\nlarge bins: ",
147651c0b2f7Stbbdev             regNum, totalMemSize/1024);
147751c0b2f7Stbbdev     freeLargeBlockBins.reportStat(f);
147851c0b2f7Stbbdev     fprintf(f, "\naligned bins: ");
147951c0b2f7Stbbdev     freeSlabAlignedBins.reportStat(f);
148051c0b2f7Stbbdev     fprintf(f, "\n");
148151c0b2f7Stbbdev }
148251c0b2f7Stbbdev #endif // __TBB_MALLOC_BACKEND_STAT
148351c0b2f7Stbbdev 
148451c0b2f7Stbbdev } } // namespaces
1485