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 "tbbmalloc_internal.h" 18*51c0b2f7Stbbdev #include <errno.h> 19*51c0b2f7Stbbdev #include <new> /* for placement new */ 20*51c0b2f7Stbbdev #include <string.h> /* for memset */ 21*51c0b2f7Stbbdev 22*51c0b2f7Stbbdev #include "tbb/version.h" 23*51c0b2f7Stbbdev #include "../tbb/environment.h" 24*51c0b2f7Stbbdev #include "../tbb/itt_notify.h" // for __TBB_load_ittnotify() 25*51c0b2f7Stbbdev 26*51c0b2f7Stbbdev #if USE_PTHREAD 27*51c0b2f7Stbbdev #define TlsSetValue_func pthread_setspecific 28*51c0b2f7Stbbdev #define TlsGetValue_func pthread_getspecific 29*51c0b2f7Stbbdev #define GetMyTID() pthread_self() 30*51c0b2f7Stbbdev #include <sched.h> 31*51c0b2f7Stbbdev inline void do_yield() {sched_yield();} 32*51c0b2f7Stbbdev extern "C" { static void mallocThreadShutdownNotification(void*); } 33*51c0b2f7Stbbdev #if __sun || __SUNPRO_CC 34*51c0b2f7Stbbdev #define __asm__ asm 35*51c0b2f7Stbbdev #endif 36*51c0b2f7Stbbdev #include <unistd.h> // sysconf(_SC_PAGESIZE) 37*51c0b2f7Stbbdev #elif USE_WINTHREAD 38*51c0b2f7Stbbdev #define GetMyTID() GetCurrentThreadId() 39*51c0b2f7Stbbdev #if __TBB_WIN8UI_SUPPORT 40*51c0b2f7Stbbdev #include<thread> 41*51c0b2f7Stbbdev #define TlsSetValue_func FlsSetValue 42*51c0b2f7Stbbdev #define TlsGetValue_func FlsGetValue 43*51c0b2f7Stbbdev #define TlsAlloc() FlsAlloc(NULL) 44*51c0b2f7Stbbdev #define TLS_ALLOC_FAILURE FLS_OUT_OF_INDEXES 45*51c0b2f7Stbbdev #define TlsFree FlsFree 46*51c0b2f7Stbbdev inline void do_yield() {std::this_thread::yield();} 47*51c0b2f7Stbbdev #else 48*51c0b2f7Stbbdev #define TlsSetValue_func TlsSetValue 49*51c0b2f7Stbbdev #define TlsGetValue_func TlsGetValue 50*51c0b2f7Stbbdev #define TLS_ALLOC_FAILURE TLS_OUT_OF_INDEXES 51*51c0b2f7Stbbdev inline void do_yield() {SwitchToThread();} 52*51c0b2f7Stbbdev #endif 53*51c0b2f7Stbbdev #else 54*51c0b2f7Stbbdev #error Must define USE_PTHREAD or USE_WINTHREAD 55*51c0b2f7Stbbdev #endif 56*51c0b2f7Stbbdev 57*51c0b2f7Stbbdev #define FREELIST_NONBLOCKING 1 58*51c0b2f7Stbbdev 59*51c0b2f7Stbbdev namespace rml { 60*51c0b2f7Stbbdev class MemoryPool; 61*51c0b2f7Stbbdev namespace internal { 62*51c0b2f7Stbbdev 63*51c0b2f7Stbbdev class Block; 64*51c0b2f7Stbbdev class MemoryPool; 65*51c0b2f7Stbbdev 66*51c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION 67*51c0b2f7Stbbdev 68*51c0b2f7Stbbdev inline bool isMallocInitialized(); 69*51c0b2f7Stbbdev 70*51c0b2f7Stbbdev bool RecursiveMallocCallProtector::noRecursion() { 71*51c0b2f7Stbbdev MALLOC_ASSERT(isMallocInitialized(), 72*51c0b2f7Stbbdev "Recursion status can be checked only when initialization was done."); 73*51c0b2f7Stbbdev return !mallocRecursionDetected; 74*51c0b2f7Stbbdev } 75*51c0b2f7Stbbdev 76*51c0b2f7Stbbdev #endif // MALLOC_CHECK_RECURSION 77*51c0b2f7Stbbdev 78*51c0b2f7Stbbdev /** Support for handling the special UNUSABLE pointer state **/ 79*51c0b2f7Stbbdev const intptr_t UNUSABLE = 0x1; 80*51c0b2f7Stbbdev inline bool isSolidPtr( void* ptr ) { 81*51c0b2f7Stbbdev return (UNUSABLE|(intptr_t)ptr)!=UNUSABLE; 82*51c0b2f7Stbbdev } 83*51c0b2f7Stbbdev inline bool isNotForUse( void* ptr ) { 84*51c0b2f7Stbbdev return (intptr_t)ptr==UNUSABLE; 85*51c0b2f7Stbbdev } 86*51c0b2f7Stbbdev 87*51c0b2f7Stbbdev /* 88*51c0b2f7Stbbdev * Block::objectSize value used to mark blocks allocated by startupAlloc 89*51c0b2f7Stbbdev */ 90*51c0b2f7Stbbdev const uint16_t startupAllocObjSizeMark = ~(uint16_t)0; 91*51c0b2f7Stbbdev 92*51c0b2f7Stbbdev /* 93*51c0b2f7Stbbdev * The following constant is used to define the size of struct Block, the block header. 94*51c0b2f7Stbbdev * The intent is to have the size of a Block multiple of the cache line size, this allows us to 95*51c0b2f7Stbbdev * get good alignment at the cost of some overhead equal to the amount of padding included in the Block. 96*51c0b2f7Stbbdev */ 97*51c0b2f7Stbbdev const int blockHeaderAlignment = estimatedCacheLineSize; 98*51c0b2f7Stbbdev 99*51c0b2f7Stbbdev /********* The data structures and global objects **************/ 100*51c0b2f7Stbbdev 101*51c0b2f7Stbbdev /* 102*51c0b2f7Stbbdev * The malloc routines themselves need to be able to occasionally malloc some space, 103*51c0b2f7Stbbdev * in order to set up the structures used by the thread local structures. This 104*51c0b2f7Stbbdev * routine performs that functions. 105*51c0b2f7Stbbdev */ 106*51c0b2f7Stbbdev class BootStrapBlocks { 107*51c0b2f7Stbbdev MallocMutex bootStrapLock; 108*51c0b2f7Stbbdev Block *bootStrapBlock; 109*51c0b2f7Stbbdev Block *bootStrapBlockUsed; 110*51c0b2f7Stbbdev FreeObject *bootStrapObjectList; 111*51c0b2f7Stbbdev public: 112*51c0b2f7Stbbdev void *allocate(MemoryPool *memPool, size_t size); 113*51c0b2f7Stbbdev void free(void* ptr); 114*51c0b2f7Stbbdev void reset(); 115*51c0b2f7Stbbdev }; 116*51c0b2f7Stbbdev 117*51c0b2f7Stbbdev #if USE_INTERNAL_TID 118*51c0b2f7Stbbdev class ThreadId { 119*51c0b2f7Stbbdev static tls_key_t Tid_key; 120*51c0b2f7Stbbdev std::atomic<intptr_t> ThreadCount; 121*51c0b2f7Stbbdev 122*51c0b2f7Stbbdev unsigned int id; 123*51c0b2f7Stbbdev 124*51c0b2f7Stbbdev static unsigned int tlsNumber() { 125*51c0b2f7Stbbdev unsigned int result = reinterpret_cast<intptr_t>(TlsGetValue_func(Tid_key)); 126*51c0b2f7Stbbdev if( !result ) { 127*51c0b2f7Stbbdev RecursiveMallocCallProtector scoped; 128*51c0b2f7Stbbdev // Thread-local value is zero -> first call from this thread, 129*51c0b2f7Stbbdev // need to initialize with next ID value (IDs start from 1) 130*51c0b2f7Stbbdev result = ++ThreadCount; // returned new value! 131*51c0b2f7Stbbdev TlsSetValue_func( Tid_key, reinterpret_cast<void*>(result) ); 132*51c0b2f7Stbbdev } 133*51c0b2f7Stbbdev return result; 134*51c0b2f7Stbbdev } 135*51c0b2f7Stbbdev public: 136*51c0b2f7Stbbdev static bool init() { 137*51c0b2f7Stbbdev #if USE_WINTHREAD 138*51c0b2f7Stbbdev Tid_key = TlsAlloc(); 139*51c0b2f7Stbbdev if (Tid_key == TLS_ALLOC_FAILURE) 140*51c0b2f7Stbbdev return false; 141*51c0b2f7Stbbdev #else 142*51c0b2f7Stbbdev int status = pthread_key_create( &Tid_key, NULL ); 143*51c0b2f7Stbbdev if ( status ) { 144*51c0b2f7Stbbdev fprintf (stderr, "The memory manager cannot create tls key during initialization\n"); 145*51c0b2f7Stbbdev return false; 146*51c0b2f7Stbbdev } 147*51c0b2f7Stbbdev #endif /* USE_WINTHREAD */ 148*51c0b2f7Stbbdev return true; 149*51c0b2f7Stbbdev } 150*51c0b2f7Stbbdev static void destroy() { 151*51c0b2f7Stbbdev if( Tid_key ) { 152*51c0b2f7Stbbdev #if USE_WINTHREAD 153*51c0b2f7Stbbdev BOOL status = !(TlsFree( Tid_key )); // fail is zero 154*51c0b2f7Stbbdev #else 155*51c0b2f7Stbbdev int status = pthread_key_delete( Tid_key ); 156*51c0b2f7Stbbdev #endif /* USE_WINTHREAD */ 157*51c0b2f7Stbbdev if ( status ) 158*51c0b2f7Stbbdev fprintf (stderr, "The memory manager cannot delete tls key\n"); 159*51c0b2f7Stbbdev Tid_key = 0; 160*51c0b2f7Stbbdev } 161*51c0b2f7Stbbdev } 162*51c0b2f7Stbbdev 163*51c0b2f7Stbbdev ThreadId() : id(ThreadId::tlsNumber()) {} 164*51c0b2f7Stbbdev bool isCurrentThreadId() const { return id == ThreadId::tlsNumber(); } 165*51c0b2f7Stbbdev 166*51c0b2f7Stbbdev #if COLLECT_STATISTICS || MALLOC_TRACE 167*51c0b2f7Stbbdev friend unsigned int getThreadId() { return ThreadId::tlsNumber(); } 168*51c0b2f7Stbbdev #endif 169*51c0b2f7Stbbdev #if COLLECT_STATISTICS 170*51c0b2f7Stbbdev static unsigned getMaxThreadId() { return ThreadCount.load(std::memory_order_relaxed); } 171*51c0b2f7Stbbdev 172*51c0b2f7Stbbdev friend int STAT_increment(ThreadId tid, int bin, int ctr); 173*51c0b2f7Stbbdev #endif 174*51c0b2f7Stbbdev }; 175*51c0b2f7Stbbdev 176*51c0b2f7Stbbdev tls_key_t ThreadId::Tid_key; 177*51c0b2f7Stbbdev intptr_t ThreadId::ThreadCount; 178*51c0b2f7Stbbdev 179*51c0b2f7Stbbdev #if COLLECT_STATISTICS 180*51c0b2f7Stbbdev int STAT_increment(ThreadId tid, int bin, int ctr) 181*51c0b2f7Stbbdev { 182*51c0b2f7Stbbdev return ::STAT_increment(tid.id, bin, ctr); 183*51c0b2f7Stbbdev } 184*51c0b2f7Stbbdev #endif 185*51c0b2f7Stbbdev 186*51c0b2f7Stbbdev #else // USE_INTERNAL_TID 187*51c0b2f7Stbbdev 188*51c0b2f7Stbbdev class ThreadId { 189*51c0b2f7Stbbdev #if USE_PTHREAD 190*51c0b2f7Stbbdev pthread_t tid; 191*51c0b2f7Stbbdev #else 192*51c0b2f7Stbbdev DWORD tid; 193*51c0b2f7Stbbdev #endif 194*51c0b2f7Stbbdev public: 195*51c0b2f7Stbbdev ThreadId() : tid(GetMyTID()) {} 196*51c0b2f7Stbbdev #if USE_PTHREAD 197*51c0b2f7Stbbdev bool isCurrentThreadId() const { return pthread_equal(pthread_self(), tid); } 198*51c0b2f7Stbbdev #else 199*51c0b2f7Stbbdev bool isCurrentThreadId() const { return GetCurrentThreadId() == tid; } 200*51c0b2f7Stbbdev #endif 201*51c0b2f7Stbbdev static bool init() { return true; } 202*51c0b2f7Stbbdev static void destroy() {} 203*51c0b2f7Stbbdev }; 204*51c0b2f7Stbbdev 205*51c0b2f7Stbbdev #endif // USE_INTERNAL_TID 206*51c0b2f7Stbbdev 207*51c0b2f7Stbbdev /*********** Code to provide thread ID and a thread-local void pointer **********/ 208*51c0b2f7Stbbdev 209*51c0b2f7Stbbdev bool TLSKey::init() 210*51c0b2f7Stbbdev { 211*51c0b2f7Stbbdev #if USE_WINTHREAD 212*51c0b2f7Stbbdev TLS_pointer_key = TlsAlloc(); 213*51c0b2f7Stbbdev if (TLS_pointer_key == TLS_ALLOC_FAILURE) 214*51c0b2f7Stbbdev return false; 215*51c0b2f7Stbbdev #else 216*51c0b2f7Stbbdev int status = pthread_key_create( &TLS_pointer_key, mallocThreadShutdownNotification ); 217*51c0b2f7Stbbdev if ( status ) 218*51c0b2f7Stbbdev return false; 219*51c0b2f7Stbbdev #endif /* USE_WINTHREAD */ 220*51c0b2f7Stbbdev return true; 221*51c0b2f7Stbbdev } 222*51c0b2f7Stbbdev 223*51c0b2f7Stbbdev bool TLSKey::destroy() 224*51c0b2f7Stbbdev { 225*51c0b2f7Stbbdev #if USE_WINTHREAD 226*51c0b2f7Stbbdev BOOL status1 = !(TlsFree(TLS_pointer_key)); // fail is zero 227*51c0b2f7Stbbdev #else 228*51c0b2f7Stbbdev int status1 = pthread_key_delete(TLS_pointer_key); 229*51c0b2f7Stbbdev #endif /* USE_WINTHREAD */ 230*51c0b2f7Stbbdev MALLOC_ASSERT(!status1, "The memory manager cannot delete tls key."); 231*51c0b2f7Stbbdev return status1==0; 232*51c0b2f7Stbbdev } 233*51c0b2f7Stbbdev 234*51c0b2f7Stbbdev inline TLSData* TLSKey::getThreadMallocTLS() const 235*51c0b2f7Stbbdev { 236*51c0b2f7Stbbdev return (TLSData *)TlsGetValue_func( TLS_pointer_key ); 237*51c0b2f7Stbbdev } 238*51c0b2f7Stbbdev 239*51c0b2f7Stbbdev inline void TLSKey::setThreadMallocTLS( TLSData * newvalue ) { 240*51c0b2f7Stbbdev RecursiveMallocCallProtector scoped; 241*51c0b2f7Stbbdev TlsSetValue_func( TLS_pointer_key, newvalue ); 242*51c0b2f7Stbbdev } 243*51c0b2f7Stbbdev 244*51c0b2f7Stbbdev /* The 'next' field in the block header has to maintain some invariants: 245*51c0b2f7Stbbdev * it needs to be on a 16K boundary and the first field in the block. 246*51c0b2f7Stbbdev * Any value stored there needs to have the lower 14 bits set to 0 247*51c0b2f7Stbbdev * so that various assert work. This means that if you want to smash this memory 248*51c0b2f7Stbbdev * for debugging purposes you will need to obey this invariant. 249*51c0b2f7Stbbdev * The total size of the header needs to be a power of 2 to simplify 250*51c0b2f7Stbbdev * the alignment requirements. For now it is a 128 byte structure. 251*51c0b2f7Stbbdev * To avoid false sharing, the fields changed only locally are separated 252*51c0b2f7Stbbdev * from the fields changed by foreign threads. 253*51c0b2f7Stbbdev * Changing the size of the block header would require to change 254*51c0b2f7Stbbdev * some bin allocation sizes, in particular "fitting" sizes (see above). 255*51c0b2f7Stbbdev */ 256*51c0b2f7Stbbdev class Bin; 257*51c0b2f7Stbbdev class StartupBlock; 258*51c0b2f7Stbbdev 259*51c0b2f7Stbbdev class MemoryPool { 260*51c0b2f7Stbbdev // if no explicit grainsize, expect to see malloc in user's pAlloc 261*51c0b2f7Stbbdev // and set reasonable low granularity 262*51c0b2f7Stbbdev static const size_t defaultGranularity = estimatedCacheLineSize; 263*51c0b2f7Stbbdev 264*51c0b2f7Stbbdev MemoryPool(); // deny 265*51c0b2f7Stbbdev public: 266*51c0b2f7Stbbdev static MallocMutex memPoolListLock; 267*51c0b2f7Stbbdev 268*51c0b2f7Stbbdev // list of all active pools is used to release 269*51c0b2f7Stbbdev // all TLS data on thread termination or library unload 270*51c0b2f7Stbbdev MemoryPool *next, 271*51c0b2f7Stbbdev *prev; 272*51c0b2f7Stbbdev ExtMemoryPool extMemPool; 273*51c0b2f7Stbbdev BootStrapBlocks bootStrapBlocks; 274*51c0b2f7Stbbdev 275*51c0b2f7Stbbdev static void initDefaultPool(); 276*51c0b2f7Stbbdev 277*51c0b2f7Stbbdev bool init(intptr_t poolId, const MemPoolPolicy* memPoolPolicy); 278*51c0b2f7Stbbdev bool reset(); 279*51c0b2f7Stbbdev bool destroy(); 280*51c0b2f7Stbbdev void onThreadShutdown(TLSData *tlsData); 281*51c0b2f7Stbbdev 282*51c0b2f7Stbbdev inline TLSData *getTLS(bool create); 283*51c0b2f7Stbbdev void clearTLS() { extMemPool.tlsPointerKey.setThreadMallocTLS(NULL); } 284*51c0b2f7Stbbdev 285*51c0b2f7Stbbdev Block *getEmptyBlock(size_t size); 286*51c0b2f7Stbbdev void returnEmptyBlock(Block *block, bool poolTheBlock); 287*51c0b2f7Stbbdev 288*51c0b2f7Stbbdev // get/put large object to/from local large object cache 289*51c0b2f7Stbbdev void *getFromLLOCache(TLSData *tls, size_t size, size_t alignment); 290*51c0b2f7Stbbdev void putToLLOCache(TLSData *tls, void *object); 291*51c0b2f7Stbbdev }; 292*51c0b2f7Stbbdev 293*51c0b2f7Stbbdev static intptr_t defaultMemPool_space[sizeof(MemoryPool)/sizeof(intptr_t) + 294*51c0b2f7Stbbdev (sizeof(MemoryPool)%sizeof(intptr_t)? 1 : 0)]; 295*51c0b2f7Stbbdev static MemoryPool *defaultMemPool = (MemoryPool*)defaultMemPool_space; 296*51c0b2f7Stbbdev const size_t MemoryPool::defaultGranularity; 297*51c0b2f7Stbbdev // zero-initialized 298*51c0b2f7Stbbdev MallocMutex MemoryPool::memPoolListLock; 299*51c0b2f7Stbbdev // TODO: move huge page status to default pool, because that's its states 300*51c0b2f7Stbbdev HugePagesStatus hugePages; 301*51c0b2f7Stbbdev static bool usedBySrcIncluded = false; 302*51c0b2f7Stbbdev 303*51c0b2f7Stbbdev // Padding helpers 304*51c0b2f7Stbbdev template<size_t padd> 305*51c0b2f7Stbbdev struct PaddingImpl { 306*51c0b2f7Stbbdev size_t __padding[padd]; 307*51c0b2f7Stbbdev }; 308*51c0b2f7Stbbdev 309*51c0b2f7Stbbdev template<> 310*51c0b2f7Stbbdev struct PaddingImpl<0> {}; 311*51c0b2f7Stbbdev 312*51c0b2f7Stbbdev template<int N> 313*51c0b2f7Stbbdev struct Padding : PaddingImpl<N/sizeof(size_t)> {}; 314*51c0b2f7Stbbdev 315*51c0b2f7Stbbdev // Slab block is 16KB-aligned. To prevent false sharing, separate locally-accessed 316*51c0b2f7Stbbdev // fields and fields commonly accessed by not owner threads. 317*51c0b2f7Stbbdev class GlobalBlockFields : public BlockI { 318*51c0b2f7Stbbdev protected: 319*51c0b2f7Stbbdev std::atomic<FreeObject*> publicFreeList; 320*51c0b2f7Stbbdev Block *nextPrivatizable; 321*51c0b2f7Stbbdev MemoryPool *poolPtr; 322*51c0b2f7Stbbdev }; 323*51c0b2f7Stbbdev 324*51c0b2f7Stbbdev class LocalBlockFields : public GlobalBlockFields, Padding<blockHeaderAlignment - sizeof(GlobalBlockFields)> { 325*51c0b2f7Stbbdev protected: 326*51c0b2f7Stbbdev Block *next; 327*51c0b2f7Stbbdev Block *previous; /* Use double linked list to speed up removal */ 328*51c0b2f7Stbbdev FreeObject *bumpPtr; /* Bump pointer moves from the end to the beginning of a block */ 329*51c0b2f7Stbbdev FreeObject *freeList; 330*51c0b2f7Stbbdev /* Pointer to local data for the owner thread. Used for fast finding tls 331*51c0b2f7Stbbdev when releasing object from a block that current thread owned. 332*51c0b2f7Stbbdev NULL for orphaned blocks. */ 333*51c0b2f7Stbbdev TLSData *tlsPtr; 334*51c0b2f7Stbbdev ThreadId ownerTid; /* the ID of the thread that owns or last owned the block */ 335*51c0b2f7Stbbdev BackRefIdx backRefIdx; 336*51c0b2f7Stbbdev uint16_t allocatedCount; /* Number of objects allocated (obviously by the owning thread) */ 337*51c0b2f7Stbbdev uint16_t objectSize; 338*51c0b2f7Stbbdev bool isFull; 339*51c0b2f7Stbbdev 340*51c0b2f7Stbbdev friend class FreeBlockPool; 341*51c0b2f7Stbbdev friend class StartupBlock; 342*51c0b2f7Stbbdev friend class LifoList; 343*51c0b2f7Stbbdev friend void *BootStrapBlocks::allocate(MemoryPool *, size_t); 344*51c0b2f7Stbbdev friend bool OrphanedBlocks::cleanup(Backend*); 345*51c0b2f7Stbbdev friend Block *MemoryPool::getEmptyBlock(size_t); 346*51c0b2f7Stbbdev }; 347*51c0b2f7Stbbdev 348*51c0b2f7Stbbdev // Use inheritance to guarantee that a user data start on next cache line. 349*51c0b2f7Stbbdev // Can't use member for it, because when LocalBlockFields already on cache line, 350*51c0b2f7Stbbdev // we must have no additional memory consumption for all compilers. 351*51c0b2f7Stbbdev class Block : public LocalBlockFields, 352*51c0b2f7Stbbdev Padding<2*blockHeaderAlignment - sizeof(LocalBlockFields)> { 353*51c0b2f7Stbbdev public: 354*51c0b2f7Stbbdev bool empty() const { 355*51c0b2f7Stbbdev if (allocatedCount > 0) return false; 356*51c0b2f7Stbbdev MALLOC_ASSERT(!isSolidPtr(publicFreeList.load(std::memory_order_relaxed)), ASSERT_TEXT); 357*51c0b2f7Stbbdev return true; 358*51c0b2f7Stbbdev } 359*51c0b2f7Stbbdev inline FreeObject* allocate(); 360*51c0b2f7Stbbdev inline FreeObject *allocateFromFreeList(); 361*51c0b2f7Stbbdev 362*51c0b2f7Stbbdev inline bool adjustFullness(); 363*51c0b2f7Stbbdev void adjustPositionInBin(Bin* bin = NULL); 364*51c0b2f7Stbbdev 365*51c0b2f7Stbbdev bool freeListNonNull() { return freeList; } 366*51c0b2f7Stbbdev void freePublicObject(FreeObject *objectToFree); 367*51c0b2f7Stbbdev inline void freeOwnObject(void *object); 368*51c0b2f7Stbbdev void reset(); 369*51c0b2f7Stbbdev void privatizePublicFreeList( bool reset = true ); 370*51c0b2f7Stbbdev void restoreBumpPtr(); 371*51c0b2f7Stbbdev void privatizeOrphaned(TLSData *tls, unsigned index); 372*51c0b2f7Stbbdev bool readyToShare(); 373*51c0b2f7Stbbdev void shareOrphaned(intptr_t binTag, unsigned index); 374*51c0b2f7Stbbdev unsigned int getSize() const { 375*51c0b2f7Stbbdev MALLOC_ASSERT(isStartupAllocObject() || objectSize<minLargeObjectSize, 376*51c0b2f7Stbbdev "Invalid object size"); 377*51c0b2f7Stbbdev return isStartupAllocObject()? 0 : objectSize; 378*51c0b2f7Stbbdev } 379*51c0b2f7Stbbdev const BackRefIdx *getBackRefIdx() const { return &backRefIdx; } 380*51c0b2f7Stbbdev inline bool isOwnedByCurrentThread() const; 381*51c0b2f7Stbbdev bool isStartupAllocObject() const { return objectSize == startupAllocObjSizeMark; } 382*51c0b2f7Stbbdev inline FreeObject *findObjectToFree(const void *object) const; 383*51c0b2f7Stbbdev void checkFreePrecond(const void *object) const { 384*51c0b2f7Stbbdev #if MALLOC_DEBUG 385*51c0b2f7Stbbdev const char *msg = "Possible double free or heap corruption."; 386*51c0b2f7Stbbdev // small objects are always at least sizeof(size_t) Byte aligned, 387*51c0b2f7Stbbdev // try to check this before this dereference as for invalid objects 388*51c0b2f7Stbbdev // this may be unreadable 389*51c0b2f7Stbbdev MALLOC_ASSERT(isAligned(object, sizeof(size_t)), "Try to free invalid small object"); 390*51c0b2f7Stbbdev // releasing to free slab 391*51c0b2f7Stbbdev MALLOC_ASSERT(allocatedCount>0, msg); 392*51c0b2f7Stbbdev // must not point to slab's header 393*51c0b2f7Stbbdev MALLOC_ASSERT((uintptr_t)object - (uintptr_t)this >= sizeof(Block), msg); 394*51c0b2f7Stbbdev if (startupAllocObjSizeMark == objectSize) // startup block 395*51c0b2f7Stbbdev MALLOC_ASSERT(object<=bumpPtr, msg); 396*51c0b2f7Stbbdev else { 397*51c0b2f7Stbbdev // non-startup objects are 8 Byte aligned 398*51c0b2f7Stbbdev MALLOC_ASSERT(isAligned(object, 8), "Try to free invalid small object"); 399*51c0b2f7Stbbdev MALLOC_ASSERT(allocatedCount <= (slabSize-sizeof(Block))/objectSize 400*51c0b2f7Stbbdev && (!bumpPtr || object>bumpPtr), msg); 401*51c0b2f7Stbbdev FreeObject *toFree = findObjectToFree(object); 402*51c0b2f7Stbbdev // check against head of freeList, as this is mostly 403*51c0b2f7Stbbdev // expected after double free 404*51c0b2f7Stbbdev MALLOC_ASSERT(toFree != freeList, msg); 405*51c0b2f7Stbbdev // check against head of publicFreeList, to detect double free 406*51c0b2f7Stbbdev // involving foreign thread 407*51c0b2f7Stbbdev MALLOC_ASSERT(toFree != publicFreeList.load(std::memory_order_relaxed), msg); 408*51c0b2f7Stbbdev } 409*51c0b2f7Stbbdev #else 410*51c0b2f7Stbbdev suppress_unused_warning(object); 411*51c0b2f7Stbbdev #endif 412*51c0b2f7Stbbdev } 413*51c0b2f7Stbbdev void initEmptyBlock(TLSData *tls, size_t size); 414*51c0b2f7Stbbdev size_t findObjectSize(void *object) const; 415*51c0b2f7Stbbdev MemoryPool *getMemPool() const { return poolPtr; } // do not use on the hot path! 416*51c0b2f7Stbbdev 417*51c0b2f7Stbbdev protected: 418*51c0b2f7Stbbdev void cleanBlockHeader(); 419*51c0b2f7Stbbdev 420*51c0b2f7Stbbdev private: 421*51c0b2f7Stbbdev static const float emptyEnoughRatio; /* Threshold on free space needed to "reactivate" a block */ 422*51c0b2f7Stbbdev 423*51c0b2f7Stbbdev inline FreeObject *allocateFromBumpPtr(); 424*51c0b2f7Stbbdev inline FreeObject *findAllocatedObject(const void *address) const; 425*51c0b2f7Stbbdev inline bool isProperlyPlaced(const void *object) const; 426*51c0b2f7Stbbdev inline void markOwned(TLSData *tls) { 427*51c0b2f7Stbbdev MALLOC_ASSERT(!tlsPtr, ASSERT_TEXT); 428*51c0b2f7Stbbdev ownerTid = ThreadId(); /* save the ID of the current thread */ 429*51c0b2f7Stbbdev tlsPtr = tls; 430*51c0b2f7Stbbdev } 431*51c0b2f7Stbbdev inline void markOrphaned() { 432*51c0b2f7Stbbdev MALLOC_ASSERT(tlsPtr, ASSERT_TEXT); 433*51c0b2f7Stbbdev tlsPtr = NULL; 434*51c0b2f7Stbbdev } 435*51c0b2f7Stbbdev 436*51c0b2f7Stbbdev friend class Bin; 437*51c0b2f7Stbbdev friend class TLSData; 438*51c0b2f7Stbbdev friend bool MemoryPool::destroy(); 439*51c0b2f7Stbbdev }; 440*51c0b2f7Stbbdev 441*51c0b2f7Stbbdev const float Block::emptyEnoughRatio = 1.0 / 4.0; 442*51c0b2f7Stbbdev 443*51c0b2f7Stbbdev static_assert(sizeof(Block) <= 2*estimatedCacheLineSize, 444*51c0b2f7Stbbdev "The class Block does not fit into 2 cache lines on this platform. " 445*51c0b2f7Stbbdev "Defining USE_INTERNAL_TID may help to fix it."); 446*51c0b2f7Stbbdev 447*51c0b2f7Stbbdev class Bin { 448*51c0b2f7Stbbdev private: 449*51c0b2f7Stbbdev Block *activeBlk; 450*51c0b2f7Stbbdev std::atomic<Block*> mailbox; 451*51c0b2f7Stbbdev MallocMutex mailLock; 452*51c0b2f7Stbbdev 453*51c0b2f7Stbbdev public: 454*51c0b2f7Stbbdev inline Block* getActiveBlock() const { return activeBlk; } 455*51c0b2f7Stbbdev void resetActiveBlock() { activeBlk = NULL; } 456*51c0b2f7Stbbdev bool activeBlockUnused() const { return activeBlk && !activeBlk->allocatedCount; } 457*51c0b2f7Stbbdev inline void setActiveBlock(Block *block); 458*51c0b2f7Stbbdev inline Block* setPreviousBlockActive(); 459*51c0b2f7Stbbdev Block* getPrivatizedFreeListBlock(); 460*51c0b2f7Stbbdev void moveBlockToFront(Block *block); 461*51c0b2f7Stbbdev bool cleanPublicFreeLists(); 462*51c0b2f7Stbbdev void processEmptyBlock(Block *block, bool poolTheBlock); 463*51c0b2f7Stbbdev void addPublicFreeListBlock(Block* block); 464*51c0b2f7Stbbdev 465*51c0b2f7Stbbdev void outofTLSBin(Block* block); 466*51c0b2f7Stbbdev void verifyTLSBin(size_t size) const; 467*51c0b2f7Stbbdev void pushTLSBin(Block* block); 468*51c0b2f7Stbbdev 469*51c0b2f7Stbbdev void verifyInitState() const { 470*51c0b2f7Stbbdev MALLOC_ASSERT( !activeBlk, ASSERT_TEXT ); 471*51c0b2f7Stbbdev MALLOC_ASSERT( !mailbox.load(std::memory_order_relaxed), ASSERT_TEXT ); 472*51c0b2f7Stbbdev } 473*51c0b2f7Stbbdev 474*51c0b2f7Stbbdev friend void Block::freePublicObject (FreeObject *objectToFree); 475*51c0b2f7Stbbdev }; 476*51c0b2f7Stbbdev 477*51c0b2f7Stbbdev /********* End of the data structures **************/ 478*51c0b2f7Stbbdev 479*51c0b2f7Stbbdev /* 480*51c0b2f7Stbbdev * There are bins for all 8 byte aligned objects less than this segregated size; 8 bins in total 481*51c0b2f7Stbbdev */ 482*51c0b2f7Stbbdev const uint32_t minSmallObjectIndex = 0; 483*51c0b2f7Stbbdev const uint32_t numSmallObjectBins = 8; 484*51c0b2f7Stbbdev const uint32_t maxSmallObjectSize = 64; 485*51c0b2f7Stbbdev 486*51c0b2f7Stbbdev /* 487*51c0b2f7Stbbdev * There are 4 bins between each couple of powers of 2 [64-128-256-...] 488*51c0b2f7Stbbdev * from maxSmallObjectSize till this size; 16 bins in total 489*51c0b2f7Stbbdev */ 490*51c0b2f7Stbbdev const uint32_t minSegregatedObjectIndex = minSmallObjectIndex+numSmallObjectBins; 491*51c0b2f7Stbbdev const uint32_t numSegregatedObjectBins = 16; 492*51c0b2f7Stbbdev const uint32_t maxSegregatedObjectSize = 1024; 493*51c0b2f7Stbbdev 494*51c0b2f7Stbbdev /* 495*51c0b2f7Stbbdev * And there are 5 bins with allocation sizes that are multiples of estimatedCacheLineSize 496*51c0b2f7Stbbdev * and selected to fit 9, 6, 4, 3, and 2 allocations in a block. 497*51c0b2f7Stbbdev */ 498*51c0b2f7Stbbdev const uint32_t minFittingIndex = minSegregatedObjectIndex+numSegregatedObjectBins; 499*51c0b2f7Stbbdev const uint32_t numFittingBins = 5; 500*51c0b2f7Stbbdev 501*51c0b2f7Stbbdev const uint32_t fittingAlignment = estimatedCacheLineSize; 502*51c0b2f7Stbbdev 503*51c0b2f7Stbbdev #define SET_FITTING_SIZE(N) ( (slabSize-sizeof(Block))/N ) & ~(fittingAlignment-1) 504*51c0b2f7Stbbdev // For blockSize=16*1024, sizeof(Block)=2*estimatedCacheLineSize and fittingAlignment=estimatedCacheLineSize, 505*51c0b2f7Stbbdev // the comments show the fitting sizes and the amounts left unused for estimatedCacheLineSize=64/128: 506*51c0b2f7Stbbdev const uint32_t fittingSize1 = SET_FITTING_SIZE(9); // 1792/1792 128/000 507*51c0b2f7Stbbdev const uint32_t fittingSize2 = SET_FITTING_SIZE(6); // 2688/2688 128/000 508*51c0b2f7Stbbdev const uint32_t fittingSize3 = SET_FITTING_SIZE(4); // 4032/3968 128/256 509*51c0b2f7Stbbdev const uint32_t fittingSize4 = SET_FITTING_SIZE(3); // 5376/5376 128/000 510*51c0b2f7Stbbdev const uint32_t fittingSize5 = SET_FITTING_SIZE(2); // 8128/8064 000/000 511*51c0b2f7Stbbdev #undef SET_FITTING_SIZE 512*51c0b2f7Stbbdev 513*51c0b2f7Stbbdev /* 514*51c0b2f7Stbbdev * The total number of thread-specific Block-based bins 515*51c0b2f7Stbbdev */ 516*51c0b2f7Stbbdev const uint32_t numBlockBins = minFittingIndex+numFittingBins; 517*51c0b2f7Stbbdev 518*51c0b2f7Stbbdev /* 519*51c0b2f7Stbbdev * Objects of this size and larger are considered large objects. 520*51c0b2f7Stbbdev */ 521*51c0b2f7Stbbdev const uint32_t minLargeObjectSize = fittingSize5 + 1; 522*51c0b2f7Stbbdev 523*51c0b2f7Stbbdev /* 524*51c0b2f7Stbbdev * Per-thread pool of slab blocks. Idea behind it is to not share with other 525*51c0b2f7Stbbdev * threads memory that are likely in local cache(s) of our CPU. 526*51c0b2f7Stbbdev */ 527*51c0b2f7Stbbdev class FreeBlockPool { 528*51c0b2f7Stbbdev private: 529*51c0b2f7Stbbdev std::atomic<Block*> head; 530*51c0b2f7Stbbdev int size; 531*51c0b2f7Stbbdev Backend *backend; 532*51c0b2f7Stbbdev bool lastAccessMiss; 533*51c0b2f7Stbbdev public: 534*51c0b2f7Stbbdev static const int POOL_HIGH_MARK = 32; 535*51c0b2f7Stbbdev static const int POOL_LOW_MARK = 8; 536*51c0b2f7Stbbdev 537*51c0b2f7Stbbdev class ResOfGet { 538*51c0b2f7Stbbdev ResOfGet(); 539*51c0b2f7Stbbdev public: 540*51c0b2f7Stbbdev Block* block; 541*51c0b2f7Stbbdev bool lastAccMiss; 542*51c0b2f7Stbbdev ResOfGet(Block *b, bool lastMiss) : block(b), lastAccMiss(lastMiss) {} 543*51c0b2f7Stbbdev }; 544*51c0b2f7Stbbdev 545*51c0b2f7Stbbdev // allocated in zero-initialized memory 546*51c0b2f7Stbbdev FreeBlockPool(Backend *bknd) : backend(bknd) {} 547*51c0b2f7Stbbdev ResOfGet getBlock(); 548*51c0b2f7Stbbdev void returnBlock(Block *block); 549*51c0b2f7Stbbdev bool externalCleanup(); // can be called by another thread 550*51c0b2f7Stbbdev }; 551*51c0b2f7Stbbdev 552*51c0b2f7Stbbdev template<int LOW_MARK, int HIGH_MARK> 553*51c0b2f7Stbbdev class LocalLOCImpl { 554*51c0b2f7Stbbdev private: 555*51c0b2f7Stbbdev static const size_t MAX_TOTAL_SIZE = 4*1024*1024; 556*51c0b2f7Stbbdev // TODO: can single-linked list be faster here? 557*51c0b2f7Stbbdev LargeMemoryBlock *tail; // need it when do releasing on overflow 558*51c0b2f7Stbbdev std::atomic<LargeMemoryBlock*> head; 559*51c0b2f7Stbbdev size_t totalSize; 560*51c0b2f7Stbbdev int numOfBlocks; 561*51c0b2f7Stbbdev public: 562*51c0b2f7Stbbdev bool put(LargeMemoryBlock *object, ExtMemoryPool *extMemPool); 563*51c0b2f7Stbbdev LargeMemoryBlock *get(size_t size); 564*51c0b2f7Stbbdev bool externalCleanup(ExtMemoryPool *extMemPool); 565*51c0b2f7Stbbdev #if __TBB_MALLOC_WHITEBOX_TEST 566*51c0b2f7Stbbdev LocalLOCImpl() : head(NULL), tail(NULL), totalSize(0), numOfBlocks(0) {} 567*51c0b2f7Stbbdev static size_t getMaxSize() { return MAX_TOTAL_SIZE; } 568*51c0b2f7Stbbdev static const int LOC_HIGH_MARK = HIGH_MARK; 569*51c0b2f7Stbbdev #else 570*51c0b2f7Stbbdev // no ctor, object must be created in zero-initialized memory 571*51c0b2f7Stbbdev #endif 572*51c0b2f7Stbbdev }; 573*51c0b2f7Stbbdev 574*51c0b2f7Stbbdev typedef LocalLOCImpl<8,32> LocalLOC; // set production code parameters 575*51c0b2f7Stbbdev 576*51c0b2f7Stbbdev class TLSData : public TLSRemote { 577*51c0b2f7Stbbdev MemoryPool *memPool; 578*51c0b2f7Stbbdev public: 579*51c0b2f7Stbbdev Bin bin[numBlockBinLimit]; 580*51c0b2f7Stbbdev FreeBlockPool freeSlabBlocks; 581*51c0b2f7Stbbdev LocalLOC lloc; 582*51c0b2f7Stbbdev unsigned currCacheIdx; 583*51c0b2f7Stbbdev private: 584*51c0b2f7Stbbdev bool unused; 585*51c0b2f7Stbbdev public: 586*51c0b2f7Stbbdev TLSData(MemoryPool *mPool, Backend *bknd) : memPool(mPool), freeSlabBlocks(bknd) {} 587*51c0b2f7Stbbdev MemoryPool *getMemPool() const { return memPool; } 588*51c0b2f7Stbbdev Bin* getAllocationBin(size_t size); 589*51c0b2f7Stbbdev void release(); 590*51c0b2f7Stbbdev bool externalCleanup(bool cleanOnlyUnused, bool cleanBins) { 591*51c0b2f7Stbbdev if (!unused && cleanOnlyUnused) return false; 592*51c0b2f7Stbbdev // Heavy operation in terms of synchronization complexity, 593*51c0b2f7Stbbdev // should be called only for the current thread 594*51c0b2f7Stbbdev bool released = cleanBins ? cleanupBlockBins() : false; 595*51c0b2f7Stbbdev // both cleanups to be called, and the order is not important 596*51c0b2f7Stbbdev return released | lloc.externalCleanup(&memPool->extMemPool) | freeSlabBlocks.externalCleanup(); 597*51c0b2f7Stbbdev } 598*51c0b2f7Stbbdev bool cleanupBlockBins(); 599*51c0b2f7Stbbdev void markUsed() { unused = false; } // called by owner when TLS touched 600*51c0b2f7Stbbdev void markUnused() { unused = true; } // can be called by not owner thread 601*51c0b2f7Stbbdev }; 602*51c0b2f7Stbbdev 603*51c0b2f7Stbbdev TLSData *TLSKey::createTLS(MemoryPool *memPool, Backend *backend) 604*51c0b2f7Stbbdev { 605*51c0b2f7Stbbdev MALLOC_ASSERT( sizeof(TLSData) >= sizeof(Bin) * numBlockBins + sizeof(FreeBlockPool), ASSERT_TEXT ); 606*51c0b2f7Stbbdev TLSData* tls = (TLSData*) memPool->bootStrapBlocks.allocate(memPool, sizeof(TLSData)); 607*51c0b2f7Stbbdev if ( !tls ) 608*51c0b2f7Stbbdev return NULL; 609*51c0b2f7Stbbdev new(tls) TLSData(memPool, backend); 610*51c0b2f7Stbbdev /* the block contains zeroes after bootStrapMalloc, so bins are initialized */ 611*51c0b2f7Stbbdev #if MALLOC_DEBUG 612*51c0b2f7Stbbdev for (uint32_t i = 0; i < numBlockBinLimit; i++) 613*51c0b2f7Stbbdev tls->bin[i].verifyInitState(); 614*51c0b2f7Stbbdev #endif 615*51c0b2f7Stbbdev setThreadMallocTLS(tls); 616*51c0b2f7Stbbdev memPool->extMemPool.allLocalCaches.registerThread(tls); 617*51c0b2f7Stbbdev return tls; 618*51c0b2f7Stbbdev } 619*51c0b2f7Stbbdev 620*51c0b2f7Stbbdev bool TLSData::cleanupBlockBins() 621*51c0b2f7Stbbdev { 622*51c0b2f7Stbbdev bool released = false; 623*51c0b2f7Stbbdev for (uint32_t i = 0; i < numBlockBinLimit; i++) { 624*51c0b2f7Stbbdev released |= bin[i].cleanPublicFreeLists(); 625*51c0b2f7Stbbdev // After cleaning public free lists, only the active block might be empty. 626*51c0b2f7Stbbdev // Do not use processEmptyBlock because it will just restore bumpPtr. 627*51c0b2f7Stbbdev Block *block = bin[i].getActiveBlock(); 628*51c0b2f7Stbbdev if (block && block->empty()) { 629*51c0b2f7Stbbdev bin[i].outofTLSBin(block); 630*51c0b2f7Stbbdev memPool->returnEmptyBlock(block, /*poolTheBlock=*/false); 631*51c0b2f7Stbbdev released = true; 632*51c0b2f7Stbbdev } 633*51c0b2f7Stbbdev } 634*51c0b2f7Stbbdev return released; 635*51c0b2f7Stbbdev } 636*51c0b2f7Stbbdev 637*51c0b2f7Stbbdev bool ExtMemoryPool::releaseAllLocalCaches() 638*51c0b2f7Stbbdev { 639*51c0b2f7Stbbdev // Iterate all registered TLS data and clean LLOC and Slab pools 640*51c0b2f7Stbbdev bool released = allLocalCaches.cleanup(/*cleanOnlyUnused=*/false); 641*51c0b2f7Stbbdev 642*51c0b2f7Stbbdev // Bins privatization is done only for the current thread 643*51c0b2f7Stbbdev if (TLSData *tlsData = tlsPointerKey.getThreadMallocTLS()) 644*51c0b2f7Stbbdev released |= tlsData->cleanupBlockBins(); 645*51c0b2f7Stbbdev 646*51c0b2f7Stbbdev return released; 647*51c0b2f7Stbbdev } 648*51c0b2f7Stbbdev 649*51c0b2f7Stbbdev void AllLocalCaches::registerThread(TLSRemote *tls) 650*51c0b2f7Stbbdev { 651*51c0b2f7Stbbdev tls->prev = NULL; 652*51c0b2f7Stbbdev MallocMutex::scoped_lock lock(listLock); 653*51c0b2f7Stbbdev MALLOC_ASSERT(head!=tls, ASSERT_TEXT); 654*51c0b2f7Stbbdev tls->next = head; 655*51c0b2f7Stbbdev if (head) 656*51c0b2f7Stbbdev head->prev = tls; 657*51c0b2f7Stbbdev head = tls; 658*51c0b2f7Stbbdev MALLOC_ASSERT(head->next!=head, ASSERT_TEXT); 659*51c0b2f7Stbbdev } 660*51c0b2f7Stbbdev 661*51c0b2f7Stbbdev void AllLocalCaches::unregisterThread(TLSRemote *tls) 662*51c0b2f7Stbbdev { 663*51c0b2f7Stbbdev MallocMutex::scoped_lock lock(listLock); 664*51c0b2f7Stbbdev MALLOC_ASSERT(head, "Can't unregister thread: no threads are registered."); 665*51c0b2f7Stbbdev if (head == tls) 666*51c0b2f7Stbbdev head = tls->next; 667*51c0b2f7Stbbdev if (tls->next) 668*51c0b2f7Stbbdev tls->next->prev = tls->prev; 669*51c0b2f7Stbbdev if (tls->prev) 670*51c0b2f7Stbbdev tls->prev->next = tls->next; 671*51c0b2f7Stbbdev MALLOC_ASSERT(!tls->next || tls->next->next!=tls->next, ASSERT_TEXT); 672*51c0b2f7Stbbdev } 673*51c0b2f7Stbbdev 674*51c0b2f7Stbbdev bool AllLocalCaches::cleanup(bool cleanOnlyUnused) 675*51c0b2f7Stbbdev { 676*51c0b2f7Stbbdev bool released = false; 677*51c0b2f7Stbbdev { 678*51c0b2f7Stbbdev MallocMutex::scoped_lock lock(listLock); 679*51c0b2f7Stbbdev for (TLSRemote *curr=head; curr; curr=curr->next) 680*51c0b2f7Stbbdev released |= static_cast<TLSData*>(curr)->externalCleanup(cleanOnlyUnused, /*cleanBins=*/false); 681*51c0b2f7Stbbdev } 682*51c0b2f7Stbbdev return released; 683*51c0b2f7Stbbdev } 684*51c0b2f7Stbbdev 685*51c0b2f7Stbbdev void AllLocalCaches::markUnused() 686*51c0b2f7Stbbdev { 687*51c0b2f7Stbbdev bool locked; 688*51c0b2f7Stbbdev MallocMutex::scoped_lock lock(listLock, /*block=*/false, &locked); 689*51c0b2f7Stbbdev if (!locked) // not wait for marking if someone doing something with it 690*51c0b2f7Stbbdev return; 691*51c0b2f7Stbbdev 692*51c0b2f7Stbbdev for (TLSRemote *curr=head; curr; curr=curr->next) 693*51c0b2f7Stbbdev static_cast<TLSData*>(curr)->markUnused(); 694*51c0b2f7Stbbdev } 695*51c0b2f7Stbbdev 696*51c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION 697*51c0b2f7Stbbdev MallocMutex RecursiveMallocCallProtector::rmc_mutex; 698*51c0b2f7Stbbdev pthread_t RecursiveMallocCallProtector::owner_thread; 699*51c0b2f7Stbbdev void *RecursiveMallocCallProtector::autoObjPtr; 700*51c0b2f7Stbbdev bool RecursiveMallocCallProtector::mallocRecursionDetected; 701*51c0b2f7Stbbdev #if __FreeBSD__ 702*51c0b2f7Stbbdev bool RecursiveMallocCallProtector::canUsePthread; 703*51c0b2f7Stbbdev #endif 704*51c0b2f7Stbbdev 705*51c0b2f7Stbbdev #endif 706*51c0b2f7Stbbdev 707*51c0b2f7Stbbdev /*********** End code to provide thread ID and a TLS pointer **********/ 708*51c0b2f7Stbbdev 709*51c0b2f7Stbbdev // Parameter for isLargeObject, keeps our expectations on memory origin. 710*51c0b2f7Stbbdev // Assertions must use unknownMem to reliably report object invalidity. 711*51c0b2f7Stbbdev enum MemoryOrigin { 712*51c0b2f7Stbbdev ourMem, // allocated by TBB allocator 713*51c0b2f7Stbbdev unknownMem // can be allocated by system allocator or TBB allocator 714*51c0b2f7Stbbdev }; 715*51c0b2f7Stbbdev 716*51c0b2f7Stbbdev template<MemoryOrigin> bool isLargeObject(void *object); 717*51c0b2f7Stbbdev static void *internalMalloc(size_t size); 718*51c0b2f7Stbbdev static void internalFree(void *object); 719*51c0b2f7Stbbdev static void *internalPoolMalloc(MemoryPool* mPool, size_t size); 720*51c0b2f7Stbbdev static bool internalPoolFree(MemoryPool *mPool, void *object, size_t size); 721*51c0b2f7Stbbdev 722*51c0b2f7Stbbdev #if !MALLOC_DEBUG 723*51c0b2f7Stbbdev #if __INTEL_COMPILER || _MSC_VER 724*51c0b2f7Stbbdev #define NOINLINE(decl) __declspec(noinline) decl 725*51c0b2f7Stbbdev #define ALWAYSINLINE(decl) __forceinline decl 726*51c0b2f7Stbbdev #elif __GNUC__ 727*51c0b2f7Stbbdev #define NOINLINE(decl) decl __attribute__ ((noinline)) 728*51c0b2f7Stbbdev #define ALWAYSINLINE(decl) decl __attribute__ ((always_inline)) 729*51c0b2f7Stbbdev #else 730*51c0b2f7Stbbdev #define NOINLINE(decl) decl 731*51c0b2f7Stbbdev #define ALWAYSINLINE(decl) decl 732*51c0b2f7Stbbdev #endif 733*51c0b2f7Stbbdev 734*51c0b2f7Stbbdev static NOINLINE( bool doInitialization() ); 735*51c0b2f7Stbbdev ALWAYSINLINE( bool isMallocInitialized() ); 736*51c0b2f7Stbbdev 737*51c0b2f7Stbbdev #undef ALWAYSINLINE 738*51c0b2f7Stbbdev #undef NOINLINE 739*51c0b2f7Stbbdev #endif /* !MALLOC_DEBUG */ 740*51c0b2f7Stbbdev 741*51c0b2f7Stbbdev 742*51c0b2f7Stbbdev /********* Now some rough utility code to deal with indexing the size bins. **************/ 743*51c0b2f7Stbbdev 744*51c0b2f7Stbbdev /* 745*51c0b2f7Stbbdev * Given a number return the highest non-zero bit in it. It is intended to work with 32-bit values only. 746*51c0b2f7Stbbdev * Moreover, on IPF, for sake of simplicity and performance, it is narrowed to only serve for 64 to 1023. 747*51c0b2f7Stbbdev * This is enough for current algorithm of distribution of sizes among bins. 748*51c0b2f7Stbbdev * __TBB_Log2 is not used here to minimize dependencies on TBB specific sources. 749*51c0b2f7Stbbdev */ 750*51c0b2f7Stbbdev #if _WIN64 && _MSC_VER>=1400 && !__INTEL_COMPILER 751*51c0b2f7Stbbdev extern "C" unsigned char _BitScanReverse( unsigned long* i, unsigned long w ); 752*51c0b2f7Stbbdev #pragma intrinsic(_BitScanReverse) 753*51c0b2f7Stbbdev #endif 754*51c0b2f7Stbbdev static inline unsigned int highestBitPos(unsigned int n) 755*51c0b2f7Stbbdev { 756*51c0b2f7Stbbdev MALLOC_ASSERT( n>=64 && n<1024, ASSERT_TEXT ); // only needed for bsr array lookup, but always true 757*51c0b2f7Stbbdev unsigned int pos; 758*51c0b2f7Stbbdev #if __ARCH_x86_32||__ARCH_x86_64 759*51c0b2f7Stbbdev 760*51c0b2f7Stbbdev # if __linux__||__APPLE__||__FreeBSD__||__NetBSD__||__OpenBSD__||__sun||__MINGW32__ 761*51c0b2f7Stbbdev __asm__ ("bsr %1,%0" : "=r"(pos) : "r"(n)); 762*51c0b2f7Stbbdev # elif (_WIN32 && (!_WIN64 || __INTEL_COMPILER)) 763*51c0b2f7Stbbdev __asm 764*51c0b2f7Stbbdev { 765*51c0b2f7Stbbdev bsr eax, n 766*51c0b2f7Stbbdev mov pos, eax 767*51c0b2f7Stbbdev } 768*51c0b2f7Stbbdev # elif _WIN64 && _MSC_VER>=1400 769*51c0b2f7Stbbdev _BitScanReverse((unsigned long*)&pos, (unsigned long)n); 770*51c0b2f7Stbbdev # else 771*51c0b2f7Stbbdev # error highestBitPos() not implemented for this platform 772*51c0b2f7Stbbdev # endif 773*51c0b2f7Stbbdev #elif __arm__ 774*51c0b2f7Stbbdev __asm__ __volatile__ 775*51c0b2f7Stbbdev ( 776*51c0b2f7Stbbdev "clz %0, %1\n" 777*51c0b2f7Stbbdev "rsb %0, %0, %2\n" 778*51c0b2f7Stbbdev :"=r" (pos) :"r" (n), "I" (31) 779*51c0b2f7Stbbdev ); 780*51c0b2f7Stbbdev #else 781*51c0b2f7Stbbdev static unsigned int bsr[16] = {0/*N/A*/,6,7,7,8,8,8,8,9,9,9,9,9,9,9,9}; 782*51c0b2f7Stbbdev pos = bsr[ n>>6 ]; 783*51c0b2f7Stbbdev #endif /* __ARCH_* */ 784*51c0b2f7Stbbdev return pos; 785*51c0b2f7Stbbdev } 786*51c0b2f7Stbbdev 787*51c0b2f7Stbbdev template<bool Is32Bit> 788*51c0b2f7Stbbdev unsigned int getSmallObjectIndex(unsigned int size) 789*51c0b2f7Stbbdev { 790*51c0b2f7Stbbdev return (size-1)>>3; 791*51c0b2f7Stbbdev } 792*51c0b2f7Stbbdev template<> 793*51c0b2f7Stbbdev unsigned int getSmallObjectIndex</*Is32Bit=*/false>(unsigned int size) 794*51c0b2f7Stbbdev { 795*51c0b2f7Stbbdev // For 64-bit malloc, 16 byte alignment is needed except for bin 0. 796*51c0b2f7Stbbdev unsigned int result = (size-1)>>3; 797*51c0b2f7Stbbdev if (result) result |= 1; // 0,1,3,5,7; bins 2,4,6 are not aligned to 16 bytes 798*51c0b2f7Stbbdev return result; 799*51c0b2f7Stbbdev } 800*51c0b2f7Stbbdev /* 801*51c0b2f7Stbbdev * Depending on indexRequest, for a given size return either the index into the bin 802*51c0b2f7Stbbdev * for objects of this size, or the actual size of objects in this bin. 803*51c0b2f7Stbbdev */ 804*51c0b2f7Stbbdev template<bool indexRequest> 805*51c0b2f7Stbbdev static unsigned int getIndexOrObjectSize (unsigned int size) 806*51c0b2f7Stbbdev { 807*51c0b2f7Stbbdev if (size <= maxSmallObjectSize) { // selection from 8/16/24/32/40/48/56/64 808*51c0b2f7Stbbdev unsigned int index = getSmallObjectIndex</*Is32Bit=*/(sizeof(size_t)<=4)>( size ); 809*51c0b2f7Stbbdev /* Bin 0 is for 8 bytes, bin 1 is for 16, and so forth */ 810*51c0b2f7Stbbdev return indexRequest ? index : (index+1)<<3; 811*51c0b2f7Stbbdev } 812*51c0b2f7Stbbdev else if (size <= maxSegregatedObjectSize ) { // 80/96/112/128 / 160/192/224/256 / 320/384/448/512 / 640/768/896/1024 813*51c0b2f7Stbbdev unsigned int order = highestBitPos(size-1); // which group of bin sizes? 814*51c0b2f7Stbbdev MALLOC_ASSERT( 6<=order && order<=9, ASSERT_TEXT ); 815*51c0b2f7Stbbdev if (indexRequest) 816*51c0b2f7Stbbdev return minSegregatedObjectIndex - (4*6) - 4 + (4*order) + ((size-1)>>(order-2)); 817*51c0b2f7Stbbdev else { 818*51c0b2f7Stbbdev unsigned int alignment = 128 >> (9-order); // alignment in the group 819*51c0b2f7Stbbdev MALLOC_ASSERT( alignment==16 || alignment==32 || alignment==64 || alignment==128, ASSERT_TEXT ); 820*51c0b2f7Stbbdev return alignUp(size,alignment); 821*51c0b2f7Stbbdev } 822*51c0b2f7Stbbdev } 823*51c0b2f7Stbbdev else { 824*51c0b2f7Stbbdev if( size <= fittingSize3 ) { 825*51c0b2f7Stbbdev if( size <= fittingSize2 ) { 826*51c0b2f7Stbbdev if( size <= fittingSize1 ) 827*51c0b2f7Stbbdev return indexRequest ? minFittingIndex : fittingSize1; 828*51c0b2f7Stbbdev else 829*51c0b2f7Stbbdev return indexRequest ? minFittingIndex+1 : fittingSize2; 830*51c0b2f7Stbbdev } else 831*51c0b2f7Stbbdev return indexRequest ? minFittingIndex+2 : fittingSize3; 832*51c0b2f7Stbbdev } else { 833*51c0b2f7Stbbdev if( size <= fittingSize5 ) { 834*51c0b2f7Stbbdev if( size <= fittingSize4 ) 835*51c0b2f7Stbbdev return indexRequest ? minFittingIndex+3 : fittingSize4; 836*51c0b2f7Stbbdev else 837*51c0b2f7Stbbdev return indexRequest ? minFittingIndex+4 : fittingSize5; 838*51c0b2f7Stbbdev } else { 839*51c0b2f7Stbbdev MALLOC_ASSERT( 0,ASSERT_TEXT ); // this should not happen 840*51c0b2f7Stbbdev return ~0U; 841*51c0b2f7Stbbdev } 842*51c0b2f7Stbbdev } 843*51c0b2f7Stbbdev } 844*51c0b2f7Stbbdev } 845*51c0b2f7Stbbdev 846*51c0b2f7Stbbdev static unsigned int getIndex (unsigned int size) 847*51c0b2f7Stbbdev { 848*51c0b2f7Stbbdev return getIndexOrObjectSize</*indexRequest=*/true>(size); 849*51c0b2f7Stbbdev } 850*51c0b2f7Stbbdev 851*51c0b2f7Stbbdev static unsigned int getObjectSize (unsigned int size) 852*51c0b2f7Stbbdev { 853*51c0b2f7Stbbdev return getIndexOrObjectSize</*indexRequest=*/false>(size); 854*51c0b2f7Stbbdev } 855*51c0b2f7Stbbdev 856*51c0b2f7Stbbdev 857*51c0b2f7Stbbdev void *BootStrapBlocks::allocate(MemoryPool *memPool, size_t size) 858*51c0b2f7Stbbdev { 859*51c0b2f7Stbbdev FreeObject *result; 860*51c0b2f7Stbbdev 861*51c0b2f7Stbbdev MALLOC_ASSERT( size == sizeof(TLSData), ASSERT_TEXT ); 862*51c0b2f7Stbbdev 863*51c0b2f7Stbbdev { // Lock with acquire 864*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(bootStrapLock); 865*51c0b2f7Stbbdev 866*51c0b2f7Stbbdev if( bootStrapObjectList) { 867*51c0b2f7Stbbdev result = bootStrapObjectList; 868*51c0b2f7Stbbdev bootStrapObjectList = bootStrapObjectList->next; 869*51c0b2f7Stbbdev } else { 870*51c0b2f7Stbbdev if (!bootStrapBlock) { 871*51c0b2f7Stbbdev bootStrapBlock = memPool->getEmptyBlock(size); 872*51c0b2f7Stbbdev if (!bootStrapBlock) return NULL; 873*51c0b2f7Stbbdev } 874*51c0b2f7Stbbdev result = bootStrapBlock->bumpPtr; 875*51c0b2f7Stbbdev bootStrapBlock->bumpPtr = (FreeObject *)((uintptr_t)bootStrapBlock->bumpPtr - bootStrapBlock->objectSize); 876*51c0b2f7Stbbdev if ((uintptr_t)bootStrapBlock->bumpPtr < (uintptr_t)bootStrapBlock+sizeof(Block)) { 877*51c0b2f7Stbbdev bootStrapBlock->bumpPtr = NULL; 878*51c0b2f7Stbbdev bootStrapBlock->next = bootStrapBlockUsed; 879*51c0b2f7Stbbdev bootStrapBlockUsed = bootStrapBlock; 880*51c0b2f7Stbbdev bootStrapBlock = NULL; 881*51c0b2f7Stbbdev } 882*51c0b2f7Stbbdev } 883*51c0b2f7Stbbdev } // Unlock with release 884*51c0b2f7Stbbdev 885*51c0b2f7Stbbdev memset (result, 0, size); 886*51c0b2f7Stbbdev return (void*)result; 887*51c0b2f7Stbbdev } 888*51c0b2f7Stbbdev 889*51c0b2f7Stbbdev void BootStrapBlocks::free(void* ptr) 890*51c0b2f7Stbbdev { 891*51c0b2f7Stbbdev MALLOC_ASSERT( ptr, ASSERT_TEXT ); 892*51c0b2f7Stbbdev { // Lock with acquire 893*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(bootStrapLock); 894*51c0b2f7Stbbdev ((FreeObject*)ptr)->next = bootStrapObjectList; 895*51c0b2f7Stbbdev bootStrapObjectList = (FreeObject*)ptr; 896*51c0b2f7Stbbdev } // Unlock with release 897*51c0b2f7Stbbdev } 898*51c0b2f7Stbbdev 899*51c0b2f7Stbbdev void BootStrapBlocks::reset() 900*51c0b2f7Stbbdev { 901*51c0b2f7Stbbdev bootStrapBlock = bootStrapBlockUsed = NULL; 902*51c0b2f7Stbbdev bootStrapObjectList = NULL; 903*51c0b2f7Stbbdev } 904*51c0b2f7Stbbdev 905*51c0b2f7Stbbdev #if !(FREELIST_NONBLOCKING) 906*51c0b2f7Stbbdev static MallocMutex publicFreeListLock; // lock for changes of publicFreeList 907*51c0b2f7Stbbdev #endif 908*51c0b2f7Stbbdev 909*51c0b2f7Stbbdev /********* End rough utility code **************/ 910*51c0b2f7Stbbdev 911*51c0b2f7Stbbdev /* LifoList assumes zero initialization so a vector of it can be created 912*51c0b2f7Stbbdev * by just allocating some space with no call to constructor. 913*51c0b2f7Stbbdev * On Linux, it seems to be necessary to avoid linking with C++ libraries. 914*51c0b2f7Stbbdev * 915*51c0b2f7Stbbdev * By usage convention there is no race on the initialization. */ 916*51c0b2f7Stbbdev LifoList::LifoList( ) : top(NULL) 917*51c0b2f7Stbbdev { 918*51c0b2f7Stbbdev // MallocMutex assumes zero initialization 919*51c0b2f7Stbbdev memset(&lock, 0, sizeof(MallocMutex)); 920*51c0b2f7Stbbdev } 921*51c0b2f7Stbbdev 922*51c0b2f7Stbbdev void LifoList::push(Block *block) 923*51c0b2f7Stbbdev { 924*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(lock); 925*51c0b2f7Stbbdev block->next = top; 926*51c0b2f7Stbbdev top = block; 927*51c0b2f7Stbbdev } 928*51c0b2f7Stbbdev 929*51c0b2f7Stbbdev Block *LifoList::pop() 930*51c0b2f7Stbbdev { 931*51c0b2f7Stbbdev Block *block=NULL; 932*51c0b2f7Stbbdev if (top) { 933*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(lock); 934*51c0b2f7Stbbdev if (top) { 935*51c0b2f7Stbbdev block = top; 936*51c0b2f7Stbbdev top = block->next; 937*51c0b2f7Stbbdev } 938*51c0b2f7Stbbdev } 939*51c0b2f7Stbbdev return block; 940*51c0b2f7Stbbdev } 941*51c0b2f7Stbbdev 942*51c0b2f7Stbbdev Block *LifoList::grab() 943*51c0b2f7Stbbdev { 944*51c0b2f7Stbbdev Block *block = NULL; 945*51c0b2f7Stbbdev if (top) { 946*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(lock); 947*51c0b2f7Stbbdev block = top; 948*51c0b2f7Stbbdev top = NULL; 949*51c0b2f7Stbbdev } 950*51c0b2f7Stbbdev return block; 951*51c0b2f7Stbbdev } 952*51c0b2f7Stbbdev 953*51c0b2f7Stbbdev /********* Thread and block related code *************/ 954*51c0b2f7Stbbdev 955*51c0b2f7Stbbdev template<bool poolDestroy> void AllLargeBlocksList::releaseAll(Backend *backend) { 956*51c0b2f7Stbbdev LargeMemoryBlock *next, *lmb = loHead; 957*51c0b2f7Stbbdev loHead = NULL; 958*51c0b2f7Stbbdev 959*51c0b2f7Stbbdev for (; lmb; lmb = next) { 960*51c0b2f7Stbbdev next = lmb->gNext; 961*51c0b2f7Stbbdev if (poolDestroy) { 962*51c0b2f7Stbbdev // as it's pool destruction, no need to return object to backend, 963*51c0b2f7Stbbdev // only remove backrefs, as they are global 964*51c0b2f7Stbbdev removeBackRef(lmb->backRefIdx); 965*51c0b2f7Stbbdev } else { 966*51c0b2f7Stbbdev // clean g(Next|Prev) to prevent removing lmb 967*51c0b2f7Stbbdev // from AllLargeBlocksList inside returnLargeObject 968*51c0b2f7Stbbdev lmb->gNext = lmb->gPrev = NULL; 969*51c0b2f7Stbbdev backend->returnLargeObject(lmb); 970*51c0b2f7Stbbdev } 971*51c0b2f7Stbbdev } 972*51c0b2f7Stbbdev } 973*51c0b2f7Stbbdev 974*51c0b2f7Stbbdev TLSData* MemoryPool::getTLS(bool create) 975*51c0b2f7Stbbdev { 976*51c0b2f7Stbbdev TLSData* tls = extMemPool.tlsPointerKey.getThreadMallocTLS(); 977*51c0b2f7Stbbdev if (create && !tls) 978*51c0b2f7Stbbdev tls = extMemPool.tlsPointerKey.createTLS(this, &extMemPool.backend); 979*51c0b2f7Stbbdev return tls; 980*51c0b2f7Stbbdev } 981*51c0b2f7Stbbdev 982*51c0b2f7Stbbdev /* 983*51c0b2f7Stbbdev * Return the bin for the given size. 984*51c0b2f7Stbbdev */ 985*51c0b2f7Stbbdev inline Bin* TLSData::getAllocationBin(size_t size) 986*51c0b2f7Stbbdev { 987*51c0b2f7Stbbdev return bin + getIndex(size); 988*51c0b2f7Stbbdev } 989*51c0b2f7Stbbdev 990*51c0b2f7Stbbdev /* Return an empty uninitialized block in a non-blocking fashion. */ 991*51c0b2f7Stbbdev Block *MemoryPool::getEmptyBlock(size_t size) 992*51c0b2f7Stbbdev { 993*51c0b2f7Stbbdev TLSData* tls = getTLS(/*create=*/false); 994*51c0b2f7Stbbdev // try to use per-thread cache, if TLS available 995*51c0b2f7Stbbdev FreeBlockPool::ResOfGet resOfGet = tls? 996*51c0b2f7Stbbdev tls->freeSlabBlocks.getBlock() : FreeBlockPool::ResOfGet(NULL, false); 997*51c0b2f7Stbbdev Block *result = resOfGet.block; 998*51c0b2f7Stbbdev 999*51c0b2f7Stbbdev if (!result) { // not found in local cache, asks backend for slabs 1000*51c0b2f7Stbbdev int num = resOfGet.lastAccMiss? Backend::numOfSlabAllocOnMiss : 1; 1001*51c0b2f7Stbbdev BackRefIdx backRefIdx[Backend::numOfSlabAllocOnMiss]; 1002*51c0b2f7Stbbdev 1003*51c0b2f7Stbbdev result = static_cast<Block*>(extMemPool.backend.getSlabBlock(num)); 1004*51c0b2f7Stbbdev if (!result) return NULL; 1005*51c0b2f7Stbbdev 1006*51c0b2f7Stbbdev if (!extMemPool.userPool()) 1007*51c0b2f7Stbbdev for (int i=0; i<num; i++) { 1008*51c0b2f7Stbbdev backRefIdx[i] = BackRefIdx::newBackRef(/*largeObj=*/false); 1009*51c0b2f7Stbbdev if (backRefIdx[i].isInvalid()) { 1010*51c0b2f7Stbbdev // roll back resource allocation 1011*51c0b2f7Stbbdev for (int j=0; j<i; j++) 1012*51c0b2f7Stbbdev removeBackRef(backRefIdx[j]); 1013*51c0b2f7Stbbdev Block *b = result; 1014*51c0b2f7Stbbdev for (int j=0; j<num; b=(Block*)((uintptr_t)b+slabSize), j++) 1015*51c0b2f7Stbbdev extMemPool.backend.putSlabBlock(b); 1016*51c0b2f7Stbbdev return NULL; 1017*51c0b2f7Stbbdev } 1018*51c0b2f7Stbbdev } 1019*51c0b2f7Stbbdev // resources were allocated, register blocks 1020*51c0b2f7Stbbdev Block *b = result; 1021*51c0b2f7Stbbdev for (int i=0; i<num; b=(Block*)((uintptr_t)b+slabSize), i++) { 1022*51c0b2f7Stbbdev // slab block in user's pool must have invalid backRefIdx 1023*51c0b2f7Stbbdev if (extMemPool.userPool()) { 1024*51c0b2f7Stbbdev new (&b->backRefIdx) BackRefIdx(); 1025*51c0b2f7Stbbdev } else { 1026*51c0b2f7Stbbdev setBackRef(backRefIdx[i], b); 1027*51c0b2f7Stbbdev b->backRefIdx = backRefIdx[i]; 1028*51c0b2f7Stbbdev } 1029*51c0b2f7Stbbdev b->tlsPtr = tls; 1030*51c0b2f7Stbbdev b->poolPtr = this; 1031*51c0b2f7Stbbdev // all but first one go to per-thread pool 1032*51c0b2f7Stbbdev if (i > 0) { 1033*51c0b2f7Stbbdev MALLOC_ASSERT(tls, ASSERT_TEXT); 1034*51c0b2f7Stbbdev tls->freeSlabBlocks.returnBlock(b); 1035*51c0b2f7Stbbdev } 1036*51c0b2f7Stbbdev } 1037*51c0b2f7Stbbdev } 1038*51c0b2f7Stbbdev MALLOC_ASSERT(result, ASSERT_TEXT); 1039*51c0b2f7Stbbdev result->initEmptyBlock(tls, size); 1040*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(result->objectSize), allocBlockNew); 1041*51c0b2f7Stbbdev return result; 1042*51c0b2f7Stbbdev } 1043*51c0b2f7Stbbdev 1044*51c0b2f7Stbbdev void MemoryPool::returnEmptyBlock(Block *block, bool poolTheBlock) 1045*51c0b2f7Stbbdev { 1046*51c0b2f7Stbbdev block->reset(); 1047*51c0b2f7Stbbdev if (poolTheBlock) { 1048*51c0b2f7Stbbdev getTLS(/*create=*/false)->freeSlabBlocks.returnBlock(block); 1049*51c0b2f7Stbbdev } else { 1050*51c0b2f7Stbbdev // slab blocks in user's pools do not have valid backRefIdx 1051*51c0b2f7Stbbdev if (!extMemPool.userPool()) 1052*51c0b2f7Stbbdev removeBackRef(*(block->getBackRefIdx())); 1053*51c0b2f7Stbbdev extMemPool.backend.putSlabBlock(block); 1054*51c0b2f7Stbbdev } 1055*51c0b2f7Stbbdev } 1056*51c0b2f7Stbbdev 1057*51c0b2f7Stbbdev bool ExtMemoryPool::init(intptr_t poolId, rawAllocType rawAlloc, 1058*51c0b2f7Stbbdev rawFreeType rawFree, size_t granularity, 1059*51c0b2f7Stbbdev bool keepAllMemory, bool fixedPool) 1060*51c0b2f7Stbbdev { 1061*51c0b2f7Stbbdev this->poolId = poolId; 1062*51c0b2f7Stbbdev this->rawAlloc = rawAlloc; 1063*51c0b2f7Stbbdev this->rawFree = rawFree; 1064*51c0b2f7Stbbdev this->granularity = granularity; 1065*51c0b2f7Stbbdev this->keepAllMemory = keepAllMemory; 1066*51c0b2f7Stbbdev this->fixedPool = fixedPool; 1067*51c0b2f7Stbbdev this->delayRegsReleasing = false; 1068*51c0b2f7Stbbdev if (!initTLS()) 1069*51c0b2f7Stbbdev return false; 1070*51c0b2f7Stbbdev loc.init(this); 1071*51c0b2f7Stbbdev backend.init(this); 1072*51c0b2f7Stbbdev MALLOC_ASSERT(isPoolValid(), NULL); 1073*51c0b2f7Stbbdev return true; 1074*51c0b2f7Stbbdev } 1075*51c0b2f7Stbbdev 1076*51c0b2f7Stbbdev bool ExtMemoryPool::initTLS() { return tlsPointerKey.init(); } 1077*51c0b2f7Stbbdev 1078*51c0b2f7Stbbdev bool MemoryPool::init(intptr_t poolId, const MemPoolPolicy *policy) 1079*51c0b2f7Stbbdev { 1080*51c0b2f7Stbbdev if (!extMemPool.init(poolId, policy->pAlloc, policy->pFree, 1081*51c0b2f7Stbbdev policy->granularity? policy->granularity : defaultGranularity, 1082*51c0b2f7Stbbdev policy->keepAllMemory, policy->fixedPool)) 1083*51c0b2f7Stbbdev return false; 1084*51c0b2f7Stbbdev { 1085*51c0b2f7Stbbdev MallocMutex::scoped_lock lock(memPoolListLock); 1086*51c0b2f7Stbbdev next = defaultMemPool->next; 1087*51c0b2f7Stbbdev defaultMemPool->next = this; 1088*51c0b2f7Stbbdev prev = defaultMemPool; 1089*51c0b2f7Stbbdev if (next) 1090*51c0b2f7Stbbdev next->prev = this; 1091*51c0b2f7Stbbdev } 1092*51c0b2f7Stbbdev return true; 1093*51c0b2f7Stbbdev } 1094*51c0b2f7Stbbdev 1095*51c0b2f7Stbbdev bool MemoryPool::reset() 1096*51c0b2f7Stbbdev { 1097*51c0b2f7Stbbdev MALLOC_ASSERT(extMemPool.userPool(), "No reset for the system pool."); 1098*51c0b2f7Stbbdev // memory is not releasing during pool reset 1099*51c0b2f7Stbbdev // TODO: mark regions to release unused on next reset() 1100*51c0b2f7Stbbdev extMemPool.delayRegionsReleasing(true); 1101*51c0b2f7Stbbdev 1102*51c0b2f7Stbbdev bootStrapBlocks.reset(); 1103*51c0b2f7Stbbdev extMemPool.lmbList.releaseAll</*poolDestroy=*/false>(&extMemPool.backend); 1104*51c0b2f7Stbbdev if (!extMemPool.reset()) 1105*51c0b2f7Stbbdev return false; 1106*51c0b2f7Stbbdev 1107*51c0b2f7Stbbdev if (!extMemPool.initTLS()) 1108*51c0b2f7Stbbdev return false; 1109*51c0b2f7Stbbdev extMemPool.delayRegionsReleasing(false); 1110*51c0b2f7Stbbdev return true; 1111*51c0b2f7Stbbdev } 1112*51c0b2f7Stbbdev 1113*51c0b2f7Stbbdev bool MemoryPool::destroy() 1114*51c0b2f7Stbbdev { 1115*51c0b2f7Stbbdev #if __TBB_MALLOC_LOCACHE_STAT 1116*51c0b2f7Stbbdev extMemPool.loc.reportStat(stdout); 1117*51c0b2f7Stbbdev #endif 1118*51c0b2f7Stbbdev #if __TBB_MALLOC_BACKEND_STAT 1119*51c0b2f7Stbbdev extMemPool.backend.reportStat(stdout); 1120*51c0b2f7Stbbdev #endif 1121*51c0b2f7Stbbdev { 1122*51c0b2f7Stbbdev MallocMutex::scoped_lock lock(memPoolListLock); 1123*51c0b2f7Stbbdev // remove itself from global pool list 1124*51c0b2f7Stbbdev if (prev) 1125*51c0b2f7Stbbdev prev->next = next; 1126*51c0b2f7Stbbdev if (next) 1127*51c0b2f7Stbbdev next->prev = prev; 1128*51c0b2f7Stbbdev } 1129*51c0b2f7Stbbdev // slab blocks in non-default pool do not have backreferences, 1130*51c0b2f7Stbbdev // only large objects do 1131*51c0b2f7Stbbdev if (extMemPool.userPool()) 1132*51c0b2f7Stbbdev extMemPool.lmbList.releaseAll</*poolDestroy=*/true>(&extMemPool.backend); 1133*51c0b2f7Stbbdev else { 1134*51c0b2f7Stbbdev // only one non-userPool() is supported now 1135*51c0b2f7Stbbdev MALLOC_ASSERT(this==defaultMemPool, NULL); 1136*51c0b2f7Stbbdev // There and below in extMemPool.destroy(), do not restore initial state 1137*51c0b2f7Stbbdev // for user pool, because it's just about to be released. But for system 1138*51c0b2f7Stbbdev // pool restoring, we do not want to do zeroing of it on subsequent reload. 1139*51c0b2f7Stbbdev bootStrapBlocks.reset(); 1140*51c0b2f7Stbbdev extMemPool.orphanedBlocks.reset(); 1141*51c0b2f7Stbbdev } 1142*51c0b2f7Stbbdev return extMemPool.destroy(); 1143*51c0b2f7Stbbdev } 1144*51c0b2f7Stbbdev 1145*51c0b2f7Stbbdev void MemoryPool::onThreadShutdown(TLSData *tlsData) 1146*51c0b2f7Stbbdev { 1147*51c0b2f7Stbbdev if (tlsData) { // might be called for "empty" TLS 1148*51c0b2f7Stbbdev tlsData->release(); 1149*51c0b2f7Stbbdev bootStrapBlocks.free(tlsData); 1150*51c0b2f7Stbbdev clearTLS(); 1151*51c0b2f7Stbbdev } 1152*51c0b2f7Stbbdev } 1153*51c0b2f7Stbbdev 1154*51c0b2f7Stbbdev #if MALLOC_DEBUG 1155*51c0b2f7Stbbdev void Bin::verifyTLSBin (size_t size) const 1156*51c0b2f7Stbbdev { 1157*51c0b2f7Stbbdev /* The debug version verifies the TLSBin as needed */ 1158*51c0b2f7Stbbdev uint32_t objSize = getObjectSize(size); 1159*51c0b2f7Stbbdev 1160*51c0b2f7Stbbdev if (activeBlk) { 1161*51c0b2f7Stbbdev MALLOC_ASSERT( activeBlk->isOwnedByCurrentThread(), ASSERT_TEXT ); 1162*51c0b2f7Stbbdev MALLOC_ASSERT( activeBlk->objectSize == objSize, ASSERT_TEXT ); 1163*51c0b2f7Stbbdev #if MALLOC_DEBUG>1 1164*51c0b2f7Stbbdev for (Block* temp = activeBlk->next; temp; temp=temp->next) { 1165*51c0b2f7Stbbdev MALLOC_ASSERT( temp!=activeBlk, ASSERT_TEXT ); 1166*51c0b2f7Stbbdev MALLOC_ASSERT( temp->isOwnedByCurrentThread(), ASSERT_TEXT ); 1167*51c0b2f7Stbbdev MALLOC_ASSERT( temp->objectSize == objSize, ASSERT_TEXT ); 1168*51c0b2f7Stbbdev MALLOC_ASSERT( temp->previous->next == temp, ASSERT_TEXT ); 1169*51c0b2f7Stbbdev if (temp->next) { 1170*51c0b2f7Stbbdev MALLOC_ASSERT( temp->next->previous == temp, ASSERT_TEXT ); 1171*51c0b2f7Stbbdev } 1172*51c0b2f7Stbbdev } 1173*51c0b2f7Stbbdev for (Block* temp = activeBlk->previous; temp; temp=temp->previous) { 1174*51c0b2f7Stbbdev MALLOC_ASSERT( temp!=activeBlk, ASSERT_TEXT ); 1175*51c0b2f7Stbbdev MALLOC_ASSERT( temp->isOwnedByCurrentThread(), ASSERT_TEXT ); 1176*51c0b2f7Stbbdev MALLOC_ASSERT( temp->objectSize == objSize, ASSERT_TEXT ); 1177*51c0b2f7Stbbdev MALLOC_ASSERT( temp->next->previous == temp, ASSERT_TEXT ); 1178*51c0b2f7Stbbdev if (temp->previous) { 1179*51c0b2f7Stbbdev MALLOC_ASSERT( temp->previous->next == temp, ASSERT_TEXT ); 1180*51c0b2f7Stbbdev } 1181*51c0b2f7Stbbdev } 1182*51c0b2f7Stbbdev #endif /* MALLOC_DEBUG>1 */ 1183*51c0b2f7Stbbdev } 1184*51c0b2f7Stbbdev } 1185*51c0b2f7Stbbdev #else /* MALLOC_DEBUG */ 1186*51c0b2f7Stbbdev inline void Bin::verifyTLSBin (size_t) const { } 1187*51c0b2f7Stbbdev #endif /* MALLOC_DEBUG */ 1188*51c0b2f7Stbbdev 1189*51c0b2f7Stbbdev /* 1190*51c0b2f7Stbbdev * Add a block to the start of this tls bin list. 1191*51c0b2f7Stbbdev */ 1192*51c0b2f7Stbbdev void Bin::pushTLSBin(Block* block) 1193*51c0b2f7Stbbdev { 1194*51c0b2f7Stbbdev /* The objectSize should be defined and not a parameter 1195*51c0b2f7Stbbdev because the function is applied to partially filled blocks as well */ 1196*51c0b2f7Stbbdev unsigned int size = block->objectSize; 1197*51c0b2f7Stbbdev 1198*51c0b2f7Stbbdev MALLOC_ASSERT( block->isOwnedByCurrentThread(), ASSERT_TEXT ); 1199*51c0b2f7Stbbdev MALLOC_ASSERT( block->objectSize != 0, ASSERT_TEXT ); 1200*51c0b2f7Stbbdev MALLOC_ASSERT( block->next == NULL, ASSERT_TEXT ); 1201*51c0b2f7Stbbdev MALLOC_ASSERT( block->previous == NULL, ASSERT_TEXT ); 1202*51c0b2f7Stbbdev 1203*51c0b2f7Stbbdev MALLOC_ASSERT( this, ASSERT_TEXT ); 1204*51c0b2f7Stbbdev verifyTLSBin(size); 1205*51c0b2f7Stbbdev 1206*51c0b2f7Stbbdev block->next = activeBlk; 1207*51c0b2f7Stbbdev if( activeBlk ) { 1208*51c0b2f7Stbbdev block->previous = activeBlk->previous; 1209*51c0b2f7Stbbdev activeBlk->previous = block; 1210*51c0b2f7Stbbdev if( block->previous ) 1211*51c0b2f7Stbbdev block->previous->next = block; 1212*51c0b2f7Stbbdev } else { 1213*51c0b2f7Stbbdev activeBlk = block; 1214*51c0b2f7Stbbdev } 1215*51c0b2f7Stbbdev 1216*51c0b2f7Stbbdev verifyTLSBin(size); 1217*51c0b2f7Stbbdev } 1218*51c0b2f7Stbbdev 1219*51c0b2f7Stbbdev /* 1220*51c0b2f7Stbbdev * Take a block out of its tls bin (e.g. before removal). 1221*51c0b2f7Stbbdev */ 1222*51c0b2f7Stbbdev void Bin::outofTLSBin(Block* block) 1223*51c0b2f7Stbbdev { 1224*51c0b2f7Stbbdev unsigned int size = block->objectSize; 1225*51c0b2f7Stbbdev 1226*51c0b2f7Stbbdev MALLOC_ASSERT( block->isOwnedByCurrentThread(), ASSERT_TEXT ); 1227*51c0b2f7Stbbdev MALLOC_ASSERT( block->objectSize != 0, ASSERT_TEXT ); 1228*51c0b2f7Stbbdev 1229*51c0b2f7Stbbdev MALLOC_ASSERT( this, ASSERT_TEXT ); 1230*51c0b2f7Stbbdev verifyTLSBin(size); 1231*51c0b2f7Stbbdev 1232*51c0b2f7Stbbdev if (block == activeBlk) { 1233*51c0b2f7Stbbdev activeBlk = block->previous? block->previous : block->next; 1234*51c0b2f7Stbbdev } 1235*51c0b2f7Stbbdev /* Unlink the block */ 1236*51c0b2f7Stbbdev if (block->previous) { 1237*51c0b2f7Stbbdev MALLOC_ASSERT( block->previous->next == block, ASSERT_TEXT ); 1238*51c0b2f7Stbbdev block->previous->next = block->next; 1239*51c0b2f7Stbbdev } 1240*51c0b2f7Stbbdev if (block->next) { 1241*51c0b2f7Stbbdev MALLOC_ASSERT( block->next->previous == block, ASSERT_TEXT ); 1242*51c0b2f7Stbbdev block->next->previous = block->previous; 1243*51c0b2f7Stbbdev } 1244*51c0b2f7Stbbdev block->next = NULL; 1245*51c0b2f7Stbbdev block->previous = NULL; 1246*51c0b2f7Stbbdev 1247*51c0b2f7Stbbdev verifyTLSBin(size); 1248*51c0b2f7Stbbdev } 1249*51c0b2f7Stbbdev 1250*51c0b2f7Stbbdev Block* Bin::getPrivatizedFreeListBlock() 1251*51c0b2f7Stbbdev { 1252*51c0b2f7Stbbdev Block* block; 1253*51c0b2f7Stbbdev MALLOC_ASSERT( this, ASSERT_TEXT ); 1254*51c0b2f7Stbbdev // if this method is called, active block usage must be unsuccessful 1255*51c0b2f7Stbbdev MALLOC_ASSERT( !activeBlk && !mailbox.load(std::memory_order_relaxed) || activeBlk && activeBlk->isFull, ASSERT_TEXT ); 1256*51c0b2f7Stbbdev 1257*51c0b2f7Stbbdev // the counter should be changed STAT_increment(getThreadId(), ThreadCommonCounters, lockPublicFreeList); 1258*51c0b2f7Stbbdev if (!mailbox.load(std::memory_order_acquire)) // hotpath is empty mailbox 1259*51c0b2f7Stbbdev return NULL; 1260*51c0b2f7Stbbdev else { // mailbox is not empty, take lock and inspect it 1261*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(mailLock); 1262*51c0b2f7Stbbdev block = mailbox.load(std::memory_order_relaxed); 1263*51c0b2f7Stbbdev if( block ) { 1264*51c0b2f7Stbbdev MALLOC_ASSERT( block->isOwnedByCurrentThread(), ASSERT_TEXT ); 1265*51c0b2f7Stbbdev MALLOC_ASSERT( !isNotForUse(block->nextPrivatizable), ASSERT_TEXT ); 1266*51c0b2f7Stbbdev mailbox.store(block->nextPrivatizable, std::memory_order_relaxed); 1267*51c0b2f7Stbbdev block->nextPrivatizable = (Block*) this; 1268*51c0b2f7Stbbdev } 1269*51c0b2f7Stbbdev } 1270*51c0b2f7Stbbdev if( block ) { 1271*51c0b2f7Stbbdev MALLOC_ASSERT( isSolidPtr(block->publicFreeList.load(std::memory_order_relaxed)), ASSERT_TEXT ); 1272*51c0b2f7Stbbdev block->privatizePublicFreeList(); 1273*51c0b2f7Stbbdev block->adjustPositionInBin(this); 1274*51c0b2f7Stbbdev } 1275*51c0b2f7Stbbdev return block; 1276*51c0b2f7Stbbdev } 1277*51c0b2f7Stbbdev 1278*51c0b2f7Stbbdev void Bin::addPublicFreeListBlock(Block* block) 1279*51c0b2f7Stbbdev { 1280*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(mailLock); 1281*51c0b2f7Stbbdev block->nextPrivatizable = mailbox.load(std::memory_order_relaxed); 1282*51c0b2f7Stbbdev mailbox.store(block, std::memory_order_relaxed); 1283*51c0b2f7Stbbdev } 1284*51c0b2f7Stbbdev 1285*51c0b2f7Stbbdev // Process publicly freed objects in all blocks and return empty blocks 1286*51c0b2f7Stbbdev // to the backend in order to reduce overall footprint. 1287*51c0b2f7Stbbdev bool Bin::cleanPublicFreeLists() 1288*51c0b2f7Stbbdev { 1289*51c0b2f7Stbbdev Block* block; 1290*51c0b2f7Stbbdev if (!mailbox.load(std::memory_order_acquire)) 1291*51c0b2f7Stbbdev return false; 1292*51c0b2f7Stbbdev else { 1293*51c0b2f7Stbbdev // Grab all the blocks in the mailbox 1294*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(mailLock); 1295*51c0b2f7Stbbdev block = mailbox.load(std::memory_order_relaxed); 1296*51c0b2f7Stbbdev mailbox.store(NULL, std::memory_order_relaxed); 1297*51c0b2f7Stbbdev } 1298*51c0b2f7Stbbdev bool released = false; 1299*51c0b2f7Stbbdev while (block) { 1300*51c0b2f7Stbbdev MALLOC_ASSERT( block->isOwnedByCurrentThread(), ASSERT_TEXT ); 1301*51c0b2f7Stbbdev Block* tmp = block->nextPrivatizable; 1302*51c0b2f7Stbbdev block->nextPrivatizable = (Block*) this; 1303*51c0b2f7Stbbdev block->privatizePublicFreeList(); 1304*51c0b2f7Stbbdev if (block->empty()) { 1305*51c0b2f7Stbbdev processEmptyBlock(block, /*poolTheBlock=*/false); 1306*51c0b2f7Stbbdev released = true; 1307*51c0b2f7Stbbdev } else 1308*51c0b2f7Stbbdev block->adjustPositionInBin(this); 1309*51c0b2f7Stbbdev block = tmp; 1310*51c0b2f7Stbbdev } 1311*51c0b2f7Stbbdev return released; 1312*51c0b2f7Stbbdev } 1313*51c0b2f7Stbbdev 1314*51c0b2f7Stbbdev bool Block::adjustFullness() 1315*51c0b2f7Stbbdev { 1316*51c0b2f7Stbbdev if (bumpPtr) { 1317*51c0b2f7Stbbdev /* If we are still using a bump ptr for this block it is empty enough to use. */ 1318*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), examineEmptyEnough); 1319*51c0b2f7Stbbdev isFull = false; 1320*51c0b2f7Stbbdev } else { 1321*51c0b2f7Stbbdev const float threshold = (slabSize - sizeof(Block)) * (1 - emptyEnoughRatio); 1322*51c0b2f7Stbbdev /* allocatedCount shows how many objects in the block are in use; however it still counts 1323*51c0b2f7Stbbdev * blocks freed by other threads; so prior call to privatizePublicFreeList() is recommended */ 1324*51c0b2f7Stbbdev isFull = (allocatedCount*objectSize > threshold) ? true : false; 1325*51c0b2f7Stbbdev #if COLLECT_STATISTICS 1326*51c0b2f7Stbbdev if (isFull) 1327*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), examineNotEmpty); 1328*51c0b2f7Stbbdev else 1329*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), examineEmptyEnough); 1330*51c0b2f7Stbbdev #endif 1331*51c0b2f7Stbbdev } 1332*51c0b2f7Stbbdev return isFull; 1333*51c0b2f7Stbbdev } 1334*51c0b2f7Stbbdev 1335*51c0b2f7Stbbdev // This method resides in class Block, and not in class Bin, in order to avoid 1336*51c0b2f7Stbbdev // calling getAllocationBin on a reasonably hot path in Block::freeOwnObject 1337*51c0b2f7Stbbdev void Block::adjustPositionInBin(Bin* bin/*=NULL*/) 1338*51c0b2f7Stbbdev { 1339*51c0b2f7Stbbdev // If the block were full, but became empty enough to use, 1340*51c0b2f7Stbbdev // move it to the front of the list 1341*51c0b2f7Stbbdev if (isFull && !adjustFullness()) { 1342*51c0b2f7Stbbdev if (!bin) 1343*51c0b2f7Stbbdev bin = tlsPtr->getAllocationBin(objectSize); 1344*51c0b2f7Stbbdev bin->moveBlockToFront(this); 1345*51c0b2f7Stbbdev } 1346*51c0b2f7Stbbdev } 1347*51c0b2f7Stbbdev 1348*51c0b2f7Stbbdev /* Restore the bump pointer for an empty block that is planned to use */ 1349*51c0b2f7Stbbdev void Block::restoreBumpPtr() 1350*51c0b2f7Stbbdev { 1351*51c0b2f7Stbbdev MALLOC_ASSERT( allocatedCount == 0, ASSERT_TEXT ); 1352*51c0b2f7Stbbdev MALLOC_ASSERT( !isSolidPtr(publicFreeList.load(std::memory_order_relaxed)), ASSERT_TEXT ); 1353*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), freeRestoreBumpPtr); 1354*51c0b2f7Stbbdev bumpPtr = (FreeObject *)((uintptr_t)this + slabSize - objectSize); 1355*51c0b2f7Stbbdev freeList = NULL; 1356*51c0b2f7Stbbdev isFull = false; 1357*51c0b2f7Stbbdev } 1358*51c0b2f7Stbbdev 1359*51c0b2f7Stbbdev void Block::freeOwnObject(void *object) 1360*51c0b2f7Stbbdev { 1361*51c0b2f7Stbbdev tlsPtr->markUsed(); 1362*51c0b2f7Stbbdev allocatedCount--; 1363*51c0b2f7Stbbdev MALLOC_ASSERT( allocatedCount < (slabSize-sizeof(Block))/objectSize, ASSERT_TEXT ); 1364*51c0b2f7Stbbdev #if COLLECT_STATISTICS 1365*51c0b2f7Stbbdev // Note that getAllocationBin is not called on the hottest path with statistics off. 1366*51c0b2f7Stbbdev if (tlsPtr->getAllocationBin(objectSize)->getActiveBlock() != this) 1367*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), freeToInactiveBlock); 1368*51c0b2f7Stbbdev else 1369*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), freeToActiveBlock); 1370*51c0b2f7Stbbdev #endif 1371*51c0b2f7Stbbdev if (empty()) { 1372*51c0b2f7Stbbdev // If the last object of a slab is freed, the slab cannot be marked full 1373*51c0b2f7Stbbdev MALLOC_ASSERT(!isFull, ASSERT_TEXT); 1374*51c0b2f7Stbbdev tlsPtr->getAllocationBin(objectSize)->processEmptyBlock(this, /*poolTheBlock=*/true); 1375*51c0b2f7Stbbdev } else { // hot path 1376*51c0b2f7Stbbdev FreeObject *objectToFree = findObjectToFree(object); 1377*51c0b2f7Stbbdev objectToFree->next = freeList; 1378*51c0b2f7Stbbdev freeList = objectToFree; 1379*51c0b2f7Stbbdev adjustPositionInBin(); 1380*51c0b2f7Stbbdev } 1381*51c0b2f7Stbbdev } 1382*51c0b2f7Stbbdev 1383*51c0b2f7Stbbdev void Block::freePublicObject (FreeObject *objectToFree) 1384*51c0b2f7Stbbdev { 1385*51c0b2f7Stbbdev FreeObject *localPublicFreeList; 1386*51c0b2f7Stbbdev 1387*51c0b2f7Stbbdev MALLOC_ITT_SYNC_RELEASING(&publicFreeList); 1388*51c0b2f7Stbbdev #if FREELIST_NONBLOCKING 1389*51c0b2f7Stbbdev // TBB_REVAMP_TODO: make it non atomic in non-blocking scenario 1390*51c0b2f7Stbbdev FreeObject *temp = publicFreeList.load(std::memory_order_acquire); 1391*51c0b2f7Stbbdev do { 1392*51c0b2f7Stbbdev localPublicFreeList = objectToFree->next = temp; 1393*51c0b2f7Stbbdev // no backoff necessary because trying to make change, not waiting for a change 1394*51c0b2f7Stbbdev } while( !publicFreeList.compare_exchange_strong(temp, objectToFree) ); 1395*51c0b2f7Stbbdev #else 1396*51c0b2f7Stbbdev STAT_increment(getThreadId(), ThreadCommonCounters, lockPublicFreeList); 1397*51c0b2f7Stbbdev { 1398*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(publicFreeListLock); 1399*51c0b2f7Stbbdev localPublicFreeList = objectToFree->next = publicFreeList; 1400*51c0b2f7Stbbdev publicFreeList = objectToFree; 1401*51c0b2f7Stbbdev } 1402*51c0b2f7Stbbdev #endif 1403*51c0b2f7Stbbdev 1404*51c0b2f7Stbbdev if( localPublicFreeList==NULL ) { 1405*51c0b2f7Stbbdev // if the block is abandoned, its nextPrivatizable pointer should be UNUSABLE 1406*51c0b2f7Stbbdev // otherwise, it should point to the bin the block belongs to. 1407*51c0b2f7Stbbdev // reading nextPrivatizable is thread-safe below, because: 1408*51c0b2f7Stbbdev // 1) the executing thread atomically got publicFreeList==NULL and changed it to non-NULL; 1409*51c0b2f7Stbbdev // 2) only owning thread can change it back to NULL, 1410*51c0b2f7Stbbdev // 3) but it can not be done until the block is put to the mailbox 1411*51c0b2f7Stbbdev // So the executing thread is now the only one that can change nextPrivatizable 1412*51c0b2f7Stbbdev if( !isNotForUse(nextPrivatizable) ) { 1413*51c0b2f7Stbbdev MALLOC_ASSERT( nextPrivatizable!=NULL, ASSERT_TEXT ); 1414*51c0b2f7Stbbdev Bin* theBin = (Bin*) nextPrivatizable; 1415*51c0b2f7Stbbdev #if MALLOC_DEBUG && TBB_REVAMP_TODO 1416*51c0b2f7Stbbdev // FIXME: The thread that returns the block is not the block's owner. 1417*51c0b2f7Stbbdev // The below assertion compares 'theBin' against the caller's local bin, thus, it always fails. 1418*51c0b2f7Stbbdev // Need to find a way to get the correct remote bin for comparison. 1419*51c0b2f7Stbbdev { // check that nextPrivatizable points to the bin the block belongs to 1420*51c0b2f7Stbbdev uint32_t index = getIndex( objectSize ); 1421*51c0b2f7Stbbdev TLSData* tls = getThreadMallocTLS(); 1422*51c0b2f7Stbbdev MALLOC_ASSERT( theBin==tls->bin+index, ASSERT_TEXT ); 1423*51c0b2f7Stbbdev } 1424*51c0b2f7Stbbdev #endif // MALLOC_DEBUG 1425*51c0b2f7Stbbdev theBin->addPublicFreeListBlock(this); 1426*51c0b2f7Stbbdev } 1427*51c0b2f7Stbbdev } 1428*51c0b2f7Stbbdev STAT_increment(getThreadId(), ThreadCommonCounters, freeToOtherThread); 1429*51c0b2f7Stbbdev STAT_increment(ownerTid, getIndex(objectSize), freeByOtherThread); 1430*51c0b2f7Stbbdev } 1431*51c0b2f7Stbbdev 1432*51c0b2f7Stbbdev // Make objects freed by other threads available for use again 1433*51c0b2f7Stbbdev void Block::privatizePublicFreeList( bool reset ) 1434*51c0b2f7Stbbdev { 1435*51c0b2f7Stbbdev FreeObject *localPublicFreeList; 1436*51c0b2f7Stbbdev // If reset is false, publicFreeList should not be zeroed but set to UNUSABLE 1437*51c0b2f7Stbbdev // to properly synchronize with other threads freeing objects to this slab. 1438*51c0b2f7Stbbdev const intptr_t endMarker = reset ? 0 : UNUSABLE; 1439*51c0b2f7Stbbdev 1440*51c0b2f7Stbbdev // Only the owner thread may reset the pointer to NULL 1441*51c0b2f7Stbbdev MALLOC_ASSERT( isOwnedByCurrentThread() || !reset, ASSERT_TEXT ); 1442*51c0b2f7Stbbdev #if FREELIST_NONBLOCKING 1443*51c0b2f7Stbbdev localPublicFreeList = publicFreeList.exchange((FreeObject*)endMarker); 1444*51c0b2f7Stbbdev #else 1445*51c0b2f7Stbbdev STAT_increment(getThreadId(), ThreadCommonCounters, lockPublicFreeList); 1446*51c0b2f7Stbbdev { 1447*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(publicFreeListLock); 1448*51c0b2f7Stbbdev localPublicFreeList = publicFreeList; 1449*51c0b2f7Stbbdev publicFreeList = endMarker; 1450*51c0b2f7Stbbdev } 1451*51c0b2f7Stbbdev #endif 1452*51c0b2f7Stbbdev MALLOC_ITT_SYNC_ACQUIRED(&publicFreeList); 1453*51c0b2f7Stbbdev MALLOC_ASSERT( !(reset && isNotForUse(publicFreeList)), ASSERT_TEXT ); 1454*51c0b2f7Stbbdev 1455*51c0b2f7Stbbdev // publicFreeList must have been UNUSABLE or valid, but not NULL 1456*51c0b2f7Stbbdev MALLOC_ASSERT( localPublicFreeList!=NULL, ASSERT_TEXT ); 1457*51c0b2f7Stbbdev if( isSolidPtr(localPublicFreeList) ) { 1458*51c0b2f7Stbbdev MALLOC_ASSERT( allocatedCount <= (slabSize-sizeof(Block))/objectSize, ASSERT_TEXT ); 1459*51c0b2f7Stbbdev /* other threads did not change the counter freeing our blocks */ 1460*51c0b2f7Stbbdev allocatedCount--; 1461*51c0b2f7Stbbdev FreeObject *temp = localPublicFreeList; 1462*51c0b2f7Stbbdev while( isSolidPtr(temp->next) ){ // the list will end with either NULL or UNUSABLE 1463*51c0b2f7Stbbdev temp = temp->next; 1464*51c0b2f7Stbbdev allocatedCount--; 1465*51c0b2f7Stbbdev MALLOC_ASSERT( allocatedCount < (slabSize-sizeof(Block))/objectSize, ASSERT_TEXT ); 1466*51c0b2f7Stbbdev } 1467*51c0b2f7Stbbdev /* merge with local freeList */ 1468*51c0b2f7Stbbdev temp->next = freeList; 1469*51c0b2f7Stbbdev freeList = localPublicFreeList; 1470*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), allocPrivatized); 1471*51c0b2f7Stbbdev } 1472*51c0b2f7Stbbdev } 1473*51c0b2f7Stbbdev 1474*51c0b2f7Stbbdev void Block::privatizeOrphaned(TLSData *tls, unsigned index) 1475*51c0b2f7Stbbdev { 1476*51c0b2f7Stbbdev Bin* bin = tls->bin + index; 1477*51c0b2f7Stbbdev STAT_increment(getThreadId(), index, allocBlockPublic); 1478*51c0b2f7Stbbdev next = NULL; 1479*51c0b2f7Stbbdev previous = NULL; 1480*51c0b2f7Stbbdev MALLOC_ASSERT( publicFreeList.load(std::memory_order_relaxed) != NULL, ASSERT_TEXT ); 1481*51c0b2f7Stbbdev /* There is not a race here since no other thread owns this block */ 1482*51c0b2f7Stbbdev markOwned(tls); 1483*51c0b2f7Stbbdev // It is safe to change nextPrivatizable, as publicFreeList is not null 1484*51c0b2f7Stbbdev MALLOC_ASSERT( isNotForUse(nextPrivatizable), ASSERT_TEXT ); 1485*51c0b2f7Stbbdev nextPrivatizable = (Block*)bin; 1486*51c0b2f7Stbbdev // the next call is required to change publicFreeList to 0 1487*51c0b2f7Stbbdev privatizePublicFreeList(); 1488*51c0b2f7Stbbdev if( empty() ) { 1489*51c0b2f7Stbbdev restoreBumpPtr(); 1490*51c0b2f7Stbbdev } else { 1491*51c0b2f7Stbbdev adjustFullness(); // check the block fullness and set isFull 1492*51c0b2f7Stbbdev } 1493*51c0b2f7Stbbdev MALLOC_ASSERT( !isNotForUse(publicFreeList.load(std::memory_order_relaxed)), ASSERT_TEXT ); 1494*51c0b2f7Stbbdev } 1495*51c0b2f7Stbbdev 1496*51c0b2f7Stbbdev 1497*51c0b2f7Stbbdev bool Block::readyToShare() 1498*51c0b2f7Stbbdev { 1499*51c0b2f7Stbbdev FreeObject* oldVal = NULL; 1500*51c0b2f7Stbbdev #if FREELIST_NONBLOCKING 1501*51c0b2f7Stbbdev publicFreeList.compare_exchange_strong(oldVal, (FreeObject*)UNUSABLE); 1502*51c0b2f7Stbbdev #else 1503*51c0b2f7Stbbdev STAT_increment(getThreadId(), ThreadCommonCounters, lockPublicFreeList); 1504*51c0b2f7Stbbdev { 1505*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(publicFreeListLock); 1506*51c0b2f7Stbbdev if ( (oldVal=publicFreeList)==NULL ) 1507*51c0b2f7Stbbdev (intptr_t&)(publicFreeList) = UNUSABLE; 1508*51c0b2f7Stbbdev } 1509*51c0b2f7Stbbdev #endif 1510*51c0b2f7Stbbdev return oldVal==NULL; 1511*51c0b2f7Stbbdev } 1512*51c0b2f7Stbbdev 1513*51c0b2f7Stbbdev void Block::shareOrphaned(intptr_t binTag, unsigned index) 1514*51c0b2f7Stbbdev { 1515*51c0b2f7Stbbdev MALLOC_ASSERT( binTag, ASSERT_TEXT ); 1516*51c0b2f7Stbbdev // unreferenced formal parameter warning 1517*51c0b2f7Stbbdev tbb::detail::suppress_unused_warning(index); 1518*51c0b2f7Stbbdev STAT_increment(getThreadId(), index, freeBlockPublic); 1519*51c0b2f7Stbbdev markOrphaned(); 1520*51c0b2f7Stbbdev if ((intptr_t)nextPrivatizable==binTag) { 1521*51c0b2f7Stbbdev // First check passed: the block is not in mailbox yet. 1522*51c0b2f7Stbbdev // Need to set publicFreeList to non-zero, so other threads 1523*51c0b2f7Stbbdev // will not change nextPrivatizable and it can be zeroed. 1524*51c0b2f7Stbbdev if ( !readyToShare() ) { 1525*51c0b2f7Stbbdev // another thread freed an object; we need to wait until it finishes. 1526*51c0b2f7Stbbdev // There is no need for exponential backoff, as the wait here is not for a lock; 1527*51c0b2f7Stbbdev // but need to yield, so the thread we wait has a chance to run. 1528*51c0b2f7Stbbdev // TODO: add a pause to also be friendly to hyperthreads 1529*51c0b2f7Stbbdev int count = 256; 1530*51c0b2f7Stbbdev while( (intptr_t)const_cast<Block* volatile &>(nextPrivatizable)==binTag ) { 1531*51c0b2f7Stbbdev if (--count==0) { 1532*51c0b2f7Stbbdev do_yield(); 1533*51c0b2f7Stbbdev count = 256; 1534*51c0b2f7Stbbdev } 1535*51c0b2f7Stbbdev } 1536*51c0b2f7Stbbdev } 1537*51c0b2f7Stbbdev } 1538*51c0b2f7Stbbdev MALLOC_ASSERT( publicFreeList.load(std::memory_order_relaxed) !=NULL, ASSERT_TEXT ); 1539*51c0b2f7Stbbdev // now it is safe to change our data 1540*51c0b2f7Stbbdev previous = NULL; 1541*51c0b2f7Stbbdev // it is caller responsibility to ensure that the list of blocks 1542*51c0b2f7Stbbdev // formed by nextPrivatizable pointers is kept consistent if required. 1543*51c0b2f7Stbbdev // if only called from thread shutdown code, it does not matter. 1544*51c0b2f7Stbbdev (intptr_t&)(nextPrivatizable) = UNUSABLE; 1545*51c0b2f7Stbbdev } 1546*51c0b2f7Stbbdev 1547*51c0b2f7Stbbdev void Block::cleanBlockHeader() 1548*51c0b2f7Stbbdev { 1549*51c0b2f7Stbbdev next = NULL; 1550*51c0b2f7Stbbdev previous = NULL; 1551*51c0b2f7Stbbdev freeList = NULL; 1552*51c0b2f7Stbbdev allocatedCount = 0; 1553*51c0b2f7Stbbdev isFull = false; 1554*51c0b2f7Stbbdev tlsPtr = NULL; 1555*51c0b2f7Stbbdev 1556*51c0b2f7Stbbdev publicFreeList.store(NULL, std::memory_order_relaxed); 1557*51c0b2f7Stbbdev } 1558*51c0b2f7Stbbdev 1559*51c0b2f7Stbbdev void Block::initEmptyBlock(TLSData *tls, size_t size) 1560*51c0b2f7Stbbdev { 1561*51c0b2f7Stbbdev // Having getIndex and getObjectSize called next to each other 1562*51c0b2f7Stbbdev // allows better compiler optimization as they basically share the code. 1563*51c0b2f7Stbbdev unsigned int index = getIndex(size); 1564*51c0b2f7Stbbdev unsigned int objSz = getObjectSize(size); 1565*51c0b2f7Stbbdev 1566*51c0b2f7Stbbdev cleanBlockHeader(); 1567*51c0b2f7Stbbdev objectSize = objSz; 1568*51c0b2f7Stbbdev markOwned(tls); 1569*51c0b2f7Stbbdev // bump pointer should be prepared for first allocation - thus mode it down to objectSize 1570*51c0b2f7Stbbdev bumpPtr = (FreeObject *)((uintptr_t)this + slabSize - objectSize); 1571*51c0b2f7Stbbdev 1572*51c0b2f7Stbbdev // each block should have the address where the head of the list of "privatizable" blocks is kept 1573*51c0b2f7Stbbdev // the only exception is a block for boot strap which is initialized when TLS is yet NULL 1574*51c0b2f7Stbbdev nextPrivatizable = tls? (Block*)(tls->bin + index) : NULL; 1575*51c0b2f7Stbbdev TRACEF(( "[ScalableMalloc trace] Empty block %p is initialized, owner is %ld, objectSize is %d, bumpPtr is %p\n", 1576*51c0b2f7Stbbdev this, tlsPtr ? getThreadId() : -1, objectSize, bumpPtr )); 1577*51c0b2f7Stbbdev } 1578*51c0b2f7Stbbdev 1579*51c0b2f7Stbbdev Block *OrphanedBlocks::get(TLSData *tls, unsigned int size) 1580*51c0b2f7Stbbdev { 1581*51c0b2f7Stbbdev // TODO: try to use index from getAllocationBin 1582*51c0b2f7Stbbdev unsigned int index = getIndex(size); 1583*51c0b2f7Stbbdev Block *block = bins[index].pop(); 1584*51c0b2f7Stbbdev if (block) { 1585*51c0b2f7Stbbdev MALLOC_ITT_SYNC_ACQUIRED(bins+index); 1586*51c0b2f7Stbbdev block->privatizeOrphaned(tls, index); 1587*51c0b2f7Stbbdev } 1588*51c0b2f7Stbbdev return block; 1589*51c0b2f7Stbbdev } 1590*51c0b2f7Stbbdev 1591*51c0b2f7Stbbdev void OrphanedBlocks::put(intptr_t binTag, Block *block) 1592*51c0b2f7Stbbdev { 1593*51c0b2f7Stbbdev unsigned int index = getIndex(block->getSize()); 1594*51c0b2f7Stbbdev block->shareOrphaned(binTag, index); 1595*51c0b2f7Stbbdev MALLOC_ITT_SYNC_RELEASING(bins+index); 1596*51c0b2f7Stbbdev bins[index].push(block); 1597*51c0b2f7Stbbdev } 1598*51c0b2f7Stbbdev 1599*51c0b2f7Stbbdev void OrphanedBlocks::reset() 1600*51c0b2f7Stbbdev { 1601*51c0b2f7Stbbdev for (uint32_t i=0; i<numBlockBinLimit; i++) 1602*51c0b2f7Stbbdev new (bins+i) LifoList(); 1603*51c0b2f7Stbbdev } 1604*51c0b2f7Stbbdev 1605*51c0b2f7Stbbdev bool OrphanedBlocks::cleanup(Backend* backend) 1606*51c0b2f7Stbbdev { 1607*51c0b2f7Stbbdev bool released = false; 1608*51c0b2f7Stbbdev for (uint32_t i=0; i<numBlockBinLimit; i++) { 1609*51c0b2f7Stbbdev Block* block = bins[i].grab(); 1610*51c0b2f7Stbbdev MALLOC_ITT_SYNC_ACQUIRED(bins+i); 1611*51c0b2f7Stbbdev while (block) { 1612*51c0b2f7Stbbdev Block* next = block->next; 1613*51c0b2f7Stbbdev block->privatizePublicFreeList( /*reset=*/false ); // do not set publicFreeList to NULL 1614*51c0b2f7Stbbdev if (block->empty()) { 1615*51c0b2f7Stbbdev block->reset(); 1616*51c0b2f7Stbbdev // slab blocks in user's pools do not have valid backRefIdx 1617*51c0b2f7Stbbdev if (!backend->inUserPool()) 1618*51c0b2f7Stbbdev removeBackRef(*(block->getBackRefIdx())); 1619*51c0b2f7Stbbdev backend->putSlabBlock(block); 1620*51c0b2f7Stbbdev released = true; 1621*51c0b2f7Stbbdev } else { 1622*51c0b2f7Stbbdev MALLOC_ITT_SYNC_RELEASING(bins+i); 1623*51c0b2f7Stbbdev bins[i].push(block); 1624*51c0b2f7Stbbdev } 1625*51c0b2f7Stbbdev block = next; 1626*51c0b2f7Stbbdev } 1627*51c0b2f7Stbbdev } 1628*51c0b2f7Stbbdev return released; 1629*51c0b2f7Stbbdev } 1630*51c0b2f7Stbbdev 1631*51c0b2f7Stbbdev FreeBlockPool::ResOfGet FreeBlockPool::getBlock() 1632*51c0b2f7Stbbdev { 1633*51c0b2f7Stbbdev Block *b = head.exchange(NULL); 1634*51c0b2f7Stbbdev 1635*51c0b2f7Stbbdev if (b) { 1636*51c0b2f7Stbbdev size--; 1637*51c0b2f7Stbbdev Block *newHead = b->next; 1638*51c0b2f7Stbbdev lastAccessMiss = false; 1639*51c0b2f7Stbbdev head.store(newHead, std::memory_order_release); 1640*51c0b2f7Stbbdev } else { 1641*51c0b2f7Stbbdev lastAccessMiss = true; 1642*51c0b2f7Stbbdev } 1643*51c0b2f7Stbbdev return ResOfGet(b, lastAccessMiss); 1644*51c0b2f7Stbbdev } 1645*51c0b2f7Stbbdev 1646*51c0b2f7Stbbdev void FreeBlockPool::returnBlock(Block *block) 1647*51c0b2f7Stbbdev { 1648*51c0b2f7Stbbdev MALLOC_ASSERT( size <= POOL_HIGH_MARK, ASSERT_TEXT ); 1649*51c0b2f7Stbbdev Block *localHead = head.exchange(NULL); 1650*51c0b2f7Stbbdev 1651*51c0b2f7Stbbdev if (!localHead) { 1652*51c0b2f7Stbbdev size = 0; // head was stolen by externalClean, correct size accordingly 1653*51c0b2f7Stbbdev } else if (size == POOL_HIGH_MARK) { 1654*51c0b2f7Stbbdev // release cold blocks and add hot one, 1655*51c0b2f7Stbbdev // so keep POOL_LOW_MARK-1 blocks and add new block to head 1656*51c0b2f7Stbbdev Block *headToFree = localHead, *helper; 1657*51c0b2f7Stbbdev for (int i=0; i<POOL_LOW_MARK-2; i++) 1658*51c0b2f7Stbbdev headToFree = headToFree->next; 1659*51c0b2f7Stbbdev Block *last = headToFree; 1660*51c0b2f7Stbbdev headToFree = headToFree->next; 1661*51c0b2f7Stbbdev last->next = NULL; 1662*51c0b2f7Stbbdev size = POOL_LOW_MARK-1; 1663*51c0b2f7Stbbdev for (Block *currBl = headToFree; currBl; currBl = helper) { 1664*51c0b2f7Stbbdev helper = currBl->next; 1665*51c0b2f7Stbbdev // slab blocks in user's pools do not have valid backRefIdx 1666*51c0b2f7Stbbdev if (!backend->inUserPool()) 1667*51c0b2f7Stbbdev removeBackRef(currBl->backRefIdx); 1668*51c0b2f7Stbbdev backend->putSlabBlock(currBl); 1669*51c0b2f7Stbbdev } 1670*51c0b2f7Stbbdev } 1671*51c0b2f7Stbbdev size++; 1672*51c0b2f7Stbbdev block->next = localHead; 1673*51c0b2f7Stbbdev head.store(block, std::memory_order_release); 1674*51c0b2f7Stbbdev } 1675*51c0b2f7Stbbdev 1676*51c0b2f7Stbbdev bool FreeBlockPool::externalCleanup() 1677*51c0b2f7Stbbdev { 1678*51c0b2f7Stbbdev Block *helper; 1679*51c0b2f7Stbbdev bool released = false; 1680*51c0b2f7Stbbdev 1681*51c0b2f7Stbbdev for (Block *currBl=head.exchange(NULL); currBl; currBl=helper) { 1682*51c0b2f7Stbbdev helper = currBl->next; 1683*51c0b2f7Stbbdev // slab blocks in user's pools do not have valid backRefIdx 1684*51c0b2f7Stbbdev if (!backend->inUserPool()) 1685*51c0b2f7Stbbdev removeBackRef(currBl->backRefIdx); 1686*51c0b2f7Stbbdev backend->putSlabBlock(currBl); 1687*51c0b2f7Stbbdev released = true; 1688*51c0b2f7Stbbdev } 1689*51c0b2f7Stbbdev return released; 1690*51c0b2f7Stbbdev } 1691*51c0b2f7Stbbdev 1692*51c0b2f7Stbbdev /* Prepare the block for returning to FreeBlockPool */ 1693*51c0b2f7Stbbdev void Block::reset() 1694*51c0b2f7Stbbdev { 1695*51c0b2f7Stbbdev // it is caller's responsibility to ensure no data is lost before calling this 1696*51c0b2f7Stbbdev MALLOC_ASSERT( allocatedCount==0, ASSERT_TEXT ); 1697*51c0b2f7Stbbdev MALLOC_ASSERT( !isSolidPtr(publicFreeList.load(std::memory_order_relaxed)), ASSERT_TEXT ); 1698*51c0b2f7Stbbdev if (!isStartupAllocObject()) 1699*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), freeBlockBack); 1700*51c0b2f7Stbbdev 1701*51c0b2f7Stbbdev cleanBlockHeader(); 1702*51c0b2f7Stbbdev 1703*51c0b2f7Stbbdev nextPrivatizable = NULL; 1704*51c0b2f7Stbbdev 1705*51c0b2f7Stbbdev objectSize = 0; 1706*51c0b2f7Stbbdev // for an empty block, bump pointer should point right after the end of the block 1707*51c0b2f7Stbbdev bumpPtr = (FreeObject *)((uintptr_t)this + slabSize); 1708*51c0b2f7Stbbdev } 1709*51c0b2f7Stbbdev 1710*51c0b2f7Stbbdev inline void Bin::setActiveBlock (Block *block) 1711*51c0b2f7Stbbdev { 1712*51c0b2f7Stbbdev // MALLOC_ASSERT( bin, ASSERT_TEXT ); 1713*51c0b2f7Stbbdev MALLOC_ASSERT( block->isOwnedByCurrentThread(), ASSERT_TEXT ); 1714*51c0b2f7Stbbdev // it is the caller responsibility to keep bin consistence (i.e. ensure this block is in the bin list) 1715*51c0b2f7Stbbdev activeBlk = block; 1716*51c0b2f7Stbbdev } 1717*51c0b2f7Stbbdev 1718*51c0b2f7Stbbdev inline Block* Bin::setPreviousBlockActive() 1719*51c0b2f7Stbbdev { 1720*51c0b2f7Stbbdev MALLOC_ASSERT( activeBlk, ASSERT_TEXT ); 1721*51c0b2f7Stbbdev Block* temp = activeBlk->previous; 1722*51c0b2f7Stbbdev if( temp ) { 1723*51c0b2f7Stbbdev MALLOC_ASSERT( !(temp->isFull), ASSERT_TEXT ); 1724*51c0b2f7Stbbdev activeBlk = temp; 1725*51c0b2f7Stbbdev } 1726*51c0b2f7Stbbdev return temp; 1727*51c0b2f7Stbbdev } 1728*51c0b2f7Stbbdev 1729*51c0b2f7Stbbdev inline bool Block::isOwnedByCurrentThread() const { 1730*51c0b2f7Stbbdev return tlsPtr && ownerTid.isCurrentThreadId(); 1731*51c0b2f7Stbbdev } 1732*51c0b2f7Stbbdev 1733*51c0b2f7Stbbdev FreeObject *Block::findObjectToFree(const void *object) const 1734*51c0b2f7Stbbdev { 1735*51c0b2f7Stbbdev FreeObject *objectToFree; 1736*51c0b2f7Stbbdev // Due to aligned allocations, a pointer passed to scalable_free 1737*51c0b2f7Stbbdev // might differ from the address of internally allocated object. 1738*51c0b2f7Stbbdev // Small objects however should always be fine. 1739*51c0b2f7Stbbdev if (objectSize <= maxSegregatedObjectSize) 1740*51c0b2f7Stbbdev objectToFree = (FreeObject*)object; 1741*51c0b2f7Stbbdev // "Fitting size" allocations are suspicious if aligned higher than naturally 1742*51c0b2f7Stbbdev else { 1743*51c0b2f7Stbbdev if ( ! isAligned(object,2*fittingAlignment) ) 1744*51c0b2f7Stbbdev // TODO: the above check is questionable - it gives false negatives in ~50% cases, 1745*51c0b2f7Stbbdev // so might even be slower in average than unconditional use of findAllocatedObject. 1746*51c0b2f7Stbbdev // here it should be a "real" object 1747*51c0b2f7Stbbdev objectToFree = (FreeObject*)object; 1748*51c0b2f7Stbbdev else 1749*51c0b2f7Stbbdev // here object can be an aligned address, so applying additional checks 1750*51c0b2f7Stbbdev objectToFree = findAllocatedObject(object); 1751*51c0b2f7Stbbdev MALLOC_ASSERT( isAligned(objectToFree,fittingAlignment), ASSERT_TEXT ); 1752*51c0b2f7Stbbdev } 1753*51c0b2f7Stbbdev MALLOC_ASSERT( isProperlyPlaced(objectToFree), ASSERT_TEXT ); 1754*51c0b2f7Stbbdev 1755*51c0b2f7Stbbdev return objectToFree; 1756*51c0b2f7Stbbdev } 1757*51c0b2f7Stbbdev 1758*51c0b2f7Stbbdev void TLSData::release() 1759*51c0b2f7Stbbdev { 1760*51c0b2f7Stbbdev memPool->extMemPool.allLocalCaches.unregisterThread(this); 1761*51c0b2f7Stbbdev externalCleanup(/*cleanOnlyUnused=*/false, /*cleanBins=*/false); 1762*51c0b2f7Stbbdev 1763*51c0b2f7Stbbdev for (unsigned index = 0; index < numBlockBins; index++) { 1764*51c0b2f7Stbbdev Block *activeBlk = bin[index].getActiveBlock(); 1765*51c0b2f7Stbbdev if (!activeBlk) 1766*51c0b2f7Stbbdev continue; 1767*51c0b2f7Stbbdev Block *threadlessBlock = activeBlk->previous; 1768*51c0b2f7Stbbdev while (threadlessBlock) { 1769*51c0b2f7Stbbdev Block *threadBlock = threadlessBlock->previous; 1770*51c0b2f7Stbbdev if (threadlessBlock->empty()) { 1771*51c0b2f7Stbbdev /* we destroy the thread, so not use its block pool */ 1772*51c0b2f7Stbbdev memPool->returnEmptyBlock(threadlessBlock, /*poolTheBlock=*/false); 1773*51c0b2f7Stbbdev } else { 1774*51c0b2f7Stbbdev memPool->extMemPool.orphanedBlocks.put(intptr_t(bin+index), threadlessBlock); 1775*51c0b2f7Stbbdev } 1776*51c0b2f7Stbbdev threadlessBlock = threadBlock; 1777*51c0b2f7Stbbdev } 1778*51c0b2f7Stbbdev threadlessBlock = activeBlk; 1779*51c0b2f7Stbbdev while (threadlessBlock) { 1780*51c0b2f7Stbbdev Block *threadBlock = threadlessBlock->next; 1781*51c0b2f7Stbbdev if (threadlessBlock->empty()) { 1782*51c0b2f7Stbbdev /* we destroy the thread, so not use its block pool */ 1783*51c0b2f7Stbbdev memPool->returnEmptyBlock(threadlessBlock, /*poolTheBlock=*/false); 1784*51c0b2f7Stbbdev } else { 1785*51c0b2f7Stbbdev memPool->extMemPool.orphanedBlocks.put(intptr_t(bin+index), threadlessBlock); 1786*51c0b2f7Stbbdev } 1787*51c0b2f7Stbbdev threadlessBlock = threadBlock; 1788*51c0b2f7Stbbdev } 1789*51c0b2f7Stbbdev bin[index].resetActiveBlock(); 1790*51c0b2f7Stbbdev } 1791*51c0b2f7Stbbdev } 1792*51c0b2f7Stbbdev 1793*51c0b2f7Stbbdev 1794*51c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION 1795*51c0b2f7Stbbdev // TODO: Use dedicated heap for this 1796*51c0b2f7Stbbdev 1797*51c0b2f7Stbbdev /* 1798*51c0b2f7Stbbdev * It's a special kind of allocation that can be used when malloc is 1799*51c0b2f7Stbbdev * not available (either during startup or when malloc was already called and 1800*51c0b2f7Stbbdev * we are, say, inside pthread_setspecific's call). 1801*51c0b2f7Stbbdev * Block can contain objects of different sizes, 1802*51c0b2f7Stbbdev * allocations are performed by moving bump pointer and increasing of object counter, 1803*51c0b2f7Stbbdev * releasing is done via counter of objects allocated in the block 1804*51c0b2f7Stbbdev * or moving bump pointer if releasing object is on a bound. 1805*51c0b2f7Stbbdev * TODO: make bump pointer to grow to the same backward direction as all the others. 1806*51c0b2f7Stbbdev */ 1807*51c0b2f7Stbbdev 1808*51c0b2f7Stbbdev class StartupBlock : public Block { 1809*51c0b2f7Stbbdev size_t availableSize() const { 1810*51c0b2f7Stbbdev return slabSize - ((uintptr_t)bumpPtr - (uintptr_t)this); 1811*51c0b2f7Stbbdev } 1812*51c0b2f7Stbbdev static StartupBlock *getBlock(); 1813*51c0b2f7Stbbdev public: 1814*51c0b2f7Stbbdev static FreeObject *allocate(size_t size); 1815*51c0b2f7Stbbdev static size_t msize(void *ptr) { return *((size_t*)ptr - 1); } 1816*51c0b2f7Stbbdev void free(void *ptr); 1817*51c0b2f7Stbbdev }; 1818*51c0b2f7Stbbdev 1819*51c0b2f7Stbbdev static MallocMutex startupMallocLock; 1820*51c0b2f7Stbbdev static StartupBlock *firstStartupBlock; 1821*51c0b2f7Stbbdev 1822*51c0b2f7Stbbdev StartupBlock *StartupBlock::getBlock() 1823*51c0b2f7Stbbdev { 1824*51c0b2f7Stbbdev BackRefIdx backRefIdx = BackRefIdx::newBackRef(/*largeObj=*/false); 1825*51c0b2f7Stbbdev if (backRefIdx.isInvalid()) return NULL; 1826*51c0b2f7Stbbdev 1827*51c0b2f7Stbbdev StartupBlock *block = static_cast<StartupBlock*>( 1828*51c0b2f7Stbbdev defaultMemPool->extMemPool.backend.getSlabBlock(1)); 1829*51c0b2f7Stbbdev if (!block) return NULL; 1830*51c0b2f7Stbbdev 1831*51c0b2f7Stbbdev block->cleanBlockHeader(); 1832*51c0b2f7Stbbdev setBackRef(backRefIdx, block); 1833*51c0b2f7Stbbdev block->backRefIdx = backRefIdx; 1834*51c0b2f7Stbbdev // use startupAllocObjSizeMark to mark objects from startup block marker 1835*51c0b2f7Stbbdev block->objectSize = startupAllocObjSizeMark; 1836*51c0b2f7Stbbdev block->bumpPtr = (FreeObject *)((uintptr_t)block + sizeof(StartupBlock)); 1837*51c0b2f7Stbbdev return block; 1838*51c0b2f7Stbbdev } 1839*51c0b2f7Stbbdev 1840*51c0b2f7Stbbdev FreeObject *StartupBlock::allocate(size_t size) 1841*51c0b2f7Stbbdev { 1842*51c0b2f7Stbbdev FreeObject *result; 1843*51c0b2f7Stbbdev StartupBlock *newBlock = NULL; 1844*51c0b2f7Stbbdev bool newBlockUnused = false; 1845*51c0b2f7Stbbdev 1846*51c0b2f7Stbbdev /* Objects must be aligned on their natural bounds, 1847*51c0b2f7Stbbdev and objects bigger than word on word's bound. */ 1848*51c0b2f7Stbbdev size = alignUp(size, sizeof(size_t)); 1849*51c0b2f7Stbbdev // We need size of an object to implement msize. 1850*51c0b2f7Stbbdev size_t reqSize = size + sizeof(size_t); 1851*51c0b2f7Stbbdev // speculatively allocates newBlock to try avoid allocation while holding lock 1852*51c0b2f7Stbbdev /* TODO: The function is called when malloc nested call is detected, 1853*51c0b2f7Stbbdev so simultaneous usage from different threads seems unlikely. 1854*51c0b2f7Stbbdev If pre-allocation is found useless, the code might be simplified. */ 1855*51c0b2f7Stbbdev if (!firstStartupBlock || firstStartupBlock->availableSize() < reqSize) { 1856*51c0b2f7Stbbdev newBlock = StartupBlock::getBlock(); 1857*51c0b2f7Stbbdev if (!newBlock) return NULL; 1858*51c0b2f7Stbbdev } 1859*51c0b2f7Stbbdev { 1860*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(startupMallocLock); 1861*51c0b2f7Stbbdev // Re-check whether we need a new block (conditions might have changed) 1862*51c0b2f7Stbbdev if (!firstStartupBlock || firstStartupBlock->availableSize() < reqSize) { 1863*51c0b2f7Stbbdev if (!newBlock) { 1864*51c0b2f7Stbbdev newBlock = StartupBlock::getBlock(); 1865*51c0b2f7Stbbdev if (!newBlock) return NULL; 1866*51c0b2f7Stbbdev } 1867*51c0b2f7Stbbdev newBlock->next = (Block*)firstStartupBlock; 1868*51c0b2f7Stbbdev if (firstStartupBlock) 1869*51c0b2f7Stbbdev firstStartupBlock->previous = (Block*)newBlock; 1870*51c0b2f7Stbbdev firstStartupBlock = newBlock; 1871*51c0b2f7Stbbdev } else 1872*51c0b2f7Stbbdev newBlockUnused = true; 1873*51c0b2f7Stbbdev result = firstStartupBlock->bumpPtr; 1874*51c0b2f7Stbbdev firstStartupBlock->allocatedCount++; 1875*51c0b2f7Stbbdev firstStartupBlock->bumpPtr = 1876*51c0b2f7Stbbdev (FreeObject *)((uintptr_t)firstStartupBlock->bumpPtr + reqSize); 1877*51c0b2f7Stbbdev } 1878*51c0b2f7Stbbdev if (newBlock && newBlockUnused) 1879*51c0b2f7Stbbdev defaultMemPool->returnEmptyBlock(newBlock, /*poolTheBlock=*/false); 1880*51c0b2f7Stbbdev 1881*51c0b2f7Stbbdev // keep object size at the negative offset 1882*51c0b2f7Stbbdev *((size_t*)result) = size; 1883*51c0b2f7Stbbdev return (FreeObject*)((size_t*)result+1); 1884*51c0b2f7Stbbdev } 1885*51c0b2f7Stbbdev 1886*51c0b2f7Stbbdev void StartupBlock::free(void *ptr) 1887*51c0b2f7Stbbdev { 1888*51c0b2f7Stbbdev Block* blockToRelease = NULL; 1889*51c0b2f7Stbbdev { 1890*51c0b2f7Stbbdev MallocMutex::scoped_lock scoped_cs(startupMallocLock); 1891*51c0b2f7Stbbdev 1892*51c0b2f7Stbbdev MALLOC_ASSERT(firstStartupBlock, ASSERT_TEXT); 1893*51c0b2f7Stbbdev MALLOC_ASSERT(startupAllocObjSizeMark==objectSize 1894*51c0b2f7Stbbdev && allocatedCount>0, ASSERT_TEXT); 1895*51c0b2f7Stbbdev MALLOC_ASSERT((uintptr_t)ptr>=(uintptr_t)this+sizeof(StartupBlock) 1896*51c0b2f7Stbbdev && (uintptr_t)ptr+StartupBlock::msize(ptr)<=(uintptr_t)this+slabSize, 1897*51c0b2f7Stbbdev ASSERT_TEXT); 1898*51c0b2f7Stbbdev if (0 == --allocatedCount) { 1899*51c0b2f7Stbbdev if (this == firstStartupBlock) 1900*51c0b2f7Stbbdev firstStartupBlock = (StartupBlock*)firstStartupBlock->next; 1901*51c0b2f7Stbbdev if (previous) 1902*51c0b2f7Stbbdev previous->next = next; 1903*51c0b2f7Stbbdev if (next) 1904*51c0b2f7Stbbdev next->previous = previous; 1905*51c0b2f7Stbbdev blockToRelease = this; 1906*51c0b2f7Stbbdev } else if ((uintptr_t)ptr + StartupBlock::msize(ptr) == (uintptr_t)bumpPtr) { 1907*51c0b2f7Stbbdev // last object in the block released 1908*51c0b2f7Stbbdev FreeObject *newBump = (FreeObject*)((size_t*)ptr - 1); 1909*51c0b2f7Stbbdev MALLOC_ASSERT((uintptr_t)newBump>(uintptr_t)this+sizeof(StartupBlock), 1910*51c0b2f7Stbbdev ASSERT_TEXT); 1911*51c0b2f7Stbbdev bumpPtr = newBump; 1912*51c0b2f7Stbbdev } 1913*51c0b2f7Stbbdev } 1914*51c0b2f7Stbbdev if (blockToRelease) { 1915*51c0b2f7Stbbdev blockToRelease->previous = blockToRelease->next = NULL; 1916*51c0b2f7Stbbdev defaultMemPool->returnEmptyBlock(blockToRelease, /*poolTheBlock=*/false); 1917*51c0b2f7Stbbdev } 1918*51c0b2f7Stbbdev } 1919*51c0b2f7Stbbdev 1920*51c0b2f7Stbbdev #endif /* MALLOC_CHECK_RECURSION */ 1921*51c0b2f7Stbbdev 1922*51c0b2f7Stbbdev /********* End thread related code *************/ 1923*51c0b2f7Stbbdev 1924*51c0b2f7Stbbdev /********* Library initialization *************/ 1925*51c0b2f7Stbbdev 1926*51c0b2f7Stbbdev //! Value indicating the state of initialization. 1927*51c0b2f7Stbbdev /* 0 = initialization not started. 1928*51c0b2f7Stbbdev * 1 = initialization started but not finished. 1929*51c0b2f7Stbbdev * 2 = initialization finished. 1930*51c0b2f7Stbbdev * In theory, we only need values 0 and 2. But value 1 is nonetheless 1931*51c0b2f7Stbbdev * useful for detecting errors in the double-check pattern. 1932*51c0b2f7Stbbdev */ 1933*51c0b2f7Stbbdev static std::atomic<intptr_t> mallocInitialized{0}; // implicitly initialized to 0 1934*51c0b2f7Stbbdev static MallocMutex initMutex; 1935*51c0b2f7Stbbdev 1936*51c0b2f7Stbbdev /** The leading "\0" is here so that applying "strings" to the binary 1937*51c0b2f7Stbbdev delivers a clean result. */ 1938*51c0b2f7Stbbdev static char VersionString[] = "\0" TBBMALLOC_VERSION_STRINGS; 1939*51c0b2f7Stbbdev 1940*51c0b2f7Stbbdev #if USE_PTHREAD && (__TBB_SOURCE_DIRECTLY_INCLUDED || __TBB_USE_DLOPEN_REENTRANCY_WORKAROUND) 1941*51c0b2f7Stbbdev 1942*51c0b2f7Stbbdev /* Decrease race interval between dynamic library unloading and pthread key 1943*51c0b2f7Stbbdev destructor. Protect only Pthreads with supported unloading. */ 1944*51c0b2f7Stbbdev class ShutdownSync { 1945*51c0b2f7Stbbdev /* flag is the number of threads in pthread key dtor body 1946*51c0b2f7Stbbdev (i.e., between threadDtorStart() and threadDtorDone()) 1947*51c0b2f7Stbbdev or the signal to skip dtor, if flag < 0 */ 1948*51c0b2f7Stbbdev std::atomic<intptr_t> flag; 1949*51c0b2f7Stbbdev static const intptr_t skipDtor = INTPTR_MIN/2; 1950*51c0b2f7Stbbdev public: 1951*51c0b2f7Stbbdev void init() { flag.store(0, std::memory_order_release); } 1952*51c0b2f7Stbbdev /* Suppose that 2*abs(skipDtor) or more threads never call threadDtorStart() 1953*51c0b2f7Stbbdev simultaneously, so flag never becomes negative because of that. */ 1954*51c0b2f7Stbbdev bool threadDtorStart() { 1955*51c0b2f7Stbbdev if (flag.load(std::memory_order_acquire) < 0) 1956*51c0b2f7Stbbdev return false; 1957*51c0b2f7Stbbdev if (++flag <= 0) { // note that new value returned 1958*51c0b2f7Stbbdev flag.fetch_sub(1); // flag is spoiled by us, restore it 1959*51c0b2f7Stbbdev return false; 1960*51c0b2f7Stbbdev } 1961*51c0b2f7Stbbdev return true; 1962*51c0b2f7Stbbdev } 1963*51c0b2f7Stbbdev void threadDtorDone() { 1964*51c0b2f7Stbbdev flag.fetch_sub(1); 1965*51c0b2f7Stbbdev } 1966*51c0b2f7Stbbdev void processExit() { 1967*51c0b2f7Stbbdev if (flag.fetch_add(skipDtor) != 0) { 1968*51c0b2f7Stbbdev SpinWaitUntilEq(flag, skipDtor); 1969*51c0b2f7Stbbdev } 1970*51c0b2f7Stbbdev } 1971*51c0b2f7Stbbdev }; 1972*51c0b2f7Stbbdev 1973*51c0b2f7Stbbdev #else 1974*51c0b2f7Stbbdev 1975*51c0b2f7Stbbdev class ShutdownSync { 1976*51c0b2f7Stbbdev public: 1977*51c0b2f7Stbbdev void init() { } 1978*51c0b2f7Stbbdev bool threadDtorStart() { return true; } 1979*51c0b2f7Stbbdev void threadDtorDone() { } 1980*51c0b2f7Stbbdev void processExit() { } 1981*51c0b2f7Stbbdev }; 1982*51c0b2f7Stbbdev 1983*51c0b2f7Stbbdev #endif // USE_PTHREAD && (__TBB_SOURCE_DIRECTLY_INCLUDED || __TBB_USE_DLOPEN_REENTRANCY_WORKAROUND) 1984*51c0b2f7Stbbdev 1985*51c0b2f7Stbbdev static ShutdownSync shutdownSync; 1986*51c0b2f7Stbbdev 1987*51c0b2f7Stbbdev inline bool isMallocInitialized() { 1988*51c0b2f7Stbbdev // Load must have acquire fence; otherwise thread taking "initialized" path 1989*51c0b2f7Stbbdev // might perform textually later loads *before* mallocInitialized becomes 2. 1990*51c0b2f7Stbbdev return 2 == mallocInitialized.load(std::memory_order_acquire); 1991*51c0b2f7Stbbdev } 1992*51c0b2f7Stbbdev 1993*51c0b2f7Stbbdev bool isMallocInitializedExt() { 1994*51c0b2f7Stbbdev return isMallocInitialized(); 1995*51c0b2f7Stbbdev } 1996*51c0b2f7Stbbdev 1997*51c0b2f7Stbbdev /* Caller is responsible for ensuring this routine is called exactly once. */ 1998*51c0b2f7Stbbdev extern "C" void MallocInitializeITT() { 1999*51c0b2f7Stbbdev #if __TBB_USE_ITT_NOTIFY 2000*51c0b2f7Stbbdev if (!usedBySrcIncluded) 2001*51c0b2f7Stbbdev tbb::detail::r1::__TBB_load_ittnotify(); 2002*51c0b2f7Stbbdev #endif 2003*51c0b2f7Stbbdev } 2004*51c0b2f7Stbbdev 2005*51c0b2f7Stbbdev void MemoryPool::initDefaultPool() { 2006*51c0b2f7Stbbdev hugePages.init(); 2007*51c0b2f7Stbbdev } 2008*51c0b2f7Stbbdev 2009*51c0b2f7Stbbdev /* 2010*51c0b2f7Stbbdev * Allocator initialization routine; 2011*51c0b2f7Stbbdev * it is called lazily on the very first scalable_malloc call. 2012*51c0b2f7Stbbdev */ 2013*51c0b2f7Stbbdev static bool initMemoryManager() 2014*51c0b2f7Stbbdev { 2015*51c0b2f7Stbbdev TRACEF(( "[ScalableMalloc trace] sizeof(Block) is %d (expected 128); sizeof(uintptr_t) is %d\n", 2016*51c0b2f7Stbbdev sizeof(Block), sizeof(uintptr_t) )); 2017*51c0b2f7Stbbdev MALLOC_ASSERT( 2*blockHeaderAlignment == sizeof(Block), ASSERT_TEXT ); 2018*51c0b2f7Stbbdev MALLOC_ASSERT( sizeof(FreeObject) == sizeof(void*), ASSERT_TEXT ); 2019*51c0b2f7Stbbdev MALLOC_ASSERT( isAligned(defaultMemPool, sizeof(intptr_t)), 2020*51c0b2f7Stbbdev "Memory pool must be void*-aligned for atomic to work over aligned arguments."); 2021*51c0b2f7Stbbdev 2022*51c0b2f7Stbbdev #if USE_WINTHREAD 2023*51c0b2f7Stbbdev const size_t granularity = 64*1024; // granulatity of VirtualAlloc 2024*51c0b2f7Stbbdev #else 2025*51c0b2f7Stbbdev // POSIX.1-2001-compliant way to get page size 2026*51c0b2f7Stbbdev const size_t granularity = sysconf(_SC_PAGESIZE); 2027*51c0b2f7Stbbdev #endif 2028*51c0b2f7Stbbdev if (!defaultMemPool) { 2029*51c0b2f7Stbbdev // Do not rely on static constructors and do the assignment in case 2030*51c0b2f7Stbbdev // of library static section not initialized at this call yet. 2031*51c0b2f7Stbbdev defaultMemPool = (MemoryPool*)defaultMemPool_space; 2032*51c0b2f7Stbbdev } 2033*51c0b2f7Stbbdev bool initOk = defaultMemPool-> 2034*51c0b2f7Stbbdev extMemPool.init(0, NULL, NULL, granularity, 2035*51c0b2f7Stbbdev /*keepAllMemory=*/false, /*fixedPool=*/false); 2036*51c0b2f7Stbbdev // TODO: extMemPool.init() to not allocate memory 2037*51c0b2f7Stbbdev if (!initOk || !initBackRefMaster(&defaultMemPool->extMemPool.backend) || !ThreadId::init()) 2038*51c0b2f7Stbbdev return false; 2039*51c0b2f7Stbbdev MemoryPool::initDefaultPool(); 2040*51c0b2f7Stbbdev // init() is required iff initMemoryManager() is called 2041*51c0b2f7Stbbdev // after mallocProcessShutdownNotification() 2042*51c0b2f7Stbbdev shutdownSync.init(); 2043*51c0b2f7Stbbdev #if COLLECT_STATISTICS 2044*51c0b2f7Stbbdev initStatisticsCollection(); 2045*51c0b2f7Stbbdev #endif 2046*51c0b2f7Stbbdev return true; 2047*51c0b2f7Stbbdev } 2048*51c0b2f7Stbbdev 2049*51c0b2f7Stbbdev static bool GetBoolEnvironmentVariable(const char* name) { 2050*51c0b2f7Stbbdev return tbb::detail::r1::GetBoolEnvironmentVariable(name); 2051*51c0b2f7Stbbdev } 2052*51c0b2f7Stbbdev 2053*51c0b2f7Stbbdev //! Ensures that initMemoryManager() is called once and only once. 2054*51c0b2f7Stbbdev /** Does not return until initMemoryManager() has been completed by a thread. 2055*51c0b2f7Stbbdev There is no need to call this routine if mallocInitialized==2 . */ 2056*51c0b2f7Stbbdev static bool doInitialization() 2057*51c0b2f7Stbbdev { 2058*51c0b2f7Stbbdev MallocMutex::scoped_lock lock( initMutex ); 2059*51c0b2f7Stbbdev if (mallocInitialized.load(std::memory_order_relaxed)!=2) { 2060*51c0b2f7Stbbdev MALLOC_ASSERT( mallocInitialized.load(std::memory_order_relaxed)==0, ASSERT_TEXT ); 2061*51c0b2f7Stbbdev mallocInitialized.store(1, std::memory_order_relaxed); 2062*51c0b2f7Stbbdev RecursiveMallocCallProtector scoped; 2063*51c0b2f7Stbbdev if (!initMemoryManager()) { 2064*51c0b2f7Stbbdev mallocInitialized.store(0, std::memory_order_relaxed); // restore and out 2065*51c0b2f7Stbbdev return false; 2066*51c0b2f7Stbbdev } 2067*51c0b2f7Stbbdev #ifdef MALLOC_EXTRA_INITIALIZATION 2068*51c0b2f7Stbbdev MALLOC_EXTRA_INITIALIZATION; 2069*51c0b2f7Stbbdev #endif 2070*51c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION 2071*51c0b2f7Stbbdev RecursiveMallocCallProtector::detectNaiveOverload(); 2072*51c0b2f7Stbbdev #endif 2073*51c0b2f7Stbbdev MALLOC_ASSERT( mallocInitialized.load(std::memory_order_relaxed)==1, ASSERT_TEXT ); 2074*51c0b2f7Stbbdev // Store must have release fence, otherwise mallocInitialized==2 2075*51c0b2f7Stbbdev // might become remotely visible before side effects of 2076*51c0b2f7Stbbdev // initMemoryManager() become remotely visible. 2077*51c0b2f7Stbbdev mallocInitialized.store(2, std::memory_order_release); 2078*51c0b2f7Stbbdev if( GetBoolEnvironmentVariable("TBB_VERSION") ) { 2079*51c0b2f7Stbbdev fputs(VersionString+1,stderr); 2080*51c0b2f7Stbbdev hugePages.printStatus(); 2081*51c0b2f7Stbbdev } 2082*51c0b2f7Stbbdev } 2083*51c0b2f7Stbbdev /* It can't be 0 or I would have initialized it */ 2084*51c0b2f7Stbbdev MALLOC_ASSERT( mallocInitialized.load(std::memory_order_relaxed)==2, ASSERT_TEXT ); 2085*51c0b2f7Stbbdev return true; 2086*51c0b2f7Stbbdev } 2087*51c0b2f7Stbbdev 2088*51c0b2f7Stbbdev /********* End library initialization *************/ 2089*51c0b2f7Stbbdev 2090*51c0b2f7Stbbdev /********* The malloc show begins *************/ 2091*51c0b2f7Stbbdev 2092*51c0b2f7Stbbdev 2093*51c0b2f7Stbbdev FreeObject *Block::allocateFromFreeList() 2094*51c0b2f7Stbbdev { 2095*51c0b2f7Stbbdev FreeObject *result; 2096*51c0b2f7Stbbdev 2097*51c0b2f7Stbbdev if (!freeList) return NULL; 2098*51c0b2f7Stbbdev 2099*51c0b2f7Stbbdev result = freeList; 2100*51c0b2f7Stbbdev MALLOC_ASSERT( result, ASSERT_TEXT ); 2101*51c0b2f7Stbbdev 2102*51c0b2f7Stbbdev freeList = result->next; 2103*51c0b2f7Stbbdev MALLOC_ASSERT( allocatedCount < (slabSize-sizeof(Block))/objectSize, ASSERT_TEXT ); 2104*51c0b2f7Stbbdev allocatedCount++; 2105*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), allocFreeListUsed); 2106*51c0b2f7Stbbdev 2107*51c0b2f7Stbbdev return result; 2108*51c0b2f7Stbbdev } 2109*51c0b2f7Stbbdev 2110*51c0b2f7Stbbdev FreeObject *Block::allocateFromBumpPtr() 2111*51c0b2f7Stbbdev { 2112*51c0b2f7Stbbdev FreeObject *result = bumpPtr; 2113*51c0b2f7Stbbdev if (result) { 2114*51c0b2f7Stbbdev bumpPtr = (FreeObject *) ((uintptr_t) bumpPtr - objectSize); 2115*51c0b2f7Stbbdev if ( (uintptr_t)bumpPtr < (uintptr_t)this+sizeof(Block) ) { 2116*51c0b2f7Stbbdev bumpPtr = NULL; 2117*51c0b2f7Stbbdev } 2118*51c0b2f7Stbbdev MALLOC_ASSERT( allocatedCount < (slabSize-sizeof(Block))/objectSize, ASSERT_TEXT ); 2119*51c0b2f7Stbbdev allocatedCount++; 2120*51c0b2f7Stbbdev STAT_increment(getThreadId(), getIndex(objectSize), allocBumpPtrUsed); 2121*51c0b2f7Stbbdev } 2122*51c0b2f7Stbbdev return result; 2123*51c0b2f7Stbbdev } 2124*51c0b2f7Stbbdev 2125*51c0b2f7Stbbdev inline FreeObject* Block::allocate() 2126*51c0b2f7Stbbdev { 2127*51c0b2f7Stbbdev MALLOC_ASSERT( isOwnedByCurrentThread(), ASSERT_TEXT ); 2128*51c0b2f7Stbbdev 2129*51c0b2f7Stbbdev /* for better cache locality, first looking in the free list. */ 2130*51c0b2f7Stbbdev if ( FreeObject *result = allocateFromFreeList() ) { 2131*51c0b2f7Stbbdev return result; 2132*51c0b2f7Stbbdev } 2133*51c0b2f7Stbbdev MALLOC_ASSERT( !freeList, ASSERT_TEXT ); 2134*51c0b2f7Stbbdev 2135*51c0b2f7Stbbdev /* if free list is empty, try thread local bump pointer allocation. */ 2136*51c0b2f7Stbbdev if ( FreeObject *result = allocateFromBumpPtr() ) { 2137*51c0b2f7Stbbdev return result; 2138*51c0b2f7Stbbdev } 2139*51c0b2f7Stbbdev MALLOC_ASSERT( !bumpPtr, ASSERT_TEXT ); 2140*51c0b2f7Stbbdev 2141*51c0b2f7Stbbdev /* the block is considered full. */ 2142*51c0b2f7Stbbdev isFull = true; 2143*51c0b2f7Stbbdev return NULL; 2144*51c0b2f7Stbbdev } 2145*51c0b2f7Stbbdev 2146*51c0b2f7Stbbdev size_t Block::findObjectSize(void *object) const 2147*51c0b2f7Stbbdev { 2148*51c0b2f7Stbbdev size_t blSize = getSize(); 2149*51c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION 2150*51c0b2f7Stbbdev // Currently, there is no aligned allocations from startup blocks, 2151*51c0b2f7Stbbdev // so we can return just StartupBlock::msize(). 2152*51c0b2f7Stbbdev // TODO: This must be extended if we add aligned allocation from startup blocks. 2153*51c0b2f7Stbbdev if (!blSize) 2154*51c0b2f7Stbbdev return StartupBlock::msize(object); 2155*51c0b2f7Stbbdev #endif 2156*51c0b2f7Stbbdev // object can be aligned, so real size can be less than block's 2157*51c0b2f7Stbbdev size_t size = 2158*51c0b2f7Stbbdev blSize - ((uintptr_t)object - (uintptr_t)findObjectToFree(object)); 2159*51c0b2f7Stbbdev MALLOC_ASSERT(size>0 && size<minLargeObjectSize, ASSERT_TEXT); 2160*51c0b2f7Stbbdev return size; 2161*51c0b2f7Stbbdev } 2162*51c0b2f7Stbbdev 2163*51c0b2f7Stbbdev void Bin::moveBlockToFront(Block *block) 2164*51c0b2f7Stbbdev { 2165*51c0b2f7Stbbdev /* move the block to the front of the bin */ 2166*51c0b2f7Stbbdev if (block == activeBlk) return; 2167*51c0b2f7Stbbdev outofTLSBin(block); 2168*51c0b2f7Stbbdev pushTLSBin(block); 2169*51c0b2f7Stbbdev } 2170*51c0b2f7Stbbdev 2171*51c0b2f7Stbbdev void Bin::processEmptyBlock(Block *block, bool poolTheBlock) 2172*51c0b2f7Stbbdev { 2173*51c0b2f7Stbbdev if (block != activeBlk) { 2174*51c0b2f7Stbbdev /* We are not using this block; return it to the pool */ 2175*51c0b2f7Stbbdev outofTLSBin(block); 2176*51c0b2f7Stbbdev block->getMemPool()->returnEmptyBlock(block, poolTheBlock); 2177*51c0b2f7Stbbdev } else { 2178*51c0b2f7Stbbdev /* all objects are free - let's restore the bump pointer */ 2179*51c0b2f7Stbbdev block->restoreBumpPtr(); 2180*51c0b2f7Stbbdev } 2181*51c0b2f7Stbbdev } 2182*51c0b2f7Stbbdev 2183*51c0b2f7Stbbdev template<int LOW_MARK, int HIGH_MARK> 2184*51c0b2f7Stbbdev bool LocalLOCImpl<LOW_MARK, HIGH_MARK>::put(LargeMemoryBlock *object, ExtMemoryPool *extMemPool) 2185*51c0b2f7Stbbdev { 2186*51c0b2f7Stbbdev const size_t size = object->unalignedSize; 2187*51c0b2f7Stbbdev // not spoil cache with too large object, that can cause its total cleanup 2188*51c0b2f7Stbbdev if (size > MAX_TOTAL_SIZE) 2189*51c0b2f7Stbbdev return false; 2190*51c0b2f7Stbbdev LargeMemoryBlock *localHead = head.exchange(NULL); 2191*51c0b2f7Stbbdev 2192*51c0b2f7Stbbdev object->prev = NULL; 2193*51c0b2f7Stbbdev object->next = localHead; 2194*51c0b2f7Stbbdev if (localHead) 2195*51c0b2f7Stbbdev localHead->prev = object; 2196*51c0b2f7Stbbdev else { 2197*51c0b2f7Stbbdev // those might not be cleaned during local cache stealing, correct them 2198*51c0b2f7Stbbdev totalSize = 0; 2199*51c0b2f7Stbbdev numOfBlocks = 0; 2200*51c0b2f7Stbbdev tail = object; 2201*51c0b2f7Stbbdev } 2202*51c0b2f7Stbbdev localHead = object; 2203*51c0b2f7Stbbdev totalSize += size; 2204*51c0b2f7Stbbdev numOfBlocks++; 2205*51c0b2f7Stbbdev // must meet both size and number of cached objects constrains 2206*51c0b2f7Stbbdev if (totalSize > MAX_TOTAL_SIZE || numOfBlocks >= HIGH_MARK) { 2207*51c0b2f7Stbbdev // scanning from tail until meet conditions 2208*51c0b2f7Stbbdev while (totalSize > MAX_TOTAL_SIZE || numOfBlocks > LOW_MARK) { 2209*51c0b2f7Stbbdev totalSize -= tail->unalignedSize; 2210*51c0b2f7Stbbdev numOfBlocks--; 2211*51c0b2f7Stbbdev tail = tail->prev; 2212*51c0b2f7Stbbdev } 2213*51c0b2f7Stbbdev LargeMemoryBlock *headToRelease = tail->next; 2214*51c0b2f7Stbbdev tail->next = NULL; 2215*51c0b2f7Stbbdev 2216*51c0b2f7Stbbdev extMemPool->freeLargeObjectList(headToRelease); 2217*51c0b2f7Stbbdev } 2218*51c0b2f7Stbbdev 2219*51c0b2f7Stbbdev head.store(localHead, std::memory_order_release); 2220*51c0b2f7Stbbdev return true; 2221*51c0b2f7Stbbdev } 2222*51c0b2f7Stbbdev 2223*51c0b2f7Stbbdev template<int LOW_MARK, int HIGH_MARK> 2224*51c0b2f7Stbbdev LargeMemoryBlock *LocalLOCImpl<LOW_MARK, HIGH_MARK>::get(size_t size) 2225*51c0b2f7Stbbdev { 2226*51c0b2f7Stbbdev LargeMemoryBlock *localHead, *res = NULL; 2227*51c0b2f7Stbbdev 2228*51c0b2f7Stbbdev if (size > MAX_TOTAL_SIZE) 2229*51c0b2f7Stbbdev return NULL; 2230*51c0b2f7Stbbdev 2231*51c0b2f7Stbbdev // TBB_REVAMP_TODO: review this line 2232*51c0b2f7Stbbdev if (!head.load(std::memory_order_acquire) || (localHead = head.exchange(NULL)) == NULL) { 2233*51c0b2f7Stbbdev // do not restore totalSize, numOfBlocks and tail at this point, 2234*51c0b2f7Stbbdev // as they are used only in put(), where they must be restored 2235*51c0b2f7Stbbdev return NULL; 2236*51c0b2f7Stbbdev } 2237*51c0b2f7Stbbdev 2238*51c0b2f7Stbbdev for (LargeMemoryBlock *curr = localHead; curr; curr=curr->next) { 2239*51c0b2f7Stbbdev if (curr->unalignedSize == size) { 2240*51c0b2f7Stbbdev res = curr; 2241*51c0b2f7Stbbdev if (curr->next) 2242*51c0b2f7Stbbdev curr->next->prev = curr->prev; 2243*51c0b2f7Stbbdev else 2244*51c0b2f7Stbbdev tail = curr->prev; 2245*51c0b2f7Stbbdev if (curr != localHead) 2246*51c0b2f7Stbbdev curr->prev->next = curr->next; 2247*51c0b2f7Stbbdev else 2248*51c0b2f7Stbbdev localHead = curr->next; 2249*51c0b2f7Stbbdev totalSize -= size; 2250*51c0b2f7Stbbdev numOfBlocks--; 2251*51c0b2f7Stbbdev break; 2252*51c0b2f7Stbbdev } 2253*51c0b2f7Stbbdev } 2254*51c0b2f7Stbbdev 2255*51c0b2f7Stbbdev head.store(localHead, std::memory_order_release); 2256*51c0b2f7Stbbdev return res; 2257*51c0b2f7Stbbdev } 2258*51c0b2f7Stbbdev 2259*51c0b2f7Stbbdev template<int LOW_MARK, int HIGH_MARK> 2260*51c0b2f7Stbbdev bool LocalLOCImpl<LOW_MARK, HIGH_MARK>::externalCleanup(ExtMemoryPool *extMemPool) 2261*51c0b2f7Stbbdev { 2262*51c0b2f7Stbbdev if (LargeMemoryBlock *localHead = head.exchange(NULL)) { 2263*51c0b2f7Stbbdev extMemPool->freeLargeObjectList(localHead); 2264*51c0b2f7Stbbdev return true; 2265*51c0b2f7Stbbdev } 2266*51c0b2f7Stbbdev return false; 2267*51c0b2f7Stbbdev } 2268*51c0b2f7Stbbdev 2269*51c0b2f7Stbbdev void *MemoryPool::getFromLLOCache(TLSData* tls, size_t size, size_t alignment) 2270*51c0b2f7Stbbdev { 2271*51c0b2f7Stbbdev LargeMemoryBlock *lmb = NULL; 2272*51c0b2f7Stbbdev 2273*51c0b2f7Stbbdev size_t headersSize = sizeof(LargeMemoryBlock)+sizeof(LargeObjectHdr); 2274*51c0b2f7Stbbdev size_t allocationSize = LargeObjectCache::alignToBin(size+headersSize+alignment); 2275*51c0b2f7Stbbdev if (allocationSize < size) // allocationSize is wrapped around after alignToBin 2276*51c0b2f7Stbbdev return NULL; 2277*51c0b2f7Stbbdev MALLOC_ASSERT(allocationSize >= alignment, "Overflow must be checked before."); 2278*51c0b2f7Stbbdev 2279*51c0b2f7Stbbdev if (tls) { 2280*51c0b2f7Stbbdev tls->markUsed(); 2281*51c0b2f7Stbbdev lmb = tls->lloc.get(allocationSize); 2282*51c0b2f7Stbbdev } 2283*51c0b2f7Stbbdev if (!lmb) 2284*51c0b2f7Stbbdev lmb = extMemPool.mallocLargeObject(this, allocationSize); 2285*51c0b2f7Stbbdev 2286*51c0b2f7Stbbdev if (lmb) { 2287*51c0b2f7Stbbdev // doing shuffle we suppose that alignment offset guarantees 2288*51c0b2f7Stbbdev // that different cache lines are in use 2289*51c0b2f7Stbbdev MALLOC_ASSERT(alignment >= estimatedCacheLineSize, ASSERT_TEXT); 2290*51c0b2f7Stbbdev 2291*51c0b2f7Stbbdev void *alignedArea = (void*)alignUp((uintptr_t)lmb+headersSize, alignment); 2292*51c0b2f7Stbbdev uintptr_t alignedRight = 2293*51c0b2f7Stbbdev alignDown((uintptr_t)lmb+lmb->unalignedSize - size, alignment); 2294*51c0b2f7Stbbdev // Has some room to shuffle object between cache lines? 2295*51c0b2f7Stbbdev // Note that alignedRight and alignedArea are aligned at alignment. 2296*51c0b2f7Stbbdev unsigned ptrDelta = alignedRight - (uintptr_t)alignedArea; 2297*51c0b2f7Stbbdev if (ptrDelta && tls) { // !tls is cold path 2298*51c0b2f7Stbbdev // for the hot path of alignment==estimatedCacheLineSize, 2299*51c0b2f7Stbbdev // allow compilers to use shift for division 2300*51c0b2f7Stbbdev // (since estimatedCacheLineSize is a power-of-2 constant) 2301*51c0b2f7Stbbdev unsigned numOfPossibleOffsets = alignment == estimatedCacheLineSize? 2302*51c0b2f7Stbbdev ptrDelta / estimatedCacheLineSize : 2303*51c0b2f7Stbbdev ptrDelta / alignment; 2304*51c0b2f7Stbbdev unsigned myCacheIdx = ++tls->currCacheIdx; 2305*51c0b2f7Stbbdev unsigned offset = myCacheIdx % numOfPossibleOffsets; 2306*51c0b2f7Stbbdev 2307*51c0b2f7Stbbdev // Move object to a cache line with an offset that is different from 2308*51c0b2f7Stbbdev // previous allocation. This supposedly allows us to use cache 2309*51c0b2f7Stbbdev // associativity more efficiently. 2310*51c0b2f7Stbbdev alignedArea = (void*)((uintptr_t)alignedArea + offset*alignment); 2311*51c0b2f7Stbbdev } 2312*51c0b2f7Stbbdev MALLOC_ASSERT((uintptr_t)lmb+lmb->unalignedSize >= 2313*51c0b2f7Stbbdev (uintptr_t)alignedArea+size, "Object doesn't fit the block."); 2314*51c0b2f7Stbbdev LargeObjectHdr *header = (LargeObjectHdr*)alignedArea-1; 2315*51c0b2f7Stbbdev header->memoryBlock = lmb; 2316*51c0b2f7Stbbdev header->backRefIdx = lmb->backRefIdx; 2317*51c0b2f7Stbbdev setBackRef(header->backRefIdx, header); 2318*51c0b2f7Stbbdev 2319*51c0b2f7Stbbdev lmb->objectSize = size; 2320*51c0b2f7Stbbdev 2321*51c0b2f7Stbbdev MALLOC_ASSERT( isLargeObject<unknownMem>(alignedArea), ASSERT_TEXT ); 2322*51c0b2f7Stbbdev MALLOC_ASSERT( isAligned(alignedArea, alignment), ASSERT_TEXT ); 2323*51c0b2f7Stbbdev 2324*51c0b2f7Stbbdev return alignedArea; 2325*51c0b2f7Stbbdev } 2326*51c0b2f7Stbbdev return NULL; 2327*51c0b2f7Stbbdev } 2328*51c0b2f7Stbbdev 2329*51c0b2f7Stbbdev void MemoryPool::putToLLOCache(TLSData *tls, void *object) 2330*51c0b2f7Stbbdev { 2331*51c0b2f7Stbbdev LargeObjectHdr *header = (LargeObjectHdr*)object - 1; 2332*51c0b2f7Stbbdev // overwrite backRefIdx to simplify double free detection 2333*51c0b2f7Stbbdev header->backRefIdx = BackRefIdx(); 2334*51c0b2f7Stbbdev 2335*51c0b2f7Stbbdev if (tls) { 2336*51c0b2f7Stbbdev tls->markUsed(); 2337*51c0b2f7Stbbdev if (tls->lloc.put(header->memoryBlock, &extMemPool)) 2338*51c0b2f7Stbbdev return; 2339*51c0b2f7Stbbdev } 2340*51c0b2f7Stbbdev extMemPool.freeLargeObject(header->memoryBlock); 2341*51c0b2f7Stbbdev } 2342*51c0b2f7Stbbdev 2343*51c0b2f7Stbbdev /* 2344*51c0b2f7Stbbdev * All aligned allocations fall into one of the following categories: 2345*51c0b2f7Stbbdev * 1. if both request size and alignment are <= maxSegregatedObjectSize, 2346*51c0b2f7Stbbdev * we just align the size up, and request this amount, because for every size 2347*51c0b2f7Stbbdev * aligned to some power of 2, the allocated object is at least that aligned. 2348*51c0b2f7Stbbdev * 2. for size<minLargeObjectSize, check if already guaranteed fittingAlignment is enough. 2349*51c0b2f7Stbbdev * 3. if size+alignment<minLargeObjectSize, we take an object of fittingSizeN and align 2350*51c0b2f7Stbbdev * its address up; given such pointer, scalable_free could find the real object. 2351*51c0b2f7Stbbdev * Wrapping of size+alignment is impossible because maximal allowed 2352*51c0b2f7Stbbdev * alignment plus minLargeObjectSize can't lead to wrapping. 2353*51c0b2f7Stbbdev * 4. otherwise, aligned large object is allocated. 2354*51c0b2f7Stbbdev */ 2355*51c0b2f7Stbbdev static void *allocateAligned(MemoryPool *memPool, size_t size, size_t alignment) 2356*51c0b2f7Stbbdev { 2357*51c0b2f7Stbbdev MALLOC_ASSERT( isPowerOfTwo(alignment), ASSERT_TEXT ); 2358*51c0b2f7Stbbdev 2359*51c0b2f7Stbbdev if (!isMallocInitialized()) 2360*51c0b2f7Stbbdev if (!doInitialization()) 2361*51c0b2f7Stbbdev return NULL; 2362*51c0b2f7Stbbdev 2363*51c0b2f7Stbbdev void *result; 2364*51c0b2f7Stbbdev if (size<=maxSegregatedObjectSize && alignment<=maxSegregatedObjectSize) 2365*51c0b2f7Stbbdev result = internalPoolMalloc(memPool, alignUp(size? size: sizeof(size_t), alignment)); 2366*51c0b2f7Stbbdev else if (size<minLargeObjectSize) { 2367*51c0b2f7Stbbdev if (alignment<=fittingAlignment) 2368*51c0b2f7Stbbdev result = internalPoolMalloc(memPool, size); 2369*51c0b2f7Stbbdev else if (size+alignment < minLargeObjectSize) { 2370*51c0b2f7Stbbdev void *unaligned = internalPoolMalloc(memPool, size+alignment); 2371*51c0b2f7Stbbdev if (!unaligned) return NULL; 2372*51c0b2f7Stbbdev result = alignUp(unaligned, alignment); 2373*51c0b2f7Stbbdev } else 2374*51c0b2f7Stbbdev goto LargeObjAlloc; 2375*51c0b2f7Stbbdev } else { 2376*51c0b2f7Stbbdev LargeObjAlloc: 2377*51c0b2f7Stbbdev TLSData *tls = memPool->getTLS(/*create=*/true); 2378*51c0b2f7Stbbdev // take into account only alignment that are higher then natural 2379*51c0b2f7Stbbdev result = 2380*51c0b2f7Stbbdev memPool->getFromLLOCache(tls, size, largeObjectAlignment>alignment? 2381*51c0b2f7Stbbdev largeObjectAlignment: alignment); 2382*51c0b2f7Stbbdev } 2383*51c0b2f7Stbbdev 2384*51c0b2f7Stbbdev MALLOC_ASSERT( isAligned(result, alignment), ASSERT_TEXT ); 2385*51c0b2f7Stbbdev return result; 2386*51c0b2f7Stbbdev } 2387*51c0b2f7Stbbdev 2388*51c0b2f7Stbbdev static void *reallocAligned(MemoryPool *memPool, void *ptr, 2389*51c0b2f7Stbbdev size_t newSize, size_t alignment = 0) 2390*51c0b2f7Stbbdev { 2391*51c0b2f7Stbbdev void *result; 2392*51c0b2f7Stbbdev size_t copySize; 2393*51c0b2f7Stbbdev 2394*51c0b2f7Stbbdev if (isLargeObject<ourMem>(ptr)) { 2395*51c0b2f7Stbbdev LargeMemoryBlock* lmb = ((LargeObjectHdr *)ptr - 1)->memoryBlock; 2396*51c0b2f7Stbbdev copySize = lmb->unalignedSize-((uintptr_t)ptr-(uintptr_t)lmb); 2397*51c0b2f7Stbbdev 2398*51c0b2f7Stbbdev // Apply different strategies if size decreases 2399*51c0b2f7Stbbdev if (newSize <= copySize && (0 == alignment || isAligned(ptr, alignment))) { 2400*51c0b2f7Stbbdev 2401*51c0b2f7Stbbdev // For huge objects (that do not fit in backend cache), keep the same space unless 2402*51c0b2f7Stbbdev // the new size is at least twice smaller 2403*51c0b2f7Stbbdev bool isMemoryBlockHuge = copySize > memPool->extMemPool.backend.getMaxBinnedSize(); 2404*51c0b2f7Stbbdev size_t threshold = isMemoryBlockHuge ? copySize / 2 : 0; 2405*51c0b2f7Stbbdev if (newSize > threshold) { 2406*51c0b2f7Stbbdev lmb->objectSize = newSize; 2407*51c0b2f7Stbbdev return ptr; 2408*51c0b2f7Stbbdev } 2409*51c0b2f7Stbbdev // TODO: For large objects suitable for the backend cache, 2410*51c0b2f7Stbbdev // split out the excessive part and put it to the backend. 2411*51c0b2f7Stbbdev } 2412*51c0b2f7Stbbdev // Reallocate for real 2413*51c0b2f7Stbbdev copySize = lmb->objectSize; 2414*51c0b2f7Stbbdev #if BACKEND_HAS_MREMAP 2415*51c0b2f7Stbbdev if (void *r = memPool->extMemPool.remap(ptr, copySize, newSize, 2416*51c0b2f7Stbbdev alignment < largeObjectAlignment ? largeObjectAlignment : alignment)) 2417*51c0b2f7Stbbdev return r; 2418*51c0b2f7Stbbdev #endif 2419*51c0b2f7Stbbdev result = alignment ? allocateAligned(memPool, newSize, alignment) : 2420*51c0b2f7Stbbdev internalPoolMalloc(memPool, newSize); 2421*51c0b2f7Stbbdev 2422*51c0b2f7Stbbdev } else { 2423*51c0b2f7Stbbdev Block* block = (Block *)alignDown(ptr, slabSize); 2424*51c0b2f7Stbbdev copySize = block->findObjectSize(ptr); 2425*51c0b2f7Stbbdev 2426*51c0b2f7Stbbdev // TODO: Move object to another bin if size decreases and the current bin is "empty enough". 2427*51c0b2f7Stbbdev // Currently, in case of size decreasing, old pointer is returned 2428*51c0b2f7Stbbdev if (newSize <= copySize && (0==alignment || isAligned(ptr, alignment))) { 2429*51c0b2f7Stbbdev return ptr; 2430*51c0b2f7Stbbdev } else { 2431*51c0b2f7Stbbdev result = alignment ? allocateAligned(memPool, newSize, alignment) : 2432*51c0b2f7Stbbdev internalPoolMalloc(memPool, newSize); 2433*51c0b2f7Stbbdev } 2434*51c0b2f7Stbbdev } 2435*51c0b2f7Stbbdev if (result) { 2436*51c0b2f7Stbbdev memcpy(result, ptr, copySize < newSize ? copySize : newSize); 2437*51c0b2f7Stbbdev internalPoolFree(memPool, ptr, 0); 2438*51c0b2f7Stbbdev } 2439*51c0b2f7Stbbdev return result; 2440*51c0b2f7Stbbdev } 2441*51c0b2f7Stbbdev 2442*51c0b2f7Stbbdev /* A predicate checks if an object is properly placed inside its block */ 2443*51c0b2f7Stbbdev inline bool Block::isProperlyPlaced(const void *object) const 2444*51c0b2f7Stbbdev { 2445*51c0b2f7Stbbdev return 0 == ((uintptr_t)this + slabSize - (uintptr_t)object) % objectSize; 2446*51c0b2f7Stbbdev } 2447*51c0b2f7Stbbdev 2448*51c0b2f7Stbbdev /* Finds the real object inside the block */ 2449*51c0b2f7Stbbdev FreeObject *Block::findAllocatedObject(const void *address) const 2450*51c0b2f7Stbbdev { 2451*51c0b2f7Stbbdev // calculate offset from the end of the block space 2452*51c0b2f7Stbbdev uint16_t offset = (uintptr_t)this + slabSize - (uintptr_t)address; 2453*51c0b2f7Stbbdev MALLOC_ASSERT( offset<=slabSize-sizeof(Block), ASSERT_TEXT ); 2454*51c0b2f7Stbbdev // find offset difference from a multiple of allocation size 2455*51c0b2f7Stbbdev offset %= objectSize; 2456*51c0b2f7Stbbdev // and move the address down to where the real object starts. 2457*51c0b2f7Stbbdev return (FreeObject*)((uintptr_t)address - (offset? objectSize-offset: 0)); 2458*51c0b2f7Stbbdev } 2459*51c0b2f7Stbbdev 2460*51c0b2f7Stbbdev /* 2461*51c0b2f7Stbbdev * Bad dereference caused by a foreign pointer is possible only here, not earlier in call chain. 2462*51c0b2f7Stbbdev * Separate function isolates SEH code, as it has bad influence on compiler optimization. 2463*51c0b2f7Stbbdev */ 2464*51c0b2f7Stbbdev static inline BackRefIdx safer_dereference (const BackRefIdx *ptr) 2465*51c0b2f7Stbbdev { 2466*51c0b2f7Stbbdev BackRefIdx id; 2467*51c0b2f7Stbbdev #if _MSC_VER 2468*51c0b2f7Stbbdev __try { 2469*51c0b2f7Stbbdev #endif 2470*51c0b2f7Stbbdev id = *ptr; 2471*51c0b2f7Stbbdev #if _MSC_VER 2472*51c0b2f7Stbbdev } __except( GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION? 2473*51c0b2f7Stbbdev EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ) { 2474*51c0b2f7Stbbdev id = BackRefIdx(); 2475*51c0b2f7Stbbdev } 2476*51c0b2f7Stbbdev #endif 2477*51c0b2f7Stbbdev return id; 2478*51c0b2f7Stbbdev } 2479*51c0b2f7Stbbdev 2480*51c0b2f7Stbbdev template<MemoryOrigin memOrigin> 2481*51c0b2f7Stbbdev bool isLargeObject(void *object) 2482*51c0b2f7Stbbdev { 2483*51c0b2f7Stbbdev if (!isAligned(object, largeObjectAlignment)) 2484*51c0b2f7Stbbdev return false; 2485*51c0b2f7Stbbdev LargeObjectHdr *header = (LargeObjectHdr*)object - 1; 2486*51c0b2f7Stbbdev BackRefIdx idx = (memOrigin == unknownMem) ? 2487*51c0b2f7Stbbdev safer_dereference(&header->backRefIdx) : header->backRefIdx; 2488*51c0b2f7Stbbdev 2489*51c0b2f7Stbbdev return idx.isLargeObject() 2490*51c0b2f7Stbbdev // in valid LargeObjectHdr memoryBlock is not NULL 2491*51c0b2f7Stbbdev && header->memoryBlock 2492*51c0b2f7Stbbdev // in valid LargeObjectHdr memoryBlock points somewhere before header 2493*51c0b2f7Stbbdev // TODO: more strict check 2494*51c0b2f7Stbbdev && (uintptr_t)header->memoryBlock < (uintptr_t)header 2495*51c0b2f7Stbbdev && getBackRef(idx) == header; 2496*51c0b2f7Stbbdev } 2497*51c0b2f7Stbbdev 2498*51c0b2f7Stbbdev static inline bool isSmallObject (void *ptr) 2499*51c0b2f7Stbbdev { 2500*51c0b2f7Stbbdev Block* expectedBlock = (Block*)alignDown(ptr, slabSize); 2501*51c0b2f7Stbbdev const BackRefIdx* idx = expectedBlock->getBackRefIdx(); 2502*51c0b2f7Stbbdev 2503*51c0b2f7Stbbdev bool isSmall = expectedBlock == getBackRef(safer_dereference(idx)); 2504*51c0b2f7Stbbdev if (isSmall) 2505*51c0b2f7Stbbdev expectedBlock->checkFreePrecond(ptr); 2506*51c0b2f7Stbbdev return isSmall; 2507*51c0b2f7Stbbdev } 2508*51c0b2f7Stbbdev 2509*51c0b2f7Stbbdev /**** Check if an object was allocated by scalable_malloc ****/ 2510*51c0b2f7Stbbdev static inline bool isRecognized (void* ptr) 2511*51c0b2f7Stbbdev { 2512*51c0b2f7Stbbdev return defaultMemPool->extMemPool.backend.ptrCanBeValid(ptr) && 2513*51c0b2f7Stbbdev (isLargeObject<unknownMem>(ptr) || isSmallObject(ptr)); 2514*51c0b2f7Stbbdev } 2515*51c0b2f7Stbbdev 2516*51c0b2f7Stbbdev static inline void freeSmallObject(void *object) 2517*51c0b2f7Stbbdev { 2518*51c0b2f7Stbbdev /* mask low bits to get the block */ 2519*51c0b2f7Stbbdev Block *block = (Block *)alignDown(object, slabSize); 2520*51c0b2f7Stbbdev block->checkFreePrecond(object); 2521*51c0b2f7Stbbdev 2522*51c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION 2523*51c0b2f7Stbbdev if (block->isStartupAllocObject()) { 2524*51c0b2f7Stbbdev ((StartupBlock *)block)->free(object); 2525*51c0b2f7Stbbdev return; 2526*51c0b2f7Stbbdev } 2527*51c0b2f7Stbbdev #endif 2528*51c0b2f7Stbbdev if (block->isOwnedByCurrentThread()) { 2529*51c0b2f7Stbbdev block->freeOwnObject(object); 2530*51c0b2f7Stbbdev } else { /* Slower path to add to the shared list, the allocatedCount is updated by the owner thread in malloc. */ 2531*51c0b2f7Stbbdev FreeObject *objectToFree = block->findObjectToFree(object); 2532*51c0b2f7Stbbdev block->freePublicObject(objectToFree); 2533*51c0b2f7Stbbdev } 2534*51c0b2f7Stbbdev } 2535*51c0b2f7Stbbdev 2536*51c0b2f7Stbbdev static void *internalPoolMalloc(MemoryPool* memPool, size_t size) 2537*51c0b2f7Stbbdev { 2538*51c0b2f7Stbbdev Bin* bin; 2539*51c0b2f7Stbbdev Block * mallocBlock; 2540*51c0b2f7Stbbdev 2541*51c0b2f7Stbbdev if (!memPool) return NULL; 2542*51c0b2f7Stbbdev 2543*51c0b2f7Stbbdev if (!size) size = sizeof(size_t); 2544*51c0b2f7Stbbdev 2545*51c0b2f7Stbbdev TLSData *tls = memPool->getTLS(/*create=*/true); 2546*51c0b2f7Stbbdev 2547*51c0b2f7Stbbdev /* Allocate a large object */ 2548*51c0b2f7Stbbdev if (size >= minLargeObjectSize) 2549*51c0b2f7Stbbdev return memPool->getFromLLOCache(tls, size, largeObjectAlignment); 2550*51c0b2f7Stbbdev 2551*51c0b2f7Stbbdev if (!tls) return NULL; 2552*51c0b2f7Stbbdev 2553*51c0b2f7Stbbdev tls->markUsed(); 2554*51c0b2f7Stbbdev /* 2555*51c0b2f7Stbbdev * Get an element in thread-local array corresponding to the given size; 2556*51c0b2f7Stbbdev * It keeps ptr to the active block for allocations of this size 2557*51c0b2f7Stbbdev */ 2558*51c0b2f7Stbbdev bin = tls->getAllocationBin(size); 2559*51c0b2f7Stbbdev if ( !bin ) return NULL; 2560*51c0b2f7Stbbdev 2561*51c0b2f7Stbbdev /* Get a block to try to allocate in. */ 2562*51c0b2f7Stbbdev for( mallocBlock = bin->getActiveBlock(); mallocBlock; 2563*51c0b2f7Stbbdev mallocBlock = bin->setPreviousBlockActive() ) // the previous block should be empty enough 2564*51c0b2f7Stbbdev { 2565*51c0b2f7Stbbdev if( FreeObject *result = mallocBlock->allocate() ) 2566*51c0b2f7Stbbdev return result; 2567*51c0b2f7Stbbdev } 2568*51c0b2f7Stbbdev 2569*51c0b2f7Stbbdev /* 2570*51c0b2f7Stbbdev * else privatize publicly freed objects in some block and allocate from it 2571*51c0b2f7Stbbdev */ 2572*51c0b2f7Stbbdev mallocBlock = bin->getPrivatizedFreeListBlock(); 2573*51c0b2f7Stbbdev if (mallocBlock) { 2574*51c0b2f7Stbbdev MALLOC_ASSERT( mallocBlock->freeListNonNull(), ASSERT_TEXT ); 2575*51c0b2f7Stbbdev if ( FreeObject *result = mallocBlock->allocateFromFreeList() ) 2576*51c0b2f7Stbbdev return result; 2577*51c0b2f7Stbbdev /* Else something strange happened, need to retry from the beginning; */ 2578*51c0b2f7Stbbdev TRACEF(( "[ScalableMalloc trace] Something is wrong: no objects in public free list; reentering.\n" )); 2579*51c0b2f7Stbbdev return internalPoolMalloc(memPool, size); 2580*51c0b2f7Stbbdev } 2581*51c0b2f7Stbbdev 2582*51c0b2f7Stbbdev /* 2583*51c0b2f7Stbbdev * no suitable own blocks, try to get a partial block that some other thread has discarded. 2584*51c0b2f7Stbbdev */ 2585*51c0b2f7Stbbdev mallocBlock = memPool->extMemPool.orphanedBlocks.get(tls, size); 2586*51c0b2f7Stbbdev while (mallocBlock) { 2587*51c0b2f7Stbbdev bin->pushTLSBin(mallocBlock); 2588*51c0b2f7Stbbdev bin->setActiveBlock(mallocBlock); // TODO: move under the below condition? 2589*51c0b2f7Stbbdev if( FreeObject *result = mallocBlock->allocate() ) 2590*51c0b2f7Stbbdev return result; 2591*51c0b2f7Stbbdev mallocBlock = memPool->extMemPool.orphanedBlocks.get(tls, size); 2592*51c0b2f7Stbbdev } 2593*51c0b2f7Stbbdev 2594*51c0b2f7Stbbdev /* 2595*51c0b2f7Stbbdev * else try to get a new empty block 2596*51c0b2f7Stbbdev */ 2597*51c0b2f7Stbbdev mallocBlock = memPool->getEmptyBlock(size); 2598*51c0b2f7Stbbdev if (mallocBlock) { 2599*51c0b2f7Stbbdev bin->pushTLSBin(mallocBlock); 2600*51c0b2f7Stbbdev bin->setActiveBlock(mallocBlock); 2601*51c0b2f7Stbbdev if( FreeObject *result = mallocBlock->allocate() ) 2602*51c0b2f7Stbbdev return result; 2603*51c0b2f7Stbbdev /* Else something strange happened, need to retry from the beginning; */ 2604*51c0b2f7Stbbdev TRACEF(( "[ScalableMalloc trace] Something is wrong: no objects in empty block; reentering.\n" )); 2605*51c0b2f7Stbbdev return internalPoolMalloc(memPool, size); 2606*51c0b2f7Stbbdev } 2607*51c0b2f7Stbbdev /* 2608*51c0b2f7Stbbdev * else nothing works so return NULL 2609*51c0b2f7Stbbdev */ 2610*51c0b2f7Stbbdev TRACEF(( "[ScalableMalloc trace] No memory found, returning NULL.\n" )); 2611*51c0b2f7Stbbdev return NULL; 2612*51c0b2f7Stbbdev } 2613*51c0b2f7Stbbdev 2614*51c0b2f7Stbbdev // When size==0 (i.e. unknown), detect here whether the object is large. 2615*51c0b2f7Stbbdev // For size is known and < minLargeObjectSize, we still need to check 2616*51c0b2f7Stbbdev // if the actual object is large, because large objects might be used 2617*51c0b2f7Stbbdev // for aligned small allocations. 2618*51c0b2f7Stbbdev static bool internalPoolFree(MemoryPool *memPool, void *object, size_t size) 2619*51c0b2f7Stbbdev { 2620*51c0b2f7Stbbdev if (!memPool || !object) return false; 2621*51c0b2f7Stbbdev 2622*51c0b2f7Stbbdev // The library is initialized at allocation call, so releasing while 2623*51c0b2f7Stbbdev // not initialized means foreign object is releasing. 2624*51c0b2f7Stbbdev MALLOC_ASSERT(isMallocInitialized(), ASSERT_TEXT); 2625*51c0b2f7Stbbdev MALLOC_ASSERT(memPool->extMemPool.userPool() || isRecognized(object), 2626*51c0b2f7Stbbdev "Invalid pointer during object releasing is detected."); 2627*51c0b2f7Stbbdev 2628*51c0b2f7Stbbdev if (size >= minLargeObjectSize || isLargeObject<ourMem>(object)) 2629*51c0b2f7Stbbdev memPool->putToLLOCache(memPool->getTLS(/*create=*/false), object); 2630*51c0b2f7Stbbdev else 2631*51c0b2f7Stbbdev freeSmallObject(object); 2632*51c0b2f7Stbbdev return true; 2633*51c0b2f7Stbbdev } 2634*51c0b2f7Stbbdev 2635*51c0b2f7Stbbdev static void *internalMalloc(size_t size) 2636*51c0b2f7Stbbdev { 2637*51c0b2f7Stbbdev if (!size) size = sizeof(size_t); 2638*51c0b2f7Stbbdev 2639*51c0b2f7Stbbdev #if MALLOC_CHECK_RECURSION 2640*51c0b2f7Stbbdev if (RecursiveMallocCallProtector::sameThreadActive()) 2641*51c0b2f7Stbbdev return size<minLargeObjectSize? StartupBlock::allocate(size) : 2642*51c0b2f7Stbbdev // nested allocation, so skip tls 2643*51c0b2f7Stbbdev (FreeObject*)defaultMemPool->getFromLLOCache(NULL, size, slabSize); 2644*51c0b2f7Stbbdev #endif 2645*51c0b2f7Stbbdev 2646*51c0b2f7Stbbdev if (!isMallocInitialized()) 2647*51c0b2f7Stbbdev if (!doInitialization()) 2648*51c0b2f7Stbbdev return NULL; 2649*51c0b2f7Stbbdev return internalPoolMalloc(defaultMemPool, size); 2650*51c0b2f7Stbbdev } 2651*51c0b2f7Stbbdev 2652*51c0b2f7Stbbdev static void internalFree(void *object) 2653*51c0b2f7Stbbdev { 2654*51c0b2f7Stbbdev internalPoolFree(defaultMemPool, object, 0); 2655*51c0b2f7Stbbdev } 2656*51c0b2f7Stbbdev 2657*51c0b2f7Stbbdev static size_t internalMsize(void* ptr) 2658*51c0b2f7Stbbdev { 2659*51c0b2f7Stbbdev MALLOC_ASSERT(ptr, "Invalid pointer passed to internalMsize"); 2660*51c0b2f7Stbbdev if (isLargeObject<ourMem>(ptr)) { 2661*51c0b2f7Stbbdev // TODO: return the maximum memory size, that can be written to this object 2662*51c0b2f7Stbbdev LargeMemoryBlock* lmb = ((LargeObjectHdr*)ptr - 1)->memoryBlock; 2663*51c0b2f7Stbbdev return lmb->objectSize; 2664*51c0b2f7Stbbdev } else { 2665*51c0b2f7Stbbdev Block *block = (Block*)alignDown(ptr, slabSize); 2666*51c0b2f7Stbbdev return block->findObjectSize(ptr); 2667*51c0b2f7Stbbdev } 2668*51c0b2f7Stbbdev } 2669*51c0b2f7Stbbdev 2670*51c0b2f7Stbbdev } // namespace internal 2671*51c0b2f7Stbbdev 2672*51c0b2f7Stbbdev using namespace rml::internal; 2673*51c0b2f7Stbbdev 2674*51c0b2f7Stbbdev // legacy entry point saved for compatibility with binaries complied 2675*51c0b2f7Stbbdev // with pre-6003 versions of TBB 2676*51c0b2f7Stbbdev rml::MemoryPool *pool_create(intptr_t pool_id, const MemPoolPolicy *policy) 2677*51c0b2f7Stbbdev { 2678*51c0b2f7Stbbdev rml::MemoryPool *pool; 2679*51c0b2f7Stbbdev MemPoolPolicy pol(policy->pAlloc, policy->pFree, policy->granularity); 2680*51c0b2f7Stbbdev 2681*51c0b2f7Stbbdev pool_create_v1(pool_id, &pol, &pool); 2682*51c0b2f7Stbbdev return pool; 2683*51c0b2f7Stbbdev } 2684*51c0b2f7Stbbdev 2685*51c0b2f7Stbbdev rml::MemPoolError pool_create_v1(intptr_t pool_id, const MemPoolPolicy *policy, 2686*51c0b2f7Stbbdev rml::MemoryPool **pool) 2687*51c0b2f7Stbbdev { 2688*51c0b2f7Stbbdev if ( !policy->pAlloc || policy->version<MemPoolPolicy::TBBMALLOC_POOL_VERSION 2689*51c0b2f7Stbbdev // empty pFree allowed only for fixed pools 2690*51c0b2f7Stbbdev || !(policy->fixedPool || policy->pFree)) { 2691*51c0b2f7Stbbdev *pool = NULL; 2692*51c0b2f7Stbbdev return INVALID_POLICY; 2693*51c0b2f7Stbbdev } 2694*51c0b2f7Stbbdev if ( policy->version>MemPoolPolicy::TBBMALLOC_POOL_VERSION // future versions are not supported 2695*51c0b2f7Stbbdev // new flags can be added in place of reserved, but default 2696*51c0b2f7Stbbdev // behaviour must be supported by this version 2697*51c0b2f7Stbbdev || policy->reserved ) { 2698*51c0b2f7Stbbdev *pool = NULL; 2699*51c0b2f7Stbbdev return UNSUPPORTED_POLICY; 2700*51c0b2f7Stbbdev } 2701*51c0b2f7Stbbdev if (!isMallocInitialized()) 2702*51c0b2f7Stbbdev if (!doInitialization()) { 2703*51c0b2f7Stbbdev *pool = NULL; 2704*51c0b2f7Stbbdev return NO_MEMORY; 2705*51c0b2f7Stbbdev } 2706*51c0b2f7Stbbdev rml::internal::MemoryPool *memPool = 2707*51c0b2f7Stbbdev (rml::internal::MemoryPool*)internalMalloc((sizeof(rml::internal::MemoryPool))); 2708*51c0b2f7Stbbdev if (!memPool) { 2709*51c0b2f7Stbbdev *pool = NULL; 2710*51c0b2f7Stbbdev return NO_MEMORY; 2711*51c0b2f7Stbbdev } 2712*51c0b2f7Stbbdev memset(memPool, 0, sizeof(rml::internal::MemoryPool)); 2713*51c0b2f7Stbbdev if (!memPool->init(pool_id, policy)) { 2714*51c0b2f7Stbbdev internalFree(memPool); 2715*51c0b2f7Stbbdev *pool = NULL; 2716*51c0b2f7Stbbdev return NO_MEMORY; 2717*51c0b2f7Stbbdev } 2718*51c0b2f7Stbbdev 2719*51c0b2f7Stbbdev *pool = (rml::MemoryPool*)memPool; 2720*51c0b2f7Stbbdev return POOL_OK; 2721*51c0b2f7Stbbdev } 2722*51c0b2f7Stbbdev 2723*51c0b2f7Stbbdev bool pool_destroy(rml::MemoryPool* memPool) 2724*51c0b2f7Stbbdev { 2725*51c0b2f7Stbbdev if (!memPool) return false; 2726*51c0b2f7Stbbdev bool ret = ((rml::internal::MemoryPool*)memPool)->destroy(); 2727*51c0b2f7Stbbdev internalFree(memPool); 2728*51c0b2f7Stbbdev 2729*51c0b2f7Stbbdev return ret; 2730*51c0b2f7Stbbdev } 2731*51c0b2f7Stbbdev 2732*51c0b2f7Stbbdev bool pool_reset(rml::MemoryPool* memPool) 2733*51c0b2f7Stbbdev { 2734*51c0b2f7Stbbdev if (!memPool) return false; 2735*51c0b2f7Stbbdev 2736*51c0b2f7Stbbdev return ((rml::internal::MemoryPool*)memPool)->reset(); 2737*51c0b2f7Stbbdev } 2738*51c0b2f7Stbbdev 2739*51c0b2f7Stbbdev void *pool_malloc(rml::MemoryPool* mPool, size_t size) 2740*51c0b2f7Stbbdev { 2741*51c0b2f7Stbbdev return internalPoolMalloc((rml::internal::MemoryPool*)mPool, size); 2742*51c0b2f7Stbbdev } 2743*51c0b2f7Stbbdev 2744*51c0b2f7Stbbdev void *pool_realloc(rml::MemoryPool* mPool, void *object, size_t size) 2745*51c0b2f7Stbbdev { 2746*51c0b2f7Stbbdev if (!object) 2747*51c0b2f7Stbbdev return internalPoolMalloc((rml::internal::MemoryPool*)mPool, size); 2748*51c0b2f7Stbbdev if (!size) { 2749*51c0b2f7Stbbdev internalPoolFree((rml::internal::MemoryPool*)mPool, object, 0); 2750*51c0b2f7Stbbdev return NULL; 2751*51c0b2f7Stbbdev } 2752*51c0b2f7Stbbdev return reallocAligned((rml::internal::MemoryPool*)mPool, object, size, 0); 2753*51c0b2f7Stbbdev } 2754*51c0b2f7Stbbdev 2755*51c0b2f7Stbbdev void *pool_aligned_malloc(rml::MemoryPool* mPool, size_t size, size_t alignment) 2756*51c0b2f7Stbbdev { 2757*51c0b2f7Stbbdev if (!isPowerOfTwo(alignment) || 0==size) 2758*51c0b2f7Stbbdev return NULL; 2759*51c0b2f7Stbbdev 2760*51c0b2f7Stbbdev return allocateAligned((rml::internal::MemoryPool*)mPool, size, alignment); 2761*51c0b2f7Stbbdev } 2762*51c0b2f7Stbbdev 2763*51c0b2f7Stbbdev void *pool_aligned_realloc(rml::MemoryPool* memPool, void *ptr, size_t size, size_t alignment) 2764*51c0b2f7Stbbdev { 2765*51c0b2f7Stbbdev if (!isPowerOfTwo(alignment)) 2766*51c0b2f7Stbbdev return NULL; 2767*51c0b2f7Stbbdev rml::internal::MemoryPool *mPool = (rml::internal::MemoryPool*)memPool; 2768*51c0b2f7Stbbdev void *tmp; 2769*51c0b2f7Stbbdev 2770*51c0b2f7Stbbdev if (!ptr) 2771*51c0b2f7Stbbdev tmp = allocateAligned(mPool, size, alignment); 2772*51c0b2f7Stbbdev else if (!size) { 2773*51c0b2f7Stbbdev internalPoolFree(mPool, ptr, 0); 2774*51c0b2f7Stbbdev return NULL; 2775*51c0b2f7Stbbdev } else 2776*51c0b2f7Stbbdev tmp = reallocAligned(mPool, ptr, size, alignment); 2777*51c0b2f7Stbbdev 2778*51c0b2f7Stbbdev return tmp; 2779*51c0b2f7Stbbdev } 2780*51c0b2f7Stbbdev 2781*51c0b2f7Stbbdev bool pool_free(rml::MemoryPool *mPool, void *object) 2782*51c0b2f7Stbbdev { 2783*51c0b2f7Stbbdev return internalPoolFree((rml::internal::MemoryPool*)mPool, object, 0); 2784*51c0b2f7Stbbdev } 2785*51c0b2f7Stbbdev 2786*51c0b2f7Stbbdev rml::MemoryPool *pool_identify(void *object) 2787*51c0b2f7Stbbdev { 2788*51c0b2f7Stbbdev rml::internal::MemoryPool *pool; 2789*51c0b2f7Stbbdev if (isLargeObject<ourMem>(object)) { 2790*51c0b2f7Stbbdev LargeObjectHdr *header = (LargeObjectHdr*)object - 1; 2791*51c0b2f7Stbbdev pool = header->memoryBlock->pool; 2792*51c0b2f7Stbbdev } else { 2793*51c0b2f7Stbbdev Block *block = (Block*)alignDown(object, slabSize); 2794*51c0b2f7Stbbdev pool = block->getMemPool(); 2795*51c0b2f7Stbbdev } 2796*51c0b2f7Stbbdev // do not return defaultMemPool, as it can't be used in pool_free() etc 2797*51c0b2f7Stbbdev __TBB_ASSERT_RELEASE(pool!=defaultMemPool, 2798*51c0b2f7Stbbdev "rml::pool_identify() can't be used for scalable_malloc() etc results."); 2799*51c0b2f7Stbbdev return (rml::MemoryPool*)pool; 2800*51c0b2f7Stbbdev } 2801*51c0b2f7Stbbdev 2802*51c0b2f7Stbbdev size_t pool_msize(rml::MemoryPool *mPool, void* object) 2803*51c0b2f7Stbbdev { 2804*51c0b2f7Stbbdev if (object) { 2805*51c0b2f7Stbbdev // No assert for object recognition, cause objects allocated from non-default 2806*51c0b2f7Stbbdev // memory pool do not participate in range checking and do not have valid backreferences for 2807*51c0b2f7Stbbdev // small objects. Instead, check that an object belong to the certain memory pool. 2808*51c0b2f7Stbbdev MALLOC_ASSERT_EX(mPool == pool_identify(object), "Object does not belong to the specified pool"); 2809*51c0b2f7Stbbdev return internalMsize(object); 2810*51c0b2f7Stbbdev } 2811*51c0b2f7Stbbdev errno = EINVAL; 2812*51c0b2f7Stbbdev // Unlike _msize, return 0 in case of parameter error. 2813*51c0b2f7Stbbdev // Returning size_t(-1) looks more like the way to troubles. 2814*51c0b2f7Stbbdev return 0; 2815*51c0b2f7Stbbdev } 2816*51c0b2f7Stbbdev 2817*51c0b2f7Stbbdev } // namespace rml 2818*51c0b2f7Stbbdev 2819*51c0b2f7Stbbdev using namespace rml::internal; 2820*51c0b2f7Stbbdev 2821*51c0b2f7Stbbdev #if MALLOC_TRACE 2822*51c0b2f7Stbbdev static unsigned int threadGoingDownCount = 0; 2823*51c0b2f7Stbbdev #endif 2824*51c0b2f7Stbbdev 2825*51c0b2f7Stbbdev /* 2826*51c0b2f7Stbbdev * When a thread is shutting down this routine should be called to remove all the thread ids 2827*51c0b2f7Stbbdev * from the malloc blocks and replace them with a NULL thread id. 2828*51c0b2f7Stbbdev * 2829*51c0b2f7Stbbdev * For pthreads, the function is set as a callback in pthread_key_create for TLS bin. 2830*51c0b2f7Stbbdev * It will be automatically called at thread exit with the key value as the argument, 2831*51c0b2f7Stbbdev * unless that value is NULL. 2832*51c0b2f7Stbbdev * For Windows, it is called from DllMain( DLL_THREAD_DETACH ). 2833*51c0b2f7Stbbdev * 2834*51c0b2f7Stbbdev * However neither of the above is called for the main process thread, so the routine 2835*51c0b2f7Stbbdev * also needs to be called during the process shutdown. 2836*51c0b2f7Stbbdev * 2837*51c0b2f7Stbbdev */ 2838*51c0b2f7Stbbdev // TODO: Consider making this function part of class MemoryPool. 2839*51c0b2f7Stbbdev void doThreadShutdownNotification(TLSData* tls, bool main_thread) 2840*51c0b2f7Stbbdev { 2841*51c0b2f7Stbbdev TRACEF(( "[ScalableMalloc trace] Thread id %d blocks return start %d\n", 2842*51c0b2f7Stbbdev getThreadId(), threadGoingDownCount++ )); 2843*51c0b2f7Stbbdev 2844*51c0b2f7Stbbdev #if USE_PTHREAD 2845*51c0b2f7Stbbdev if (tls) { 2846*51c0b2f7Stbbdev if (!shutdownSync.threadDtorStart()) return; 2847*51c0b2f7Stbbdev tls->getMemPool()->onThreadShutdown(tls); 2848*51c0b2f7Stbbdev shutdownSync.threadDtorDone(); 2849*51c0b2f7Stbbdev } else 2850*51c0b2f7Stbbdev #endif 2851*51c0b2f7Stbbdev { 2852*51c0b2f7Stbbdev suppress_unused_warning(tls); // not used on Windows 2853*51c0b2f7Stbbdev // The default pool is safe to use at this point: 2854*51c0b2f7Stbbdev // on Linux, only the main thread can go here before destroying defaultMemPool; 2855*51c0b2f7Stbbdev // on Windows, shutdown is synchronized via loader lock and isMallocInitialized(). 2856*51c0b2f7Stbbdev // See also __TBB_mallocProcessShutdownNotification() 2857*51c0b2f7Stbbdev defaultMemPool->onThreadShutdown(defaultMemPool->getTLS(/*create=*/false)); 2858*51c0b2f7Stbbdev // Take lock to walk through other pools; but waiting might be dangerous at this point 2859*51c0b2f7Stbbdev // (e.g. on Windows the main thread might deadlock) 2860*51c0b2f7Stbbdev bool locked; 2861*51c0b2f7Stbbdev MallocMutex::scoped_lock lock(MemoryPool::memPoolListLock, /*wait=*/!main_thread, &locked); 2862*51c0b2f7Stbbdev if (locked) { // the list is safe to process 2863*51c0b2f7Stbbdev for (MemoryPool *memPool = defaultMemPool->next; memPool; memPool = memPool->next) 2864*51c0b2f7Stbbdev memPool->onThreadShutdown(memPool->getTLS(/*create=*/false)); 2865*51c0b2f7Stbbdev } 2866*51c0b2f7Stbbdev } 2867*51c0b2f7Stbbdev 2868*51c0b2f7Stbbdev TRACEF(( "[ScalableMalloc trace] Thread id %d blocks return end\n", getThreadId() )); 2869*51c0b2f7Stbbdev } 2870*51c0b2f7Stbbdev 2871*51c0b2f7Stbbdev #if USE_PTHREAD 2872*51c0b2f7Stbbdev void mallocThreadShutdownNotification(void* arg) 2873*51c0b2f7Stbbdev { 2874*51c0b2f7Stbbdev // The routine is called for each pool (as TLS dtor) on each thread, except for the main thread 2875*51c0b2f7Stbbdev if (!isMallocInitialized()) return; 2876*51c0b2f7Stbbdev doThreadShutdownNotification((TLSData*)arg, false); 2877*51c0b2f7Stbbdev } 2878*51c0b2f7Stbbdev #else 2879*51c0b2f7Stbbdev extern "C" void __TBB_mallocThreadShutdownNotification() 2880*51c0b2f7Stbbdev { 2881*51c0b2f7Stbbdev // The routine is called once per thread on Windows 2882*51c0b2f7Stbbdev if (!isMallocInitialized()) return; 2883*51c0b2f7Stbbdev doThreadShutdownNotification(NULL, false); 2884*51c0b2f7Stbbdev } 2885*51c0b2f7Stbbdev #endif 2886*51c0b2f7Stbbdev 2887*51c0b2f7Stbbdev extern "C" void __TBB_mallocProcessShutdownNotification(bool windows_process_dying) 2888*51c0b2f7Stbbdev { 2889*51c0b2f7Stbbdev if (!isMallocInitialized()) return; 2890*51c0b2f7Stbbdev 2891*51c0b2f7Stbbdev // Don't clean allocator internals if the entire process is exiting 2892*51c0b2f7Stbbdev if (!windows_process_dying) { 2893*51c0b2f7Stbbdev doThreadShutdownNotification(NULL, /*main_thread=*/true); 2894*51c0b2f7Stbbdev } 2895*51c0b2f7Stbbdev #if __TBB_MALLOC_LOCACHE_STAT 2896*51c0b2f7Stbbdev printf("cache hit ratio %f, size hit %f\n", 2897*51c0b2f7Stbbdev 1.*cacheHits/mallocCalls, 1.*memHitKB/memAllocKB); 2898*51c0b2f7Stbbdev defaultMemPool->extMemPool.loc.reportStat(stdout); 2899*51c0b2f7Stbbdev #endif 2900*51c0b2f7Stbbdev 2901*51c0b2f7Stbbdev shutdownSync.processExit(); 2902*51c0b2f7Stbbdev #if __TBB_SOURCE_DIRECTLY_INCLUDED 2903*51c0b2f7Stbbdev /* Pthread keys must be deleted as soon as possible to not call key dtor 2904*51c0b2f7Stbbdev on thread termination when then the tbbmalloc code can be already unloaded. 2905*51c0b2f7Stbbdev */ 2906*51c0b2f7Stbbdev defaultMemPool->destroy(); 2907*51c0b2f7Stbbdev destroyBackRefMaster(&defaultMemPool->extMemPool.backend); 2908*51c0b2f7Stbbdev ThreadId::destroy(); // Delete key for thread id 2909*51c0b2f7Stbbdev hugePages.reset(); 2910*51c0b2f7Stbbdev // new total malloc initialization is possible after this point 2911*51c0b2f7Stbbdev mallocInitialized.store(0, std::memory_order_release); 2912*51c0b2f7Stbbdev #elif __TBB_USE_DLOPEN_REENTRANCY_WORKAROUND 2913*51c0b2f7Stbbdev /* In most cases we prevent unloading tbbmalloc, and don't clean up memory 2914*51c0b2f7Stbbdev on process shutdown. When impossible to prevent, library unload results 2915*51c0b2f7Stbbdev in shutdown notification, and it makes sense to release unused memory 2916*51c0b2f7Stbbdev at that point (we can't release all memory because it's possible that 2917*51c0b2f7Stbbdev it will be accessed after this point). 2918*51c0b2f7Stbbdev TODO: better support systems where we can't prevent unloading by removing 2919*51c0b2f7Stbbdev pthread destructors and releasing caches. 2920*51c0b2f7Stbbdev */ 2921*51c0b2f7Stbbdev defaultMemPool->extMemPool.hardCachesCleanup(); 2922*51c0b2f7Stbbdev #endif // __TBB_SOURCE_DIRECTLY_INCLUDED 2923*51c0b2f7Stbbdev 2924*51c0b2f7Stbbdev #if COLLECT_STATISTICS 2925*51c0b2f7Stbbdev unsigned nThreads = ThreadId::getMaxThreadId(); 2926*51c0b2f7Stbbdev for( int i=1; i<=nThreads && i<MAX_THREADS; ++i ) 2927*51c0b2f7Stbbdev STAT_print(i); 2928*51c0b2f7Stbbdev #endif 2929*51c0b2f7Stbbdev if (!usedBySrcIncluded) 2930*51c0b2f7Stbbdev MALLOC_ITT_FINI_ITTLIB(); 2931*51c0b2f7Stbbdev } 2932*51c0b2f7Stbbdev 2933*51c0b2f7Stbbdev extern "C" void * scalable_malloc(size_t size) 2934*51c0b2f7Stbbdev { 2935*51c0b2f7Stbbdev void *ptr = internalMalloc(size); 2936*51c0b2f7Stbbdev if (!ptr) errno = ENOMEM; 2937*51c0b2f7Stbbdev return ptr; 2938*51c0b2f7Stbbdev } 2939*51c0b2f7Stbbdev 2940*51c0b2f7Stbbdev extern "C" void scalable_free(void *object) 2941*51c0b2f7Stbbdev { 2942*51c0b2f7Stbbdev internalFree(object); 2943*51c0b2f7Stbbdev } 2944*51c0b2f7Stbbdev 2945*51c0b2f7Stbbdev #if MALLOC_ZONE_OVERLOAD_ENABLED 2946*51c0b2f7Stbbdev extern "C" void __TBB_malloc_free_definite_size(void *object, size_t size) 2947*51c0b2f7Stbbdev { 2948*51c0b2f7Stbbdev internalPoolFree(defaultMemPool, object, size); 2949*51c0b2f7Stbbdev } 2950*51c0b2f7Stbbdev #endif 2951*51c0b2f7Stbbdev 2952*51c0b2f7Stbbdev /* 2953*51c0b2f7Stbbdev * A variant that provides additional memory safety, by checking whether the given address 2954*51c0b2f7Stbbdev * was obtained with this allocator, and if not redirecting to the provided alternative call. 2955*51c0b2f7Stbbdev */ 2956*51c0b2f7Stbbdev extern "C" void __TBB_malloc_safer_free(void *object, void (*original_free)(void*)) 2957*51c0b2f7Stbbdev { 2958*51c0b2f7Stbbdev if (!object) 2959*51c0b2f7Stbbdev return; 2960*51c0b2f7Stbbdev 2961*51c0b2f7Stbbdev // tbbmalloc can allocate object only when tbbmalloc has been initialized 2962*51c0b2f7Stbbdev if (mallocInitialized.load(std::memory_order_acquire) && defaultMemPool->extMemPool.backend.ptrCanBeValid(object)) { 2963*51c0b2f7Stbbdev if (isLargeObject<unknownMem>(object)) { 2964*51c0b2f7Stbbdev // must check 1st for large object, because small object check touches 4 pages on left, 2965*51c0b2f7Stbbdev // and it can be inaccessible 2966*51c0b2f7Stbbdev TLSData *tls = defaultMemPool->getTLS(/*create=*/false); 2967*51c0b2f7Stbbdev 2968*51c0b2f7Stbbdev defaultMemPool->putToLLOCache(tls, object); 2969*51c0b2f7Stbbdev return; 2970*51c0b2f7Stbbdev } else if (isSmallObject(object)) { 2971*51c0b2f7Stbbdev freeSmallObject(object); 2972*51c0b2f7Stbbdev return; 2973*51c0b2f7Stbbdev } 2974*51c0b2f7Stbbdev } 2975*51c0b2f7Stbbdev if (original_free) 2976*51c0b2f7Stbbdev original_free(object); 2977*51c0b2f7Stbbdev } 2978*51c0b2f7Stbbdev 2979*51c0b2f7Stbbdev /********* End the free code *************/ 2980*51c0b2f7Stbbdev 2981*51c0b2f7Stbbdev /********* Code for scalable_realloc ***********/ 2982*51c0b2f7Stbbdev 2983*51c0b2f7Stbbdev /* 2984*51c0b2f7Stbbdev * From K&R 2985*51c0b2f7Stbbdev * "realloc changes the size of the object pointed to by p to size. The contents will 2986*51c0b2f7Stbbdev * be unchanged up to the minimum of the old and the new sizes. If the new size is larger, 2987*51c0b2f7Stbbdev * the new space is uninitialized. realloc returns a pointer to the new space, or 2988*51c0b2f7Stbbdev * NULL if the request cannot be satisfied, in which case *p is unchanged." 2989*51c0b2f7Stbbdev * 2990*51c0b2f7Stbbdev */ 2991*51c0b2f7Stbbdev extern "C" void* scalable_realloc(void* ptr, size_t size) 2992*51c0b2f7Stbbdev { 2993*51c0b2f7Stbbdev void *tmp; 2994*51c0b2f7Stbbdev 2995*51c0b2f7Stbbdev if (!ptr) 2996*51c0b2f7Stbbdev tmp = internalMalloc(size); 2997*51c0b2f7Stbbdev else if (!size) { 2998*51c0b2f7Stbbdev internalFree(ptr); 2999*51c0b2f7Stbbdev return NULL; 3000*51c0b2f7Stbbdev } else 3001*51c0b2f7Stbbdev tmp = reallocAligned(defaultMemPool, ptr, size, 0); 3002*51c0b2f7Stbbdev 3003*51c0b2f7Stbbdev if (!tmp) errno = ENOMEM; 3004*51c0b2f7Stbbdev return tmp; 3005*51c0b2f7Stbbdev } 3006*51c0b2f7Stbbdev 3007*51c0b2f7Stbbdev /* 3008*51c0b2f7Stbbdev * A variant that provides additional memory safety, by checking whether the given address 3009*51c0b2f7Stbbdev * was obtained with this allocator, and if not redirecting to the provided alternative call. 3010*51c0b2f7Stbbdev */ 3011*51c0b2f7Stbbdev extern "C" void* __TBB_malloc_safer_realloc(void* ptr, size_t sz, void* original_realloc) 3012*51c0b2f7Stbbdev { 3013*51c0b2f7Stbbdev void *tmp; // TODO: fix warnings about uninitialized use of tmp 3014*51c0b2f7Stbbdev 3015*51c0b2f7Stbbdev if (!ptr) { 3016*51c0b2f7Stbbdev tmp = internalMalloc(sz); 3017*51c0b2f7Stbbdev } else if (mallocInitialized.load(std::memory_order_acquire) && isRecognized(ptr)) { 3018*51c0b2f7Stbbdev if (!sz) { 3019*51c0b2f7Stbbdev internalFree(ptr); 3020*51c0b2f7Stbbdev return NULL; 3021*51c0b2f7Stbbdev } else { 3022*51c0b2f7Stbbdev tmp = reallocAligned(defaultMemPool, ptr, sz, 0); 3023*51c0b2f7Stbbdev } 3024*51c0b2f7Stbbdev } 3025*51c0b2f7Stbbdev #if USE_WINTHREAD 3026*51c0b2f7Stbbdev else if (original_realloc && sz) { 3027*51c0b2f7Stbbdev orig_ptrs *original_ptrs = static_cast<orig_ptrs*>(original_realloc); 3028*51c0b2f7Stbbdev if ( original_ptrs->msize ){ 3029*51c0b2f7Stbbdev size_t oldSize = original_ptrs->msize(ptr); 3030*51c0b2f7Stbbdev tmp = internalMalloc(sz); 3031*51c0b2f7Stbbdev if (tmp) { 3032*51c0b2f7Stbbdev memcpy(tmp, ptr, sz<oldSize? sz : oldSize); 3033*51c0b2f7Stbbdev if ( original_ptrs->free ){ 3034*51c0b2f7Stbbdev original_ptrs->free( ptr ); 3035*51c0b2f7Stbbdev } 3036*51c0b2f7Stbbdev } 3037*51c0b2f7Stbbdev } else 3038*51c0b2f7Stbbdev tmp = NULL; 3039*51c0b2f7Stbbdev } 3040*51c0b2f7Stbbdev #else 3041*51c0b2f7Stbbdev else if (original_realloc) { 3042*51c0b2f7Stbbdev typedef void* (*realloc_ptr_t)(void*,size_t); 3043*51c0b2f7Stbbdev realloc_ptr_t original_realloc_ptr; 3044*51c0b2f7Stbbdev (void *&)original_realloc_ptr = original_realloc; 3045*51c0b2f7Stbbdev tmp = original_realloc_ptr(ptr,sz); 3046*51c0b2f7Stbbdev } 3047*51c0b2f7Stbbdev #endif 3048*51c0b2f7Stbbdev else tmp = NULL; 3049*51c0b2f7Stbbdev 3050*51c0b2f7Stbbdev if (!tmp) errno = ENOMEM; 3051*51c0b2f7Stbbdev return tmp; 3052*51c0b2f7Stbbdev } 3053*51c0b2f7Stbbdev 3054*51c0b2f7Stbbdev /********* End code for scalable_realloc ***********/ 3055*51c0b2f7Stbbdev 3056*51c0b2f7Stbbdev /********* Code for scalable_calloc ***********/ 3057*51c0b2f7Stbbdev 3058*51c0b2f7Stbbdev /* 3059*51c0b2f7Stbbdev * From K&R 3060*51c0b2f7Stbbdev * calloc returns a pointer to space for an array of nobj objects, 3061*51c0b2f7Stbbdev * each of size size, or NULL if the request cannot be satisfied. 3062*51c0b2f7Stbbdev * The space is initialized to zero bytes. 3063*51c0b2f7Stbbdev * 3064*51c0b2f7Stbbdev */ 3065*51c0b2f7Stbbdev 3066*51c0b2f7Stbbdev extern "C" void * scalable_calloc(size_t nobj, size_t size) 3067*51c0b2f7Stbbdev { 3068*51c0b2f7Stbbdev // it's square root of maximal size_t value 3069*51c0b2f7Stbbdev const size_t mult_not_overflow = size_t(1) << (sizeof(size_t)*CHAR_BIT/2); 3070*51c0b2f7Stbbdev const size_t arraySize = nobj * size; 3071*51c0b2f7Stbbdev 3072*51c0b2f7Stbbdev // check for overflow during multiplication: 3073*51c0b2f7Stbbdev if (nobj>=mult_not_overflow || size>=mult_not_overflow) // 1) heuristic check 3074*51c0b2f7Stbbdev if (nobj && arraySize / nobj != size) { // 2) exact check 3075*51c0b2f7Stbbdev errno = ENOMEM; 3076*51c0b2f7Stbbdev return NULL; 3077*51c0b2f7Stbbdev } 3078*51c0b2f7Stbbdev void* result = internalMalloc(arraySize); 3079*51c0b2f7Stbbdev if (result) 3080*51c0b2f7Stbbdev memset(result, 0, arraySize); 3081*51c0b2f7Stbbdev else 3082*51c0b2f7Stbbdev errno = ENOMEM; 3083*51c0b2f7Stbbdev return result; 3084*51c0b2f7Stbbdev } 3085*51c0b2f7Stbbdev 3086*51c0b2f7Stbbdev /********* End code for scalable_calloc ***********/ 3087*51c0b2f7Stbbdev 3088*51c0b2f7Stbbdev /********* Code for aligned allocation API **********/ 3089*51c0b2f7Stbbdev 3090*51c0b2f7Stbbdev extern "C" int scalable_posix_memalign(void **memptr, size_t alignment, size_t size) 3091*51c0b2f7Stbbdev { 3092*51c0b2f7Stbbdev if ( !isPowerOfTwoAtLeast(alignment, sizeof(void*)) ) 3093*51c0b2f7Stbbdev return EINVAL; 3094*51c0b2f7Stbbdev void *result = allocateAligned(defaultMemPool, size, alignment); 3095*51c0b2f7Stbbdev if (!result) 3096*51c0b2f7Stbbdev return ENOMEM; 3097*51c0b2f7Stbbdev *memptr = result; 3098*51c0b2f7Stbbdev return 0; 3099*51c0b2f7Stbbdev } 3100*51c0b2f7Stbbdev 3101*51c0b2f7Stbbdev extern "C" void * scalable_aligned_malloc(size_t size, size_t alignment) 3102*51c0b2f7Stbbdev { 3103*51c0b2f7Stbbdev if (!isPowerOfTwo(alignment) || 0==size) { 3104*51c0b2f7Stbbdev errno = EINVAL; 3105*51c0b2f7Stbbdev return NULL; 3106*51c0b2f7Stbbdev } 3107*51c0b2f7Stbbdev void *tmp = allocateAligned(defaultMemPool, size, alignment); 3108*51c0b2f7Stbbdev if (!tmp) errno = ENOMEM; 3109*51c0b2f7Stbbdev return tmp; 3110*51c0b2f7Stbbdev } 3111*51c0b2f7Stbbdev 3112*51c0b2f7Stbbdev extern "C" void * scalable_aligned_realloc(void *ptr, size_t size, size_t alignment) 3113*51c0b2f7Stbbdev { 3114*51c0b2f7Stbbdev if (!isPowerOfTwo(alignment)) { 3115*51c0b2f7Stbbdev errno = EINVAL; 3116*51c0b2f7Stbbdev return NULL; 3117*51c0b2f7Stbbdev } 3118*51c0b2f7Stbbdev void *tmp; 3119*51c0b2f7Stbbdev 3120*51c0b2f7Stbbdev if (!ptr) 3121*51c0b2f7Stbbdev tmp = allocateAligned(defaultMemPool, size, alignment); 3122*51c0b2f7Stbbdev else if (!size) { 3123*51c0b2f7Stbbdev scalable_free(ptr); 3124*51c0b2f7Stbbdev return NULL; 3125*51c0b2f7Stbbdev } else 3126*51c0b2f7Stbbdev tmp = reallocAligned(defaultMemPool, ptr, size, alignment); 3127*51c0b2f7Stbbdev 3128*51c0b2f7Stbbdev if (!tmp) errno = ENOMEM; 3129*51c0b2f7Stbbdev return tmp; 3130*51c0b2f7Stbbdev } 3131*51c0b2f7Stbbdev 3132*51c0b2f7Stbbdev extern "C" void * __TBB_malloc_safer_aligned_realloc(void *ptr, size_t size, size_t alignment, void* orig_function) 3133*51c0b2f7Stbbdev { 3134*51c0b2f7Stbbdev /* corner cases left out of reallocAligned to not deal with errno there */ 3135*51c0b2f7Stbbdev if (!isPowerOfTwo(alignment)) { 3136*51c0b2f7Stbbdev errno = EINVAL; 3137*51c0b2f7Stbbdev return NULL; 3138*51c0b2f7Stbbdev } 3139*51c0b2f7Stbbdev void *tmp = NULL; 3140*51c0b2f7Stbbdev 3141*51c0b2f7Stbbdev if (!ptr) { 3142*51c0b2f7Stbbdev tmp = allocateAligned(defaultMemPool, size, alignment); 3143*51c0b2f7Stbbdev } else if (mallocInitialized.load(std::memory_order_acquire) && isRecognized(ptr)) { 3144*51c0b2f7Stbbdev if (!size) { 3145*51c0b2f7Stbbdev internalFree(ptr); 3146*51c0b2f7Stbbdev return NULL; 3147*51c0b2f7Stbbdev } else { 3148*51c0b2f7Stbbdev tmp = reallocAligned(defaultMemPool, ptr, size, alignment); 3149*51c0b2f7Stbbdev } 3150*51c0b2f7Stbbdev } 3151*51c0b2f7Stbbdev #if USE_WINTHREAD 3152*51c0b2f7Stbbdev else { 3153*51c0b2f7Stbbdev orig_aligned_ptrs *original_ptrs = static_cast<orig_aligned_ptrs*>(orig_function); 3154*51c0b2f7Stbbdev if (size) { 3155*51c0b2f7Stbbdev // Without orig_msize, we can't do anything with this. 3156*51c0b2f7Stbbdev // Just keeping old pointer. 3157*51c0b2f7Stbbdev if ( original_ptrs->aligned_msize ){ 3158*51c0b2f7Stbbdev // set alignment and offset to have possibly correct oldSize 3159*51c0b2f7Stbbdev size_t oldSize = original_ptrs->aligned_msize(ptr, sizeof(void*), 0); 3160*51c0b2f7Stbbdev tmp = allocateAligned(defaultMemPool, size, alignment); 3161*51c0b2f7Stbbdev if (tmp) { 3162*51c0b2f7Stbbdev memcpy(tmp, ptr, size<oldSize? size : oldSize); 3163*51c0b2f7Stbbdev if ( original_ptrs->aligned_free ){ 3164*51c0b2f7Stbbdev original_ptrs->aligned_free( ptr ); 3165*51c0b2f7Stbbdev } 3166*51c0b2f7Stbbdev } 3167*51c0b2f7Stbbdev } 3168*51c0b2f7Stbbdev } else { 3169*51c0b2f7Stbbdev if ( original_ptrs->aligned_free ){ 3170*51c0b2f7Stbbdev original_ptrs->aligned_free( ptr ); 3171*51c0b2f7Stbbdev } 3172*51c0b2f7Stbbdev return NULL; 3173*51c0b2f7Stbbdev } 3174*51c0b2f7Stbbdev } 3175*51c0b2f7Stbbdev #else 3176*51c0b2f7Stbbdev // As original_realloc can't align result, and there is no way to find 3177*51c0b2f7Stbbdev // size of reallocating object, we are giving up. 3178*51c0b2f7Stbbdev suppress_unused_warning(orig_function); 3179*51c0b2f7Stbbdev #endif 3180*51c0b2f7Stbbdev if (!tmp) errno = ENOMEM; 3181*51c0b2f7Stbbdev return tmp; 3182*51c0b2f7Stbbdev } 3183*51c0b2f7Stbbdev 3184*51c0b2f7Stbbdev extern "C" void scalable_aligned_free(void *ptr) 3185*51c0b2f7Stbbdev { 3186*51c0b2f7Stbbdev internalFree(ptr); 3187*51c0b2f7Stbbdev } 3188*51c0b2f7Stbbdev 3189*51c0b2f7Stbbdev /********* end code for aligned allocation API **********/ 3190*51c0b2f7Stbbdev 3191*51c0b2f7Stbbdev /********* Code for scalable_msize ***********/ 3192*51c0b2f7Stbbdev 3193*51c0b2f7Stbbdev /* 3194*51c0b2f7Stbbdev * Returns the size of a memory block allocated in the heap. 3195*51c0b2f7Stbbdev */ 3196*51c0b2f7Stbbdev extern "C" size_t scalable_msize(void* ptr) 3197*51c0b2f7Stbbdev { 3198*51c0b2f7Stbbdev if (ptr) { 3199*51c0b2f7Stbbdev MALLOC_ASSERT(isRecognized(ptr), "Invalid pointer in scalable_msize detected."); 3200*51c0b2f7Stbbdev return internalMsize(ptr); 3201*51c0b2f7Stbbdev } 3202*51c0b2f7Stbbdev errno = EINVAL; 3203*51c0b2f7Stbbdev // Unlike _msize, return 0 in case of parameter error. 3204*51c0b2f7Stbbdev // Returning size_t(-1) looks more like the way to troubles. 3205*51c0b2f7Stbbdev return 0; 3206*51c0b2f7Stbbdev } 3207*51c0b2f7Stbbdev 3208*51c0b2f7Stbbdev /* 3209*51c0b2f7Stbbdev * A variant that provides additional memory safety, by checking whether the given address 3210*51c0b2f7Stbbdev * was obtained with this allocator, and if not redirecting to the provided alternative call. 3211*51c0b2f7Stbbdev */ 3212*51c0b2f7Stbbdev extern "C" size_t __TBB_malloc_safer_msize(void *object, size_t (*original_msize)(void*)) 3213*51c0b2f7Stbbdev { 3214*51c0b2f7Stbbdev if (object) { 3215*51c0b2f7Stbbdev // Check if the memory was allocated by scalable_malloc 3216*51c0b2f7Stbbdev if (mallocInitialized.load(std::memory_order_acquire) && isRecognized(object)) 3217*51c0b2f7Stbbdev return internalMsize(object); 3218*51c0b2f7Stbbdev else if (original_msize) 3219*51c0b2f7Stbbdev return original_msize(object); 3220*51c0b2f7Stbbdev } 3221*51c0b2f7Stbbdev // object is NULL or unknown, or foreign and no original_msize 3222*51c0b2f7Stbbdev #if USE_WINTHREAD 3223*51c0b2f7Stbbdev errno = EINVAL; // errno expected to be set only on this platform 3224*51c0b2f7Stbbdev #endif 3225*51c0b2f7Stbbdev return 0; 3226*51c0b2f7Stbbdev } 3227*51c0b2f7Stbbdev 3228*51c0b2f7Stbbdev /* 3229*51c0b2f7Stbbdev * The same as above but for _aligned_msize case 3230*51c0b2f7Stbbdev */ 3231*51c0b2f7Stbbdev extern "C" size_t __TBB_malloc_safer_aligned_msize(void *object, size_t alignment, size_t offset, size_t (*orig_aligned_msize)(void*,size_t,size_t)) 3232*51c0b2f7Stbbdev { 3233*51c0b2f7Stbbdev if (object) { 3234*51c0b2f7Stbbdev // Check if the memory was allocated by scalable_malloc 3235*51c0b2f7Stbbdev if (mallocInitialized.load(std::memory_order_acquire) && isRecognized(object)) 3236*51c0b2f7Stbbdev return internalMsize(object); 3237*51c0b2f7Stbbdev else if (orig_aligned_msize) 3238*51c0b2f7Stbbdev return orig_aligned_msize(object,alignment,offset); 3239*51c0b2f7Stbbdev } 3240*51c0b2f7Stbbdev // object is NULL or unknown 3241*51c0b2f7Stbbdev errno = EINVAL; 3242*51c0b2f7Stbbdev return 0; 3243*51c0b2f7Stbbdev } 3244*51c0b2f7Stbbdev 3245*51c0b2f7Stbbdev /********* End code for scalable_msize ***********/ 3246*51c0b2f7Stbbdev 3247*51c0b2f7Stbbdev extern "C" int scalable_allocation_mode(int param, intptr_t value) 3248*51c0b2f7Stbbdev { 3249*51c0b2f7Stbbdev if (param == TBBMALLOC_SET_SOFT_HEAP_LIMIT) { 3250*51c0b2f7Stbbdev defaultMemPool->extMemPool.backend.setRecommendedMaxSize((size_t)value); 3251*51c0b2f7Stbbdev return TBBMALLOC_OK; 3252*51c0b2f7Stbbdev } else if (param == USE_HUGE_PAGES) { 3253*51c0b2f7Stbbdev #if __linux__ 3254*51c0b2f7Stbbdev switch (value) { 3255*51c0b2f7Stbbdev case 0: 3256*51c0b2f7Stbbdev case 1: 3257*51c0b2f7Stbbdev hugePages.setMode(value); 3258*51c0b2f7Stbbdev return TBBMALLOC_OK; 3259*51c0b2f7Stbbdev default: 3260*51c0b2f7Stbbdev return TBBMALLOC_INVALID_PARAM; 3261*51c0b2f7Stbbdev } 3262*51c0b2f7Stbbdev #else 3263*51c0b2f7Stbbdev return TBBMALLOC_NO_EFFECT; 3264*51c0b2f7Stbbdev #endif 3265*51c0b2f7Stbbdev #if __TBB_SOURCE_DIRECTLY_INCLUDED 3266*51c0b2f7Stbbdev } else if (param == TBBMALLOC_INTERNAL_SOURCE_INCLUDED) { 3267*51c0b2f7Stbbdev switch (value) { 3268*51c0b2f7Stbbdev case 0: // used by dynamic library 3269*51c0b2f7Stbbdev case 1: // used by static library or directly included sources 3270*51c0b2f7Stbbdev usedBySrcIncluded = value; 3271*51c0b2f7Stbbdev return TBBMALLOC_OK; 3272*51c0b2f7Stbbdev default: 3273*51c0b2f7Stbbdev return TBBMALLOC_INVALID_PARAM; 3274*51c0b2f7Stbbdev } 3275*51c0b2f7Stbbdev #endif 3276*51c0b2f7Stbbdev } else if (param == TBBMALLOC_SET_HUGE_SIZE_THRESHOLD) { 3277*51c0b2f7Stbbdev defaultMemPool->extMemPool.loc.setHugeSizeThreshold((size_t)value); 3278*51c0b2f7Stbbdev return TBBMALLOC_OK; 3279*51c0b2f7Stbbdev } 3280*51c0b2f7Stbbdev return TBBMALLOC_INVALID_PARAM; 3281*51c0b2f7Stbbdev } 3282*51c0b2f7Stbbdev 3283*51c0b2f7Stbbdev extern "C" int scalable_allocation_command(int cmd, void *param) 3284*51c0b2f7Stbbdev { 3285*51c0b2f7Stbbdev if (param) 3286*51c0b2f7Stbbdev return TBBMALLOC_INVALID_PARAM; 3287*51c0b2f7Stbbdev 3288*51c0b2f7Stbbdev bool released = false; 3289*51c0b2f7Stbbdev switch(cmd) { 3290*51c0b2f7Stbbdev case TBBMALLOC_CLEAN_THREAD_BUFFERS: 3291*51c0b2f7Stbbdev if (TLSData *tls = defaultMemPool->getTLS(/*create=*/false)) 3292*51c0b2f7Stbbdev released = tls->externalCleanup(/*cleanOnlyUnused*/false, /*cleanBins=*/true); 3293*51c0b2f7Stbbdev break; 3294*51c0b2f7Stbbdev case TBBMALLOC_CLEAN_ALL_BUFFERS: 3295*51c0b2f7Stbbdev released = defaultMemPool->extMemPool.hardCachesCleanup(); 3296*51c0b2f7Stbbdev break; 3297*51c0b2f7Stbbdev default: 3298*51c0b2f7Stbbdev return TBBMALLOC_INVALID_PARAM; 3299*51c0b2f7Stbbdev } 3300*51c0b2f7Stbbdev return released ? TBBMALLOC_OK : TBBMALLOC_NO_EFFECT; 3301*51c0b2f7Stbbdev } 3302