1 // Copyright (c) 1999-2023 OPEN CASCADE SAS
3 // This file is part of Open CASCADE Technology software library.
5 // This library is free software; you can redistribute it and/or modify it under
6 // the terms of the GNU Lesser General Public License version 2.1 as published
7 // by the Free Software Foundation, with special exception defined in the file
8 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
9 // distribution for complete text of the license and disclaimer of any warranty.
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
14 #include <Standard.hxx>
16 #include <Standard_OutOfMemory.hxx>
20 #if(defined(_WIN32) || defined(__WIN32__))
26 #if defined(_MSC_VER) || defined(__ANDROID__) || defined(__QNX__)
28 #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && (defined(__i386) || defined(__x86_64)))
29 #include <mm_malloc.h>
31 extern "C" int posix_memalign(void** thePtr, size_t theAlign, size_t theSize);
36 static Standard::AllocatorType& allocatorTypeInstance()
38 static Standard::AllocatorType aType =
39 #ifdef OCCT_MMGT_OPT_FLEXIBLE
40 Standard::AllocatorType::NATIVE;
41 #elif defined OCCT_MMGT_OPT_TBB
42 Standard::AllocatorType::TBB;
43 #elif defined OCCT_MMGT_OPT_JEMALLOC
44 Standard::AllocatorType::JEMALLOC;
46 Standard::AllocatorType::NATIVE;
52 #ifdef OCCT_MMGT_OPT_JEMALLOC
53 #define JEMALLOC_NO_DEMANGLE
55 #endif // OCCT_MMGT_OPT_JEMALLOC
57 // paralleling with Intel TBB
59 #include <tbb/scalable_allocator.h>
61 #ifdef OCCT_MMGT_OPT_TBB
62 #undef OCCT_MMGT_OPT_TBB
64 #define scalable_malloc malloc
65 #define scalable_calloc calloc
66 #define scalable_realloc realloc
67 #define scalable_free free
70 // Available macros definition
71 // - OCCT_MMGT_OPT_FLEXIBLE, modifiable in real time
72 // - OCCT_MMGT_OPT_TBB, using tbb::scalable_allocator
73 // - OCCT_MMGT_OPT_NATIVE, using native calloc, free
74 // - OCCT_MMGT_OPT_JEMALLOC, using external jecalloc, jefree
75 #ifdef OCCT_MMGT_OPT_FLEXIBLE
76 #include <Standard_MMgrOpt.hxx>
77 #include <Standard_Assert.hxx>
79 // There is no support for environment variables in UWP
80 // OSD_Environment could not be used here because of cyclic dependency
82 #define getenv(x) NULL
85 #ifndef OCCT_MMGT_OPT_DEFAULT
86 #define OCCT_MMGT_OPT_DEFAULT 0
91 * Implementation of raw OCC memory manager which uses standard C
92 * functions: malloc (or calloc), free and realloc
93 * without any optimization
95 class Standard_MMgrRaw : public Standard_MMgrRoot
98 //! Constructor; if theToClear is True, the memory will be nullified
100 Standard_MMgrRaw(const Standard_Boolean theToClear = Standard_False)
102 myClear = theToClear;
105 //! Allocate theSize bytes
106 Standard_Address Allocate(const Standard_Size theSize) override
108 // we use ?: operator instead of if() since it is faster :-)
109 Standard_Address aPtr = (myClear ? calloc(theSize, sizeof(char)) :
112 throw Standard_OutOfMemory("Standard_MMgrRaw::Allocate(): malloc failed");
116 //! Reallocate thePtr to the size theSize.
117 //! The new pointer is returned.
118 Standard_Address Reallocate(Standard_Address thePtr,
119 const Standard_Size theSize) override
121 Standard_Address aNewStorage = (Standard_Address)realloc(thePtr, theSize);
123 throw Standard_OutOfMemory("Standard_MMgrRaw::Reallocate(): realloc failed");
124 // Note that it is not possible to ensure that additional memory
125 // allocated by realloc will be cleared (so as to satisfy myClear mode);
126 // in order to do that we would need using memset...
130 //! Free allocated memory. The pointer is nullified.
131 void Free(Standard_Address thePtr) override
137 Standard_Boolean myClear; //! Option to nullify allocated memory
140 //! Implementation of OCC memory manager which uses Intel TBB
141 //! scalable allocator.
143 //! On configurations where TBB is not available standard RTL functions
144 //! malloc() / free() are used.
145 class Standard_MMgrTBBalloc : public Standard_MMgrRoot
148 //! Constructor; if theClear is True, the memory will be nullified
150 Standard_MMgrTBBalloc(const Standard_Boolean theClear = Standard_False)
155 //! Allocate theSize bytes
156 Standard_Address Allocate(const Standard_Size theSize) override
158 // we use ?: operator instead of if() since it is faster :-)
159 Standard_Address aPtr = (myClear ? scalable_calloc(theSize, sizeof(char)) :
160 scalable_malloc(theSize));
162 throw Standard_OutOfMemory("Standard_MMgrTBBalloc::Allocate(): malloc failed");
166 //! Reallocate thePtr to the size theSize.
167 //! The new pointer is returned.
168 Standard_Address Reallocate(Standard_Address thePtr,
169 const Standard_Size theSize) override
171 Standard_Address aNewStorage = (Standard_Address)scalable_realloc(thePtr, theSize);
173 throw Standard_OutOfMemory("Standard_MMgrTBBalloc::Reallocate(): realloc failed");
174 // Note that it is not possible to ensure that additional memory
175 // allocated by realloc will be cleared (so as to satisfy myClear mode);
176 // in order to do that we would need using memset...
180 //! Free allocated memory
181 void Free(Standard_Address thePtr) override
183 scalable_free(thePtr);
187 Standard_Boolean myClear; //! Option to nullify allocated memory
190 //=======================================================================
191 //class : Standard_MMgrFactory
192 //purpose : Container for pointer to memory manager;
193 // used to construct appropriate memory manager according
194 // to environment settings, and to ensure destruction upon exit
195 //=======================================================================
196 class Standard_MMgrFactory
199 static Standard_MMgrRoot* GetMMgr();
200 ~Standard_MMgrFactory();
203 Standard_MMgrFactory();
204 Standard_MMgrFactory(const Standard_MMgrFactory&);
205 Standard_MMgrFactory& operator= (const Standard_MMgrFactory&);
208 Standard_MMgrRoot* myFMMgr;
211 //=======================================================================
212 //function : Standard_MMgrFactory
213 //purpose : Check environment variables and create appropriate memory manager
214 //=======================================================================
216 Standard_MMgrFactory::Standard_MMgrFactory()
219 /*#if defined(_MSC_VER) && (_MSC_VER > 1400)
220 // Turn ON thread-safe C locale globally to avoid side effects by setlocale() calls between threads.
221 // After this call all following _configthreadlocale() will be ignored assuming
222 // Notice that this is MSVCRT feature - on POSIX systems xlocale API (uselocale instead of setlocale)
223 // should be used explicitly to ensure thread-safety!
225 // This is not well documented call because _ENABLE_PER_THREAD_LOCALE_GLOBAL flag is defined but not implemented for some reason.
226 // -1 will set global locale flag to force _ENABLE_PER_THREAD_LOCALE_GLOBAL + _ENABLE_PER_THREAD_LOCALE_NEW behaviour
227 // although there NO way to turn it off again and following calls will have no effect (locale will be changed only for current thread).
228 _configthreadlocale (-1);
231 // Check basic assumption.
232 // If assertion happens, then OCCT should be corrected for compatibility with such CPU architecture.
233 Standard_STATIC_ASSERT(sizeof(Standard_Utf8Char) == 1);
234 Standard_STATIC_ASSERT(sizeof(short) == 2);
235 Standard_STATIC_ASSERT(sizeof(Standard_Utf16Char) == 2);
236 Standard_STATIC_ASSERT(sizeof(Standard_Utf32Char) == 4);
238 Standard_STATIC_ASSERT(sizeof(Standard_WideChar) == sizeof(Standard_Utf16Char));
242 aVar = getenv("MMGT_OPT");
243 Standard_Integer anAllocId = (aVar ? atoi(aVar) : OCCT_MMGT_OPT_DEFAULT);
245 #if defined(HAVE_TBB) && defined(_M_IX86)
248 // CR25396: Check if SSE2 instructions are supported on 32-bit x86 processor on Windows platform,
249 // if not then use MMgrRaw instead of MMgrTBBalloc.
250 // It is to avoid runtime crash when running on a CPU
251 // that supports SSE but does not support SSE2 (some modifications of AMD Sempron).
252 static const DWORD _SSE2_FEATURE_BIT(0x04000000);
253 DWORD volatile dwFeature;
261 // get the CPU feature bits
271 if ((dwFeature & _SSE2_FEATURE_BIT) == 0)
276 aVar = getenv("MMGT_CLEAR");
277 Standard_Boolean toClear = (aVar ? (atoi(aVar) != 0) : Standard_True);
279 // on Windows (actual for XP and 2000) activate low fragmentation heap
280 // for CRT heap in order to get best performance.
281 // Environment variable MMGT_LFH can be used to switch off this action (if set to 0)
282 #if defined(_MSC_VER)
283 aVar = getenv("MMGT_LFH");
284 if (aVar == NULL || atoi(aVar) != 0)
287 HANDLE aCRTHeap = (HANDLE)_get_heap_handle();
288 HeapSetInformation(aCRTHeap, HeapCompatibilityInformation, &aHeapInfo, sizeof(aHeapInfo));
294 case 1: // OCCT optimized memory allocator
296 aVar = getenv("MMGT_MMAP");
297 Standard_Boolean bMMap = (aVar ? (atoi(aVar) != 0) : Standard_True);
298 aVar = getenv("MMGT_CELLSIZE");
299 Standard_Integer aCellSize = (aVar ? atoi(aVar) : 200);
300 aVar = getenv("MMGT_NBPAGES");
301 Standard_Integer aNbPages = (aVar ? atoi(aVar) : 1000);
302 aVar = getenv("MMGT_THRESHOLD");
303 Standard_Integer aThreshold = (aVar ? atoi(aVar) : 40000);
304 myFMMgr = new Standard_MMgrOpt(toClear, bMMap, aCellSize, aNbPages, aThreshold);
307 case 2: // TBB memory allocator
308 myFMMgr = new Standard_MMgrTBBalloc(toClear);
311 default: // system default memory allocator
312 myFMMgr = new Standard_MMgrRaw(toClear);
314 allocatorTypeInstance() = static_cast<Standard::AllocatorType>(anAllocId);
317 //=======================================================================
318 //function : ~Standard_MMgrFactory
320 //=======================================================================
322 Standard_MMgrFactory::~Standard_MMgrFactory()
325 myFMMgr->Purge(Standard_True);
328 //=======================================================================
331 // This static function has a purpose to wrap static holder for memory
334 // Wrapping holder inside a function is needed to ensure that it will
335 // be initialized not later than the first call to memory manager (that
336 // would be impossible to guarantee if holder was static variable on
337 // global or file scope, because memory manager may be called from
338 // constructors of other static objects).
340 // Note that at the same time we could not guarantee that the holder
341 // object is destroyed after last call to memory manager, since that
342 // last call may be from static Handle() object which has been initialized
343 // dynamically during program execution rather than in its constructor.
345 // Therefore holder currently does not call destructor of the memory manager
346 // but only its method Purge() with Standard_True.
348 // To free the memory completely, we probably could use compiler-specific
349 // pragmas (such as '#pragma fini' on SUN Solaris and '#pragma init_seg' on
350 // WNT MSVC++) to put destructing function in code segment that is called
351 // after destructors of other (even static) objects. However, this is not
352 // done by the moment since it is compiler-dependent and there is no guarantee
353 // that some other object calling memory manager is not placed also in that segment...
355 // Note that C runtime function atexit() could not help in this problem
356 // since its behaviour is the same as for destructors of static objects
357 // (see ISO 14882:1998 "Programming languages -- C++" 3.6.3)
359 // The correct approach to deal with the problem would be to have memory manager
360 // to properly control its memory allocation and caching free blocks so
361 // as to release all memory as soon as it is returned to it, and probably
362 // even delete itself if all memory it manages has been released and
363 // last call to method Purge() was with True.
365 // Note that one possible method to control memory allocations could
366 // be counting calls to Allocate() and Free()...
368 //=======================================================================
369 Standard_MMgrRoot* Standard_MMgrFactory::GetMMgr()
371 static Standard_MMgrFactory aFactory;
372 return aFactory.myFMMgr;
375 #endif // OCCT_MMGT_OPT_FLEXIBLE
377 //=======================================================================
378 //function : Allocate
380 //=======================================================================
381 Standard::AllocatorType Standard::GetAllocatorType()
383 return allocatorTypeInstance();
386 //=======================================================================
387 //function : Allocate
389 //=======================================================================
390 Standard_Address Standard::Allocate(const Standard_Size theSize)
392 #ifdef OCCT_MMGT_OPT_FLEXIBLE
393 return Standard_MMgrFactory::GetMMgr()->Allocate(theSize);
394 #elif defined OCCT_MMGT_OPT_JEMALLOC
395 Standard_Address aPtr = je_calloc(theSize, sizeof(char));
397 throw Standard_OutOfMemory("Standard_MMgrRaw::Allocate(): malloc failed");
399 #elif defined OCCT_MMGT_OPT_TBB
400 Standard_Address aPtr = scalable_calloc(theSize, sizeof(char));
402 throw Standard_OutOfMemory("Standard_MMgrRaw::Allocate(): malloc failed");
405 Standard_Address aPtr = calloc(theSize, sizeof(char));
407 throw Standard_OutOfMemory("Standard_MMgrRaw::Allocate(): malloc failed");
409 #endif // OCCT_MMGT_OPT_FLEXIBLE
412 //=======================================================================
413 //function : AllocateOptimal
415 //=======================================================================
416 Standard_Address Standard::AllocateOptimal(const Standard_Size theSize)
418 #ifdef OCCT_MMGT_OPT_FLEXIBLE
419 return Standard_MMgrFactory::GetMMgr()->Allocate(theSize);
420 #elif defined OCCT_MMGT_OPT_JEMALLOC
421 return je_malloc(theSize);
422 #elif defined OCCT_MMGT_OPT_TBB
423 return scalable_malloc(theSize);
425 return malloc(theSize);
429 //=======================================================================
432 //=======================================================================
433 void Standard::Free(Standard_Address theStorage)
435 #ifdef OCCT_MMGT_OPT_FLEXIBLE
436 Standard_MMgrFactory::GetMMgr()->Free(theStorage);
437 #elif defined OCCT_MMGT_OPT_JEMALLOC
439 #elif defined OCCT_MMGT_OPT_TBB
440 scalable_free(theStorage);
446 //=======================================================================
447 //function : Reallocate
449 //=======================================================================
450 Standard_Address Standard::Reallocate(Standard_Address theStorage,
451 const Standard_Size theSize)
453 // Note that it is not possible to ensure that additional memory
454 // allocated by realloc will be cleared (so as to satisfy myClear mode);
455 // in order to do that we would need using memset..
456 #ifdef OCCT_MMGT_OPT_FLEXIBLE
457 return Standard_MMgrFactory::GetMMgr()->Reallocate(theStorage, theSize);
458 #elif defined OCCT_MMGT_OPT_JEMALLOC
459 Standard_Address aNewStorage = (Standard_Address)je_realloc(theStorage, theSize);
461 throw Standard_OutOfMemory("Standard_MMgrRaw::Reallocate(): realloc failed");
463 #elif defined OCCT_MMGT_OPT_TBB
464 Standard_Address aNewStorage = (Standard_Address)scalable_realloc(theStorage, theSize);
466 throw Standard_OutOfMemory("Standard_MMgrRaw::Reallocate(): realloc failed");
469 Standard_Address aNewStorage = (Standard_Address)realloc(theStorage, theSize);
471 throw Standard_OutOfMemory("Standard_MMgrRaw::Reallocate(): realloc failed");
476 //=======================================================================
479 //=======================================================================
480 Standard_Integer Standard::Purge()
482 #ifdef OCCT_MMGT_OPT_FLEXIBLE
483 return Standard_MMgrFactory::GetMMgr()->Purge();
486 #endif // OCCT_MMGT_OPT_FLEXIBLE
489 //=======================================================================
490 //function : AllocateAligned
492 //=======================================================================
493 Standard_Address Standard::AllocateAligned(const Standard_Size theSize,
494 const Standard_Size theAlign)
496 #ifdef OCCT_MMGT_OPT_JEMALLOC
497 return je_aligned_alloc(theAlign, theSize);
498 #elif defined OCCT_MMGT_OPT_TBB
499 return scalable_aligned_malloc(theSize, theAlign);
501 #if defined(_MSC_VER)
502 return _aligned_malloc(theSize, theAlign);
503 #elif defined(__ANDROID__) || defined(__QNX__)
504 return memalign(theAlign, theSize);
505 #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && (defined(__i386) || defined(__x86_64)))
506 return _mm_malloc(theSize, theAlign);
509 if (posix_memalign(&aPtr, theAlign, theSize))
518 //=======================================================================
519 //function : FreeAligned
521 //=======================================================================
522 void Standard::FreeAligned(Standard_Address thePtrAligned)
524 #ifdef OCCT_MMGT_OPT_JEMALLOC
525 return je_free(thePtrAligned);
526 #elif defined OCCT_MMGT_OPT_TBB
527 return scalable_aligned_free(thePtrAligned);
529 #if defined(_MSC_VER)
530 _aligned_free(thePtrAligned);
531 #elif defined(__ANDROID__) || defined(__QNX__)
533 #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && (defined(__i386) || defined(__x86_64)))
534 _mm_free(thePtrAligned);