Browse Source

Changed plenty of stuff: fixed free list joining and splitting, better printing, better realloc, better interfaces, better stuff in general

main
Titouan Rigoudy 11 years ago
parent
commit
66dbd45fc1
5 changed files with 128 additions and 41 deletions
  1. +16
    -13
      ya_block.c
  2. +4
    -4
      ya_block.h
  3. +55
    -15
      ya_freelist.c
  4. +16
    -2
      ya_freelist.h
  5. +37
    -7
      yamalloc.c

+ 16
- 13
ya_block.c View File

@ -66,8 +66,8 @@ void block_print_range(intptr_t *start, intptr_t *end) {
intptr_t size; intptr_t size;
for (block = start; block < end; block += size) { for (block = start; block < end; block += size) {
size = block_size(block); size = block_size(block);
printf("Block at %p size = %ld alloc = %d\n",
block, size, block_is_alloc(block));
ya_debug("%d %p:%ld\n",
block_is_alloc(block), block, size);
} }
} }
#endif #endif
@ -164,8 +164,8 @@ intptr_t *block_split(intptr_t *block, intptr_t size) {
return block + size; return block + size;
} }
/* Try to find a free block at least size words big by walking the boundary
* tags. If no block is found the heap is grown adequately.
/* Try to find a free block at least size min_size words large by walking the
* boundary tags. Does not grow the heap.
* Returns a pointer to the block or NULL in case of failure. */ * Returns a pointer to the block or NULL in case of failure. */
intptr_t *block_find(intptr_t min_size) { intptr_t *block_find(intptr_t min_size) {
intptr_t *block; intptr_t *block;
@ -176,8 +176,8 @@ intptr_t *block_find(intptr_t min_size) {
return block; return block;
} }
} }
// could not find block, extend heap
return heap_extend(min_size);
// could not find block
return NULL;
} }
/* Initializes the heap by calling sbrk to allocate some starter memory. /* Initializes the heap by calling sbrk to allocate some starter memory.
@ -195,26 +195,29 @@ intptr_t *heap_init() {
heap_start += 2; // space for the first block's tags + dword alignment heap_start += 2; // space for the first block's tags + dword alignment
heap_end = heap_start + size; heap_end = heap_start + size;
block_init(heap_start, size); block_init(heap_start, size);
fl_set_prev(heap_start, NULL);
fl_set_next(heap_start, NULL);
fl_free(heap_start);
ya_debug("heap_init: start = %p, end = %p, size = %ld\n", ya_debug("heap_init: start = %p, end = %p, size = %ld\n",
heap_start, heap_end, size); heap_start, heap_end, size);
return heap_start; return heap_start;
} }
/* Extends the heap by at least size_w words by calling sbrk.
/* Extends the heap by at least n_bytes bytes by calling sbrk.
* Returns a pointer to the last (free) block or NULL in case of failure. */ * Returns a pointer to the last (free) block or NULL in case of failure. */
intptr_t *heap_extend(intptr_t size) {
size = block_fit(round_to(WORD_SIZE * size, CHUNK_SIZE));
intptr_t *heap_extend(size_t n_bytes) {
// request an integer number of blocks
intptr_t size = block_fit(round_to(n_bytes, CHUNK_SIZE));
void *ptr = sbrk(WORD_SIZE * size); void *ptr = sbrk(WORD_SIZE * size);
if (ptr == (void *) - 1) {
if (ptr == (void *) -1) {
return NULL; return NULL;
} }
intptr_t *block = ptr; // == old heap_end intptr_t *block = ptr; // == old heap_end
heap_end = block + size; heap_end = block + size;
block_init(block, size); block_init(block, size);
fl_free(block);
fl_join(block);
block = block_join(block);
ya_debug("heap_extend: old end = %p, new end = %p, size = %ld\n", ya_debug("heap_extend: old end = %p, new end = %p, size = %ld\n",
block, heap_end, size); block, heap_end, size);
ya_print_blocks(); ya_print_blocks();
return block_join(ptr);
return block;
} }

+ 4
- 4
ya_block.h View File

@ -63,9 +63,9 @@ static inline intptr_t block_size(intptr_t *block) {
* Returns the pointer to the start of the heap or NULL in case of failure. */ * Returns the pointer to the start of the heap or NULL in case of failure. */
intptr_t *heap_init(); intptr_t *heap_init();
/* Extends the heap by at least size_w words by calling sbrk.
/* Extends the heap by at least n_bytes bytes by calling sbrk.
* Returns a pointer to the last (free) block or NULL in case of failure. */ * Returns a pointer to the last (free) block or NULL in case of failure. */
intptr_t *heap_extend(intptr_t size_w);
intptr_t *heap_extend(size_t n_bytes);
#ifdef YA_DEBUG #ifdef YA_DEBUG
/* Prints each block in the range from the block at start to the one at end */ /* Prints each block in the range from the block at start to the one at end */
@ -104,8 +104,8 @@ intptr_t *block_join(intptr_t *block);
* Returns a pointer to the second block or NULL if no split occurred. */ * Returns a pointer to the second block or NULL if no split occurred. */
intptr_t *block_split(intptr_t *block, intptr_t size); intptr_t *block_split(intptr_t *block, intptr_t size);
/* Try to find a free block at least min_size words big by walking the
* boundary tags. If no block is found the heap is grown adequately.
/* Try to find a free block at least min_size words large by walking the
* boundary tags. Does not grow the heap.
* Returns a pointer to the block or NULL in case of failure. */ * Returns a pointer to the block or NULL in case of failure. */
intptr_t *block_find(intptr_t min_size); intptr_t *block_find(intptr_t min_size);


+ 55
- 15
ya_freelist.c View File

@ -3,15 +3,6 @@
* ya_freelist.c * ya_freelist.c
*/ */
/* Block layout:
*
* -2 -1 0 size-4 size-3
* +------+------+-------- - - - - - - - --------+------+------+
* | prev | size | data... | size | next |
* +------+------+-------- - - - - - - - --------+------+------+
*
*/
/*----------*/ /*----------*/
/* Includes */ /* Includes */
/*----------*/ /*----------*/
@ -35,7 +26,7 @@ intptr_t *fl_end = NULL; // sorted decreasing
#ifdef YA_DEBUG #ifdef YA_DEBUG
void fl_debug_print() { void fl_debug_print() {
for (intptr_t *block = fl_start; block != NULL; block = fl_next(block)) { for (intptr_t *block = fl_start; block != NULL; block = fl_next(block)) {
printf("Free block %p:%ld\n", block, block_size(block));
ya_debug("%p:%ld\n", block, block_size(block));
} }
} }
#endif #endif
@ -62,11 +53,15 @@ void fl_alloc(intptr_t *block) {
/* Adds the freed block to the appropriate in the free list, which is kept /* Adds the freed block to the appropriate in the free list, which is kept
* in sorted increasing order. */ * in sorted increasing order. */
void fl_free(intptr_t *block) { void fl_free(intptr_t *block) {
intptr_t *next;
for (next = fl_start; next && block < next; next = fl_next(next)) {
// *whistle*
if (!fl_start && !fl_end) {
// add to empty list
fl_set_prev(block, NULL);
fl_set_next(block, NULL);
fl_start = block;
fl_end = block;
return;
} }
if (next == fl_start) {
if (block < fl_start) {
// preprend block to the free list // preprend block to the free list
fl_set_prev(block, NULL); fl_set_prev(block, NULL);
fl_set_next(block, fl_start); fl_set_next(block, fl_start);
@ -74,7 +69,7 @@ void fl_free(intptr_t *block) {
fl_start = block; fl_start = block;
return; return;
} }
if (!next) {
if (block > fl_end) {
// append block to the free list // append block to the free list
fl_set_prev(block, fl_end); fl_set_prev(block, fl_end);
fl_set_next(block, NULL); fl_set_next(block, NULL);
@ -83,6 +78,10 @@ void fl_free(intptr_t *block) {
return; return;
} }
// splice the block in the middle of the list // splice the block in the middle of the list
intptr_t *next;
for (next = fl_start; next && next < block; next = fl_next(next)) {
// *whistle*
}
intptr_t *prev = fl_prev(next); intptr_t *prev = fl_prev(next);
fl_set_prev(block, prev); fl_set_prev(block, prev);
fl_set_next(block, next); fl_set_next(block, next);
@ -104,6 +103,47 @@ intptr_t *fl_find(intptr_t min_size) {
/* Force split of free block [block_size] into [size, block_size - size]. */ /* Force split of free block [block_size] into [size, block_size - size]. */
void fl_split(intptr_t *block, intptr_t size) { void fl_split(intptr_t *block, intptr_t size) {
fl_set_next(block, block+size); fl_set_next(block, block+size);
if (block == fl_end) {
fl_end = block+size;
}
fl_set_prev(block+size, block); fl_set_prev(block+size, block);
} }
void fl_join_next(intptr_t *block) {
intptr_t *next = block + block_size(block);
if (fl_next(block) == next) {
// skip a block
ya_debug("fl_join_next: %p:%ld + %p:%ld -> %p:%ld\n",
block, block_size(block), next, block_size(next),
block, block_size(block) + block_size(next));
fl_set_next(block, fl_next(next));
fl_set_prev(next, fl_prev(block));
if (fl_next(next) == NULL) {
fl_end = block;
}
}
}
void fl_join_prev(intptr_t *block) {
intptr_t *free_prev = fl_prev(block);
if (!free_prev) {
return;
}
// will not segfault because there is at least one block preceding
intptr_t *prev = block - block[-4];
if (prev == free_prev) {
ya_debug("fl_join_prev: %p:%ld + %p:%ld -> %p:%ld\n",
block, block_size(block), prev, block_size(prev),
prev, block_size(block) + block_size(prev));
fl_set_prev(block, fl_prev(prev));
fl_set_next(prev, fl_next(block));
if (fl_next(block) == NULL) {
fl_end = prev;
}
}
}
void fl_join(intptr_t *block) {
fl_join_next(block);
fl_join_prev(block);
}

+ 16
- 2
ya_freelist.h View File

@ -3,6 +3,15 @@
* ya_freelist.h * ya_freelist.h
*/ */
/* Block layout:
*
* -2 -1 0 size-4 size-3
* +------+------+-------- - - - - - - - --------+------+------+
* | prev | size | data... | size | next |
* +------+------+-------- - - - - - - - --------+------+------+
*
*/
#ifndef YA_FREELIST_H #ifndef YA_FREELIST_H
#define YA_FREELIST_H #define YA_FREELIST_H
@ -13,6 +22,7 @@
#include <stdint.h> // for intptr_t #include <stdint.h> // for intptr_t
#include "ya_block.h" #include "ya_block.h"
#include "ya_debug.h"
/*---------*/ /*---------*/
/* Inlines */ /* Inlines */
@ -45,7 +55,7 @@ void fl_debug_print();
/* Splices the allocated block out of the free list. */ /* Splices the allocated block out of the free list. */
void fl_alloc(intptr_t *block); void fl_alloc(intptr_t *block);
/* Adds the freed block to the start of the free list. */
/* Adds the freed block to the appropriate place in free list. */
void fl_free(intptr_t *block); void fl_free(intptr_t *block);
/* Returns the first block in the free list at min_size words long. /* Returns the first block in the free list at min_size words long.
@ -55,6 +65,10 @@ intptr_t *fl_find(intptr_t min_size);
/* Force split of block [block_size] into [size, block_size - size]. */ /* Force split of block [block_size] into [size, block_size - size]. */
void fl_split(intptr_t *block, intptr_t size); void fl_split(intptr_t *block, intptr_t size);
/* Joins are unnecessary because the pointers will still be ok after a join. */
void fl_join_prev(intptr_t *block);
void fl_join_next(intptr_t *block);
void fl_join(intptr_t *block);
#endif #endif

+ 37
- 7
yamalloc.c View File

@ -37,10 +37,17 @@ void *malloc(size_t n_bytes) {
return NULL; return NULL;
} }
} }
intptr_t min_size = block_fit(n_bytes);
intptr_t *block = block_find(min_size);
block_split(block, min_size);
intptr_t size = block_fit(n_bytes);
intptr_t *block = block_find(size);
if (!block) {
block = heap_extend(n_bytes);
}
if (block_split(block, size)) {
// block was indeed split, so splt it in the free list as well
fl_split(block, size);
}
block_alloc(block); block_alloc(block);
fl_alloc(block);
return block; return block;
} }
@ -53,6 +60,8 @@ void free(void *ptr) {
return; // TODO: provoke segfault return; // TODO: provoke segfault
} }
block_free(block); block_free(block);
fl_free(block);
fl_join(block);
block_join(block); block_join(block);
} }
@ -78,6 +87,7 @@ void *realloc(void *ptr, size_t n_bytes) {
} }
if (n_bytes == 0) { if (n_bytes == 0) {
free(ptr); free(ptr);
return NULL;
} }
intptr_t *block = ptr; intptr_t *block = ptr;
if (block < heap_start) { if (block < heap_start) {
@ -91,26 +101,46 @@ void *realloc(void *ptr, size_t n_bytes) {
if (new_size < size) { if (new_size < size) {
intptr_t *next = block_split(block, new_size); intptr_t *next = block_split(block, new_size);
if (next) { if (next) {
fl_free(next); // add the next from the free list
fl_join_next(next);
block_join_next(next); // coalesce the leftovers block_join_next(next); // coalesce the leftovers
} }
block_alloc(block); block_alloc(block);
// no need to remove it from the free list, it wasn't in it
return block; return block;
} }
intptr_t *next = block + size; intptr_t *next = block + size;
if (next == heap_end ||
(!block_is_alloc(next) && next + block_size(next) == heap_end)) {
// grow the heap and extend block
next = heap_extend(n_bytes);
fl_free(block);
fl_join_next(block);
block_join_next(block);
if (block_split(block, new_size)) {
fl_split(block, new_size);
}
block_alloc(block);
fl_alloc(block);
return block;
}
// try to use next free block // try to use next free block
if (next < heap_end) { if (next < heap_end) {
intptr_t next_size = block_size(next); intptr_t next_size = block_size(next);
if (!block_is_alloc(next) && new_size <= size + next_size) { if (!block_is_alloc(next) && new_size <= size + next_size) {
// try to split the next block at the right size
if (block_split(next, new_size - size)) {
// split successful, must split in the free list too
fl_split(block, new_size);
}
fl_alloc(next); // remove the next block from the free list
fl_join_next(block);
block_join_next(block); // coalesce block_join_next(block); // coalesce
block_split(block, new_size); // split if possible
// no need to coalesce
block_alloc(block); // mark block as allocated block_alloc(block); // mark block as allocated
return block; return block;
} }
} }
// resizing failed, so allocate a whole new block and copy // resizing failed, so allocate a whole new block and copy
// could handle the case when the block is the last in the heap
// a bit more gracefully and just grow the heap instead of copying
intptr_t *new_block = malloc(n_bytes); intptr_t *new_block = malloc(n_bytes);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
new_block[i] = block[i]; new_block[i] = block[i];


Loading…
Cancel
Save