bcb39e139ced600c17062d3ab8f0a62153b54060
[occt.git] / src / Standard / Standard_MMgrOpt.cxx
1 // Created on: 2005-03-15
2 // Created by: Peter KURNEV
3 // Copyright (c) 2005-2012 OPEN CASCADE SAS
4 //
5 // The content of this file is subject to the Open CASCADE Technology Public
6 // License Version 6.5 (the "License"). You may not use the content of this file
7 // except in compliance with the License. Please obtain a copy of the License
8 // at http://www.opencascade.org and read it completely before using this file.
9 //
10 // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its
11 // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France.
12 //
13 // The Original Code and all software distributed under the License is
14 // distributed on an "AS IS" basis, without warranty of any kind, and the
15 // Initial Developer hereby disclaims all such warranties, including without
16 // limitation, any warranties of merchantability, fitness for a particular
17 // purpose or non-infringement. Please see the License for the specific terms
18 // and conditions governing the rights and limitations under the License.
19
20
21 #include <Standard_MMgrOpt.hxx>
22 #include <Standard_OutOfMemory.hxx>
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27
28 #include <stdio.h>
29
30 #ifdef HAVE_STRING_H
31 # include <string.h>
32 #endif
33
34 #ifndef WNT
35 # include <stdlib.h>
36 # include <errno.h>
37 #endif
38
39 #ifdef WNT
40 #include <windows.h>
41 #else
42 # ifdef HAVE_UNISTD_H
43 #  include <unistd.h>
44 # endif
45 # ifdef HAVE_SYS_MMAN_H
46 #  include <sys/mman.h>    /* mmap() */
47 # endif
48 #endif
49 #ifdef HAVE_MALLOC_H
50 # include <malloc.h>
51 #endif
52 #include <stdlib.h>
53 #include <sys/types.h>
54 #include <sys/stat.h>
55 #include <fcntl.h>
56 //
57 #if defined (__sun) || defined(SOLARIS)
58 extern "C" int getpagesize() ;
59 #endif
60
61 //======================================================================
62 // Assumptions
63 //======================================================================
64
65 // This implementation makes a number of assumptions regarding size of 
66 // types:
67 //
68 // sizeof(Standard_Size) == sizeof(Standard_Address==void*)
69 //
70 // On WNT, sizeof(HANDLE) is equal of multiple of sizeof(Standard_Size)
71
72 //======================================================================
73 // Naming conventions
74 //======================================================================
75
76 // For clarity of implementation, the following conventions are used
77 // for naming variables:
78 // 
79 // ...Size: size in bytes
80 //
81 // RoundSize, RSize etc.: size in bytes, rounded according to allocation granularity
82 //
83 // ...SizeN: size counted in number of items of sizeof(Standard_Size) bytes each
84 //
85 // ...Storage: address of the user area of the memory block (Standard_Address)
86 //
87 // ...Block: address of the hole memory block (header) (Standard_Size*)
88
89 //======================================================================
90 // Macro definitions
91 //======================================================================
92
93 //     
94 // MMAP_BASE_ADDRESS,  MMAP_FLAGS
95 #if defined (__hpux) || defined(HPUX)
96 #define MMAP_BASE_ADDRESS 0x80000000
97 #define MMAP_FLAGS (MAP_ANONYMOUS | MAP_PRIVATE | MAP_VARIABLE)
98 #elif defined (__osf__) || defined(DECOSF1)
99 #define MMAP_BASE_ADDRESS 0x1000000000
100 #define MMAP_FLAGS (MAP_ANONYMOUS | MAP_PRIVATE | MAP_VARIABLE)
101 #elif defined(_AIX)
102 #define MMAP_BASE_ADDRESS  0x80000000
103 #define MMAP_FLAGS (MAP_ANONYMOUS | MAP_PRIVATE | MAP_VARIABLE)
104 #elif defined(__APPLE__)
105 #define MMAP_BASE_ADDRESS  0x80000000
106 #define MMAP_FLAGS (MAP_ANON | MAP_PRIVATE)
107 #elif defined(LIN)
108 #define MMAP_BASE_ADDRESS 0x20000000
109 #define MMAP_FLAGS (MAP_PRIVATE)
110 #elif defined(WNT)
111 //static HANDLE myhMap;
112 #else
113 #define MMAP_BASE_ADDRESS 0x60000000
114 #define MMAP_FLAGS (MAP_PRIVATE)
115 #endif
116
117 // Round size up to the specified page size
118 #define PAGE_ALIGN(size,thePageSize)                            \
119   (((size) + (thePageSize) - 1) &  ~((thePageSize) - 1))
120
121 // Round size up to 4, 8, or 16 bytes
122 // Note that 0 yields 0
123 #define ROUNDUP16(size)                (((size) + 0xf) & ~(Standard_Size)0xf)
124 #define ROUNDUP8(size)                 (((size) + 0x7) & ~(Standard_Size)0x7)
125 #define ROUNDUP4(size)                 (((size) + 0x3) & ~(Standard_Size)0x3)
126 #define ROUNDDOWN8(size)               ((size) & ~(Standard_Size)0x7)
127
128 // The following two macros define granularity of memory allocation,
129 // by rounding size to the size of the allocation cell,
130 // and obtaining cell index from rounded size.
131 // Note that granularity shall be not less than sizeof(Standard_Size)
132
133 // Traditional implementation: granularity 16 bytes
134 //#define ROUNDUP_CELL(size)             ROUNDUP16(size)
135 //#define INDEX_CELL(rsize)              ((rsize) >> 4)
136
137 // Reduced granularity: 8 bytes
138 #define ROUNDUP_CELL(size)             ROUNDUP8(size)
139 #define ROUNDDOWN_CELL(size)           ROUNDDOWN8(size)
140 #define INDEX_CELL(rsize)              ((rsize) >> 3)
141
142 // Minimal granularity: 4 bytes (32-bit systems only)
143 #ifndef _OCC64
144 //#define ROUNDUP_CELL(size)             ROUNDUP4(size)
145 //#define INDEX_CELL(rsize)              ((rsize) >> 2)
146 #endif
147
148 // Adaptive granularity, less for little blocks and greater for bigger ones: 
149 /*
150 #if _OCC64
151 #define ROUNDUP_CELL(size) ((size)  <= 0x40 ? ROUNDUP8(size) : ROUNDUP16(size))
152 #define INDEX_CELL(rsize)  ((rsize) <= 0x40 ? ((rsize) >> 3) : (4 + ((rsize) >> 4)))
153 #else
154 #define ROUNDUP_CELL(size) ((size)  <= 0x40 ? ROUNDUP4(size) : ROUNDUP8(size))
155 #define INDEX_CELL(rsize)  ((rsize) <= 0x40 ? ((rsize) >> 2) : (8 + ((rsize) >> 3)))
156 #endif
157 */
158
159
160 /* In the allocated block, first bytes are used for storing of memory manager's data.
161    (size of block). The minimal size of these data is sizeof(int).
162    The memory allocated in system usually alligned by 16 bytes.Tthe aligment of the 
163    data area in the memory block is shfted on BLOCK_SHIFT*sizeof(Standard_Size) 
164    bytes.
165    It is OK for WNT, SUN and Linux systems, but on SGI aligment should be 8 bytes.
166    So, BLOCK_SHIFT is formed as macro for support on other possible platforms.
167 */
168
169 #if defined(IRIX) || defined(SOLARIS)
170 #define BLOCK_SHIFT 2
171 #else
172 #define BLOCK_SHIFT 1
173 #endif
174
175 // Get address of user area from block address, and vice-versa
176 #define GET_USER(block)    (((Standard_Size*)(block)) + BLOCK_SHIFT)
177 #define GET_BLOCK(storage) (((Standard_Size*)(storage))-BLOCK_SHIFT)
178
179 // create static instance of out-of-memory exception to protect
180 // against possible lack of memory for its raising
181 static Handle(Standard_OutOfMemory) anOutOfMemError = new Standard_OutOfMemory;
182
183 //=======================================================================
184 //function : Standard_MMgr
185 //purpose  : 
186 //=======================================================================
187
188 Standard_MMgrOpt::Standard_MMgrOpt(const Standard_Boolean aClear,
189                                    const Standard_Boolean aMMap,
190                                    const Standard_Size aCellSize,
191                                    const Standard_Integer aNbPages,
192                                    const Standard_Size aThreshold,
193                                    const Standard_Boolean isReentrant)
194 {
195   // check basic assumption
196   if ( sizeof(Standard_Size) != sizeof(Standard_Address) )
197   {
198     cerr << "Fatal error: Open CASCADE Optimized Memory manager: this platform is not supported!" << endl;
199     exit(1);
200   }
201
202   // clear buffer fields
203   myFreeListMax = 0;
204   myFreeList = NULL;
205   myPageSize = 0;
206   myAllocList = NULL;
207   myNextAddr = NULL;
208   myEndBlock = NULL;
209
210   // initialize parameters
211   myClear = aClear;
212   myMMap = (Standard_Integer)aMMap;
213   myCellSize = aCellSize;
214   myNbPages = aNbPages;
215   myThreshold = aThreshold;
216   myReentrant = isReentrant;
217   
218   // initialize 
219   Initialize();
220 }
221
222 //=======================================================================
223 //function : ~Standard_MMgrOpt
224 //purpose  : 
225 //=======================================================================
226
227 Standard_MMgrOpt::~Standard_MMgrOpt()
228 {
229   Purge(Standard_True);
230   free(myFreeList);
231   
232   // NOTE: freeing pools may be dangerous if not all memory taken by 
233   //       this instance of the memory manager has been freed 
234   FreePools();
235 }
236
237 // interface level
238
239 //=======================================================================
240 //function : Initialize
241 //purpose  : 
242 //=======================================================================
243
244 void Standard_MMgrOpt::Initialize()
245 {
246   // check number of pages in small blocks pools 
247   if ( myNbPages < 100 ) 
248     myNbPages = 1000;
249   
250   // get system-dependent page size
251 #ifndef WNT
252   myPageSize = getpagesize();
253   if ( ! myPageSize )
254     myMMap = 0;
255 #else
256   SYSTEM_INFO SystemInfo;
257   GetSystemInfo (&SystemInfo);
258   myPageSize = SystemInfo.dwPageSize;
259 #endif
260
261   // initialize memory mapped files
262   if(myMMap) {
263 #if defined (__sgi) || defined(IRIX)
264     /* Probleme de conflit en la zone des malloc et la zone des mmap sur SGI */
265     /* Ce probleme a ete identifie en IRIX 5.3 jusqu'en  IRIX 6.2. Le probleme */
266     /* ne semble pas apparaitre en IRIX 6.4 */
267     /* Les malloc successifs donnent des adresses croissantes (a partir de 0x0x10000000) */
268     /* ce que l'on appelle le pointeur de BREAK */
269     /* Le premier mmap est force a l'addresse MMAP_BASE_ADDRESS (soit 0x60000000 sur SGI) */
270     /* mais les mmap suivants sont decides par le systeme (flag MAP_VARIABLE). Malheureusement */
271     /* il renvoie une addresse la plus basse possible dans la zone des malloc juste au dessus */
272     /* du BREAK soit 0x18640000 ce qui donne un espace d'allocation d'environ 140 Mo pour les */
273     /* malloc. Sur des gros modeles on peut avoir des pointes a 680 Mo en Rev6 pour une maquette */
274     /* de 2 000 000 de points. En Rev7, la meme maquette n'excedera pas 286 Mo (voir vision.for) */
275     /* Pour palier ce comportement, la solution adoptee est la suivante :                        */
276     /*   Lorsque l'on entre dans alloc_startup (ici), on n'a pas encore fait de mmap.            */
277     /*   On fait alors un malloc (d'environ 700Mo) que l'on libere de suite. Cela a pour         */
278     /*  consequence de deplacer le BREAK tres haut. Le BREAK ne redescend jamais meme lors du free */
279     /*  Le mmap donnant une adresse (environ 100 Mo au dessus du BREAK) on se retrouve alors avec */
280     /* le partage des zones de memoire suivant :                                                  */
281     /*   700 Mo pour les malloc  - 500 Mo (1,2Go - 700Mo )  pour les mmap. Avec un CLD_SD_SIZE  */
282     /* de 2 000 000 on atteind jamais 500 Mo de mmap, meme en chargeant des applications (qui   */
283     /* utilisent la zone de mmap                                                                    */
284     /* Ce partage des zones memoire pourra eventuellemt etre regle par une variable d'environnement */
285     /* CLD_HIGH_SBRK                                                                                */
286     char *var;
287     Standard_Size high_sbrk;
288     
289     high_sbrk = 700*1024*1024;
290     if ( (var=getenv("CLD_HIGH_SBRK")) != NULL ) {
291       high_sbrk = atoi(var);
292     }
293
294     var = (char*)malloc(high_sbrk); // 700 Mb
295     if ( var )
296       free(var);
297     else
298       perror("ERR_MEMRY_FAIL");
299 #endif
300     
301 #if defined(IRIX) || defined(__sgi) || defined(SOLARIS) || defined(__sun) || defined(LIN) || defined(linux) || defined(__FreeBSD__)
302     if ((myMMap = open ("/dev/zero", O_RDWR)) < 0) {
303       if ((myMMap = open ("/dev/null", O_RDWR)) < 0){
304         myMMap = 0;
305       }
306     }
307     if (!myMMap)
308       perror("ERR_MMAP_FAIL");
309 #else
310     myMMap = -1;
311 #endif
312   }
313   
314   // initialize free lists
315   myFreeListMax = INDEX_CELL(ROUNDUP_CELL(myThreshold-BLOCK_SHIFT)); // all blocks less than myThreshold are to be recycled
316   myFreeList = (Standard_Size **) calloc (myFreeListMax+1, sizeof(Standard_Size *));
317   myCellSize = ROUNDUP16(myCellSize);
318 }
319
320 //=======================================================================
321 //function : SetMMgrOptCallBack
322 //purpose  : Sets a callback function to be called on each alloc/free
323 //=======================================================================
324
325 static Standard_MMgrOpt::TPCallBackFunc MyPCallBackFunc = NULL;
326
327 Standard_EXPORT void Standard_MMgrOpt::SetCallBackFunction(TPCallBackFunc pFunc)
328 {
329   MyPCallBackFunc = pFunc;
330 }
331
332 inline void callBack(const Standard_Boolean isAlloc,
333                      const Standard_Address aStorage,
334                      const Standard_Size aRoundSize,
335                      const Standard_Size aSize)
336 {
337   if (MyPCallBackFunc)
338     (*MyPCallBackFunc)(isAlloc, aStorage, aRoundSize, aSize);
339 }
340
341 //=======================================================================
342 //function : Allocate
343 //purpose  : 
344 //=======================================================================
345
346 Standard_Address Standard_MMgrOpt::Allocate(const Standard_Size aSize)
347 {
348   Standard_Size * aStorage = NULL;
349   
350   // round up size according to allocation granularity
351   // The keyword 'volatile' is only used here for GCC 64-bit compilations
352   // otherwise this method would crash in runtime in optimized build.
353   volatile Standard_Size RoundSize = ROUNDUP_CELL(aSize);
354   const Standard_Size Index = INDEX_CELL(RoundSize);
355
356   // blocks of small and medium size are recyclable
357   if ( Index <= myFreeListMax ) {
358     const Standard_Size RoundSizeN = RoundSize / sizeof(Standard_Size);
359
360     // Lock access to critical data (myFreeList and other fields) by mutex.
361     // Note that we do not lock fields that do not change during the 
362     // object life (such as myThreshold), and assume that calls to functions 
363     // of standard library are already protected by their implementation.
364     // The unlock is called as soon as possible, for every treatment case.
365     // We also do not use Sentry, since in case if OCC signal or exception is
366     // caused by this block we will have deadlock anyway...
367     if (myReentrant) myMutex.Lock();
368     
369     // if free block of the requested size is available, return it
370     if ( myFreeList[Index] ) {
371       // the address of the next free block is stored in the header
372       // of the memory block; use it to update list pointer
373       // to point to next free block
374       Standard_Size* aBlock = myFreeList[Index];
375       myFreeList[Index] = *(Standard_Size**)aBlock;
376
377       // unlock the mutex
378       if ( myReentrant ) myMutex.Unlock();
379
380       // record size of the allocated block in the block header and
381       // shift the pointer to the beginning of the user part of block
382       aBlock[0] = RoundSize;
383       aStorage = GET_USER(aBlock);
384
385       // clear block if requested
386       if (myClear)
387         memset (aStorage, 0, RoundSize);
388     }
389     // else if block size is small allocate it in pools
390     else if ( RoundSize <= myCellSize ) {
391       // unlock the mutex for free lists 
392       if ( myReentrant ) myMutex.Unlock();
393
394       // and lock the specific mutex used to protect access to small blocks pools;
395       // note that this is done by sentry class so as to ensure unlocking in case of 
396       // possible exception that may be thrown from AllocMemory()
397       Standard_Mutex::SentryNested aSentry ( myMutexPools, myReentrant );
398
399       // check for availability of requested space in the current pool
400       Standard_Size *aBlock = myNextAddr;
401       if ( &aBlock[ BLOCK_SHIFT+RoundSizeN] > myEndBlock ) {
402         // otherwise, allocate new memory pool with page-aligned size
403         Standard_Size Size = myPageSize * myNbPages;
404         aBlock = AllocMemory(Size); // note that size may be aligned by this call
405
406         if (myEndBlock > myNextAddr) {
407           // put the remaining piece to the free lists
408           const Standard_Size aPSize = (myEndBlock - GET_USER(myNextAddr))
409             * sizeof(Standard_Size);
410           const Standard_Size aRPSize = ROUNDDOWN_CELL(aPSize);
411           const Standard_Size aPIndex = INDEX_CELL(aRPSize);
412           if ( aPIndex > 0 && aPIndex <= myFreeListMax ) {
413             if (myReentrant) myMutex.Lock();
414             *(Standard_Size**)myNextAddr = myFreeList[aPIndex];
415             myFreeList[aPIndex] = myNextAddr;
416             if (myReentrant) myMutex.Unlock();
417           }
418         }
419
420         // set end pointer to the end of the new pool
421         myEndBlock = aBlock + Size / sizeof(Standard_Size);
422         // record in the first bytes of the pool the address of the previous one
423         *(Standard_Size**)aBlock = myAllocList;
424         // and make new pool current (last)
425         // and get pointer to the first memory block in the pool
426         myAllocList = aBlock;
427         aBlock+=BLOCK_SHIFT;
428       }
429
430       // initialize header of the new block by its size
431       // and get the pointer to the user part of block
432       aBlock[0] = RoundSize;
433       aStorage = GET_USER(aBlock);
434
435       // and advance pool pointer to the next free piece of pool
436       myNextAddr = &aStorage[RoundSizeN];
437     }
438     // blocks of medium size are allocated directly
439     else {
440       // unlock the mutex immediately, as we do not need further to access any field
441       if ( myReentrant ) myMutex.Unlock();
442
443       // we use operator ?: instead of if() since it is faster
444       Standard_Size *aBlock = (Standard_Size*) (myClear ? calloc( RoundSizeN+BLOCK_SHIFT,   sizeof(Standard_Size)) :
445                                                           malloc((RoundSizeN+BLOCK_SHIFT) * sizeof(Standard_Size)) );
446
447       // if allocation failed, try to free some memory by purging free lists, and retry
448       if ( ! aBlock ) {
449         if ( Purge (Standard_False) )
450           aBlock = (Standard_Size*)calloc(RoundSizeN+BLOCK_SHIFT, sizeof(Standard_Size));
451         // if still not succeeded, raise exception
452         if ( ! aBlock )
453           anOutOfMemError->Reraise ("Standard_MMgrOpt::Allocate(): malloc failed");
454       }
455
456       // initialize new block header by its size
457       // and get the pointer to the user part of block
458       aBlock[0] = RoundSize;
459       aStorage = GET_USER(aBlock);
460     }
461   }
462   // blocks of big size may be allocated as memory mapped files
463   else {
464     // Compute size of the block to be allocated, including header,
465     // Note that we use rounded size, even if this block will not be stored in 
466     // the free list, for consistency of calls to AllocMemory() / FreeMemory()
467     // and calculation of index in the free list
468     Standard_Size AllocSize = RoundSize + sizeof(Standard_Size);
469
470     // allocate memory
471     Standard_Size* aBlock = AllocMemory(AllocSize);
472
473     // initialize new block header by its size
474     // and get the pointer to the user part of block.
475     aBlock[0] = RoundSize;
476     aStorage = GET_USER(aBlock);
477   }
478
479   callBack(Standard_True, aStorage, RoundSize, aSize);
480
481   return aStorage;
482 }
483
484 //=======================================================================
485 //function : Free
486 //purpose  : 
487 //=======================================================================
488
489 void Standard_MMgrOpt::Free(Standard_Address& theStorage)
490 {
491   // safely return if attempt to free null pointer
492   if ( ! theStorage )
493     return;
494
495   // get the pointer to the memory block header
496   Standard_Size* aBlock = GET_BLOCK(theStorage);
497   
498   // and get the allocated size of the block
499   Standard_Size RoundSize = aBlock[0];
500   
501   callBack(Standard_False, theStorage, RoundSize, 0);
502   
503   // check whether blocks with that size are recyclable
504   const Standard_Size Index = INDEX_CELL(RoundSize);
505   if ( Index <= myFreeListMax ) {
506     // Lock access to critical data (myFreeList and other) by mutex
507     // Note that we do not lock fields that do not change during the 
508     // object life (such as myThreshold), and assume that calls to functions 
509     // of standard library are already protected by their implementation.
510     // We also do not use Sentry, since in case if OCC signal or exception is
511     // caused by this block we will have deadlock anyway...
512     if (myReentrant) myMutex.Lock();
513     
514     // in the memory block header, record address of the next free block
515     *(Standard_Size**)aBlock = myFreeList[Index];
516     // add new block to be first in the list
517     myFreeList[Index] = aBlock;
518
519     if (myReentrant) myMutex.Unlock();
520   }
521   // otherwise, we have block of big size which shall be simply released
522   else 
523     FreeMemory (aBlock, RoundSize);
524
525   theStorage = NULL;
526 }
527
528 //=======================================================================
529 //function : Purge
530 //purpose  : Frees all free lists except small blocks (less than CellSize)
531 //=======================================================================
532
533 Standard_Integer Standard_MMgrOpt::Purge(Standard_Boolean )//isDeleted)
534 {
535   // Lock access to critical data by mutex
536   Standard_Mutex::SentryNested aSentry (myMutex, myReentrant);
537
538   // TODO: implement support for isDeleted = True
539   
540   // free memory blocks contained in free lists
541   // whose sizes are greater than cellsize
542   Standard_Integer nbFreed = 0;
543   Standard_Size i = INDEX_CELL(ROUNDUP_CELL(myCellSize+BLOCK_SHIFT));
544   for (; i <= myFreeListMax; i++ ) {
545     Standard_Size * aFree = myFreeList[i];      
546     while(aFree) {
547       Standard_Size * anOther = aFree;
548       aFree = * (Standard_Size **) aFree;
549       free(anOther); 
550       nbFreed++;
551     }
552     myFreeList[i] = NULL;
553   }
554
555   // Lock access to critical data by mutex
556   Standard_Mutex::SentryNested aSentry1 ( myMutexPools, myReentrant );
557
558   // release memory pools containing no busy memory;
559   // for that for each pool count the summary size of blocks
560   // got from the free lists allocated from this pool
561 #ifndef WNT
562   const Standard_Size PoolSize = myPageSize * myNbPages;
563 #else
564   const Standard_Size PoolSize =
565     PAGE_ALIGN(myPageSize * myNbPages + sizeof(HANDLE), myPageSize) -
566     sizeof(HANDLE);
567 #endif
568   const Standard_Size RPoolSize = ROUNDDOWN_CELL(PoolSize);
569   const Standard_Size PoolSizeN = RPoolSize / sizeof(Standard_Size);
570
571   // declare the table of pools;
572   // (we map free blocks onto a number of pools simultaneously)
573   static const Standard_Integer NB_POOLS_WIN = 512;
574   static Standard_Size* aPools[NB_POOLS_WIN];
575   static Standard_Size aFreeSize[NB_POOLS_WIN];
576   static Standard_Integer aFreePools[NB_POOLS_WIN];
577
578   Standard_Size * aNextPool = myAllocList;
579   Standard_Size * aPrevPool = NULL;
580   const Standard_Size nCells = INDEX_CELL(myCellSize);
581   Standard_Integer nPool = 0, nPoolFreed = 0;
582
583   while (aNextPool) {
584     // fill the table of pools
585     Standard_Integer iPool;
586     for (iPool = 0; aNextPool && iPool < NB_POOLS_WIN; iPool++) {
587       aPools[iPool] = aNextPool;
588       aFreeSize[iPool] = 0;
589       aNextPool = * (Standard_Size **) aNextPool; // get next pool
590     }
591     const Standard_Integer iLast = iPool - 1;
592     nPool += iPool;
593
594     // scan free blocks, find corresponding pools and increment
595     // counters
596     for (i = 0; i <= nCells; i++ ) {
597       Standard_Size * aFree = myFreeList[i];
598       Standard_Size aSize = BLOCK_SHIFT * sizeof(Standard_Size) +
599         ROUNDUP_CELL(1) * i;
600       while(aFree) {
601         for (iPool = 0; iPool <= iLast; iPool++) {
602           if (aFree >= aPools[iPool] && aFree < aPools[iPool] + PoolSizeN) {
603             aFreeSize[iPool] += aSize;
604             break;
605           }
606         }
607         aFree = * (Standard_Size **) aFree; // get next free block
608       }
609     }
610
611     // scan the table and make the list of free pools
612     Standard_Integer iLastFree = -1;
613     for (iPool = 0; iPool <= iLast; iPool++) {
614       aFreeSize[iPool] = ROUNDUP_CELL(aFreeSize[iPool]);
615       if (aFreeSize[iPool] == RPoolSize)
616         aFreePools[++iLastFree] = iPool;
617     }
618     if (iLastFree == -1) {
619       // no free pools found in this table
620       aPrevPool = aPools[iLast];
621       continue;
622     }
623
624     // scan free blocks again, and remove those of them
625     // that belong to free pools
626     Standard_Integer j;
627     for (i = 0; i <= nCells; i++ ) {
628       Standard_Size * aFree = myFreeList[i];
629       Standard_Size * aPrevFree = NULL;
630       while(aFree) {
631         for (j = 0; j <= iLastFree; j++) {
632           iPool = aFreePools[j];
633           if (aFree >= aPools[iPool] && aFree < aPools[iPool] + PoolSizeN)
634             break;
635         }
636         if (j <= iLastFree)
637         {
638           // remove
639           aFree = * (Standard_Size **) aFree;
640           if (aPrevFree)
641             * (Standard_Size **) aPrevFree = aFree; // link to previous
642           else
643             myFreeList[i] = aFree;
644           nbFreed++;
645         }
646         else {
647           // skip
648           aPrevFree = aFree;
649           aFree = * (Standard_Size **) aFree;
650         }
651       }
652     }
653
654     // release free pools, and reconnect remaining pools
655     // in the linked list
656     Standard_Size * aPrev = (aFreePools[0] == 0
657                              ? aPrevPool
658                              : aPools[aFreePools[0] - 1]);
659     for (j = 0; j <= iLastFree; j++) {
660       iPool = aFreePools[j];
661       if (j > 0) {
662         // update the pointer to the previous non-free pool
663         if (iPool - aFreePools[j - 1] > 1)
664           aPrev = aPools[iPool - 1];
665       }
666       if (j == iLastFree || aFreePools[j + 1] - iPool > 1) {
667         // get next non-free pool
668         Standard_Size * aNext =
669           (j == iLastFree && aFreePools[j] == iLast)
670           ? aNextPool
671           : aPools[iPool + 1];
672         // and connect it to the list of pools that have been processed
673         // and remain non-free
674         if (aPrev)
675           * (Standard_Size **) aPrev = aNext;
676         else
677           myAllocList = aNext;
678       }
679       FreeMemory(aPools[iPool], PoolSize);
680     }
681     // update the pointer to the previous non-free pool
682     aPrevPool = (aFreePools[iLastFree] == iLast
683                  ? aPrev
684                  : aPools[iLast]);
685     nPoolFreed += iLastFree + 1;
686   }
687
688   return nbFreed;
689 }
690
691 //=======================================================================
692 //function : FreePools
693 //purpose  : Frees all memory pools allocated for small blocks
694 //=======================================================================
695
696 void Standard_MMgrOpt::FreePools()
697 {
698   // Lock access to critical data by mutex
699   Standard_Mutex::SentryNested aSentry ( myMutexPools, myReentrant );
700     
701   // last pool is remembered in myAllocList
702   Standard_Size * aFree = myAllocList;
703   myAllocList = 0;
704   while (aFree) {
705     Standard_Size * aBlock = aFree;
706     // next pool address is stored in first 8 bytes of each pool
707     aFree = * (Standard_Size **) aFree;
708     // free pool (note that its size is calculated rather than stored)
709     FreeMemory ( aBlock, myPageSize * myNbPages );
710   }
711 }
712
713 //=======================================================================
714 //function : Reallocate
715 //purpose  : 
716 //=======================================================================
717
718 Standard_Address Standard_MMgrOpt::Reallocate(Standard_Address&   theStorage,
719                                               const Standard_Size theNewSize)
720 {
721   Standard_Size * aBlock = GET_BLOCK(theStorage);
722   Standard_Address newStorage = NULL;
723
724   // get current size of the memory block from its header
725   Standard_Size OldSize = aBlock[0];
726
727   // if new size is less than old one, just do nothing
728   if (theNewSize <= OldSize) {
729     newStorage = theStorage;
730   }
731   // otherwise, allocate new block and copy the data to it
732   else {
733     newStorage = Allocate(theNewSize);
734     memcpy (newStorage, theStorage, OldSize);
735     Free( theStorage );
736     // clear newly added part of the block
737     if ( myClear )
738       memset(((char*)newStorage) + OldSize, 0, theNewSize-OldSize);
739   }
740   theStorage = NULL;
741   return newStorage;
742 }
743
744 //=======================================================================
745 //function : AllocMemory
746 //purpose  : Allocate a big block of memory using either malloc/calloc
747 //           or memory mapped file
748 //=======================================================================
749
750 Standard_Size * Standard_MMgrOpt::AllocMemory(Standard_Size &Size)
751 {
752   // goto is used as efficient method for a possibility to retry allocation
753 retry:
754
755   Standard_Size * aBlock = NULL;
756
757   // if MMap option is ON, allocate using memory mapped files
758   if (myMMap) {
759 #ifndef WNT
760
761     // align size to page size
762     const Standard_Size AlignedSize = PAGE_ALIGN(Size, myPageSize);
763
764     // allocate memory
765     // note that on UNIX myMMap is file descriptor for /dev/null
766     aBlock = (Standard_Size * )mmap((char*)MMAP_BASE_ADDRESS, AlignedSize,
767                                     PROT_READ | PROT_WRITE, MMAP_FLAGS,
768                                     myMMap, 0);
769     if (aBlock == MAP_FAILED /* -1 */) {
770       int errcode = errno;
771       // as a last resort, try freeing some memory by calling Purge()
772       if ( Purge(Standard_False) )
773         goto retry;
774       // if nothing helps, raise exception
775       anOutOfMemError->Reraise (strerror(errcode));
776     }
777
778     // save actually allocated size into argument
779     Size = AlignedSize;
780
781 #else /* WNT */
782
783     // align size to page size, taking into account additional space needed to
784     // store handle to the memory map
785     const Standard_Size AlignedSize = PAGE_ALIGN(Size+sizeof(HANDLE), myPageSize);
786
787     // allocate mapped file
788     HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, 
789                                     PAGE_READWRITE,
790                                     DWORD(AlignedSize / 0x80000000),
791                                     DWORD(AlignedSize % 0x80000000), NULL); 
792     HANDLE * aMBlock = NULL;
793     // check for error and try allocating address space
794     if ( ! hMap || GetLastError() == ERROR_ALREADY_EXISTS ||
795          ! (aMBlock = (HANDLE*)MapViewOfFile(hMap,FILE_MAP_WRITE,0,0,0)) ) 
796     {
797       // close handle if allocated
798       if ( hMap ) 
799         CloseHandle(hMap); 
800       hMap = 0;
801       // as a last resort, try freeing some memory by calling Purge() and retry
802       if ( Purge(Standard_False) )
803         goto retry;
804       // if nothing helps, make error message and raise exception
805       const int BUFSIZE=1024;
806       char message[BUFSIZE];
807       if ( FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, 0, GetLastError(), 0, message, BUFSIZE-1, 0) <=0 )
808         strcpy (message, "Standard_MMgrOpt::AllocMemory() failed to mmap");
809       anOutOfMemError->Reraise (message);
810     }
811
812     // record map handle in the beginning
813     aMBlock[0] = hMap;
814
815     // and shift to the beginning of usable area
816     aBlock = (Standard_Size*)(aMBlock+1);
817
818     // save actually allocated size into argument
819     Size = AlignedSize - sizeof(HANDLE);
820 #endif    
821   }
822   // else just allocate by malloc or calloc
823   else {
824     aBlock = (Standard_Size *) (myClear ? calloc(Size,sizeof(char)) : malloc(Size));
825     // check the result
826     if ( ! aBlock ) 
827     {
828       // as a last resort, try freeing some memory by calling Purge()
829       if ( Purge(Standard_False) )
830         goto retry;
831       // if nothing helps, raise exception
832       anOutOfMemError->Reraise ("Standard_MMgrOpt::Allocate(): malloc failed");
833     }
834   }
835   // clear whole block if clearing option is set
836   if (myClear)
837     memset (aBlock, 0, Size);
838   return aBlock;
839 }
840
841 //=======================================================================
842 //function : FreeMemory
843 //purpose  : 
844 //=======================================================================
845
846 void Standard_MMgrOpt::FreeMemory (Standard_Address aBlock, 
847                                    const Standard_Size
848 #ifndef WNT                                   
849                                    aSize
850 #endif
851                                   )
852 {
853   // release memory (either free or unmap)
854   if ( myMMap ) {
855 #ifndef WNT
856     // align size to page size, just the same as in AllocMemory()
857     const Standard_Size AlignedSize = PAGE_ALIGN(aSize, myPageSize);
858     munmap((char*)aBlock, AlignedSize);
859 #else
860     // recover handle to the memory mapping stored just before the block
861     const HANDLE * aMBlock = (const HANDLE *)aBlock;
862     HANDLE hMap = *(--aMBlock);
863     UnmapViewOfFile((LPCVOID)aMBlock);
864     CloseHandle (hMap);
865 #endif
866   }
867   else
868     free(aBlock);
869 }
870
871 //=======================================================================
872 //function : SetReentrant
873 //purpose  : 
874 //=======================================================================
875
876 void Standard_MMgrOpt::SetReentrant(Standard_Boolean isReentrant)
877 {
878   myReentrant = isReentrant;
879 }