diff options
author | 3gg <3gg@shellblade.net> | 2024-02-13 17:51:51 -0800 |
---|---|---|
committer | 3gg <3gg@shellblade.net> | 2024-02-13 17:51:51 -0800 |
commit | e153be0be2fb8df6656292daab3fa59963c76737 (patch) | |
tree | 7cca3fc134553017cb3c259db1dca33c98556109 /mem | |
parent | 84bdfa4a23f5b8daa7921541b007518bc634be0f (diff) |
Let memory allocators trap by default when attempting to allocate beyond capacity.
Diffstat (limited to 'mem')
-rw-r--r-- | mem/CMakeLists.txt | 1 | ||||
-rw-r--r-- | mem/include/mem.h | 11 | ||||
-rw-r--r-- | mem/src/mem.c | 12 | ||||
-rw-r--r-- | mem/test/mem_test.c | 1 |
4 files changed, 23 insertions, 2 deletions
diff --git a/mem/CMakeLists.txt b/mem/CMakeLists.txt index 233d2be..e4b28c3 100644 --- a/mem/CMakeLists.txt +++ b/mem/CMakeLists.txt | |||
@@ -11,6 +11,7 @@ target_include_directories(mem PUBLIC | |||
11 | include) | 11 | include) |
12 | 12 | ||
13 | target_link_libraries(mem | 13 | target_link_libraries(mem |
14 | cassert | ||
14 | list) | 15 | list) |
15 | 16 | ||
16 | target_compile_options(mem PRIVATE -Wall -Wextra) | 17 | target_compile_options(mem PRIVATE -Wall -Wextra) |
diff --git a/mem/include/mem.h b/mem/include/mem.h index bcff39f..892ea4f 100644 --- a/mem/include/mem.h +++ b/mem/include/mem.h | |||
@@ -66,8 +66,10 @@ | |||
66 | #define mem_clear(MEM) mem_clear_(&(MEM)->mem) | 66 | #define mem_clear(MEM) mem_clear_(&(MEM)->mem) |
67 | 67 | ||
68 | /// Allocate a new chunk of N blocks. | 68 | /// Allocate a new chunk of N blocks. |
69 | /// Return a pointer to the first block of the chunk, or 0 if there is no memory | 69 | /// Return a pointer to the first block of the chunk. |
70 | /// left. | 70 | /// When there is no space left in the allocator, allocation can either trap |
71 | /// (default) or gracefully return 0. Call mem_enable_traps() to toggle this | ||
72 | /// behaviour. | ||
71 | /// New chunks are conveniently zeroed out. | 73 | /// New chunks are conveniently zeroed out. |
72 | #define mem_alloc(MEM, num_blocks) mem_alloc_(&(MEM)->mem, num_blocks) | 74 | #define mem_alloc(MEM, num_blocks) mem_alloc_(&(MEM)->mem, num_blocks) |
73 | 75 | ||
@@ -87,6 +89,9 @@ | |||
87 | /// Return the total capacity of the allocator in bytes. | 89 | /// Return the total capacity of the allocator in bytes. |
88 | #define mem_capacity(MEM) mem_capacity_(&(MEM)->mem) | 90 | #define mem_capacity(MEM) mem_capacity_(&(MEM)->mem) |
89 | 91 | ||
92 | /// Set whether to trap when attempting to allocate beyond capacity. | ||
93 | #define mem_enable_traps(MEM, enable) mem_enable_traps_(&(MEM)->mem, enable) | ||
94 | |||
90 | /// Iterate over the used chunks of the allocator. | 95 | /// Iterate over the used chunks of the allocator. |
91 | /// | 96 | /// |
92 | /// The caller can use 'i' as the index of the current chunk. | 97 | /// The caller can use 'i' as the index of the current chunk. |
@@ -134,6 +139,7 @@ typedef struct Memory { | |||
134 | size_t num_blocks; | 139 | size_t num_blocks; |
135 | size_t next_free_chunk; | 140 | size_t next_free_chunk; |
136 | bool dynamic; /// True if blocks and chunks are dynamically-allocated. | 141 | bool dynamic; /// True if blocks and chunks are dynamically-allocated. |
142 | bool trap; /// Whether to trap when allocating beyond capacity. | ||
137 | Chunk* chunks; /// Array of chunk information. | 143 | Chunk* chunks; /// Array of chunk information. |
138 | uint8_t* blocks; /// Array of blocks; | 144 | uint8_t* blocks; /// Array of blocks; |
139 | } Memory; | 145 | } Memory; |
@@ -159,3 +165,4 @@ void mem_free_(Memory*, void** chunk_ptr); | |||
159 | void* mem_get_chunk_(const Memory*, size_t chunk_handle); | 165 | void* mem_get_chunk_(const Memory*, size_t chunk_handle); |
160 | size_t mem_get_chunk_handle_(const Memory*, const void* chunk); | 166 | size_t mem_get_chunk_handle_(const Memory*, const void* chunk); |
161 | size_t mem_capacity_(const Memory*); | 167 | size_t mem_capacity_(const Memory*); |
168 | void mem_enable_traps_(Memory*, bool); | ||
diff --git a/mem/src/mem.c b/mem/src/mem.c index 056d947..2904035 100644 --- a/mem/src/mem.c +++ b/mem/src/mem.c | |||
@@ -1,5 +1,7 @@ | |||
1 | #include "mem.h" | 1 | #include "mem.h" |
2 | 2 | ||
3 | #include <cassert.h> | ||
4 | |||
3 | #include <stdlib.h> | 5 | #include <stdlib.h> |
4 | #include <string.h> | 6 | #include <string.h> |
5 | 7 | ||
@@ -13,6 +15,7 @@ bool mem_make_( | |||
13 | mem->block_size_bytes = block_size_bytes; | 15 | mem->block_size_bytes = block_size_bytes; |
14 | mem->num_blocks = num_blocks; | 16 | mem->num_blocks = num_blocks; |
15 | mem->next_free_chunk = 0; | 17 | mem->next_free_chunk = 0; |
18 | mem->trap = true; | ||
16 | 19 | ||
17 | // Allocate chunks and blocks if necessary and zero them out. | 20 | // Allocate chunks and blocks if necessary and zero them out. |
18 | if (!chunks) { | 21 | if (!chunks) { |
@@ -107,6 +110,10 @@ void* mem_alloc_(Memory* mem, size_t num_blocks) { | |||
107 | mem->next_free_chunk = mem->chunks[chunk_idx].next; | 110 | mem->next_free_chunk = mem->chunks[chunk_idx].next; |
108 | return &mem->blocks[chunk_idx * mem->block_size_bytes]; | 111 | return &mem->blocks[chunk_idx * mem->block_size_bytes]; |
109 | } else { | 112 | } else { |
113 | if (mem->trap) { | ||
114 | FAIL("Memory allocation failed, increase the allocator's capacity or " | ||
115 | "avoid fragmentation."); | ||
116 | } | ||
110 | return 0; // Large-enough free chunk not found. | 117 | return 0; // Large-enough free chunk not found. |
111 | } | 118 | } |
112 | } | 119 | } |
@@ -186,3 +193,8 @@ size_t mem_capacity_(const Memory* mem) { | |||
186 | assert(mem); | 193 | assert(mem); |
187 | return mem->num_blocks * mem->block_size_bytes; | 194 | return mem->num_blocks * mem->block_size_bytes; |
188 | } | 195 | } |
196 | |||
197 | void mem_enable_traps_(Memory* mem, bool enable) { | ||
198 | assert(mem); | ||
199 | mem->trap = enable; | ||
200 | } | ||
diff --git a/mem/test/mem_test.c b/mem/test/mem_test.c index 2f242c3..d3c43b9 100644 --- a/mem/test/mem_test.c +++ b/mem/test/mem_test.c | |||
@@ -67,6 +67,7 @@ TEST_CASE(mem_fill_then_free) { | |||
67 | TEST_CASE(mem_allocate_beyond_max_size) { | 67 | TEST_CASE(mem_allocate_beyond_max_size) { |
68 | test_mem mem; | 68 | test_mem mem; |
69 | mem_make(&mem); | 69 | mem_make(&mem); |
70 | mem_enable_traps(&mem, false); | ||
70 | 71 | ||
71 | // Fully allocate the mem. | 72 | // Fully allocate the mem. |
72 | for (int i = 0; i < NUM_BLOCKS; ++i) { | 73 | for (int i = 0; i < NUM_BLOCKS; ++i) { |