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
IMEM_SIZE(const size_t theSize)31 inline size_t IMEM_SIZE (const size_t theSize)
32 {
33 return (theSize - 1) / sizeof(NCollection_IncAllocator::aligned_t) + 1;
34 }
35
IMEM_ALIGN(const void * theAddress)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
place_for_breakpoint()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 //=======================================================================
StorageIDMap()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 //=======================================================================
StorageIDSet()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
IncAllocator_SetDebugFlag(const Standard_Boolean theDebug)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
Debug_Create(Standard_Address theAlloc)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
Debug_Destroy(Standard_Address theAlloc)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
IncAllocator_PrintAlive()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
NCollection_IncAllocator(size_t theBlockSize)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
~NCollection_IncAllocator()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 //=======================================================================
SetThreadSafe(bool theIsThreadSafe)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
Allocate(const size_t aSize)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
Reallocate(void * theAddress,const size_t oldSize,const size_t newSize)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
Free(void *)363 void NCollection_IncAllocator::Free (void *)
364 {}
365
366 //=======================================================================
367 //function : Clean
368 //purpose :
369 //=======================================================================
370
Clean()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
Reset(const Standard_Boolean doReleaseMem)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
GetMemSize() const428 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
allocateNewBlock(const size_t cSize)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 }
459