aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2024-02-13 17:51:51 -0800
committer3gg <3gg@shellblade.net>2024-02-13 17:51:51 -0800
commite153be0be2fb8df6656292daab3fa59963c76737 (patch)
tree7cca3fc134553017cb3c259db1dca33c98556109
parent84bdfa4a23f5b8daa7921541b007518bc634be0f (diff)
Let memory allocators trap by default when attempting to allocate beyond capacity.
-rw-r--r--CMakeLists.txt1
-rw-r--r--cassert/CMakeLists.txt8
-rw-r--r--cassert/include/cassert.h29
-rw-r--r--mem/CMakeLists.txt1
-rw-r--r--mem/include/mem.h11
-rw-r--r--mem/src/mem.c12
-rw-r--r--mem/test/mem_test.c1
-rw-r--r--mempool/CMakeLists.txt3
-rw-r--r--mempool/include/mempool.h9
-rw-r--r--mempool/src/mempool.c11
-rw-r--r--mempool/test/mempool_test.c1
11 files changed, 85 insertions, 2 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 868268d..e2206ac 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.0)
2 2
3project(clib) 3project(clib)
4 4
5add_subdirectory(cassert)
5add_subdirectory(cstring) 6add_subdirectory(cstring)
6add_subdirectory(error) 7add_subdirectory(error)
7add_subdirectory(filesystem) 8add_subdirectory(filesystem)
diff --git a/cassert/CMakeLists.txt b/cassert/CMakeLists.txt
new file mode 100644
index 0000000..855f261
--- /dev/null
+++ b/cassert/CMakeLists.txt
@@ -0,0 +1,8 @@
1cmake_minimum_required(VERSION 3.0)
2
3project(cassert)
4
5add_library(cassert INTERFACE)
6
7target_include_directories(cassert INTERFACE
8 include)
diff --git a/cassert/include/cassert.h b/cassert/include/cassert.h
new file mode 100644
index 0000000..3349b55
--- /dev/null
+++ b/cassert/include/cassert.h
@@ -0,0 +1,29 @@
1#pragma once
2
3#include <assert.h> // For convenience, bring in soft assertions with assert().
4#include <signal.h>
5
6// Allow the client to define their own LOGE() macro.
7#ifndef LOGE
8#include <stdio.h>
9#define LOGE(...) \
10 { \
11 fprintf(stderr, "[ASSERT] %s:%d: ", __FILE__, __LINE__); \
12 fprintf(stderr, __VA_ARGS__); \
13 fprintf(stderr, "\n"); \
14 }
15#endif // LOGE
16
17#define TRAP() raise(SIGTRAP)
18
19/// Unconditional hard assert.
20#define FAIL(message) \
21 LOGE(message); \
22 TRAP();
23
24/// Conditional hard assert.
25#define ASSERT(condition) \
26 if (!condition) { \
27 LOGE("Assertion failed: " #condition) \
28 TRAP(); \
29 }
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
13target_link_libraries(mem 13target_link_libraries(mem
14 cassert
14 list) 15 list)
15 16
16target_compile_options(mem PRIVATE -Wall -Wextra) 17target_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);
159void* mem_get_chunk_(const Memory*, size_t chunk_handle); 165void* mem_get_chunk_(const Memory*, size_t chunk_handle);
160size_t mem_get_chunk_handle_(const Memory*, const void* chunk); 166size_t mem_get_chunk_handle_(const Memory*, const void* chunk);
161size_t mem_capacity_(const Memory*); 167size_t mem_capacity_(const Memory*);
168void 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
197void 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) {
67TEST_CASE(mem_allocate_beyond_max_size) { 67TEST_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) {
diff --git a/mempool/CMakeLists.txt b/mempool/CMakeLists.txt
index fe3e2a5..8c9dd30 100644
--- a/mempool/CMakeLists.txt
+++ b/mempool/CMakeLists.txt
@@ -10,6 +10,9 @@ add_library(mempool
10target_include_directories(mempool PUBLIC 10target_include_directories(mempool PUBLIC
11 include) 11 include)
12 12
13target_link_libraries(mempool PRIVATE
14 cassert)
15
13target_compile_options(mempool PRIVATE -Wall -Wextra) 16target_compile_options(mempool PRIVATE -Wall -Wextra)
14 17
15# Test 18# Test
diff --git a/mempool/include/mempool.h b/mempool/include/mempool.h
index bd4d4dd..de9ea4f 100644
--- a/mempool/include/mempool.h
+++ b/mempool/include/mempool.h
@@ -65,6 +65,9 @@
65 65
66/// Allocate a new block. 66/// Allocate a new block.
67/// Return 0 if there is no memory left. 67/// Return 0 if there is no memory left.
68/// When there is no space left in the pool, allocation can either trap
69/// (default) or gracefully return 0. Call mem_enable_traps() to toggle this
70/// behaviour.
68/// New blocks are conveniently zeroed out. 71/// New blocks are conveniently zeroed out.
69#define mempool_alloc(POOL) mempool_alloc_(&(POOL)->pool) 72#define mempool_alloc(POOL) mempool_alloc_(&(POOL)->pool)
70 73
@@ -86,6 +89,10 @@
86/// Return the total capacity of the mempool in bytes. 89/// Return the total capacity of the mempool in bytes.
87#define mempool_capacity(POOL) mempool_capacity_(&(POOL)->pool) 90#define mempool_capacity(POOL) mempool_capacity_(&(POOL)->pool)
88 91
92/// Set whether to trap when attempting to allocate beyond capacity.
93#define mempool_enable_traps(POOL, enable) \
94 mempool_enable_traps_(&(POOL)->pool, enable)
95
89/// Iterate over the used blocks of the pool. 96/// Iterate over the used blocks of the pool.
90/// 97///
91/// The caller can use 'i' as the index of the current block. 98/// The caller can use 'i' as the index of the current block.
@@ -129,6 +136,7 @@ typedef struct mempool {
129 size_t head; /// Points to the first block in the free list. 136 size_t head; /// Points to the first block in the free list.
130 size_t used; /// Points to the first block in the used list. 137 size_t used; /// Points to the first block in the used list.
131 bool dynamic; /// True if blocks and info are dynamically-allocated. 138 bool dynamic; /// True if blocks and info are dynamically-allocated.
139 bool trap; /// Whether to trap when allocating beyond capacity.
132 BlockInfo* block_info; 140 BlockInfo* block_info;
133 uint8_t* blocks; 141 uint8_t* blocks;
134} mempool; 142} mempool;
@@ -154,3 +162,4 @@ void mempool_free_(mempool*, void** block_ptr);
154void* mempool_get_block_(const mempool*, size_t block_index); 162void* mempool_get_block_(const mempool*, size_t block_index);
155size_t mempool_get_block_index_(const mempool*, const void* block); 163size_t mempool_get_block_index_(const mempool*, const void* block);
156size_t mempool_capacity_(const mempool*); 164size_t mempool_capacity_(const mempool*);
165void mempool_enable_traps_(mempool*, bool);
diff --git a/mempool/src/mempool.c b/mempool/src/mempool.c
index 1100dad..b09038b 100644
--- a/mempool/src/mempool.c
+++ b/mempool/src/mempool.c
@@ -1,5 +1,7 @@
1#include "mempool.h" 1#include "mempool.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
@@ -24,6 +26,7 @@ bool mempool_make_(
24 pool->num_blocks = num_blocks; 26 pool->num_blocks = num_blocks;
25 pool->head = 0; 27 pool->head = 0;
26 pool->used = 0; 28 pool->used = 0;
29 pool->trap = true;
27 30
28 // Initialize blocks and block info. 31 // Initialize blocks and block info.
29 if (!block_info) { 32 if (!block_info) {
@@ -74,6 +77,9 @@ void* mempool_alloc_(mempool* pool) {
74 77
75 BlockInfo* head = &pool->block_info[pool->head]; 78 BlockInfo* head = &pool->block_info[pool->head];
76 if (head->used) { 79 if (head->used) {
80 if (pool->trap) {
81 FAIL("mempool allocation failed, increase the pool's capacity.");
82 }
77 return 0; // Pool is full. 83 return 0; // Pool is full.
78 } 84 }
79 85
@@ -134,3 +140,8 @@ size_t mempool_capacity_(const mempool* pool) {
134 assert(pool); 140 assert(pool);
135 return pool->num_blocks * pool->block_size_bytes; 141 return pool->num_blocks * pool->block_size_bytes;
136} 142}
143
144void mempool_enable_traps_(mempool* pool, bool enable) {
145 assert(pool);
146 pool->trap = enable;
147}
diff --git a/mempool/test/mempool_test.c b/mempool/test/mempool_test.c
index d5ed1ea..6c48a2a 100644
--- a/mempool/test/mempool_test.c
+++ b/mempool/test/mempool_test.c
@@ -67,6 +67,7 @@ TEST_CASE(mempool_fill_then_free) {
67TEST_CASE(mempool_allocate_beyond_max_size) { 67TEST_CASE(mempool_allocate_beyond_max_size) {
68 test_pool pool; 68 test_pool pool;
69 mempool_make(&pool); 69 mempool_make(&pool);
70 mempool_enable_traps(&pool, false);
70 71
71 // Fully allocate the pool. 72 // Fully allocate the pool.
72 for (int i = 0; i < NUM_BLOCKS; ++i) { 73 for (int i = 0; i < NUM_BLOCKS; ++i) {