source: mainline/uspace/lib/c/generic/malloc.c@ 86d7bfa

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 86d7bfa was 47b7006, checked in by Martin Decky <martin@…>, 14 years ago

improve run-time termination

  • get rid of exit()
  • get rid of _exit(), use the common exit()
  • get rid of core(), use the common abort()
  • make main() more fail-safe (call abort() on unhealthy conditions), call async_sess_init() explicitly
  • add several libc-private headers for cleaner environment
  • use SYS_TASK_EXIT in exit() and abort()
  • Property mode set to 100644
File size: 14.7 KB
Line 
1/*
2 * Copyright (c) 2009 Martin Decky
3 * Copyright (c) 2009 Petr Tuma
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libc
31 * @{
32 */
33/** @file
34 */
35
36#include <malloc.h>
37#include <bool.h>
38#include <as.h>
39#include <align.h>
40#include <macros.h>
41#include <assert.h>
42#include <errno.h>
43#include <bitops.h>
44#include <mem.h>
45#include <futex.h>
46#include <adt/gcdlcm.h>
47#include "private/malloc.h"
48
49/* Magic used in heap headers. */
50#define HEAP_BLOCK_HEAD_MAGIC 0xBEEF0101
51
52/* Magic used in heap footers. */
53#define HEAP_BLOCK_FOOT_MAGIC 0xBEEF0202
54
55/** Allocation alignment (this also covers the alignment of fields
56 in the heap header and footer) */
57#define BASE_ALIGN 16
58
59/**
60 * Either 4 * 256M on 32-bit architecures or 16 * 256M on 64-bit architectures
61 */
62#define MAX_HEAP_SIZE (sizeof(uintptr_t) << 28)
63
64/**
65 *
66 */
67#define STRUCT_OVERHEAD (sizeof(heap_block_head_t) + sizeof(heap_block_foot_t))
68
69/**
70 * Calculate real size of a heap block (with header and footer)
71 */
72#define GROSS_SIZE(size) ((size) + STRUCT_OVERHEAD)
73
74/**
75 * Calculate net size of a heap block (without header and footer)
76 */
77#define NET_SIZE(size) ((size) - STRUCT_OVERHEAD)
78
79/** Header of a heap block
80 *
81 */
82typedef struct {
83 /* Size of the block (including header and footer) */
84 size_t size;
85
86 /* Indication of a free block */
87 bool free;
88
89 /* A magic value to detect overwrite of heap header */
90 uint32_t magic;
91} heap_block_head_t;
92
93/** Footer of a heap block
94 *
95 */
96typedef struct {
97 /* Size of the block (including header and footer) */
98 size_t size;
99
100 /* A magic value to detect overwrite of heap footer */
101 uint32_t magic;
102} heap_block_foot_t;
103
104/** Linker heap symbol */
105extern char _heap;
106
107/** Futex for thread-safe heap manipulation */
108static futex_t malloc_futex = FUTEX_INITIALIZER;
109
110/** Address of heap start */
111static void *heap_start = 0;
112
113/** Address of heap end */
114static void *heap_end = 0;
115
116/** Maximum heap size */
117static size_t max_heap_size = (size_t) -1;
118
119/** Current number of pages of heap area */
120static size_t heap_pages = 0;
121
122/** Initialize a heap block
123 *
124 * Fills in the structures related to a heap block.
125 * Should be called only inside the critical section.
126 *
127 * @param addr Address of the block.
128 * @param size Size of the block including the header and the footer.
129 * @param free Indication of a free block.
130 *
131 */
132static void block_init(void *addr, size_t size, bool free)
133{
134 /* Calculate the position of the header and the footer */
135 heap_block_head_t *head = (heap_block_head_t *) addr;
136 heap_block_foot_t *foot =
137 (heap_block_foot_t *) (addr + size - sizeof(heap_block_foot_t));
138
139 head->size = size;
140 head->free = free;
141 head->magic = HEAP_BLOCK_HEAD_MAGIC;
142
143 foot->size = size;
144 foot->magic = HEAP_BLOCK_FOOT_MAGIC;
145}
146
147/** Check a heap block
148 *
149 * Verifies that the structures related to a heap block still contain
150 * the magic constants. This helps detect heap corruption early on.
151 * Should be called only inside the critical section.
152 *
153 * @param addr Address of the block.
154 *
155 */
156static void block_check(void *addr)
157{
158 heap_block_head_t *head = (heap_block_head_t *) addr;
159
160 assert(head->magic == HEAP_BLOCK_HEAD_MAGIC);
161
162 heap_block_foot_t *foot =
163 (heap_block_foot_t *) (addr + head->size - sizeof(heap_block_foot_t));
164
165 assert(foot->magic == HEAP_BLOCK_FOOT_MAGIC);
166 assert(head->size == foot->size);
167}
168
169/** Increase the heap area size
170 *
171 * Should be called only inside the critical section.
172 *
173 * @param size Number of bytes to grow the heap by.
174 *
175 */
176static bool grow_heap(size_t size)
177{
178 if (size == 0)
179 return false;
180
181 if ((heap_start + size < heap_start) || (heap_end + size < heap_end))
182 return false;
183
184 size_t heap_size = (size_t) (heap_end - heap_start);
185
186 if ((max_heap_size != (size_t) -1) && (heap_size + size > max_heap_size))
187 return false;
188
189 size_t pages = (size - 1) / PAGE_SIZE + 1;
190
191 if (as_area_resize((void *) &_heap, (heap_pages + pages) * PAGE_SIZE, 0)
192 == EOK) {
193 void *end = (void *) ALIGN_DOWN(((uintptr_t) &_heap) +
194 (heap_pages + pages) * PAGE_SIZE, BASE_ALIGN);
195 block_init(heap_end, end - heap_end, true);
196 heap_pages += pages;
197 heap_end = end;
198 return true;
199 }
200
201 return false;
202}
203
204/** Decrease the heap area
205 *
206 * Should be called only inside the critical section.
207 *
208 * @param size Number of bytes to shrink the heap by.
209 *
210 */
211static void shrink_heap(void)
212{
213 // TODO
214}
215
216/** Initialize the heap allocator
217 *
218 * Create initial heap memory area. This routine is
219 * only called from libc initialization, thus we do not
220 * take any locks.
221 *
222 */
223void __malloc_init(void)
224{
225 if (!as_area_create((void *) &_heap, PAGE_SIZE,
226 AS_AREA_WRITE | AS_AREA_READ))
227 abort();
228
229 heap_pages = 1;
230 heap_start = (void *) ALIGN_UP((uintptr_t) &_heap, BASE_ALIGN);
231 heap_end =
232 (void *) ALIGN_DOWN(((uintptr_t) &_heap) + PAGE_SIZE, BASE_ALIGN);
233
234 /* Make the entire area one large block. */
235 block_init(heap_start, heap_end - heap_start, true);
236}
237
238/** Get maximum heap address
239 *
240 */
241uintptr_t get_max_heap_addr(void)
242{
243 futex_down(&malloc_futex);
244
245 if (max_heap_size == (size_t) -1)
246 max_heap_size =
247 max((size_t) (heap_end - heap_start), MAX_HEAP_SIZE);
248
249 uintptr_t max_heap_addr = (uintptr_t) heap_start + max_heap_size;
250
251 futex_up(&malloc_futex);
252
253 return max_heap_addr;
254}
255
256/** Split heap block and mark it as used.
257 *
258 * Should be called only inside the critical section.
259 *
260 * @param cur Heap block to split.
261 * @param size Number of bytes to split and mark from the beginning
262 * of the block.
263 *
264 */
265static void split_mark(heap_block_head_t *cur, const size_t size)
266{
267 assert(cur->size >= size);
268
269 /* See if we should split the block. */
270 size_t split_limit = GROSS_SIZE(size);
271
272 if (cur->size > split_limit) {
273 /* Block big enough -> split. */
274 void *next = ((void *) cur) + size;
275 block_init(next, cur->size - size, true);
276 block_init(cur, size, false);
277 } else {
278 /* Block too small -> use as is. */
279 cur->free = false;
280 }
281}
282
283/** Allocate a memory block
284 *
285 * Should be called only inside the critical section.
286 *
287 * @param size The size of the block to allocate.
288 * @param align Memory address alignment.
289 *
290 * @return the address of the block or NULL when not enough memory.
291 *
292 */
293static void *malloc_internal(const size_t size, const size_t align)
294{
295 if (align == 0)
296 return NULL;
297
298 size_t falign = lcm(align, BASE_ALIGN);
299 size_t real_size = GROSS_SIZE(ALIGN_UP(size, falign));
300
301 bool grown = false;
302 void *result;
303
304loop:
305 result = NULL;
306 heap_block_head_t *cur = (heap_block_head_t *) heap_start;
307
308 while ((result == NULL) && ((void *) cur < heap_end)) {
309 block_check(cur);
310
311 /* Try to find a block that is free and large enough. */
312 if ((cur->free) && (cur->size >= real_size)) {
313 /* We have found a suitable block.
314 Check for alignment properties. */
315 void *addr = ((void *) cur) + sizeof(heap_block_head_t);
316 void *aligned = (void *) ALIGN_UP(addr, falign);
317
318 if (addr == aligned) {
319 /* Exact block start including alignment. */
320 split_mark(cur, real_size);
321 result = addr;
322 } else {
323 /* Block start has to be aligned */
324 size_t excess = (size_t) (aligned - addr);
325
326 if (cur->size >= real_size + excess) {
327 /* The current block is large enough to fit
328 data in including alignment */
329 if ((void *) cur > heap_start) {
330 /* There is a block before the current block.
331 This previous block can be enlarged to compensate
332 for the alignment excess */
333 heap_block_foot_t *prev_foot =
334 ((void *) cur) - sizeof(heap_block_foot_t);
335
336 heap_block_head_t *prev_head =
337 (heap_block_head_t *) (((void *) cur) - prev_foot->size);
338
339 block_check(prev_head);
340
341 size_t reduced_size = cur->size - excess;
342 heap_block_head_t *next_head = ((void *) cur) + excess;
343
344 if ((!prev_head->free) && (excess >= STRUCT_OVERHEAD)) {
345 /* The previous block is not free and there is enough
346 space to fill in a new free block between the previous
347 and current block */
348 block_init(cur, excess, true);
349 } else {
350 /* The previous block is free (thus there is no need to
351 induce additional fragmentation to the heap) or the
352 excess is small, thus just enlarge the previous block */
353 block_init(prev_head, prev_head->size + excess, prev_head->free);
354 }
355
356 block_init(next_head, reduced_size, true);
357 split_mark(next_head, real_size);
358 result = aligned;
359 cur = next_head;
360 } else {
361 /* The current block is the first block on the heap.
362 We have to make sure that the alignment excess
363 is large enough to fit a new free block just
364 before the current block */
365 while (excess < STRUCT_OVERHEAD) {
366 aligned += falign;
367 excess += falign;
368 }
369
370 /* Check for current block size again */
371 if (cur->size >= real_size + excess) {
372 size_t reduced_size = cur->size - excess;
373 cur = (heap_block_head_t *) (heap_start + excess);
374
375 block_init(heap_start, excess, true);
376 block_init(cur, reduced_size, true);
377 split_mark(cur, real_size);
378 result = aligned;
379 }
380 }
381 }
382 }
383 }
384
385 /* Advance to the next block. */
386 cur = (heap_block_head_t *) (((void *) cur) + cur->size);
387 }
388
389 if ((result == NULL) && (!grown)) {
390 if (grow_heap(real_size)) {
391 grown = true;
392 goto loop;
393 }
394 }
395
396 return result;
397}
398
399/** Allocate memory by number of elements
400 *
401 * @param nmemb Number of members to allocate.
402 * @param size Size of one member in bytes.
403 *
404 * @return Allocated memory or NULL.
405 *
406 */
407void *calloc(const size_t nmemb, const size_t size)
408{
409 void *block = malloc(nmemb * size);
410 if (block == NULL)
411 return NULL;
412
413 memset(block, 0, nmemb * size);
414 return block;
415}
416
417/** Allocate memory
418 *
419 * @param size Number of bytes to allocate.
420 *
421 * @return Allocated memory or NULL.
422 *
423 */
424void *malloc(const size_t size)
425{
426 futex_down(&malloc_futex);
427 void *block = malloc_internal(size, BASE_ALIGN);
428 futex_up(&malloc_futex);
429
430 return block;
431}
432
433/** Allocate memory with specified alignment
434 *
435 * @param align Alignment in byes.
436 * @param size Number of bytes to allocate.
437 *
438 * @return Allocated memory or NULL.
439 *
440 */
441void *memalign(const size_t align, const size_t size)
442{
443 if (align == 0)
444 return NULL;
445
446 size_t palign =
447 1 << (fnzb(max(sizeof(void *), align) - 1) + 1);
448
449 futex_down(&malloc_futex);
450 void *block = malloc_internal(size, palign);
451 futex_up(&malloc_futex);
452
453 return block;
454}
455
456/** Reallocate memory block
457 *
458 * @param addr Already allocated memory or NULL.
459 * @param size New size of the memory block.
460 *
461 * @return Reallocated memory or NULL.
462 *
463 */
464void *realloc(const void *addr, const size_t size)
465{
466 if (addr == NULL)
467 return malloc(size);
468
469 futex_down(&malloc_futex);
470
471 /* Calculate the position of the header. */
472 heap_block_head_t *head =
473 (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
474
475 assert((void *) head >= heap_start);
476 assert((void *) head < heap_end);
477
478 block_check(head);
479 assert(!head->free);
480
481 void *ptr = NULL;
482 bool reloc = false;
483 size_t real_size = GROSS_SIZE(ALIGN_UP(size, BASE_ALIGN));
484 size_t orig_size = head->size;
485
486 if (orig_size > real_size) {
487 /* Shrink */
488 if (orig_size - real_size >= STRUCT_OVERHEAD) {
489 /* Split the original block to a full block
490 and a trailing free block */
491 block_init((void *) head, real_size, false);
492 block_init((void *) head + real_size,
493 orig_size - real_size, true);
494 shrink_heap();
495 }
496
497 ptr = ((void *) head) + sizeof(heap_block_head_t);
498 } else {
499 /* Look at the next block. If it is free and the size is
500 sufficient then merge the two. Otherwise just allocate
501 a new block, copy the original data into it and
502 free the original block. */
503 heap_block_head_t *next_head =
504 (heap_block_head_t *) (((void *) head) + head->size);
505
506 if (((void *) next_head < heap_end) &&
507 (head->size + next_head->size >= real_size) &&
508 (next_head->free)) {
509 block_check(next_head);
510 block_init(head, head->size + next_head->size, false);
511 split_mark(head, real_size);
512
513 ptr = ((void *) head) + sizeof(heap_block_head_t);
514 } else
515 reloc = true;
516 }
517
518 futex_up(&malloc_futex);
519
520 if (reloc) {
521 ptr = malloc(size);
522 if (ptr != NULL) {
523 memcpy(ptr, addr, NET_SIZE(orig_size));
524 free(addr);
525 }
526 }
527
528 return ptr;
529}
530
531/** Free a memory block
532 *
533 * @param addr The address of the block.
534 *
535 */
536void free(const void *addr)
537{
538 futex_down(&malloc_futex);
539
540 /* Calculate the position of the header. */
541 heap_block_head_t *head
542 = (heap_block_head_t *) (addr - sizeof(heap_block_head_t));
543
544 assert((void *) head >= heap_start);
545 assert((void *) head < heap_end);
546
547 block_check(head);
548 assert(!head->free);
549
550 /* Mark the block itself as free. */
551 head->free = true;
552
553 /* Look at the next block. If it is free, merge the two. */
554 heap_block_head_t *next_head
555 = (heap_block_head_t *) (((void *) head) + head->size);
556
557 if ((void *) next_head < heap_end) {
558 block_check(next_head);
559 if (next_head->free)
560 block_init(head, head->size + next_head->size, true);
561 }
562
563 /* Look at the previous block. If it is free, merge the two. */
564 if ((void *) head > heap_start) {
565 heap_block_foot_t *prev_foot =
566 (heap_block_foot_t *) (((void *) head) - sizeof(heap_block_foot_t));
567
568 heap_block_head_t *prev_head =
569 (heap_block_head_t *) (((void *) head) - prev_foot->size);
570
571 block_check(prev_head);
572
573 if (prev_head->free)
574 block_init(prev_head, prev_head->size + head->size, true);
575 }
576
577 shrink_heap();
578
579 futex_up(&malloc_futex);
580}
581
582/** @}
583 */
Note: See TracBrowser for help on using the repository browser.