2 * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
7 #include "runtime_loader_private.h"
10 #include <util/kernel_cpp.h>
17 static const size_t kInitialHeapSize
= 65536;
20 /* This is a very simple malloc()/free() implementation - it only
21 * manages a free list.
22 * After heap_init() is called, all free memory is contained in one
23 * big chunk, the only entry in the free link list (which is a single
25 * When memory is allocated, the smallest free chunk that contains
26 * the requested size is split (or taken as a whole if it can't be
27 * splitted anymore), and it's lower half will be removed from the
29 * The free list is ordered by size, starting with the smallest
30 * free chunk available. When a chunk is freed, it will be joint
31 * with its predecessor or successor, if possible.
32 * To ease list handling, the list anchor itself is a free chunk with
33 * size 0 that can't be allocated.
42 free_chunk
*Split(size_t splitSize
);
43 bool IsTouching(free_chunk
*link
);
44 free_chunk
*Join(free_chunk
*link
);
45 void Remove(free_chunk
*previous
= NULL
);
48 void *AllocatedAddress() const;
49 static free_chunk
*SetToAllocated(void *allocated
);
53 static size_t sAvailable
;
54 static free_chunk sFreeAnchor
;
57 /** Returns the amount of bytes that can be allocated
62 free_chunk::Size() const
64 return size
- sizeof(size_t);
68 /** Splits the upper half at the requested location
73 free_chunk::Split(size_t splitSize
)
75 free_chunk
*chunk
= (free_chunk
*)((uint8
*)this + sizeof(size_t) + splitSize
);
76 chunk
->size
= size
- splitSize
- sizeof(size_t);
79 size
= splitSize
+ sizeof(size_t);
85 /** Checks if the specified chunk touches this chunk, so
86 * that they could be joined.
90 free_chunk::IsTouching(free_chunk
*chunk
)
93 && (((uint8
*)this + size
== (uint8
*)chunk
)
94 || (uint8
*)chunk
+ chunk
->size
== (uint8
*)this);
98 /** Joins the chunk to this chunk and returns the pointer
99 * to the new chunk - which will either be one of the
101 * Note, the chunks must be joinable, or else this method
102 * doesn't work correctly. Use free_chunk::IsTouching()
103 * to check if this method can be applied.
107 free_chunk::Join(free_chunk
*chunk
)
124 free_chunk::Remove(free_chunk
*previous
)
126 if (previous
== NULL
) {
127 // find the previous chunk in the list
128 free_chunk
*chunk
= sFreeAnchor
.next
;
130 while (chunk
!= NULL
&& chunk
!= this) {
136 printf("runtime_loader: try to remove chunk that's not in list");
141 previous
->next
= this->next
;
147 free_chunk::Enqueue()
149 free_chunk
*chunk
= sFreeAnchor
.next
, *last
= &sFreeAnchor
;
150 while (chunk
&& chunk
->Size() < size
) {
161 free_chunk::AllocatedAddress() const
163 return (void *)&next
;
168 free_chunk::SetToAllocated(void *allocated
)
170 return (free_chunk
*)((uint8
*)allocated
- sizeof(size_t));
174 // #pragma mark - private functions
178 add_area(size_t size
)
181 area_id area
= _kern_create_area("rld heap", &base
,
182 B_RANDOMIZED_ANY_ADDRESS
, size
, B_NO_LOCK
, B_READ_AREA
| B_WRITE_AREA
);
186 sAvailable
+= size
- sizeof(size_t);
188 // declare the whole heap as one chunk, and add it
191 free_chunk
*chunk
= (free_chunk
*)base
;
193 chunk
->next
= sFreeAnchor
.next
;
195 sFreeAnchor
.next
= chunk
;
201 grow_heap(size_t bytes
)
203 // align the area size to an 32768 bytes boundary
204 bytes
= (bytes
+ 32767) & ~32767;
205 return add_area(bytes
);
209 // #pragma mark - public API
215 status_t status
= add_area(kInitialHeapSize
);
219 sFreeAnchor
.size
= 0;
228 free_chunk
*chunk
= sFreeAnchor
.next
, *last
= &sFreeAnchor
;
229 while (chunk
!= NULL
) {
232 printf("\t%p: chunk size = %ld, end = %p, next = %p\n", chunk
, chunk
->size
, (uint8
*)chunk
+ chunk
->size
, chunk
->next
);
240 realloc(void *allocation
, size_t newSize
)
242 // free, if new size is 0
248 // just malloc(), if no previous allocation
249 if (allocation
== NULL
)
250 return malloc(newSize
);
252 // we're lazy and don't shrink allocations
253 free_chunk
* chunk
= free_chunk::SetToAllocated(allocation
);
254 if (chunk
->Size() >= newSize
)
257 // the allocation needs to grow -- allocate a new one and memcpy()
258 void* newAllocation
= malloc(newSize
);
259 if (newAllocation
!= NULL
) {
260 memcpy(newAllocation
, allocation
, chunk
->Size());
264 return newAllocation
;
274 // align the size requirement to an 8 bytes boundary
275 size
= (size
+ 7) & ~7;
278 if (size
> sAvailable
) {
279 // try to enlarge heap
280 if (grow_heap(size
) < B_OK
)
284 free_chunk
*chunk
= sFreeAnchor
.next
, *last
= &sFreeAnchor
;
285 while (chunk
&& chunk
->Size() < size
) {
291 // could not find a free chunk as large as needed
292 if (grow_heap(size
) < B_OK
)
298 if (chunk
->Size() > size
+ sizeof(free_chunk
) + sizeof(size_t)) {
299 // if this chunk is bigger than the requested size,
300 // we split it to form two chunks (with a minimal
301 // size of sizeof(size_t) allocatable bytes).
303 free_chunk
*freeChunk
= chunk
->Split(size
);
304 last
->next
= freeChunk
;
306 // re-enqueue the free chunk at the correct position
307 freeChunk
->Remove(last
);
308 freeChunk
->Enqueue();
310 // remove the chunk from the free list
312 last
->next
= chunk
->next
;
315 sAvailable
-= size
+ sizeof(size_t);
317 return chunk
->AllocatedAddress();
322 free(void *allocated
)
324 if (allocated
== NULL
)
327 free_chunk
*freedChunk
= free_chunk::SetToAllocated(allocated
);
328 sAvailable
+= freedChunk
->size
;
330 // try to join the new free chunk with an existing one
331 // it may be joined with up to two chunks
333 free_chunk
*chunk
= sFreeAnchor
.next
, *last
= &sFreeAnchor
;
337 if (chunk
->IsTouching(freedChunk
)) {
338 // almost "insert" it into the list before joining
339 // because the next pointer is inherited by the chunk
340 freedChunk
->next
= chunk
->next
;
341 freedChunk
= chunk
->Join(freedChunk
);
343 // remove the joined chunk from the list
344 last
->next
= freedChunk
->next
;
347 if (++joinCount
== 2)
355 // enqueue the link at the right position; the
356 // free link queue is ordered by size
358 freedChunk
->Enqueue();