0033661: Data Exchange, Step Import - Tessellated GDTs are not imported
[occt.git] / src / Standard / Standard.cxx
1 // Copyright (c) 1999-2023 OPEN CASCADE SAS
2 //
3 // This file is part of Open CASCADE Technology software library.
4 //
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.
10 //
11 // Alternatively, this file may be used under the terms of Open CASCADE
12 // commercial license or contractual agreement.
13
14 #include <Standard.hxx>
15
16 #include <Standard_OutOfMemory.hxx>
17
18 #include <stdlib.h>
19
20 #if(defined(_WIN32) || defined(__WIN32__))
21 #include <windows.h>
22 #include <malloc.h>
23 #include <locale.h>
24 #endif
25
26 #if defined(_MSC_VER) || defined(__ANDROID__) || defined(__QNX__)
27 #include <malloc.h>
28 #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && (defined(__i386) || defined(__x86_64)))
29 #include <mm_malloc.h>
30 #else
31 extern "C" int posix_memalign(void** thePtr, size_t theAlign, size_t theSize);
32 #endif
33
34 namespace
35 {
36   static Standard::AllocatorType& allocatorTypeInstance()
37   {
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;
45 #else
46       Standard::AllocatorType::NATIVE;
47 #endif
48     return aType;
49   }
50 }
51
52 #ifdef OCCT_MMGT_OPT_JEMALLOC
53 #define JEMALLOC_NO_DEMANGLE
54 #include <jemalloc.h>
55 #endif // OCCT_MMGT_OPT_JEMALLOC
56
57 // paralleling with Intel TBB
58 #ifdef HAVE_TBB
59 #include <tbb/scalable_allocator.h>
60 #else
61 #ifdef OCCT_MMGT_OPT_TBB
62 #undef OCCT_MMGT_OPT_TBB
63 #endif
64 #define scalable_malloc malloc
65 #define scalable_calloc calloc
66 #define scalable_realloc realloc
67 #define scalable_free free
68 #endif
69
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>
78
79 // There is no support for environment variables in UWP
80 // OSD_Environment could not be used here because of cyclic dependency
81 #ifdef OCCT_UWP
82 #define getenv(x) NULL
83 #endif
84
85 #ifndef OCCT_MMGT_OPT_DEFAULT
86 #define OCCT_MMGT_OPT_DEFAULT 0
87 #endif
88 namespace
89 {
90   /**
91   * Implementation of raw OCC memory manager which uses standard C
92   * functions: malloc (or calloc), free and realloc
93   * without any optimization
94   */
95   class Standard_MMgrRaw : public Standard_MMgrRoot
96   {
97   public:
98     //! Constructor; if theToClear is True, the memory will be nullified
99     //! upon allocation.
100     Standard_MMgrRaw(const Standard_Boolean theToClear = Standard_False)
101     {
102       myClear = theToClear;
103     }
104
105     //! Allocate theSize bytes 
106     Standard_Address Allocate(const Standard_Size theSize) override
107     {
108       // we use ?: operator instead of if() since it is faster :-)
109       Standard_Address aPtr = (myClear ? calloc(theSize, sizeof(char)) :
110                                malloc(theSize));
111       if (!aPtr)
112         throw Standard_OutOfMemory("Standard_MMgrRaw::Allocate(): malloc failed");
113       return aPtr;
114     }
115
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
120     {
121       Standard_Address aNewStorage = (Standard_Address)realloc(thePtr, theSize);
122       if (!aNewStorage)
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...
127       return aNewStorage;
128     }
129
130     //! Free allocated memory. The pointer is nullified.
131     void Free(Standard_Address thePtr) override
132     {
133       free(thePtr);
134     }
135
136   protected:
137     Standard_Boolean myClear; //! Option to nullify allocated memory
138   };
139
140   //! Implementation of OCC memory manager which uses Intel TBB
141   //! scalable allocator.
142   //!
143   //! On configurations where TBB is not available standard RTL functions 
144   //! malloc() / free() are used.
145   class Standard_MMgrTBBalloc : public Standard_MMgrRoot
146   {
147   public:
148     //! Constructor; if theClear is True, the memory will be nullified
149     //! upon allocation.
150     Standard_MMgrTBBalloc(const Standard_Boolean theClear = Standard_False)
151     {
152       myClear = theClear;
153     }
154
155     //! Allocate theSize bytes 
156     Standard_Address Allocate(const Standard_Size theSize) override
157     {
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));
161       if (!aPtr)
162         throw Standard_OutOfMemory("Standard_MMgrTBBalloc::Allocate(): malloc failed");
163       return aPtr;
164     }
165
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
170     {
171       Standard_Address aNewStorage = (Standard_Address)scalable_realloc(thePtr, theSize);
172       if (!aNewStorage)
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...
177       return aNewStorage;
178     }
179
180     //! Free allocated memory
181     void Free(Standard_Address thePtr) override
182     {
183       scalable_free(thePtr);
184     }
185
186   protected:
187     Standard_Boolean myClear; //! Option to nullify allocated memory
188   };
189
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
197   {
198   public:
199     static Standard_MMgrRoot* GetMMgr();
200     ~Standard_MMgrFactory();
201
202   private:
203     Standard_MMgrFactory();
204     Standard_MMgrFactory(const Standard_MMgrFactory&);
205     Standard_MMgrFactory& operator= (const Standard_MMgrFactory&);
206
207   private:
208     Standard_MMgrRoot* myFMMgr;
209   };
210
211   //=======================================================================
212   //function : Standard_MMgrFactory
213   //purpose  : Check environment variables and create appropriate memory manager
214   //=======================================================================
215
216   Standard_MMgrFactory::Standard_MMgrFactory()
217     : myFMMgr(NULL)
218   {
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!
224
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);
229     #endif*/
230
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);
237 #ifdef _WIN32
238     Standard_STATIC_ASSERT(sizeof(Standard_WideChar) == sizeof(Standard_Utf16Char));
239 #endif
240
241     char* aVar;
242     aVar = getenv("MMGT_OPT");
243     Standard_Integer anAllocId = (aVar ? atoi(aVar) : OCCT_MMGT_OPT_DEFAULT);
244
245 #if defined(HAVE_TBB) && defined(_M_IX86)
246     if (anAllocId == 2)
247     {
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;
254       _asm
255       {
256         push eax
257         push ebx
258         push ecx
259         push edx
260
261         // get the CPU feature bits
262         mov eax, 1
263         cpuid
264         mov dwFeature, edx
265
266         pop edx
267         pop ecx
268         pop ebx
269         pop eax
270       }
271       if ((dwFeature & _SSE2_FEATURE_BIT) == 0)
272         anAllocId = 0;
273     }
274 #endif
275
276     aVar = getenv("MMGT_CLEAR");
277     Standard_Boolean toClear = (aVar ? (atoi(aVar) != 0) : Standard_True);
278
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)
285     {
286       ULONG aHeapInfo = 2;
287       HANDLE aCRTHeap = (HANDLE)_get_heap_handle();
288       HeapSetInformation(aCRTHeap, HeapCompatibilityInformation, &aHeapInfo, sizeof(aHeapInfo));
289     }
290 #endif
291
292     switch (anAllocId)
293     {
294       case 1:  // OCCT optimized memory allocator
295       {
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);
305         break;
306       }
307       case 2:  // TBB memory allocator
308         myFMMgr = new Standard_MMgrTBBalloc(toClear);
309         break;
310       case 0:
311       default: // system default memory allocator
312         myFMMgr = new Standard_MMgrRaw(toClear);
313     }
314     allocatorTypeInstance() = static_cast<Standard::AllocatorType>(anAllocId);
315   }
316
317   //=======================================================================
318   //function : ~Standard_MMgrFactory
319   //purpose  : 
320   //=======================================================================
321
322   Standard_MMgrFactory::~Standard_MMgrFactory()
323   {
324     if (myFMMgr)
325       myFMMgr->Purge(Standard_True);
326   }
327
328   //=======================================================================
329   // function: GetMMgr
330   //
331   // This static function has a purpose to wrap static holder for memory 
332   // manager instance. 
333   //
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).
339   //
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.
344   //
345   // Therefore holder currently does not call destructor of the memory manager 
346   // but only its method Purge() with Standard_True.
347   //
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...
354   //
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)
358   //
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.
364   //
365   // Note that one possible method to control memory allocations could
366   // be counting calls to Allocate() and Free()...
367   //
368   //=======================================================================
369   Standard_MMgrRoot* Standard_MMgrFactory::GetMMgr()
370   {
371     static Standard_MMgrFactory aFactory;
372     return aFactory.myFMMgr;
373   }
374 }
375 #endif // OCCT_MMGT_OPT_FLEXIBLE
376
377 //=======================================================================
378 //function : Allocate
379 //purpose  :
380 //=======================================================================
381 Standard::AllocatorType Standard::GetAllocatorType()
382 {
383   return allocatorTypeInstance();
384 }
385
386 //=======================================================================
387 //function : Allocate
388 //purpose  :
389 //=======================================================================
390 Standard_Address Standard::Allocate(const Standard_Size theSize)
391 {
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));
396   if (!aPtr)
397     throw Standard_OutOfMemory("Standard_MMgrRaw::Allocate(): malloc failed");
398   return aPtr;
399 #elif defined OCCT_MMGT_OPT_TBB
400   Standard_Address aPtr = scalable_calloc(theSize, sizeof(char));
401   if (!aPtr)
402     throw Standard_OutOfMemory("Standard_MMgrRaw::Allocate(): malloc failed");
403   return aPtr;
404 #else
405   Standard_Address aPtr = calloc(theSize, sizeof(char));
406   if (!aPtr)
407     throw Standard_OutOfMemory("Standard_MMgrRaw::Allocate(): malloc failed");
408   return aPtr;
409 #endif // OCCT_MMGT_OPT_FLEXIBLE
410 }
411
412 //=======================================================================
413 //function : AllocateOptimal
414 //purpose  :
415 //=======================================================================
416 Standard_Address Standard::AllocateOptimal(const Standard_Size theSize)
417 {
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);
424 #else
425   return malloc(theSize);
426 #endif
427 }
428
429 //=======================================================================
430 //function : Free
431 //purpose  :
432 //=======================================================================
433 void Standard::Free(Standard_Address theStorage)
434 {
435 #ifdef OCCT_MMGT_OPT_FLEXIBLE
436   Standard_MMgrFactory::GetMMgr()->Free(theStorage);
437 #elif defined OCCT_MMGT_OPT_JEMALLOC
438   je_free(theStorage);
439 #elif defined OCCT_MMGT_OPT_TBB
440   scalable_free(theStorage);
441 #else
442   free(theStorage);
443 #endif
444 }
445
446 //=======================================================================
447 //function : Reallocate
448 //purpose  :
449 //=======================================================================
450 Standard_Address Standard::Reallocate(Standard_Address theStorage,
451                                       const Standard_Size theSize)
452 {
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);
460   if (!aNewStorage)
461     throw Standard_OutOfMemory("Standard_MMgrRaw::Reallocate(): realloc failed");
462   return aNewStorage;
463 #elif defined OCCT_MMGT_OPT_TBB
464   Standard_Address aNewStorage = (Standard_Address)scalable_realloc(theStorage, theSize);
465   if (!aNewStorage)
466     throw Standard_OutOfMemory("Standard_MMgrRaw::Reallocate(): realloc failed");
467   return aNewStorage;
468 #else
469   Standard_Address aNewStorage = (Standard_Address)realloc(theStorage, theSize);
470   if (!aNewStorage)
471     throw Standard_OutOfMemory("Standard_MMgrRaw::Reallocate(): realloc failed");
472   return aNewStorage;
473 #endif
474 }
475
476 //=======================================================================
477 //function : Purge
478 //purpose  :
479 //=======================================================================
480 Standard_Integer Standard::Purge()
481 {
482 #ifdef OCCT_MMGT_OPT_FLEXIBLE
483   return Standard_MMgrFactory::GetMMgr()->Purge();
484 #else
485   return true;
486 #endif // OCCT_MMGT_OPT_FLEXIBLE
487 }
488
489 //=======================================================================
490 //function : AllocateAligned
491 //purpose  :
492 //=======================================================================
493 Standard_Address Standard::AllocateAligned(const Standard_Size theSize,
494                                            const Standard_Size theAlign)
495 {
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);
500 #else
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);
507 #else
508   void* aPtr;
509   if (posix_memalign(&aPtr, theAlign, theSize))
510   {
511     return NULL;
512   }
513   return aPtr;
514 #endif
515 #endif
516 }
517
518 //=======================================================================
519 //function : FreeAligned
520 //purpose  :
521 //=======================================================================
522 void Standard::FreeAligned(Standard_Address thePtrAligned)
523 {
524 #ifdef OCCT_MMGT_OPT_JEMALLOC
525 return je_free(thePtrAligned);
526 #elif defined OCCT_MMGT_OPT_TBB
527   return scalable_aligned_free(thePtrAligned);
528 #else
529 #if defined(_MSC_VER)
530   _aligned_free(thePtrAligned);
531 #elif defined(__ANDROID__) || defined(__QNX__)
532   free(thePtrAligned);
533 #elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && (defined(__i386) || defined(__x86_64)))
534   _mm_free(thePtrAligned);
535 #else
536   free(thePtrAligned);
537 #endif
538 #endif
539 }