Use memory pools to allocate nodes, renderers, materials and animations
[platform/core/uifw/dali-core.git] / dali / internal / common / fixed-size-memory-pool.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/common/fixed-size-memory-pool.h>
20
21 // INTERNAL HEADERS
22 #include <dali/devel-api/threading/mutex.h>
23 #include <dali/public-api/common/dali-common.h>
24
25 namespace Dali
26 {
27
28 namespace Internal
29 {
30
31 /**
32  * @brief Private implementation class
33  */
34 struct FixedSizeMemoryPool::Impl
35 {
36   /**
37    * @brief Struct to represent a block of memory from which allocations can be made.
38    *
39    * The block forms a linked list.
40    */
41   struct Block
42   {
43     void* blockMemory;      ///< The allocated memory from which allocations can be made
44     Block* nextBlock;       ///< The next block in the linked list
45
46     /**
47      * @brief Construct a new block with given size
48      *
49      * @param size The size of the memory block to allocate in bytes. Must be non-zero.
50      */
51     Block( SizeType size )
52     : nextBlock( NULL )
53     {
54       blockMemory = ::operator new( size );
55       DALI_ASSERT_ALWAYS( blockMemory && "Out of memory" );
56     }
57
58     /**
59      * @brief Destructor
60      */
61     ~Block()
62     {
63       ::operator delete( blockMemory );
64     }
65
66   private:
67     // Undefined
68     Block( const Block& block );
69
70     // Undefined
71     Block& operator=( const Block& block );
72   };
73
74   /**
75    * @brief Constructor
76    */
77   Impl( SizeType fixedSize, SizeType initialCapacity, SizeType maximumBlockCapacity )
78   :  mMutex(),
79      mFixedSize( fixedSize ),
80      mMemoryBlocks( initialCapacity * mFixedSize ),
81      mMaximumBlockCapacity( maximumBlockCapacity ),
82      mCurrentBlock( &mMemoryBlocks ),
83      mCurrentBlockCapacity( initialCapacity ),
84      mCurrentBlockSize( 0 ),
85      mDeletedObjects( NULL )
86   {
87     // We need enough room to store the deleted list in the data
88     DALI_ASSERT_DEBUG( mFixedSize >= sizeof( void* ) );
89   }
90
91   /**
92    * @brief Destructor
93    */
94   ~Impl()
95   {
96     // Clean up memory block linked list (mMemoryBlocks will be auto-destroyed by its destructor)
97     Block* block = mMemoryBlocks.nextBlock;
98     while( block )
99     {
100       Block* nextBlock = block->nextBlock;
101       delete block;
102       block = nextBlock;
103     }
104   }
105
106   /**
107    * @brief Allocate a new block for allocating memory from
108    */
109   void AllocateNewBlock()
110   {
111     // Double capacity for the new block
112     SizeType size = mCurrentBlockCapacity * 2;
113     if( size > mMaximumBlockCapacity || size < mCurrentBlockCapacity )    // Check for overflow of size type
114     {
115       size = mMaximumBlockCapacity;
116     }
117
118     mCurrentBlockCapacity = size;
119
120     // Allocate
121     Block* block = new Block( mCurrentBlockCapacity * mFixedSize );
122     mCurrentBlock->nextBlock = block;       // Add to end of linked list
123     mCurrentBlock = block;
124
125     mCurrentBlockSize = 0;
126   }
127
128   Mutex mMutex;                       ///< Mutex for thread-safe allocation and deallocation
129
130   SizeType mFixedSize;                ///< The size of each allocation in bytes
131
132   Block mMemoryBlocks;                ///< Linked list of allocated memory blocks
133   SizeType mMaximumBlockCapacity;     ///< The maximum allowed capacity of allocations in a new memory block
134
135   Block* mCurrentBlock;               ///< Pointer to the active block
136   SizeType mCurrentBlockCapacity;     ///< The maximum number of allocations that can be allocated for the current block
137   SizeType mCurrentBlockSize;         ///< The number of allocations allocated to the current block
138
139   void* mDeletedObjects;              ///< Pointer to the head of the list of deleted objects. The addresses are stored in the allocated memory blocks.
140 };
141
142 FixedSizeMemoryPool::FixedSizeMemoryPool( SizeType fixedSize, SizeType initialCapacity, SizeType maximumBlockCapacity )
143 {
144   mImpl = new Impl( fixedSize, initialCapacity, maximumBlockCapacity );
145 }
146
147 FixedSizeMemoryPool::~FixedSizeMemoryPool()
148 {
149   delete mImpl;
150 }
151
152 void* FixedSizeMemoryPool::Allocate()
153 {
154   Mutex::ScopedLock( mImpl->mMutex );
155
156   // First, recycle deleted objects
157   if( mImpl->mDeletedObjects )
158   {
159     void* recycled = mImpl->mDeletedObjects;
160     mImpl->mDeletedObjects = *( reinterpret_cast< void** >( mImpl->mDeletedObjects ) );  // Pop head off front of deleted objects list
161     return recycled;
162   }
163
164   // Check if current block is full
165   if( mImpl->mCurrentBlockSize >= mImpl->mCurrentBlockCapacity )
166   {
167     mImpl->AllocateNewBlock();
168   }
169
170   // Placement new the object in block memory
171   unsigned char* objectAddress = static_cast< unsigned char* >( mImpl->mCurrentBlock->blockMemory );
172   objectAddress += mImpl->mCurrentBlockSize * mImpl->mFixedSize;
173   mImpl->mCurrentBlockSize++;
174
175   return objectAddress;
176 }
177
178 void FixedSizeMemoryPool::Free( void* memory )
179 {
180   Mutex::ScopedLock( mImpl->mMutex );
181
182   // Add memory to head of deleted objects list. Store next address in the same memory space as the old object.
183   *( reinterpret_cast< void** >( memory ) ) = mImpl->mDeletedObjects;
184   mImpl->mDeletedObjects = memory;
185 }
186
187 void* FixedSizeMemoryPool::AllocateThreadSafe()
188 {
189   Mutex::ScopedLock( mImpl->mMutex );
190   return Allocate();
191 }
192
193 void FixedSizeMemoryPool::FreeThreadSafe( void* memory )
194 {
195   Mutex::ScopedLock( mImpl->mMutex );
196   Free( memory );
197 }
198
199
200 } // namespace Internal
201
202 } // namespace Dali