xref: /oneTBB/src/tbbmalloc/frontend.cpp (revision 51c0b2f7)
1*51c0b2f7Stbbdev /*
2*51c0b2f7Stbbdev     Copyright (c) 2005-2020 Intel Corporation
3*51c0b2f7Stbbdev 
4*51c0b2f7Stbbdev     Licensed under the Apache License, Version 2.0 (the "License");
5*51c0b2f7Stbbdev     you may not use this file except in compliance with the License.
6*51c0b2f7Stbbdev     You may obtain a copy of the License at
7*51c0b2f7Stbbdev 
8*51c0b2f7Stbbdev         http://www.apache.org/licenses/LICENSE-2.0
9*51c0b2f7Stbbdev 
10*51c0b2f7Stbbdev     Unless required by applicable law or agreed to in writing, software
11*51c0b2f7Stbbdev     distributed under the License is distributed on an "AS IS" BASIS,
12*51c0b2f7Stbbdev     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*51c0b2f7Stbbdev     See the License for the specific language governing permissions and
14*51c0b2f7Stbbdev     limitations under the License.
15*51c0b2f7Stbbdev */
16*51c0b2f7Stbbdev 
17*51c0b2f7Stbbdev #include "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