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