b311480e |
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 | |
7fd59977 |
20 | |
21 | #include <Standard_MMgrOpt.hxx> |
22 | #include <Standard_OutOfMemory.hxx> |
23 | |
7fd59977 |
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 | } |