diff --git a/mm.c b/mm.c index fe76e53..cb649e0 100644 --- a/mm.c +++ b/mm.c @@ -1,38 +1,48 @@ /* * mm.c - Dynamic Memory Allocator * - * This is a segregated free list implementation with multiple - * lists to hold different block sizes. The size ranges for our 8 lists * are powers of 2, with - * the smallest size on the lower bound being 8 words, + * This is an external, segregated free list implementation with multiple lists that hold blocks of + * different size ranges. The size ranges for our 8 lists are determined by powers of 2. + * - 1st list holds blocks that are 8 * 1 = 8 words. + * - 2nd list holds blocks that are 8 * 2 = 16 words. (the constant 8 is the minimum block size) + * - 3rd list holds blocks that are 8 * 4 = 32 words. + * (and so on) * - * A seperate list is used to store the sizes, and is checked when a - * free block is added to a list. + * When a free block is inserted to a list, it's placed in the front, and when a free block is + * removed from a list, it's taken from the back. In other words, last in, first out (LIFO) was + * implemented. * - * Both allocated and free blocks are stored in the heap - * with the pointer heap_listp. - * Each block has a header and a footer, which - * store the size in 31 bits, and 1 bit for determining whether it is - * free. - * - * Blocks have a payload which holds hold necessary data and a list_elem struct so blocks can be stored in lists + * The lists are stored in an array (free_lists) based on their max size. + * + * An array is used to store the max sizes (list_sizes), and is checked when a free block is added + * to a list. + * + * Both allocated and free blocks use the same structure (block) and are stored in the heap with the + * pointer heap_listp. + * - Each block has a header and a footer, which store the size in 31 bits, and 1 bit for determining + * whether it is free. + * - Blocks have a payload which holds hold necessary data. + * - They also have a list_elem struct so blocks can be stored in lists. */ #include #include #include +#include #include "mm.h" #include "memlib.h" #include "list.h" #include "config.h" - struct boundary_tag { int inuse:1; int size:31; }; -/* FENCE is used for heap prologue/epilogue. */ +/* + * FENCE is used for heap prologue/epilogue. + */ const struct boundary_tag FENCE = { .inuse = 1, .size = 0 @@ -44,184 +54,204 @@ struct block { struct list_elem elem; }; -static struct block *heap_listp = 0; - -/* Basic constants and macros */ -#define WSIZE sizeof(struct boundary_tag) /* Word and header/footer size (bytes) */ +/* + * Basic constants and macros: + */ +#define WSIZE sizeof(struct boundary_tag) // Word and header/footer size (in bytes) #define DSIZE 2*WSIZE -#define MIN_BLOCK_SIZE_WORDS 8 /* Minimum block size in words */ -#define CHUNKSIZE (1<<10) /* Extend heap by this amount (words) */ - -#define NUM_LISTS 8 /* Number of free lists */ +#define MIN_BLOCK_SIZE_WORDS 8 // Minimum block size (in words) +#define CHUNKSIZE (1<<10) // Extend heap by this amount (in words) +#define NUM_LISTS 8 // Number of free lists static inline size_t max(size_t x, size_t y) { return x > y ? x : y; } - - - static size_t align(size_t size) { return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); } - +/* + * Global variables: + */ +static struct block *heap_listp = 0; static struct list free_lists[NUM_LISTS]; static size_t list_sizes[NUM_LISTS]; -/* Function prototypes for internal helper routines */ +/* + * Function prototypes for internal helper routines: + */ static struct block *extend_heap(size_t words); static void place(struct block *blck, size_t asize); static struct list* find_list(int not_empty, size_t size); static struct block *find_fit(size_t num_words); static struct block *coalesce(struct block *bp); -/* Given a block, obtain previous's block footer. - Works for left-most block also. */ +/* + * Given a block, obtains previous's block footer. Works for left-most block also. + */ static struct boundary_tag * prev_blk_footer(struct block *blk) { return &blk->header - 1; } -/* Return if block is free */ +/* + * Returns if block is free. + */ static bool blk_free(struct block *blk) { return !blk->header.inuse; } -/* Return size of block is free */ +/* + * Returns size of block. + */ static size_t blk_size(struct block *blk) { return blk->header.size; } -/* Given a block, obtain pointer to previous block. - Not meaningful for left-most block. */ +/* + * Given a block, obtains pointer to previous block. Not meaningful for left-most block. + */ static struct block *prev_blk(struct block *blk) { struct boundary_tag *prevfooter = prev_blk_footer(blk); assert(prevfooter->size != 0); return (struct block *)((void *)blk - WSIZE * prevfooter->size); } -/* Given a block, obtain pointer to next block. - Not meaningful for right-most block. */ +/* + * Given a block, obtains pointer to next block. Not meaningful for right-most block. + */ static struct block *next_blk(struct block *blk) { assert(blk_size(blk) != 0); return (struct block *)((void *)blk + WSIZE * blk->header.size); } -/* Given a block, obtain its footer boundary tag */ +/* + * Given a block, obtain its footer boundary tag. +*/ static struct boundary_tag * get_footer(struct block *blk) { return ((void *)blk + WSIZE * blk->header.size) - sizeof(struct boundary_tag); } -/* Set a block's size and inuse bit in header and footer */ +/* + * Sets a block's size and inuse bit in header and footer. + */ static void set_header_and_footer(struct block *blk, int size, int inuse) { blk->header.inuse = inuse; blk->header.size = size; - * get_footer(blk) = blk->header; /* Copy header to footer */ + *get_footer(blk) = blk->header; // copies header to footer } -/* Mark a block as used and set its size. */ +/* + * Sets a block as used and sets its size. + */ static void set_block_used(struct block *blk, int size) { set_header_and_footer(blk, size, 1); } - -/* Mark a block as free and set its size. */ +/* + * Sets a block as free and sets its size. Adds the block to a free list. + */ static void set_block_free(struct block *blk, int size) { set_header_and_footer(blk, size, 0); - list_push_front(find_list(0, size), &blk->elem); + list_push_front(find_list(0, size), &blk->elem); // (LIFO) } +/* + * Initializes the memory manager. + */ int mm_init(void) { assert (offsetof(struct block, payload) == 4); assert (sizeof(struct boundary_tag) == 4); - /* Create the initial empty heap */ + // Creates the initial empty heap. struct boundary_tag * initial = mem_sbrk(4 * sizeof(struct boundary_tag)); if (initial == NULL) return -1; - /* We use a slightly different strategy than suggested in the book. - * Rather than placing a min-sized prologue block at the beginning - * of the heap, we simply place two fences. - * The consequence is that coalesce() must call prev_blk_footer() - * and not prev_blk() because prev_blk() cannot be called on the - * left-most block. + /* We use a slightly different strategy than suggested in the book. Rather than placing a + * min-sized prologue block at the beginning of the heap, we simply place two fences. The + * consequence is that coalesce() must call prev_blk_footer() and not prev_blk() because + * prev_blk() cannot be called on the left-most block. */ - initial[2] = FENCE; /* Prologue footer */ + initial[2] = FENCE; // prologue header heap_listp = (struct block *)&initial[3]; - initial[3] = FENCE; /* Epilogue header */ + initial[3] = FENCE; // epilogue header - for (int i = 0; i < NUM_LISTS; i++) { - + // Initializes the free_lists. + for (int i = 0; i < NUM_LISTS; i++) list_init(&free_lists[i]); - - } + // Initializes list_sizes (powers of 2). for (int i = 0; i < NUM_LISTS; i++) { int size = 1; - for (int j = 0; j < i; j++) { + for (int j = 0; j < i; j++) size *= 2; - } list_sizes[i] = MIN_BLOCK_SIZE_WORDS * (size); } - /* Extend the empty heap with a free block of CHUNKSIZE bytes */ + // Extends the empty heap with a free block of CHUNKSIZE bytes. if (extend_heap(CHUNKSIZE) == NULL) return -1; + return 0; } /* -* The mm malloc routine returns a pointer to an allocated block payload of at -* least size bytes. The entire allocated block should lie within the heap region and should -* not overlap with any other allocated chunk. + * Returns a pointer to an allocated block with a payload of at least the given size in (bytes). The + * entire allocated block should lie within the heap region and should not overlap with other + * allocated blocks. */ void *mm_malloc(size_t size) { - struct block *blck; - - if (size == 0) { + if (size == 0) return NULL; - } - + + struct block *blck; size_t words = max(MIN_BLOCK_SIZE_WORDS, align(size + DSIZE) / WSIZE); - /* Find fit for size */ + // Attempts to find a block from the free lists that fit. if ((blck = find_fit(words)) != NULL) { - place(blck, words); + place(blck, words); // malloc successful return blck->payload; } - - if ((blck = extend_heap(words)) == NULL){ + + // Attempts to extend the heap to accomodate. + if ((blck = extend_heap(words)) == NULL) return NULL; - } + place(blck, words); - return blck->payload; + return blck->payload; // malloc successful } -void mm_free(void *bp) -{ - assert (heap_listp != 0); // assert that mm_init was called +/* + * Frees the block pointed by the given pointer. + */ +void mm_free(void *bp) { + // Asserts that mm_init() was called. + assert (heap_listp != 0); + if (bp == 0) return; - /* Find block from user pointer */ + // Finds a block from the given pointer and frees it. struct block *blk = bp - offsetof(struct block, payload); - size_t size = blk_size(blk); - - set_block_free(blk, size); + set_block_free(blk, blk_size(blk)); coalesce(blk); } +/* + * Frees the block pointed by the given pointer. + */ void *mm_realloc(void *ptr, size_t size) { size_t bytesize; void *new_ptr; - if (ptr == NULL) { + // Equivalent to mallocing. + if (ptr == NULL) return mm_malloc(size); - } - + + // Equivalent to freeing. if (size == 0) { mm_free(ptr); return NULL; @@ -231,8 +261,8 @@ void *mm_realloc(void *ptr, size_t size) { bool prev_alloc = prev_blk_footer(oldblock)->inuse; bool next_alloc = !blk_free(next_blk(oldblock)); - if (!next_alloc) - { + // CASE 1: The next block is free. + if (!next_alloc) { struct block *right; right = next_blk(oldblock); list_remove(&right->elem); @@ -240,11 +270,11 @@ void *mm_realloc(void *ptr, size_t size) { } if (blk_size(oldblock) >= size) { - return oldblock->payload; - } - - if (!prev_alloc) - { + return oldblock->payload; // reallocing was successful + } + + // CASE 2: The previous block is free. + if (!prev_alloc) { struct block *left; left = prev_blk(oldblock); list_remove(&left->elem); @@ -252,153 +282,152 @@ void *mm_realloc(void *ptr, size_t size) { oldblock = left; } - - if (blk_size(oldblock) >= size) - { + if (blk_size(oldblock) >= size) { bytesize = blk_size(oldblock) * WSIZE; - if(size < bytesize) bytesize = size; + if(size < bytesize) + bytesize = size; memcpy(oldblock->payload, ptr, bytesize); - return oldblock->payload; + return oldblock->payload; // reallocing was successful } - + // DEFAULT: new_ptr = mm_malloc(size); - - - if(!new_ptr) return 0; - - + if(!new_ptr) + return 0; + struct block *copyblk = ptr - offsetof(struct block, payload); - bytesize = blk_size(copyblk) * WSIZE; //find number of BYTES, not words - if(size < bytesize) bytesize = size; + bytesize = blk_size(copyblk) * WSIZE; + if(size < bytesize) + bytesize = size; memcpy(new_ptr, ptr, bytesize); - mm_free(oldblock->payload); return new_ptr; } - +/* + * Combines adjacent free blocks. Returns a pointer to the coalesced block. + */ static struct block *coalesce(struct block *bp) { - bool prev_alloc = prev_blk_footer(bp)->inuse; /* is previous block allocated? */ - bool next_alloc = ! blk_free(next_blk(bp)); /* is next block allocated? */ + int prev_alloc = prev_blk_footer(bp)->inuse; // previous block allocated? + int next_alloc = !blk_free(next_blk(bp)); // next block allocated? size_t size = blk_size(bp); - - if (prev_alloc && next_alloc) { /* Case 1 */ - // both are allocated, nothing to coalesce - return bp; - } - - else if (prev_alloc && !next_alloc) { /* Case 2 */ - // combine this block and next block by extending it - list_remove(&bp->elem); - list_remove(&next_blk(bp)->elem); - size_t new_size = size + blk_size(next_blk(bp)); - set_block_free(bp, new_size); - } - - else if (!prev_alloc && next_alloc) { /* Case 3 */ - // combine previous and this block by extending previous - list_remove(&prev_blk(bp)->elem); - list_remove(&bp->elem); - size_t new_size = blk_size(prev_blk(bp)) + size; - bp = prev_blk(bp); - set_block_free(bp, new_size); - } - - else { /* Case 4 */ - // combine all previous, this, and next block into one - list_remove(&prev_blk(bp)->elem); - list_remove(&bp->elem); - list_remove(&next_blk(bp)->elem); - size_t new_size = blk_size(prev_blk(bp)) + size + blk_size(next_blk(bp)); - bp = prev_blk(bp); - set_block_free(bp, new_size); + + // CASE 1: Both are allocated, nothing to coalesce. + if (prev_alloc && next_alloc) + return bp; + + // CASE 2: Next block is free, combine with next block. + else if (prev_alloc && !next_alloc) { + list_remove(&bp->elem); + list_remove(&next_blk(bp)->elem); + size_t new_size = size + blk_size(next_blk(bp)); + set_block_free(bp, new_size); + } + + // CASE 3: Previous block is free, combine with previous block. + else if (!prev_alloc && next_alloc) { + list_remove(&prev_blk(bp)->elem); + list_remove(&bp->elem); + size_t new_size = blk_size(prev_blk(bp)) + size; + bp = prev_blk(bp); + set_block_free(bp, new_size); + } + + // CASE 4: Previous and next blocks are free, combine with both. + else if (!prev_alloc && !next_alloc) { + list_remove(&prev_blk(bp)->elem); + list_remove(&bp->elem); + list_remove(&next_blk(bp)->elem); + size_t new_size = blk_size(prev_blk(bp)) + size + blk_size(next_blk(bp)); + bp = prev_blk(bp); + set_block_free(bp, new_size); } + return bp; } /* - * Extend heap with free block and return its block pointer - * Does not add new block to free list if called by realloc + * Extends the heap with free block and return its block pointer. */ -static struct block *extend_heap(size_t words) -{ +static struct block *extend_heap(size_t words) { void *blck = mem_sbrk(words * WSIZE); if (blck == NULL) return NULL; - /* Initialize free block header, footer and epilogue header. */ + /* Initializes a free block header, footer, and epilogue header. */ struct block * blk = blck - sizeof(FENCE); set_block_free(blk, words); next_blk(blk)->header = FENCE; return coalesce(blk); - } /* - * Allocate block of memory at address blck + * Allocates block of memory at address block. */ static void place(struct block *blck, size_t asize) { + // Removes the block since it's no longer free. + list_remove(&blck->elem); + size_t csize = blk_size(blck); - - //~ print_lists(); - list_remove(&blck->elem); - //~ print_lists(); - size_t remaining_size = csize - asize; + + // Determines if the block has extra space that can be freed. if (remaining_size >= MIN_BLOCK_SIZE_WORDS) { set_block_used(blck, asize); blck = next_blk(blck); set_block_free(blck, remaining_size); - //~ print_lists(); - } - else { - set_block_used(blck, csize); } + else + set_block_used(blck, csize); } -/** - * Find free list with blocks of given size +/* + * Finds a free list that can fit a block with the given size (in words). * * not_empty == 0, find "any" list - * not_empty == 1, find a not empty list + * not_empty == 1, find a non-empty list */ static struct list* find_list(int not_empty, size_t n_words) { - + // Parses the segregated free lists. for (int i = 0; i < NUM_LISTS; i++) { + /* + * Attempts to return the current list if... + * a.) the given size is less than/equal to the current list's max size. + * b.) the current list is the last one. + */ if ((n_words <= list_sizes[i]) || (i == (NUM_LISTS - 1))) { - if (not_empty && list_empty(&free_lists[i])) { + // Continues searching if a non-empty list is needed and the current list is empty. + if (not_empty && list_empty(&free_lists[i])) continue; - } return &free_lists[i]; } } + return NULL; } /* - * Find a fit for a block with num_words + * Finds a free block that fits the given size (in words). */ -static struct block *find_fit(size_t num_words) -{ +static struct block *find_fit(size_t num_words) { + // Selects a free list that contains a free block that fits. struct list* free_list = find_list(1, num_words); - if (free_list == NULL) { - return NULL; - } + if (free_list == NULL) + return NULL; - /* Search for block from selected free list*/ + // Searches for a free block from the selected free list that fits. (LIFO) for (struct list_elem* e = list_back(free_list); e != list_head(free_list); e = list_prev(e)) { struct block* bp = list_entry(e, struct block, elem); - if (num_words <= blk_size(bp)) { + if (num_words <= blk_size(bp)) return bp; - } } + return NULL; }