1 // Created on: 2002-04-12
2 // Created by: Alexander GRIGORIEV
3 // Copyright (c) 2002-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
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>
26 IMPLEMENT_STANDARD_RTTIEXT(NCollection_IncAllocator,NCollection_BaseAllocator)
31 inline size_t IMEM_SIZE (const size_t theSize)
33 return (theSize - 1) / sizeof(NCollection_IncAllocator::aligned_t) + 1;
36 inline size_t IMEM_ALIGN (const void* theAddress)
38 return sizeof(NCollection_IncAllocator::aligned_t) * IMEM_SIZE (size_t(theAddress));
41 #define IMEM_FREE(p_bl) (size_t(p_bl->p_end_block - p_bl->p_free_space))
44 // auxiliary dummy function used to get a place where break point can be set
45 inline void place_for_breakpoint() {}
51 static Standard_Boolean IS_DEBUG = Standard_False;
53 //=======================================================================
55 * Static data map (address -> AllocatorID)
57 //=======================================================================
58 static NCollection_DataMap<Standard_Address, Standard_Size>& StorageIDMap()
60 static NCollection_DataMap<Standard_Address, Standard_Size> TheMap;
64 //=======================================================================
66 * Static map (AllocatorID)
68 //=======================================================================
69 static NCollection_Map<Standard_Size>& StorageIDSet()
71 static NCollection_Map<Standard_Size> TheMap;
75 //=======================================================================
76 //function : IncAllocator_SetDebugFlag
77 //purpose : Turn on/off debugging of memory allocation
78 //=======================================================================
80 Standard_EXPORT void IncAllocator_SetDebugFlag(const Standard_Boolean theDebug)
87 //=======================================================================
89 * Static value of the current allocation ID. It provides unique
90 * numbering of allocators.
92 //=======================================================================
93 static Standard_Size CurrentID = 0;
94 static Standard_Size CATCH_ID = 0;
96 //=======================================================================
97 //function : Debug_Create
98 //purpose : Store the allocator address in the internal maps
99 //=======================================================================
101 static void Debug_Create(Standard_Address theAlloc)
103 static Standard_Mutex aMutex;
105 StorageIDMap().Bind(theAlloc, ++CurrentID);
106 StorageIDSet().Add(CurrentID);
107 if (CurrentID == CATCH_ID)
108 place_for_breakpoint();
112 //=======================================================================
113 //function : Debug_Destroy
114 //purpose : Forget the allocator address from the internal maps
115 //=======================================================================
117 static void Debug_Destroy(Standard_Address theAlloc)
119 static Standard_Mutex aMutex;
121 if (StorageIDMap().IsBound(theAlloc))
123 Standard_Size anID = StorageIDMap()(theAlloc);
124 StorageIDSet().Remove(anID);
125 StorageIDMap().UnBind(theAlloc);
130 #endif /* OCCT_DEBUG */
132 //=======================================================================
133 //function : IncAllocator_PrintAlive
134 //purpose : Outputs the alive numbers to the file inc_alive.d
135 //=======================================================================
137 Standard_EXPORT void IncAllocator_PrintAlive()
139 if (StorageIDSet().IsEmpty())
144 std::ofstream aFileOut ("inc_alive.d", std::ios_base::trunc | std::ios_base::out);
145 if (!aFileOut.is_open())
147 std::cout << "failure writing file inc_alive.d" << std::endl;
150 aFileOut.imbue (std::locale ("C"));
151 aFileOut << std::fixed << std::setprecision(1);
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())
159 const NCollection_IncAllocator* anAlloc = static_cast<NCollection_IncAllocator*>(itMap.Key());
160 Standard_Size anID = itMap.Value();
161 Standard_Size aSize = anAlloc->GetMemSize();
164 aFileOut << std::setw(20) << anID << ' '
165 << std::setw(20) << (double(aSize) / 1024.0)
168 aFileOut << "Total:\n"
169 << std::setw(20) << nbAlloc << ' '
170 << std::setw(20) << (double(aTotSize) / 1024.0)
175 //=======================================================================
176 //function : NCollection_IncAllocator()
177 //purpose : Constructor
178 //=======================================================================
180 NCollection_IncAllocator::NCollection_IncAllocator (size_t theBlockSize)
183 #ifdef ALLOC_TRACK_USAGE
184 printf ("\n..NCollection_IncAllocator: Created (%x)\n",this);
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);
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;
204 //=======================================================================
205 //function : ~NCollection_IncAllocator
206 //purpose : Destructor
207 //=======================================================================
209 NCollection_IncAllocator::~NCollection_IncAllocator ()
220 //=======================================================================
221 //function : SetThreadSafe
223 //=======================================================================
224 void NCollection_IncAllocator::SetThreadSafe (bool theIsThreadSafe)
229 myMutex = new Standard_Mutex();
231 else if (!theIsThreadSafe)
238 //=======================================================================
239 //function : Allocate
240 //purpose : allocate a memory
241 //remark : returns NULL if allocation fails
242 //=======================================================================
244 void * NCollection_IncAllocator::Allocate (const size_t aSize)
246 aligned_t * aResult = NULL;
247 const size_t cSize = aSize ? IMEM_SIZE(aSize) : 0;
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);
255 myFirstBlock -> p_free_space = myFirstBlock -> p_end_block;
257 throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
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);
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);
271 aCurrentBlock = aCurrentBlock -> p_next;
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);
278 myFirstBlock -> p_free_space = aResult + cSize;
281 const size_t aDefault = IMEM_SIZE(DefaultBlockSize);
282 if (cSize > aDefault)
283 throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
286 aResult = (aligned_t *) allocateNewBlock (aDefault);
288 myFirstBlock -> p_free_space = aResult + cSize;
290 throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
298 //=======================================================================
299 //function : Reallocate
301 //=======================================================================
303 void * NCollection_IncAllocator::Reallocate (void * theAddress,
304 const size_t oldSize,
305 const size_t newSize)
307 // Check that the dummy parameters are OK
308 if (theAddress == NULL || oldSize == 0)
309 return Allocate (newSize);
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;
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;
326 // In case of contraction of non-terminating allocation, do nothing
327 else if (cOldSize >= cNewSize)
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);
334 for (unsigned i = 0; i < cOldSize; i++)
335 aResult[i] = anAddress[i];
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);
347 myFirstBlock -> p_free_space = aResult + cNewSize;
348 for (unsigned i = 0; i < cOldSize; i++)
349 aResult[i] = anAddress[i];
353 throw Standard_OutOfMemory("NCollection_IncAllocator: out of memory");
358 //=======================================================================
361 //=======================================================================
363 void NCollection_IncAllocator::Free (void *)
366 //=======================================================================
369 //=======================================================================
371 void NCollection_IncAllocator::Clean ()
373 #ifdef ALLOC_TRACK_USAGE
374 printf ("\n..NCollection_IncAllocator: Memory size to clean:%8.1f kB (%x)\n",
375 double(GetMemSize())/1024, this);
377 IBlock * aBlock = myFirstBlock;
379 aBlock -> p_free_space = (aligned_t *) &aBlock[1];
380 aBlock = aBlock -> p_next;
382 IBlock * aNext = aBlock -> p_next;
386 myFirstBlock -> p_next = NULL;
391 //=======================================================================
394 //=======================================================================
396 void NCollection_IncAllocator::Reset (const Standard_Boolean doReleaseMem)
398 Standard_Mutex::Sentry aLock (myMutex);
402 Standard_Integer aBlockCount(0);
403 IBlock * aBlock = myFirstBlock;
405 if (aBlockCount++ < MaxLookup) {
406 aBlock -> p_free_space = (aligned_t *) &aBlock[1];
407 if (aBlockCount < MaxLookup)
408 aBlock = aBlock -> p_next;
410 IBlock * aNext = aBlock -> p_next;
411 aBlock -> p_next = NULL;
415 IBlock * aNext = aBlock -> p_next;
416 myMemSize -= (aBlock -> p_end_block - (aligned_t *) aBlock) * sizeof (aligned_t);
423 //=======================================================================
424 //function : GetMemSize
425 //purpose : diagnostic utility
426 //=======================================================================
428 size_t NCollection_IncAllocator::GetMemSize () const
430 // size_t aResult = 0;
431 // IBlock * aBlock = myFirstBlock;
433 // aResult += (aBlock -> p_end_block - (aligned_t *) aBlock);
434 // aBlock = aBlock -> p_next;
436 // return aResult * sizeof (aligned_t);
440 //=======================================================================
441 //function : allocateNewBlock
443 //=======================================================================
445 void * NCollection_IncAllocator::allocateNewBlock (const size_t cSize)
447 aligned_t * aResult = 0L;
448 const size_t aSz = cSize + IMEM_SIZE(sizeof(IBlock));
449 IBlock * aBlock = (IBlock *) malloc (aSz * sizeof(aligned_t));
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);