Merge "Updated all code to new format" into devel/master
[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 namespace Internal
28 {
29 /**
30  * @brief Private implementation class
31  */
32 struct FixedSizeMemoryPool::Impl
33 {
34   /**
35    * @brief Struct to represent a block of memory from which allocations can be made.
36    *
37    * The block forms a linked list.
38    */
39   struct Block
40   {
41     void*  blockMemory; ///< The allocated memory from which allocations can be made
42     Block* nextBlock;   ///< The next block in the linked list
43 #ifdef DEBUG_ENABLED
44     SizeType mBlockSize; ///< Size of the block in bytes
45 #endif
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(nullptr)
53 #ifdef DEBUG_ENABLED
54       ,
55       mBlockSize(size)
56 #endif
57     {
58       blockMemory = ::operator new(size);
59       DALI_ASSERT_ALWAYS(blockMemory && "Out of memory");
60     }
61
62     /**
63      * @brief Destructor
64      */
65     ~Block()
66     {
67       ::operator delete(blockMemory);
68     }
69
70   private:
71     // Undefined
72     Block(const Block& block);
73
74     // Undefined
75     Block& operator=(const Block& block);
76   };
77
78   /**
79    * @brief Constructor
80    */
81   Impl(SizeType fixedSize, SizeType initialCapacity, SizeType maximumBlockCapacity)
82   : mMutex(),
83     mFixedSize(fixedSize),
84     mMemoryBlocks(initialCapacity * mFixedSize),
85     mMaximumBlockCapacity(maximumBlockCapacity),
86     mCurrentBlock(&mMemoryBlocks),
87     mCurrentBlockCapacity(initialCapacity),
88     mCurrentBlockSize(0),
89     mDeletedObjects(nullptr)
90   {
91     // We need enough room to store the deleted list in the data
92     DALI_ASSERT_DEBUG(mFixedSize >= sizeof(void*));
93   }
94
95   /**
96    * @brief Destructor
97    */
98   ~Impl()
99   {
100     // Clean up memory block linked list (mMemoryBlocks will be auto-destroyed by its destructor)
101     Block* block = mMemoryBlocks.nextBlock;
102     while(block)
103     {
104       Block* nextBlock = block->nextBlock;
105       delete block;
106       block = nextBlock;
107     }
108   }
109
110   /**
111    * @brief Allocate a new block for allocating memory from
112    */
113   void AllocateNewBlock()
114   {
115     // Double capacity for the new block
116     SizeType size = mCurrentBlockCapacity * 2;
117     if(size > mMaximumBlockCapacity || size < mCurrentBlockCapacity) // Check for overflow of size type
118     {
119       size = mMaximumBlockCapacity;
120     }
121
122     mCurrentBlockCapacity = size;
123
124     // Allocate
125     Block* block             = new Block(mCurrentBlockCapacity * mFixedSize);
126     mCurrentBlock->nextBlock = block; // Add to end of linked list
127     mCurrentBlock            = block;
128
129     mCurrentBlockSize = 0;
130   }
131 #ifdef DEBUG_ENABLED
132
133   /**
134    * @brief check the memory being free'd exists inside the memory pool
135    * @param[in] memory address of object to remove
136    */
137   void CheckMemoryIsInsidePool(const void* const memory)
138   {
139     bool         inRange = false;
140     const Block* block   = &mMemoryBlocks;
141
142     while(block)
143     {
144       const void* const endOfBlock = reinterpret_cast<char*>(block->blockMemory) + block->mBlockSize;
145
146       if((memory >= block->blockMemory) && (memory < (endOfBlock)))
147       {
148         inRange = true;
149         break;
150       }
151       block = block->nextBlock;
152     }
153     DALI_ASSERT_DEBUG(inRange && "Freeing memory that does not exist in memory pool");
154   }
155 #endif
156
157   Mutex mMutex; ///< Mutex for thread-safe allocation and deallocation
158
159   SizeType mFixedSize; ///< The size of each allocation in bytes
160
161   Block    mMemoryBlocks;         ///< Linked list of allocated memory blocks
162   SizeType mMaximumBlockCapacity; ///< The maximum allowed capacity of allocations in a new memory block
163
164   Block*   mCurrentBlock;         ///< Pointer to the active block
165   SizeType mCurrentBlockCapacity; ///< The maximum number of allocations that can be allocated for the current block
166   SizeType mCurrentBlockSize;     ///< The number of allocations allocated to the current block
167
168   void* mDeletedObjects; ///< Pointer to the head of the list of deleted objects. The addresses are stored in the allocated memory blocks.
169 };
170
171 FixedSizeMemoryPool::FixedSizeMemoryPool(SizeType fixedSize, SizeType initialCapacity, SizeType maximumBlockCapacity)
172 {
173   mImpl = new Impl(fixedSize, initialCapacity, maximumBlockCapacity);
174 }
175
176 FixedSizeMemoryPool::~FixedSizeMemoryPool()
177 {
178   delete mImpl;
179 }
180
181 void* FixedSizeMemoryPool::Allocate()
182 {
183   // First, recycle deleted objects
184   if(mImpl->mDeletedObjects)
185   {
186     void* recycled         = mImpl->mDeletedObjects;
187     mImpl->mDeletedObjects = *(reinterpret_cast<void**>(mImpl->mDeletedObjects)); // Pop head off front of deleted objects list
188     return recycled;
189   }
190
191   // Check if current block is full
192   if(mImpl->mCurrentBlockSize >= mImpl->mCurrentBlockCapacity)
193   {
194     mImpl->AllocateNewBlock();
195   }
196
197   // Placement new the object in block memory
198   uint8_t* objectAddress = static_cast<uint8_t*>(mImpl->mCurrentBlock->blockMemory);
199   objectAddress += mImpl->mCurrentBlockSize * mImpl->mFixedSize;
200   mImpl->mCurrentBlockSize++;
201
202   return objectAddress;
203 }
204
205 void FixedSizeMemoryPool::Free(void* memory)
206 {
207   if(memory)
208   {
209 #ifdef DEBUG_ENABLED
210     mImpl->CheckMemoryIsInsidePool(memory);
211 #endif
212
213     // Add memory to head of deleted objects list. Store next address in the same memory space as the old object.
214     *(reinterpret_cast<void**>(memory)) = mImpl->mDeletedObjects;
215     mImpl->mDeletedObjects              = memory;
216   }
217 }
218
219 void* FixedSizeMemoryPool::AllocateThreadSafe()
220 {
221   Mutex::ScopedLock lock(mImpl->mMutex);
222   return Allocate();
223 }
224
225 void FixedSizeMemoryPool::FreeThreadSafe(void* memory)
226 {
227   if(memory)
228   {
229     Mutex::ScopedLock lock(mImpl->mMutex);
230     Free(memory);
231   }
232 }
233
234 } // namespace Internal
235
236 } // namespace Dali