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