FreeNOS
PoolAllocator.cpp
Go to the documentation of this file.
1/*
2 * Copyright (C) 2009 Niek Linnenbank
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <Assert.h>
19#include <Macros.h>
20#include <MemoryBlock.h>
21#include "PoolAllocator.h"
22
29
31{
32 Size totalSize, totalUsed;
33 calculateUsage(totalSize, totalUsed);
34
35 return totalSize;
36}
37
39{
40 Size totalSize, totalUsed;
41 calculateUsage(totalSize, totalUsed);
42
43 assert(totalUsed <= totalSize);
44
45 return totalSize - totalUsed;
46}
47
49{
50 assert(index >= MinimumPoolSize);
51 assert(index <= MaximumPoolSize);
52
53 return 1U << index;
54}
55
57{
58 assert(objectSize > 0);
59 assert(isPowerOfTwo(objectSize));
60
61 if (objectSize >= KiloByte(16))
62 return 1;
63 else
64 return KiloByte(16) / objectSize;
65}
66
67void PoolAllocator::calculateUsage(Size & totalSize, Size & totalUsed) const
68{
69 totalSize = 0;
70 totalUsed = 0;
71
72 for (Size index = MinimumPoolSize; index <= MaximumPoolSize; index++)
73 {
74 for (Pool *pool = m_pools[index]; pool != NULL; pool = pool->next)
75 {
76 totalSize += sizeof(Pool);
77 totalSize += pool->bitmapSize;
78 totalSize += pool->size();
79
80 totalUsed += sizeof(Pool);
81 totalUsed += pool->bitmapSize;
82 totalUsed += (pool->size() - pool->available());
83 }
84 }
85}
86
88{
89 const Size inputSize = aligned(args.size, sizeof(u32));
90 Pool *pool = ZERO;
91
92 // Verify input arguments
93 if (args.alignment != 0)
94 {
95 return InvalidAlignment;
96 }
97 else if (inputSize == 0)
98 {
99 return InvalidSize;
100 }
101
102 // Find the proper pool first
103 pool = retrievePool(inputSize);
104
105 // Attempt to allocate
106 if (pool)
107 {
108 Result result = static_cast<Allocator *>(pool)->allocate(args);
109 if (result == Success)
110 {
111 ObjectPrefix *prefix = (ObjectPrefix *) args.address;
112 prefix->signature = ObjectSignature;
113 prefix->pool = pool;
114
115 ObjectPostfix *postfix = (ObjectPostfix *) (args.address + sizeof(ObjectPrefix) + inputSize);
116 postfix->signature = ObjectSignature;
117
118 args.address += sizeof(ObjectPrefix);
119 }
120
121 return result;
122 }
123 else
124 {
125 args.address = 0;
126 return OutOfMemory;
127 }
128}
129
131{
132 const Address actualAddr = addr - sizeof(ObjectPrefix);
133 const ObjectPrefix *prefix = (const ObjectPrefix *) (actualAddr);
134 ObjectPostfix *postfix = ZERO;
135
136 // Verify the object prefix signature
137 assert(prefix->signature == ObjectSignature);
138 assert(prefix->pool != NULL);
139
140 // Do a reverse memory scan to find the object postfix.
141 for (Size i = prefix->pool->chunkSize() - sizeof(u32); i > sizeof(ObjectPrefix); i -= sizeof(u32))
142 {
143 postfix = (ObjectPostfix *)(actualAddr + i);
144 if (postfix->signature == ObjectSignature)
145 break;
146 }
147
148 // Verify the object postfix signature
149 assert(postfix != ZERO);
150 assert(postfix->signature == ObjectSignature);
151
152 // Release the object
153 Result result = prefix->pool->release(actualAddr);
154 assert(result == Success);
155
156 // Also try to release the pool itself, if no longer used
157 if (prefix->pool->available() == prefix->pool->size())
158 {
159 releasePool(prefix->pool);
160 }
161
162 return result;
163}
164
166{
167 const Size requestedSize = inputSize + sizeof(ObjectPrefix) + sizeof(ObjectPostfix);
168 Size index, nPools = 1, objectSize = 0;
169 Pool *pool = ZERO;
170
171 // Find the correct pool index
172 for (index = MinimumPoolSize; index <= MaximumPoolSize; index++)
173 {
174 objectSize = calculateObjectSize(index);
175
176 if (requestedSize <= objectSize)
177 break;
178 }
179
180 // Handle too large object requests
181 if (requestedSize > objectSize)
182 {
183 return ZERO;
184 }
185
186 // Do we need to allocate an initial pool?
187 if (!m_pools[index])
188 {
189 pool = m_pools[index] = allocatePool(index, calculateObjectCount(objectSize));
190 }
191 // Search for existing pool with enough memory
192 else
193 {
194 for (pool = m_pools[index]; pool; pool = pool->next, nPools++)
195 {
196 // Use current pool or if out of space allocate another
197 if (pool->available())
198 {
199 break;
200 }
201 else if (!pool->next)
202 {
203 pool = allocatePool(index, calculateObjectCount(objectSize) * nPools);
204 break;
205 }
206 }
207 }
208
209 return pool;
210}
211
213{
214 const Size objectSize = calculateObjectSize(index);
215 const Size requestBitmapSize = objectCount;
216 const Size requestPayloadSize = objectCount * objectSize;
217 const Size requestTotalSize = aligned(sizeof(Pool) + requestBitmapSize + requestPayloadSize, sizeof(u32));
218 Pool *pool = 0;
219 Allocator::Range alloc_args;
220
221 // Allocate single buffer to store the Pool and its payload
222 alloc_args.address = 0;
223 alloc_args.alignment = sizeof(u32);
224 alloc_args.size = requestTotalSize;
225
226 if (parent()->allocate(alloc_args) != Allocator::Success)
227 {
228 return ZERO;
229 }
230
231 // The parent must have given us minimum the requested size
232 assert(alloc_args.size >= requestTotalSize);
233
234 // The parent might have returned more space than requested.
235 // Calculate the optimum usage of the full space.
236 Size actualObjectCount = (alloc_args.size - sizeof(Pool) - requestBitmapSize) / objectSize;
237 Size actualPayloadSize = 0;
238 Size actualBitmapSize = 0;
239 Size actualTotalSize = 0;
240
241 assert(actualObjectCount >= objectCount);
242
243 while (actualObjectCount >= objectCount)
244 {
245 actualPayloadSize = actualObjectCount * objectSize;
246 actualBitmapSize = aligned(actualObjectCount, sizeof(u32));
247 actualTotalSize = sizeof(Pool) + actualBitmapSize + actualPayloadSize;
248
249 if (actualTotalSize <= alloc_args.size)
250 break;
251 else
252 actualObjectCount--;
253 }
254
255 // Calculate inputs for Pool object
256 const Address bitmapAddr = alloc_args.address + sizeof(Pool);
257 const Allocator::Range range = { bitmapAddr + actualBitmapSize, actualPayloadSize, sizeof(u32) };
258
259 // Instantiate the Pool object
260 pool = new (alloc_args.address) Pool(range, objectSize, actualBitmapSize, (u8 *) bitmapAddr);
261 pool->index = index;
262 pool->prev = ZERO;
263 pool->next = m_pools[index];
264 m_pools[index] = pool;
265
266 if (pool->next != NULL)
267 pool->next->prev = pool;
268
269 return pool;
270}
271
273{
274 Pool *prevPool = pool->prev;
275 Pool *nextPool = pool->next;
276 const Size index = pool->index;
277 const Result parentResult = parent()->release((Address) pool);
278
279 // Only update Pool administration if memory was released at parent
280 if (parentResult == Success)
281 {
282 if (prevPool != NULL)
283 {
284 prevPool->next = nextPool;
285 }
286
287 if (nextPool != NULL)
288 {
289 nextPool->prev = prevPool;
290 }
291
292 if (m_pools[index] == pool)
293 {
294 m_pools[index] = nextPool;
295 }
296 }
297
298 return parentResult;
299}
Memory Allocator.
Definition Allocator.h:47
virtual Result release(const Address addr)
Release memory.
Definition Allocator.cpp:89
Allocator * parent()
Get parent Allocator.
Definition Allocator.cpp:49
virtual Result allocate(Range &range)
Allocate memory.
Definition Allocator.cpp:84
void setParent(Allocator *parent)
Set parent allocator.
Definition Allocator.cpp:44
Result
Allocation results.
Definition Allocator.h:54
@ InvalidSize
Definition Allocator.h:57
@ InvalidAlignment
Definition Allocator.h:58
@ OutOfMemory
Definition Allocator.h:59
Address aligned(const Address addr, const Size boundary) const
Align memory address.
Definition Allocator.cpp:94
virtual Size available() const
Get available memory.
static void * set(void *dest, int ch, unsigned count)
Fill memory with a constant byte.
static const Size MaximumPoolSize
Maximum power of two size a pool can be (128MiB).
Size calculateObjectCount(const Size objectSize) const
Calculate minimum object count for a Pool.
static const Size MinimumPoolSize
Minimum power of two for a pool size.
Pool * m_pools[MaximumPoolSize+1]
Array of memory pools.
PoolAllocator(Allocator *parent)
Constructor.
virtual Result allocate(Range &args)
Allocate memory.
Size calculateObjectSize(const Size index) const
Calculate object size given the Pool index number.
virtual Size size() const
Get memory size.
void calculateUsage(Size &totalSize, Size &totalUsed) const
Determine total memory usage.
static const u32 ObjectSignature
Signature value is used to detect object corruption/overflows.
virtual Size available() const
Get memory available.
Pool * retrievePool(const Size inputSize)
Find a Pool of sufficient size.
Result releasePool(Pool *pool)
Release Pool instance memory.
Pool * allocatePool(const uint index, const Size objectCount)
Creates a new Pool instance.
virtual Result release(const Address addr)
Release memory.
#define assert(exp)
Insert program diagnostics.
Definition assert.h:60
#define NULL
NULL means zero.
Definition Macros.h:39
unsigned int u32
Unsigned 32-bit number.
Definition Types.h:53
unsigned long Address
A memory address.
Definition Types.h:131
bool isPowerOfTwo(unsigned number)
Check if a number is power of two.
Definition Macros.h:101
#define KiloByte(v)
Convert kilobytes to bytes.
Definition Macros.h:54
unsigned int Size
Any sane size indicator cannot go negative.
Definition Types.h:128
#define ZERO
Zero value.
Definition Macros.h:43
unsigned char u8
Unsigned 8-bit number.
Definition Types.h:59
Describes a range of memory.
Definition Allocator.h:66
Size alignment
Alignment in bytes or ZERO for default alignment.
Definition Allocator.h:69
Address address
Starting address of the memory range.
Definition Allocator.h:67
Size size
Amount of memory in bytes.
Definition Allocator.h:68
Appended in memory after each object.
u32 signature
Filled with a fixed value to detect corruption/overflows.
This data structure is prepended in memory before each object.
Pool * pool
Points to the Pool instance where this object belongs to.
u32 signature
Filled with a fixed value to detect corruption/overflows.
Allocates same-sized objects from a contiguous block of memory.
Pool * next
Points to the next pool of this size (if any).
Pool * prev
Points to the previous pool of this size (if any).
Size index
Index number in the m_pools array where this Pool is stored.