0031642: Visualization - crash in Graphic3d_Structure::SetVisual() on redisplaying...
[occt.git] / src / NCollection / NCollection_IncAllocator.cxx
1 // Created on: 2002-04-12
2 // Created by: Alexander GRIGORIEV
3 // Copyright (c) 2002-2014 OPEN CASCADE SAS
4 //
5 // This file is part of Open CASCADE Technology software library.
6 //
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
12 //
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
15
16 #include <NCollection_IncAllocator.hxx>
17 #include <NCollection_DataMap.hxx>
18 #include <NCollection_Map.hxx>
19 #include <Standard_Mutex.hxx>
20 #include <Standard_OutOfMemory.hxx>
21 #include <stdio.h>
22 #include <fstream>
23 #include <iomanip>
24
25
26 IMPLEMENT_STANDARD_RTTIEXT(NCollection_IncAllocator,NCollection_BaseAllocator)
27
28 namespace
29 {
30
31   inline size_t IMEM_SIZE (const size_t theSize)
32   {
33     return (theSize - 1) / sizeof(NCollection_IncAllocator::aligned_t) + 1;
34   }
35
36   inline size_t IMEM_ALIGN (const void* theAddress)
37   {
38     return sizeof(NCollection_IncAllocator::aligned_t) * IMEM_SIZE (size_t(theAddress));
39   }
40
41   #define IMEM_FREE(p_bl) (size_t(p_bl->p_end_block - p_bl->p_free_space))
42
43 #ifdef OCCT_DEBUG
44   // auxiliary dummy function used to get a place where break point can be set
45   inline void place_for_breakpoint() {}
46 #endif
47 }
48
49 #define MaxLookup 16
50
51 static Standard_Boolean IS_DEBUG = Standard_False;
52
53 //=======================================================================
54 /**
55  * Static data map (address -> AllocatorID)
56  */
57 //=======================================================================
58 static NCollection_DataMap<Standard_Address, Standard_Size>& StorageIDMap()
59 {
60   static NCollection_DataMap<Standard_Address, Standard_Size> TheMap;
61   return TheMap;
62 }
63
64 //=======================================================================
65 /**
66  * Static map (AllocatorID)
67  */
68 //=======================================================================
69 static NCollection_Map<Standard_Size>& StorageIDSet()
70 {
71   static NCollection_Map<Standard_Size> TheMap;
72   return TheMap;
73 }
74
75 //=======================================================================
76 //function : IncAllocator_SetDebugFlag
77 //purpose  : Turn on/off debugging of memory allocation
78 //=======================================================================
79
80 Standard_EXPORT void IncAllocator_SetDebugFlag(const Standard_Boolean theDebug)
81 {
82   IS_DEBUG = theDebug;
83 }
84
85 #ifdef OCCT_DEBUG
86
87 //=======================================================================
88 /**
89  * Static value of the current allocation ID. It provides unique
90  * numbering of allocators.
91  */
92 //=======================================================================
93 static Standard_Size CurrentID = 0;
94 static Standard_Size CATCH_ID = 0;
95
96 //=======================================================================
97 //function : Debug_Create
98 //purpose  : Store the allocator address in the internal maps
99 //=======================================================================
100
101 static void Debug_Create(Standard_Address theAlloc)
102 {
103   static Standard_Mutex aMutex;
104   aMutex.Lock();
105   StorageIDMap().Bind(theAlloc, ++CurrentID);
106   StorageIDSet().Add(CurrentID);
107   if (CurrentID == CATCH_ID)
108     place_for_breakpoint();
109   aMutex.Unlock();
110 }
111
112 //=======================================================================
113 //function : Debug_Destroy
114 //purpose  : Forget the allocator address from the internal maps
115 //=======================================================================
116
117 static void Debug_Destroy(Standard_Address theAlloc)
118 {
119   static Standard_Mutex aMutex;
120   aMutex.Lock();
121   if (StorageIDMap().IsBound(theAlloc))
122   {
123     Standard_Size anID = StorageIDMap()(theAlloc);
124     StorageIDSet().Remove(anID);
125     StorageIDMap().UnBind(theAlloc);
126   }
127   aMutex.Unlock();
128 }
129
130 #endif /* OCCT_DEBUG */
131
132 //=======================================================================
133 //function : IncAllocator_PrintAlive
134 //purpose  : Outputs the alive numbers to the file inc_alive.d
135 //=======================================================================
136
137 Standard_EXPORT void IncAllocator_PrintAlive()
138 {
139   if (StorageIDSet().IsEmpty())
140   {
141     return;
142   }
143
144   std::ofstream aFileOut ("inc_alive.d", std::ios_base::trunc | std::ios_base::out);
145   if (!aFileOut.is_open())
146   {
147     std::cout << "failure writing file inc_alive.d" << std::endl;
148     return;
149   }
150   aFileOut.imbue (std::locale ("C"));
151   aFileOut << std::fixed << std::setprecision(1);
152
153   aFileOut << "Alive IncAllocators (number, size in Kb)\n";
154   Standard_Size    aTotSize = 0;
155   Standard_Integer nbAlloc  = 0;
156   for (NCollection_DataMap<Standard_Address, Standard_Size>::Iterator itMap (StorageIDMap());
157        itMap.More(); itMap.Next())
158   {
159     const NCollection_IncAllocator* anAlloc = static_cast<NCollection_IncAllocator*>(itMap.Key());
160     Standard_Size anID  = itMap.Value();
161     Standard_Size aSize = anAlloc->GetMemSize();
162     aTotSize += aSize;
163     nbAlloc++;
164     aFileOut << std::setw(20) << anID << ' '
165              << std::setw(20) << (double(aSize) / 1024.0)
166              << '\n';
167   }
168   aFileOut << "Total:\n"
169            << std::setw(20) << nbAlloc << ' '
170            << std::setw(20) << (double(aTotSize) / 1024.0)
171            << '\n';
172   aFileOut.close();
173 }
174
175 //=======================================================================
176 //function : NCollection_IncAllocator()
177 //purpose  : Constructor
178 //=======================================================================
179
180 NCollection_IncAllocator::NCollection_IncAllocator (size_t theBlockSize)
181 : myMutex (NULL)
182 {
183 #ifdef ALLOC_TRACK_USAGE
184   printf ("\n..NCollection_IncAllocator: Created (%x)\n",this);
185 #endif
186 #ifdef OCCT_DEBUG
187   if (IS_DEBUG)
188     Debug_Create(this);
189 #endif
190   const size_t aDefault = DefaultBlockSize;
191   const size_t aSize = IMEM_SIZE(sizeof(IBlock)) +
192       IMEM_SIZE((theBlockSize > 2*sizeof(IBlock)) ? theBlockSize : aDefault);
193   IBlock * const aBlock = (IBlock *) malloc (aSize * sizeof(aligned_t));
194   myFirstBlock = aBlock;
195   mySize = aSize - IMEM_SIZE(sizeof(IBlock));
196   myMemSize = aSize * sizeof(aligned_t);
197   if (aBlock == NULL)
198     throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
199   aBlock -> p_free_space = (aligned_t *) IMEM_ALIGN (&aBlock[1]);
200   aBlock -> p_end_block  = ((aligned_t *) aBlock) + aSize;
201   aBlock -> p_next       = NULL;
202 }
203
204 //=======================================================================
205 //function : ~NCollection_IncAllocator
206 //purpose  : Destructor
207 //=======================================================================
208
209 NCollection_IncAllocator::~NCollection_IncAllocator ()
210 {
211   delete myMutex;
212 #ifdef OCCT_DEBUG
213   if (IS_DEBUG)
214     Debug_Destroy(this);
215 #endif
216   Clean();
217   free (myFirstBlock);
218 }
219
220 //=======================================================================
221 //function : SetThreadSafe
222 //purpose  :
223 //=======================================================================
224 void NCollection_IncAllocator::SetThreadSafe (bool theIsThreadSafe)
225 {
226   if (myMutex == NULL
227    && theIsThreadSafe)
228   {
229     myMutex = new Standard_Mutex();
230   }
231   else if (!theIsThreadSafe)
232   {
233     delete myMutex;
234     myMutex = NULL;
235   }
236 }
237
238 //=======================================================================
239 //function : Allocate
240 //purpose  : allocate a memory
241 //remark   : returns NULL if allocation fails
242 //=======================================================================
243
244 void * NCollection_IncAllocator::Allocate (const size_t aSize)
245 {
246   aligned_t * aResult = NULL;
247   const size_t cSize = aSize ? IMEM_SIZE(aSize) : 0;
248
249   Standard_Mutex::Sentry aLock (myMutex);
250   if (cSize > mySize) {
251     /* If the requested size exceeds normal allocation size, allocate
252        a separate block and place it as the head of the list              */
253     aResult = (aligned_t *) allocateNewBlock (cSize+1);
254     if (aResult)
255       myFirstBlock -> p_free_space = myFirstBlock -> p_end_block;
256     else
257       throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
258   } else
259     if (cSize <= IMEM_FREE(myFirstBlock)) {
260       /* If the requested size fits into the free space in the 1st block  */
261       aResult = myFirstBlock -> allocateInBlock (cSize);
262     } else {
263       /* Search for a block in the list with enough free space            */
264       int aMaxLookup = MaxLookup;   /* limit the number of blocks to query */
265       IBlock * aCurrentBlock = myFirstBlock -> p_next;
266       while (aCurrentBlock && aMaxLookup--) {
267         if (cSize <= IMEM_FREE(aCurrentBlock)) {
268           aResult = aCurrentBlock -> allocateInBlock (cSize);
269           break;
270         }
271         aCurrentBlock = aCurrentBlock -> p_next;
272       }
273       if (aResult == NULL) {
274         /* There is no available block with enough free space. Create a new
275            one and place it in the head of the list                       */
276         aResult = (aligned_t *) allocateNewBlock (mySize);
277         if (aResult)
278           myFirstBlock -> p_free_space = aResult + cSize;
279         else
280         {
281           const size_t aDefault = IMEM_SIZE(DefaultBlockSize);
282           if (cSize > aDefault)
283               throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
284           else
285           {            
286             aResult = (aligned_t *) allocateNewBlock (aDefault);
287             if (aResult)
288               myFirstBlock -> p_free_space = aResult + cSize;
289             else
290               throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
291           }
292         }
293       }
294     }
295   return aResult;
296 }
297
298 //=======================================================================
299 //function : Reallocate
300 //purpose  : 
301 //=======================================================================
302
303 void * NCollection_IncAllocator::Reallocate (void         * theAddress,
304                                              const size_t oldSize,
305                                              const size_t newSize)
306 {
307 // Check that the dummy parameters are OK
308   if (theAddress == NULL || oldSize == 0)
309     return Allocate (newSize);
310
311   const size_t cOldSize = IMEM_SIZE(oldSize);
312   const size_t cNewSize = newSize ? IMEM_SIZE(newSize) : 0;
313   aligned_t * anAddress = (aligned_t *) theAddress;
314
315   Standard_Mutex::Sentry aLock (myMutex);
316 // We check only the LAST allocation to do the real extension/contraction
317   if (anAddress + cOldSize == myFirstBlock -> p_free_space) {
318     myFirstBlock -> p_free_space = anAddress;
319 // If the new size fits into the memory block => OK
320 // This also includes any case of contraction
321     if (cNewSize <= IMEM_FREE(myFirstBlock)) {
322       myFirstBlock -> p_free_space += cNewSize;
323       return anAddress;
324     }
325   }
326 // In case of contraction of non-terminating allocation, do nothing
327   else if (cOldSize >= cNewSize)
328     return anAddress;
329 // Extension of non-terminated allocation if there is enough room in the
330 // current memory block 
331   if (cNewSize <= IMEM_FREE(myFirstBlock)) {
332     aligned_t * aResult = myFirstBlock -> allocateInBlock (cNewSize);
333     if (aResult)
334       for (unsigned i = 0; i < cOldSize; i++)
335         aResult[i] = anAddress[i];
336     return aResult;
337   }
338
339 // This is either of the cases:
340 //   - extension of non-terminating allocation, or
341 //   - extension of terminating allocation when the new size is too big
342 // In both cases create a new memory block, allocate memory and copy there
343 // the reallocated memory.
344   size_t cMaxSize = mySize > cNewSize ? mySize : cNewSize;
345   aligned_t * aResult = (aligned_t *) allocateNewBlock (cMaxSize);
346   if (aResult) {
347     myFirstBlock -> p_free_space = aResult + cNewSize;
348     for (unsigned i = 0; i < cOldSize; i++)
349       aResult[i] = anAddress[i];
350   }
351   else
352   {
353     throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
354   }
355   return aResult;
356 }
357
358 //=======================================================================
359 //function : Free
360 //purpose  : 
361 //=======================================================================
362
363 void NCollection_IncAllocator::Free (void *)
364 {}
365
366 //=======================================================================
367 //function : Clean
368 //purpose  : 
369 //=======================================================================
370
371 void NCollection_IncAllocator::Clean ()
372 {
373 #ifdef ALLOC_TRACK_USAGE
374   printf ("\n..NCollection_IncAllocator: Memory size to clean:%8.1f kB (%x)\n",
375            double(GetMemSize())/1024, this);
376 #endif
377   IBlock * aBlock = myFirstBlock;
378   if (aBlock) {
379     aBlock -> p_free_space = (aligned_t *) &aBlock[1];
380     aBlock = aBlock -> p_next;
381     while (aBlock) {
382       IBlock * aNext = aBlock -> p_next;
383       free (aBlock);
384       aBlock = aNext;
385     }
386     myFirstBlock -> p_next = NULL;
387   }
388   myMemSize = 0;
389 }
390
391 //=======================================================================
392 //function : Reset
393 //purpose  : 
394 //=======================================================================
395
396 void NCollection_IncAllocator::Reset (const Standard_Boolean doReleaseMem)
397 {
398   Standard_Mutex::Sentry aLock (myMutex);
399   if (doReleaseMem)
400     Clean();
401   else {
402     Standard_Integer aBlockCount(0);
403     IBlock * aBlock = myFirstBlock;
404     while (aBlock)
405       if (aBlockCount++ < MaxLookup) {
406         aBlock -> p_free_space = (aligned_t *) &aBlock[1];
407         if (aBlockCount < MaxLookup)
408           aBlock = aBlock -> p_next;
409         else {
410           IBlock * aNext = aBlock -> p_next;
411           aBlock -> p_next = NULL;
412           aBlock = aNext;
413         }
414       } else {
415         IBlock * aNext = aBlock -> p_next;
416         myMemSize -= (aBlock -> p_end_block - (aligned_t *) aBlock) * sizeof (aligned_t);
417         free (aBlock);
418         aBlock = aNext;
419       }
420   }
421 }
422
423 //=======================================================================
424 //function : GetMemSize
425 //purpose  : diagnostic utility
426 //=======================================================================
427
428 size_t NCollection_IncAllocator::GetMemSize () const
429 {
430 //   size_t aResult = 0;
431 //   IBlock * aBlock = myFirstBlock;
432 //   while (aBlock) {
433 //     aResult += (aBlock -> p_end_block - (aligned_t *) aBlock);
434 //     aBlock = aBlock -> p_next;
435 //   }
436 //   return aResult * sizeof (aligned_t);
437   return myMemSize;
438 }
439
440 //=======================================================================
441 //function : allocateNewBlock
442 //purpose  : 
443 //=======================================================================
444
445 void * NCollection_IncAllocator::allocateNewBlock (const size_t cSize)
446 {
447   aligned_t * aResult = 0L;
448   const size_t aSz = cSize + IMEM_SIZE(sizeof(IBlock));
449   IBlock * aBlock = (IBlock *) malloc (aSz * sizeof(aligned_t));
450   if (aBlock) {
451     aBlock -> p_end_block  = ((aligned_t *)aBlock) + aSz;
452     aBlock -> p_next = myFirstBlock;
453     myFirstBlock = aBlock;
454     aResult = (aligned_t *) IMEM_ALIGN(&aBlock[1]);
455     myMemSize += aSz * sizeof(aligned_t);
456   }
457   return aResult;
458 }