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