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 
24 {
25  assert(parent != NULL);
27  MemoryBlock::set(m_pools, 0, sizeof(m_pools));
28 }
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 
67 void 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 
212 PoolAllocator::Pool * PoolAllocator::allocatePool(const Size index, const Size objectCount)
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 }
PoolAllocator::Pool::index
Size index
Index number in the m_pools array where this Pool is stored.
Definition: PoolAllocator.h:77
PoolAllocator::m_pools
Pool * m_pools[MaximumPoolSize+1]
Array of memory pools.
Definition: PoolAllocator.h:201
PoolAllocator::size
virtual Size size() const
Get memory size.
Definition: PoolAllocator.cpp:30
Macros.h
PoolAllocator::allocatePool
Pool * allocatePool(const uint index, const Size objectCount)
Creates a new Pool instance.
Definition: PoolAllocator.cpp:212
MemoryBlock::set
static void * set(void *dest, int ch, unsigned count)
Fill memory with a constant byte.
Definition: MemoryBlock.cpp:25
PoolAllocator::ObjectPostfix::signature
u32 signature
Filled with a fixed value to detect corruption/overflows.
Definition: PoolAllocator.h:95
Allocator
Memory Allocator.
Definition: Allocator.h:46
PoolAllocator::ObjectPrefix::signature
u32 signature
Filled with a fixed value to detect corruption/overflows.
Definition: PoolAllocator.h:86
PoolAllocator::ObjectSignature
static const u32 ObjectSignature
Signature value is used to detect object corruption/overflows.
Definition: PoolAllocator.h:56
PoolAllocator::ObjectPrefix
This data structure is prepended in memory before each object.
Definition: PoolAllocator.h:84
PoolAllocator::Pool::prev
Pool * prev
Points to the previous pool of this size (if any).
Definition: PoolAllocator.h:75
Assert.h
Address
unsigned long Address
A memory address.
Definition: Types.h:131
PoolAllocator::Pool
Allocates same-sized objects from a contiguous block of memory.
Definition: PoolAllocator.h:61
Allocator::Range::address
Address address
Starting address of the memory range.
Definition: Allocator.h:67
MemoryBlock.h
PoolAllocator::calculateObjectCount
Size calculateObjectCount(const Size objectSize) const
Calculate minimum object count for a Pool.
Definition: PoolAllocator.cpp:56
PoolAllocator::ObjectPostfix
Appended in memory after each object.
Definition: PoolAllocator.h:93
Allocator::Range::alignment
Size alignment
Alignment in bytes or ZERO for default alignment.
Definition: Allocator.h:69
PoolAllocator::MinimumPoolSize
static const Size MinimumPoolSize
Minimum power of two for a pool size.
Definition: PoolAllocator.h:50
PoolAllocator::Pool::next
Pool * next
Points to the next pool of this size (if any).
Definition: PoolAllocator.h:76
Allocator::OutOfMemory
@ OutOfMemory
Definition: Allocator.h:59
BitAllocator::available
virtual Size available() const
Get available memory.
Definition: BitAllocator.cpp:36
Allocator::InvalidAlignment
@ InvalidAlignment
Definition: Allocator.h:58
Allocator::Range::size
Size size
Amount of memory in bytes.
Definition: Allocator.h:68
PoolAllocator::Pool
PoolAllocator::Pool Pool
Allocates same-sized objects from a contiguous block of memory.
PoolAllocator::MaximumPoolSize
static const Size MaximumPoolSize
Maximum power of two size a pool can be (128MiB).
Definition: PoolAllocator.h:53
PoolAllocator::ObjectPrefix
struct PoolAllocator::ObjectPrefix ObjectPrefix
This data structure is prepended in memory before each object.
PoolAllocator::calculateObjectSize
Size calculateObjectSize(const Size index) const
Calculate object size given the Pool index number.
Definition: PoolAllocator.cpp:48
PoolAllocator::available
virtual Size available() const
Get memory available.
Definition: PoolAllocator.cpp:38
KiloByte
#define KiloByte(v)
Convert kilobytes to bytes.
Definition: Macros.h:54
Allocator::parent
Allocator * parent()
Get parent Allocator.
Definition: Allocator.cpp:49
Allocator::Success
@ Success
Definition: Allocator.h:55
NULL
#define NULL
NULL means zero.
Definition: Macros.h:39
Allocator::allocate
virtual Result allocate(Range &range)
Allocate memory.
Definition: Allocator.cpp:84
u32
unsigned int u32
Unsigned 32-bit number.
Definition: Types.h:53
Size
unsigned int Size
Any sane size indicator cannot go negative.
Definition: Types.h:128
PoolAllocator::releasePool
Result releasePool(Pool *pool)
Release Pool instance memory.
Definition: PoolAllocator.cpp:272
PoolAllocator::ObjectPrefix::pool
Pool * pool
Points to the Pool instance where this object belongs to.
Definition: PoolAllocator.h:87
PoolAllocator::retrievePool
Pool * retrievePool(const Size inputSize)
Find a Pool of sufficient size.
Definition: PoolAllocator.cpp:165
PoolAllocator::PoolAllocator
PoolAllocator(Allocator *parent)
Constructor.
Definition: PoolAllocator.cpp:23
Allocator::Range
Describes a range of memory.
Definition: Allocator.h:65
PoolAllocator::calculateUsage
void calculateUsage(Size &totalSize, Size &totalUsed) const
Determine total memory usage.
Definition: PoolAllocator.cpp:67
assert
#define assert(exp)
Insert program diagnostics.
Definition: assert.h:60
Allocator::release
virtual Result release(const Address addr)
Release memory.
Definition: Allocator.cpp:89
u8
unsigned char u8
Unsigned 8-bit number.
Definition: Types.h:59
Allocator::setParent
void setParent(Allocator *parent)
Set parent allocator.
Definition: Allocator.cpp:44
isPowerOfTwo
bool isPowerOfTwo(unsigned number)
Check if a number is power of two.
Definition: Macros.h:101
PoolAllocator::release
virtual Result release(const Address addr)
Release memory.
Definition: PoolAllocator.cpp:130
PoolAllocator::allocate
virtual Result allocate(Range &args)
Allocate memory.
Definition: PoolAllocator.cpp:87
ZERO
#define ZERO
Zero value.
Definition: Macros.h:43
Allocator::InvalidSize
@ InvalidSize
Definition: Allocator.h:57
Allocator::Result
Result
Allocation results.
Definition: Allocator.h:53
PoolAllocator.h
Allocator::aligned
Address aligned(const Address addr, const Size boundary) const
Align memory address.
Definition: Allocator.cpp:94