xref: /oneTBB/src/tbbmalloc/tbbmalloc_internal.h (revision 49e08aac)
1 /*
2     Copyright (c) 2005-2020 Intel Corporation
3 
4     Licensed under the Apache License, Version 2.0 (the "License");
5     you may not use this file except in compliance with the License.
6     You may obtain a copy of the License at
7 
8         http://www.apache.org/licenses/LICENSE-2.0
9 
10     Unless required by applicable law or agreed to in writing, software
11     distributed under the License is distributed on an "AS IS" BASIS,
12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13     See the License for the specific language governing permissions and
14     limitations under the License.
15 */
16 
17 #ifndef __TBB_tbbmalloc_internal_H
18 #define __TBB_tbbmalloc_internal_H
19 
20 #include "TypeDefinitions.h" /* Also includes customization layer Customize.h */
21 
22 #if USE_PTHREAD
23     // Some pthreads documentation says that <pthreads.h> must be first header.
24     #include <pthread.h>
25     typedef pthread_key_t tls_key_t;
26 #elif USE_WINTHREAD
27     #include <windows.h>
28     typedef DWORD tls_key_t;
29 #else
30     #error Must define USE_PTHREAD or USE_WINTHREAD
31 #endif
32 
33 #include <atomic>
34 
35 // TODO: *BSD also has it
36 #define BACKEND_HAS_MREMAP __linux__
37 #define CHECK_ALLOCATION_RANGE MALLOC_DEBUG || MALLOC_ZONE_OVERLOAD_ENABLED || MALLOC_UNIXLIKE_OVERLOAD_ENABLED
38 
39 #include "oneapi/tbb/detail/_config.h" // for __TBB_LIBSTDCPP_EXCEPTION_HEADERS_BROKEN
40 #include "oneapi/tbb/detail/_template_helpers.h"
41 #if __TBB_LIBSTDCPP_EXCEPTION_HEADERS_BROKEN
42   #define _EXCEPTION_PTR_H /* prevents exception_ptr.h inclusion */
43   #define _GLIBCXX_NESTED_EXCEPTION_H /* prevents nested_exception.h inclusion */
44 #endif
45 
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <limits.h> // for CHAR_BIT
49 #include <string.h> // for memset
50 #if MALLOC_CHECK_RECURSION
51 #include <new>        /* for placement new */
52 #endif
53 #include "oneapi/tbb/scalable_allocator.h"
54 #include "tbbmalloc_internal_api.h"
55 
56 /********* Various compile-time options        **************/
57 
58 #if !__TBB_DEFINE_MIC && __TBB_MIC_NATIVE
59  #error Intel(R) Many Integrated Core Compiler does not define __MIC__ anymore.
60 #endif
61 
62 #define MALLOC_TRACE 0
63 
64 #if MALLOC_TRACE
65 #define TRACEF(x) printf x
66 #else
67 #define TRACEF(x) ((void)0)
68 #endif /* MALLOC_TRACE */
69 
70 #define ASSERT_TEXT NULL
71 
72 #define COLLECT_STATISTICS ( MALLOC_DEBUG && MALLOCENV_COLLECT_STATISTICS )
73 #ifndef USE_INTERNAL_TID
74 #define USE_INTERNAL_TID COLLECT_STATISTICS || MALLOC_TRACE
75 #endif
76 
77 #include "Statistics.h"
78 
79 // call yield for whitebox testing, skip in real library
80 #ifndef WhiteboxTestingYield
81 #define WhiteboxTestingYield() ((void)0)
82 #endif
83 
84 
85 /********* End compile-time options        **************/
86 
87 namespace rml {
88 
89 namespace internal {
90 
91 #if __TBB_MALLOC_LOCACHE_STAT
92 extern intptr_t mallocCalls, cacheHits;
93 extern intptr_t memAllocKB, memHitKB;
94 #endif
95 
96 //! Utility template function to prevent "unused" warnings by various compilers.
97 template<typename T>
98 void suppress_unused_warning( const T& ) {}
99 
100 /********** Various global default constants ********/
101 
102 /*
103  * Default huge page size
104  */
105 static const size_t HUGE_PAGE_SIZE = 2 * 1024 * 1024;
106 
107 /********** End of global default constatns *********/
108 
109 /********** Various numeric parameters controlling allocations ********/
110 
111 /*
112  * slabSize - the size of a block for allocation of small objects,
113  * it must be larger than maxSegregatedObjectSize.
114  */
115 const uintptr_t slabSize = 16*1024;
116 
117 /*
118  * Large blocks cache cleanup frequency.
119  * It should be power of 2 for the fast checking.
120  */
121 const unsigned cacheCleanupFreq = 256;
122 
123 /*
124  * Alignment of large (>= minLargeObjectSize) objects.
125  */
126 const size_t largeObjectAlignment = estimatedCacheLineSize;
127 
128 /*
129  * This number of bins in the TLS that leads to blocks that we can allocate in.
130  */
131 const uint32_t numBlockBinLimit = 31;
132 
133 /********** End of numeric parameters controlling allocations *********/
134 
135 class BlockI;
136 class Block;
137 struct LargeMemoryBlock;
138 struct ExtMemoryPool;
139 struct MemRegion;
140 class FreeBlock;
141 class TLSData;
142 class Backend;
143 class MemoryPool;
144 struct CacheBinOperation;
145 extern const uint32_t minLargeObjectSize;
146 
147 enum DecreaseOrIncrease {
148     decrease, increase
149 };
150 
151 class TLSKey {
152     tls_key_t TLS_pointer_key;
153 public:
154     bool init();
155     bool destroy();
156     TLSData* getThreadMallocTLS() const;
157     void setThreadMallocTLS( TLSData * newvalue );
158     TLSData* createTLS(MemoryPool *memPool, Backend *backend);
159 };
160 
161 template<typename Arg, typename Compare>
162 inline void AtomicUpdate(std::atomic<Arg>& location, Arg newVal, const Compare &cmp)
163 {
164     static_assert(sizeof(Arg) == sizeof(intptr_t), "Type of argument must match AtomicCompareExchange type.");
165     Arg old = location.load(std::memory_order_acquire);
166     for (; cmp(old, newVal); ) {
167         if (location.compare_exchange_strong(old, newVal))
168             break;
169         // TODO: do we need backoff after unsuccessful CAS?
170         //old = val;
171     }
172 }
173 
174 // TODO: make BitMaskBasic more general
175 // TODO: check that BitMaskBasic is not used for synchronization
176 // (currently, it fits BitMaskMin well, but not as suitable for BitMaskMax)
177 template<unsigned NUM>
178 class BitMaskBasic {
179     static const unsigned SZ = (NUM-1)/(CHAR_BIT*sizeof(uintptr_t))+1;
180     static const unsigned WORD_LEN = CHAR_BIT*sizeof(uintptr_t);
181 
182     std::atomic<uintptr_t> mask[SZ];
183 
184 protected:
185     void set(size_t idx, bool val) {
186         MALLOC_ASSERT(idx<NUM, ASSERT_TEXT);
187 
188         size_t i = idx / WORD_LEN;
189         int pos = WORD_LEN - idx % WORD_LEN - 1;
190         if (val) {
191             mask[i].fetch_or(1ULL << pos);
192         } else {
193             mask[i].fetch_and(~(1ULL << pos));
194         }
195     }
196     int getMinTrue(unsigned startIdx) const {
197         unsigned idx = startIdx / WORD_LEN;
198         int pos;
199 
200         if (startIdx % WORD_LEN) {
201             // only interested in part of a word, clear bits before startIdx
202             pos = WORD_LEN - startIdx % WORD_LEN;
203             uintptr_t actualMask = mask[idx].load(std::memory_order_relaxed) & (((uintptr_t)1<<pos) - 1);
204             idx++;
205             if (-1 != (pos = BitScanRev(actualMask)))
206                 return idx*WORD_LEN - pos - 1;
207         }
208 
209         while (idx<SZ)
210             if (-1 != (pos = BitScanRev(mask[idx++].load(std::memory_order_relaxed))))
211                 return idx*WORD_LEN - pos - 1;
212         return -1;
213     }
214 public:
215     void reset() { for (unsigned i=0; i<SZ; i++) mask[i].store(0, std::memory_order_relaxed); }
216 };
217 
218 template<unsigned NUM>
219 class BitMaskMin : public BitMaskBasic<NUM> {
220 public:
221     void set(size_t idx, bool val) { BitMaskBasic<NUM>::set(idx, val); }
222     int getMinTrue(unsigned startIdx) const {
223         return BitMaskBasic<NUM>::getMinTrue(startIdx);
224     }
225 };
226 
227 template<unsigned NUM>
228 class BitMaskMax : public BitMaskBasic<NUM> {
229 public:
230     void set(size_t idx, bool val) {
231         BitMaskBasic<NUM>::set(NUM - 1 - idx, val);
232     }
233     int getMaxTrue(unsigned startIdx) const {
234         int p = BitMaskBasic<NUM>::getMinTrue(NUM-startIdx-1);
235         return -1==p? -1 : (int)NUM - 1 - p;
236     }
237 };
238 
239 
240 // The part of thread-specific data that can be modified by other threads.
241 // Such modifications must be protected by AllLocalCaches::listLock.
242 struct TLSRemote {
243     TLSRemote *next,
244               *prev;
245 };
246 
247 // The list of all thread-local data; supporting cleanup of thread caches
248 class AllLocalCaches {
249     TLSRemote  *head;
250     MallocMutex listLock; // protects operations in the list
251 public:
252     void registerThread(TLSRemote *tls);
253     void unregisterThread(TLSRemote *tls);
254     bool cleanup(bool cleanOnlyUnused);
255     void markUnused();
256     void reset() { head = NULL; }
257 };
258 
259 class LifoList {
260 public:
261     inline LifoList();
262     inline void push(Block *block);
263     inline Block *pop();
264     inline Block *grab();
265 
266 private:
267     Block *top;
268     MallocMutex lock;
269 };
270 
271 /*
272  * When a block that is not completely free is returned for reuse by other threads
273  * this is where the block goes.
274  *
275  * LifoList assumes zero initialization; so below its constructors are omitted,
276  * to avoid linking with C++ libraries on Linux.
277  */
278 
279 class OrphanedBlocks {
280     LifoList bins[numBlockBinLimit];
281 public:
282     Block *get(TLSData *tls, unsigned int size);
283     void put(intptr_t binTag, Block *block);
284     void reset();
285     bool cleanup(Backend* backend);
286 };
287 
288 /* Large objects entities */
289 #include "large_objects.h"
290 
291 // select index size for BackRefMaster based on word size: default is uint32_t,
292 // uint16_t for 32-bit platforms
293 template<bool>
294 struct MasterIndexSelect {
295     typedef uint32_t master_type;
296 };
297 
298 template<>
299 struct MasterIndexSelect<false> {
300     typedef uint16_t master_type;
301 };
302 
303 class BackRefIdx { // composite index to backreference array
304 public:
305     typedef MasterIndexSelect<4 < sizeof(uintptr_t)>::master_type master_t;
306 private:
307     static const master_t invalid = ~master_t(0);
308     master_t master;      // index in BackRefMaster
309     uint16_t largeObj:1;  // is this object "large"?
310     uint16_t offset  :15; // offset from beginning of BackRefBlock
311 public:
312     BackRefIdx() : master(invalid), largeObj(0), offset(0) {}
313     bool isInvalid() const { return master == invalid; }
314     bool isLargeObject() const { return largeObj; }
315     master_t getMaster() const { return master; }
316     uint16_t getOffset() const { return offset; }
317 
318     // only newBackRef can modify BackRefIdx
319     static BackRefIdx newBackRef(bool largeObj);
320 };
321 
322 // Block header is used during block coalescing
323 // and must be preserved in used blocks.
324 class BlockI {
325     intptr_t     blockState[2];
326 };
327 
328 struct LargeMemoryBlock : public BlockI {
329     MemoryPool       *pool;          // owner pool
330     LargeMemoryBlock *next,          // ptrs in list of cached blocks
331                      *prev,
332     // 2-linked list of pool's large objects
333     // Used to destroy backrefs on pool destroy (backrefs are global)
334     // and for object releasing during pool reset.
335                      *gPrev,
336                      *gNext;
337     uintptr_t         age;           // age of block while in cache
338     size_t            objectSize;    // the size requested by a client
339     size_t            unalignedSize; // the size requested from backend
340     BackRefIdx        backRefIdx;    // cached here, used copy is in LargeObjectHdr
341 };
342 
343 // Classes and methods for backend.cpp
344 #include "backend.h"
345 
346 // An TBB allocator mode that can be controlled by user
347 // via API/environment variable. Must be placed in zero-initialized memory.
348 // External synchronization assumed.
349 // TODO: TBB_VERSION support
350 class AllocControlledMode {
351     intptr_t val;
352     bool     setDone;
353 
354 public:
355     intptr_t get() const {
356         MALLOC_ASSERT(setDone, ASSERT_TEXT);
357         return val;
358     }
359 
360     // Note: set() can be called before init()
361     void set(intptr_t newVal) {
362         val = newVal;
363         setDone = true;
364     }
365 
366     bool ready() const {
367         return setDone;
368     }
369 
370     // envName - environment variable to get controlled mode
371     void initReadEnv(const char *envName, intptr_t defaultVal) {
372         if (!setDone) {
373             // unreferenced formal parameter warning
374             tbb::detail::suppress_unused_warning(envName);
375 #if !__TBB_WIN8UI_SUPPORT
376         // TODO: use strtol to get the actual value of the envirable
377             const char *envVal = getenv(envName);
378             if (envVal && !strcmp(envVal, "1"))
379                 val = 1;
380             else
381 #endif
382                 val = defaultVal;
383             setDone = true;
384         }
385     }
386 };
387 
388 // Page type to be used inside MapMemory.
389 // Regular (4KB aligned), Huge and Transparent Huge Pages (2MB aligned).
390 enum PageType {
391     REGULAR = 0,
392     PREALLOCATED_HUGE_PAGE,
393     TRANSPARENT_HUGE_PAGE
394 };
395 
396 // init() and printStatus() is called only under global initialization lock.
397 // Race is possible between registerAllocation() and registerReleasing(),
398 // harm is that up to single huge page releasing is missed (because failure
399 // to get huge page is registered only 1st time), that is negligible.
400 // setMode is also can be called concurrently.
401 // Object must reside in zero-initialized memory
402 // TODO: can we check for huge page presence during every 10th mmap() call
403 // in case huge page is released by another process?
404 class HugePagesStatus {
405 private:
406     AllocControlledMode requestedMode; // changed only by user
407                                        // to keep enabled and requestedMode consistent
408     MallocMutex setModeLock;
409     size_t      pageSize;
410     std::atomic<intptr_t> needActualStatusPrint;
411 
412     static void doPrintStatus(bool state, const char *stateName) {
413         // Under macOS* fprintf/snprintf acquires an internal lock, so when
414         // 1st allocation is done under the lock, we got a deadlock.
415         // Do not use fprintf etc during initialization.
416         fputs("TBBmalloc: huge pages\t", stderr);
417         if (!state)
418             fputs("not ", stderr);
419         fputs(stateName, stderr);
420         fputs("\n", stderr);
421     }
422 
423     void parseSystemMemInfo() {
424         bool hpAvailable  = false;
425         bool thpAvailable = false;
426         unsigned long long hugePageSize = 0;
427 
428 #if __linux__
429         // Check huge pages existence
430         unsigned long long meminfoHugePagesTotal = 0;
431 
432         parseFileItem meminfoItems[] = {
433             // Parse system huge page size
434             { "Hugepagesize: %llu kB", hugePageSize },
435             // Check if there are preallocated huge pages on the system
436             // https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
437             { "HugePages_Total: %llu", meminfoHugePagesTotal } };
438 
439         parseFile</*BUFF_SIZE=*/100>("/proc/meminfo", meminfoItems);
440 
441         // Double check another system information regarding preallocated
442         // huge pages if there are no information in /proc/meminfo
443         unsigned long long vmHugePagesTotal = 0;
444 
445         parseFileItem vmItem[] = { { "%llu", vmHugePagesTotal } };
446 
447         // We parse a counter number, it can't be huge
448         parseFile</*BUFF_SIZE=*/100>("/proc/sys/vm/nr_hugepages", vmItem);
449 
450         if (meminfoHugePagesTotal > 0 || vmHugePagesTotal > 0) {
451             MALLOC_ASSERT(hugePageSize != 0, "Huge Page size can't be zero if we found preallocated.");
452 
453             // Any non zero value clearly states that there are preallocated
454             // huge pages on the system
455             hpAvailable = true;
456         }
457 
458         // Check if there is transparent huge pages support on the system
459         unsigned long long thpPresent = 'n';
460         parseFileItem thpItem[] = { { "[alwa%cs] madvise never\n", thpPresent } };
461         parseFile</*BUFF_SIZE=*/100>("/sys/kernel/mm/transparent_hugepage/enabled", thpItem);
462 
463         if (thpPresent == 'y') {
464             MALLOC_ASSERT(hugePageSize != 0, "Huge Page size can't be zero if we found thp existence.");
465             thpAvailable = true;
466         }
467 #endif
468         MALLOC_ASSERT(!pageSize, "Huge page size can't be set twice. Double initialization.");
469 
470         // Initialize object variables
471         pageSize       = hugePageSize * 1024; // was read in KB from meminfo
472         isHPAvailable  = hpAvailable;
473         isTHPAvailable = thpAvailable;
474     }
475 
476 public:
477 
478     // System information
479     bool isHPAvailable;
480     bool isTHPAvailable;
481 
482     // User defined value
483     bool isEnabled;
484 
485     void init() {
486         parseSystemMemInfo();
487         MallocMutex::scoped_lock lock(setModeLock);
488         requestedMode.initReadEnv("TBB_MALLOC_USE_HUGE_PAGES", 0);
489         isEnabled = (isHPAvailable || isTHPAvailable) && requestedMode.get();
490     }
491 
492     // Could be set from user code at any place.
493     // If we didn't call init() at this place, isEnabled will be false
494     void setMode(intptr_t newVal) {
495         MallocMutex::scoped_lock lock(setModeLock);
496         requestedMode.set(newVal);
497         isEnabled = (isHPAvailable || isTHPAvailable) && newVal;
498     }
499 
500     bool isRequested() const {
501         return requestedMode.ready() ? requestedMode.get() : false;
502     }
503 
504     void reset() {
505         needActualStatusPrint.store(0, std::memory_order_relaxed);
506         pageSize = 0;
507         isEnabled = isHPAvailable = isTHPAvailable = false;
508     }
509 
510     // If memory mapping size is a multiple of huge page size, some OS kernels
511     // can use huge pages transparently. Use this when huge pages are requested.
512     size_t getGranularity() const {
513         if (requestedMode.ready())
514             return requestedMode.get() ? pageSize : 0;
515         else
516             return HUGE_PAGE_SIZE; // the mode is not yet known; assume typical 2MB huge pages
517     }
518 
519     void printStatus() {
520         doPrintStatus(requestedMode.get(), "requested");
521         if (requestedMode.get()) { // report actual status iff requested
522             if (pageSize)
523                 needActualStatusPrint.store(1, std::memory_order_release);
524             else
525                 doPrintStatus(/*state=*/false, "available");
526         }
527     }
528 };
529 
530 class AllLargeBlocksList {
531     MallocMutex       largeObjLock;
532     LargeMemoryBlock *loHead;
533 public:
534     void add(LargeMemoryBlock *lmb);
535     void remove(LargeMemoryBlock *lmb);
536     template<bool poolDestroy> void releaseAll(Backend *backend);
537 };
538 
539 struct ExtMemoryPool {
540     Backend           backend;
541     LargeObjectCache  loc;
542     AllLocalCaches    allLocalCaches;
543     OrphanedBlocks    orphanedBlocks;
544 
545     intptr_t          poolId;
546     // To find all large objects. Used during user pool destruction,
547     // to release all backreferences in large blocks (slab blocks do not have them).
548     AllLargeBlocksList lmbList;
549     // Callbacks to be used instead of MapMemory/UnmapMemory.
550     rawAllocType      rawAlloc;
551     rawFreeType       rawFree;
552     size_t            granularity;
553     bool              keepAllMemory,
554                       delayRegsReleasing,
555     // TODO: implements fixedPool with calling rawFree on destruction
556                       fixedPool;
557     TLSKey            tlsPointerKey;  // per-pool TLS key
558 
559     bool init(intptr_t poolId, rawAllocType rawAlloc, rawFreeType rawFree,
560               size_t granularity, bool keepAllMemory, bool fixedPool);
561     bool initTLS();
562 
563     // i.e., not system default pool for scalable_malloc/scalable_free
564     bool userPool() const { return rawAlloc; }
565 
566      // true if something has been released
567     bool softCachesCleanup();
568     bool releaseAllLocalCaches();
569     bool hardCachesCleanup();
570     void *remap(void *ptr, size_t oldSize, size_t newSize, size_t alignment);
571     bool reset() {
572         loc.reset();
573         allLocalCaches.reset();
574         orphanedBlocks.reset();
575         bool ret = tlsPointerKey.destroy();
576         backend.reset();
577         return ret;
578     }
579     bool destroy() {
580         MALLOC_ASSERT(isPoolValid(),
581                       "Possible double pool_destroy or heap corruption");
582         if (!userPool()) {
583             loc.reset();
584             allLocalCaches.reset();
585         }
586         // pthread_key_dtors must be disabled before memory unmapping
587         // TODO: race-free solution
588         bool ret = tlsPointerKey.destroy();
589         if (rawFree || !userPool())
590             ret &= backend.destroy();
591         // pool is not valid after this point
592         granularity = 0;
593         return ret;
594     }
595     void delayRegionsReleasing(bool mode) { delayRegsReleasing = mode; }
596     inline bool regionsAreReleaseable() const;
597 
598     LargeMemoryBlock *mallocLargeObject(MemoryPool *pool, size_t allocationSize);
599     void freeLargeObject(LargeMemoryBlock *lmb);
600     void freeLargeObjectList(LargeMemoryBlock *head);
601     // use granulatity as marker for pool validity
602     bool isPoolValid() const { return granularity; }
603 };
604 
605 inline bool Backend::inUserPool() const { return extMemPool->userPool(); }
606 
607 struct LargeObjectHdr {
608     LargeMemoryBlock *memoryBlock;
609     /* Backreference points to LargeObjectHdr.
610        Duplicated in LargeMemoryBlock to reuse in subsequent allocations. */
611     BackRefIdx       backRefIdx;
612 };
613 
614 struct FreeObject {
615     FreeObject  *next;
616 };
617 
618 
619 /******* A helper class to support overriding malloc with scalable_malloc *******/
620 #if MALLOC_CHECK_RECURSION
621 
622 class RecursiveMallocCallProtector {
623     // pointer to an automatic data of holding thread
624     static void       *autoObjPtr;
625     static MallocMutex rmc_mutex;
626     static pthread_t   owner_thread;
627 /* Under FreeBSD 8.0 1st call to any pthread function including pthread_self
628    leads to pthread initialization, that causes malloc calls. As 1st usage of
629    RecursiveMallocCallProtector can be before pthread initialized, pthread calls
630    can't be used in 1st instance of RecursiveMallocCallProtector.
631    RecursiveMallocCallProtector is used 1st time in checkInitialization(),
632    so there is a guarantee that on 2nd usage pthread is initialized.
633    No such situation observed with other supported OSes.
634  */
635 #if __FreeBSD__
636     static bool        canUsePthread;
637 #else
638     static const bool  canUsePthread = true;
639 #endif
640 /*
641   The variable modified in checkInitialization,
642   so can be read without memory barriers.
643  */
644     static bool mallocRecursionDetected;
645 
646     MallocMutex::scoped_lock* lock_acquired;
647     char scoped_lock_space[sizeof(MallocMutex::scoped_lock)+1];
648 
649     static uintptr_t absDiffPtr(void *x, void *y) {
650         uintptr_t xi = (uintptr_t)x, yi = (uintptr_t)y;
651         return xi > yi ? xi - yi : yi - xi;
652     }
653 public:
654 
655     RecursiveMallocCallProtector() : lock_acquired(NULL) {
656         lock_acquired = new (scoped_lock_space) MallocMutex::scoped_lock( rmc_mutex );
657         if (canUsePthread)
658             owner_thread = pthread_self();
659         autoObjPtr = &scoped_lock_space;
660     }
661     ~RecursiveMallocCallProtector() {
662         if (lock_acquired) {
663             autoObjPtr = NULL;
664             lock_acquired->~scoped_lock();
665         }
666     }
667     static bool sameThreadActive() {
668         if (!autoObjPtr) // fast path
669             return false;
670         // Some thread has an active recursive call protector; check if the current one.
671         // Exact pthread_self based test
672         if (canUsePthread) {
673             if (pthread_equal( owner_thread, pthread_self() )) {
674                 mallocRecursionDetected = true;
675                 return true;
676             } else
677                 return false;
678         }
679         // inexact stack size based test
680         const uintptr_t threadStackSz = 2*1024*1024;
681         int dummy;
682         return absDiffPtr(autoObjPtr, &dummy)<threadStackSz;
683     }
684     static bool noRecursion();
685 /* The function is called on 1st scalable_malloc call to check if malloc calls
686    scalable_malloc (nested call must set mallocRecursionDetected). */
687     static void detectNaiveOverload() {
688         if (!malloc_proxy) {
689 #if __FreeBSD__
690 /* If !canUsePthread, we can't call pthread_self() before, but now pthread
691    is already on, so can do it. */
692             if (!canUsePthread) {
693                 canUsePthread = true;
694                 owner_thread = pthread_self();
695             }
696 #endif
697             free(malloc(1));
698         }
699     }
700 };
701 
702 #else
703 
704 class RecursiveMallocCallProtector {
705 public:
706     RecursiveMallocCallProtector() {}
707     ~RecursiveMallocCallProtector() {}
708 };
709 
710 #endif  /* MALLOC_CHECK_RECURSION */
711 
712 bool isMallocInitializedExt();
713 
714 unsigned int getThreadId();
715 
716 bool initBackRefMaster(Backend *backend);
717 void destroyBackRefMaster(Backend *backend);
718 void removeBackRef(BackRefIdx backRefIdx);
719 void setBackRef(BackRefIdx backRefIdx, void *newPtr);
720 void *getBackRef(BackRefIdx backRefIdx);
721 
722 } // namespace internal
723 } // namespace rml
724 
725 #endif // __TBB_tbbmalloc_internal_H
726