simple_allocator

A simple, poorly-performing memory allocator
git clone https://git.bracken.jp/simple_allocator.git
Log | Files | Refs | Submodules | README | LICENSE

commit d2ed6f4dbec88868b42ec90ae6707e25a7dba95b
parent 82b49fd168f0c3c91518f68a37c9e3fd8216f049
Author: Chris Bracken <chris@bracken.jp>
Date:   Sat, 22 Oct 2022 00:16:04 -0700

Extract malloc/free to memory.h

This also renames the .cc files to .c files since these no longer
include C++ code.

Diffstat:
Msrc/CMakeLists.txt | 5+++--
Asrc/main.c | 21+++++++++++++++++++++
Dsrc/main.cc | 173-------------------------------------------------------------------------------
Asrc/memory.c | 140+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/memory.h | 21+++++++++++++++++++++
5 files changed, 185 insertions(+), 175 deletions(-)

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -add_definitions(-std=c++17) +add_definitions(-std=c17) set(CXX_FLAGS "-Wall" @@ -13,6 +13,7 @@ set(CXX_TEST_FLAGS ) add_executable(main - main.cc + main.c + memory.c ) target_compile_options(main PRIVATE ${CXX_FLAGS}) diff --git a/src/main.c b/src/main.c @@ -0,0 +1,21 @@ +#include <stdio.h> + +#include "memory.h" + +int main(int argc, char** argv) { + print_brk(); + print_alloc_list(); + printf("\n"); + + void *p = malloc(20); + printf("*** malloced a block\n"); + print_block(p); + print_brk(); + print_alloc_list(); + printf("\n"); + + free(p); + printf("*** freed a block\n"); + print_brk(); + print_alloc_list(); +} diff --git a/src/main.cc b/src/main.cc @@ -1,173 +0,0 @@ -#include <pthread.h> -#include <stdio.h> -#include <unistd.h> - -struct header_t { - size_t size; - struct header_t *next; - unsigned char is_free; -}; - -struct header_t *get_free_block(size_t size); -void *malloc(size_t size); -void print_brk(); -void print_header(struct header_t *header); -void print_block(void *p); -void print_alloc_list(); - -static struct header_t *head, *tail; -static pthread_mutex_t global_malloc_lock; - -//////////////////////////////////////////////////////////////////////// -// Implementation. - -/** - * Return the first free block of at least the specified size, or nullptr if no - * such block exists. - */ -struct header_t *get_free_block(size_t size) { - struct header_t *curr = head; - while (curr) { - if (curr->is_free && curr->size >= size) - return curr; - curr = curr->next; - } - return nullptr; -} - -/** - * Allocate a block of memory of a specific size on the heap. - */ -void *malloc(size_t size) { - size_t total_size; - void *block; - struct header_t *header; - - if (!size) - return nullptr; - - // Check for free previously-allocated block of the right size. - pthread_mutex_lock(&global_malloc_lock); - header = get_free_block(size); - if (header) { - header->is_free = 0; - pthread_mutex_unlock(&global_malloc_lock); - return (void*)(header + 1); - } - - // If none, allocate a new block. - total_size = sizeof(struct header_t) + size; - block = sbrk((int)total_size); - if (block == (void*)(-1)) { - pthread_mutex_unlock(&global_malloc_lock); - return nullptr; - } - - header = (struct header_t*)block; - header->size = size; - header->is_free = 0; - header->next = nullptr; - if (!head) - head = header; - if (tail) - tail->next = header; - tail = header; - pthread_mutex_unlock(&global_malloc_lock); - return (void*)(header + 1); -} - -/** - * Frees the specified block. - */ -void free(void *block) { - if (!block) - return; - - pthread_mutex_lock(&global_malloc_lock); - - // Get block header. - struct header_t *header = (struct header_t*)block - 1; - - void *program_break = sbrk(0); - if ((char*)block + header->size == program_break) { - // If we're the last allocated block before brk, decrement it. - if (head == tail) { - head = tail = nullptr; - } else { - for (struct header_t *p = head; p != nullptr; p = p->next) { - if (p->next == tail) { - p->next = nullptr; - tail = p; - } - } - } - size_t total_size = sizeof(struct header_t) + header->size; - sbrk(-(int)total_size); - pthread_mutex_unlock(&global_malloc_lock); - return; - } else { - // Otherwise, mark the block as free. - header->is_free = 1; - } - - pthread_mutex_unlock(&global_malloc_lock); -} - -/** - * Compute and report brk. - */ -void print_brk() { - void *p = sbrk(0); - if (p == (void*)(-1)) - fprintf(stderr, "sbrk() failed\n"); - printf("brk: %p\n", p); -} - -void print_header(struct header_t *header) { - printf("hdr: %p\n", header); - if (!header) { - return; - } - printf("size: %#zx\n", header->size); - printf("free: %s\n", header->is_free ? "true" : "false"); -} - -/** - * Print the header for a block. - */ -void print_block(void *block) { - printf("addr: %p\n", block); - if (!block) - return; - struct header_t *h = (struct header_t*)(block) - 1; - print_header(h); -} - -/** - * Print the alloc list. - */ -void print_alloc_list() { - printf("== head\n"); - print_header(head); - printf("== tail\n"); - print_header(tail); -} - -int main(int argc, char** argv) { - print_brk(); - printf("Header size is: %#zx\n", sizeof(struct header_t)); - print_alloc_list(); - printf("\n"); - - void *p = malloc(20); - printf("*** malloced a block\n"); - print_block(p); - print_brk(); - print_alloc_list(); - printf("\n"); - - free(p); - printf("*** freed a block\n"); - print_brk(); - print_alloc_list(); -} diff --git a/src/memory.c b/src/memory.c @@ -0,0 +1,140 @@ +#include "memory.h" + +#include <pthread.h> +#include <stdio.h> +#include <unistd.h> + +// An allocation header block. +// +// Allocation header blocks sit immediately before the allocated pointer in +// memory and form a linked list staring at |head| and ending at |tail|. +struct header_t { + size_t size; + struct header_t *next; + unsigned char is_free; +}; + +static struct header_t *head, *tail; +static pthread_mutex_t global_malloc_lock; + +// Returns the first free block of at least the specified size, or null if +// no such block exists. +static struct header_t *get_free_block(size_t size); + +// Print header struct debug dump. +void print_header(struct header_t *header); + +static struct header_t *get_free_block(size_t size) { + struct header_t *curr = head; + while (curr) { + if (curr->is_free && curr->size >= size) + return curr; + curr = curr->next; + } + return NULL; +} + +void *malloc(size_t size) { + size_t total_size; + void *block; + struct header_t *header; + + if (!size) + return NULL; + + // Check for free previously-allocated block of the right size. + pthread_mutex_lock(&global_malloc_lock); + header = get_free_block(size); + if (header) { + header->is_free = 0; + pthread_mutex_unlock(&global_malloc_lock); + return (void*)(header + 1); + } + + // If none, allocate a new block. + total_size = sizeof(struct header_t) + size; + block = sbrk((int)total_size); + if (block == (void*)-1) { + pthread_mutex_unlock(&global_malloc_lock); + return NULL; + } + + header = (struct header_t*)block; + header->size = size; + header->is_free = 0; + header->next = NULL; + if (!head) + head = header; + if (tail) + tail->next = header; + tail = header; + pthread_mutex_unlock(&global_malloc_lock); + return (void*)(header + 1); +} + +void free(void *block) { + if (!block) + return; + + pthread_mutex_lock(&global_malloc_lock); + + // Get block header. + struct header_t *header = (struct header_t*)block - 1; + + void *program_break = sbrk(0); + if ((char*)block + header->size == program_break) { + // If we're the last allocated block before brk, decrement it. + if (head == tail) { + head = tail = NULL; + } else { + for (struct header_t *p = head; p != NULL; p = p->next) { + if (p->next == tail) { + p->next = NULL; + tail = p; + } + } + } + size_t total_size = sizeof(struct header_t) + header->size; + sbrk(-(int)total_size); + pthread_mutex_unlock(&global_malloc_lock); + return; + } else { + // Otherwise, mark the block as free. + header->is_free = 1; + } + + pthread_mutex_unlock(&global_malloc_lock); +} + +void print_brk() { + void *p = sbrk(0); + if (p == (void*)(-1)) + fprintf(stderr, "sbrk() failed\n"); + printf("brk: %p\n", p); +} + +void print_header(struct header_t *header) { + printf("hdr: %p\n", header); + printf("sizeof(hdr): %#zx\n", sizeof(struct header_t)); + if (!header) { + return; + } + printf("size: %#zx\n", header->size); + printf("free: %s\n", header->is_free ? "true" : "false"); +} + +void print_block(void *block) { + printf("addr: %p\n", block); + if (!block) + return; + struct header_t *h = (struct header_t*)(block) - 1; + print_header(h); +} + +void print_alloc_list() { + printf("== head\n"); + print_header(head); + printf("== tail\n"); + print_header(tail); +} + diff --git a/src/memory.h b/src/memory.h @@ -0,0 +1,21 @@ +#ifndef MEMORY_H_ +#define MEMORY_H_ + +#include <unistd.h> + +// Allocate a block of memory of a specific size on the heap. +void *malloc(size_t size); + +// Frees the specified block. +void free(void *block); + +// Compute and write the program break to stdout. +void print_brk(); + +// Write debug info for a block to stdout. +void print_block(void *p); + +// Write debug info for the allocations list to stdout. +void print_alloc_list(); + +#endif // MEMORY_H_