[dali_1.1.15] Merge branch '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
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   // First, recycle deleted objects
155   if( mImpl->mDeletedObjects )
156   {
157     void* recycled = mImpl->mDeletedObjects;
158     mImpl->mDeletedObjects = *( reinterpret_cast< void** >( mImpl->mDeletedObjects ) );  // Pop head off front of deleted objects list
159     return recycled;
160   }
161
162   // Check if current block is full
163   if( mImpl->mCurrentBlockSize >= mImpl->mCurrentBlockCapacity )
164   {
165     mImpl->AllocateNewBlock();
166   }
167
168   // Placement new the object in block memory
169   unsigned char* objectAddress = static_cast< unsigned char* >( mImpl->mCurrentBlock->blockMemory );
170   objectAddress += mImpl->mCurrentBlockSize * mImpl->mFixedSize;
171   mImpl->mCurrentBlockSize++;
172
173   return objectAddress;
174 }
175
176 void FixedSizeMemoryPool::Free( void* memory )
177 {
178   // Add memory to head of deleted objects list. Store next address in the same memory space as the old object.
179   *( reinterpret_cast< void** >( memory ) ) = mImpl->mDeletedObjects;
180   mImpl->mDeletedObjects = memory;
181 }
182
183 void* FixedSizeMemoryPool::AllocateThreadSafe()
184 {
185   Mutex::ScopedLock lock( mImpl->mMutex );
186   return Allocate();
187 }
188
189 void FixedSizeMemoryPool::FreeThreadSafe( void* memory )
190 {
191   Mutex::ScopedLock lock( mImpl->mMutex );
192   Free( memory );
193 }
194
195
196 } // namespace Internal
197
198 } // namespace Dali