cwist
Modern C web framework with libttak runtime
Loading...
Searching...
No Matches
arena.c
Go to the documentation of this file.
1
17
18#include <cwist/sys/platform.h>
19#if !defined(CWIST_PLATFORM_HOSTED)
20/* WASM hosts are single-threaded and lack libttak's epoch/GC machinery;
21 * the arena degenerates to a plain malloc-backed bump buffer. */
22#include <stdbool.h>
23#include <stdint.h>
24#include <stdlib.h>
25
26struct cwist_arena {
27 uint8_t *base;
28 size_t capacity;
29 size_t used;
30};
31
32cwist_arena_t *cwist_arena_create(size_t generation_bytes) {
33 cwist_arena_t *arena = (cwist_arena_t *)calloc(1, sizeof(cwist_arena_t));
34 if (!arena) return NULL;
35 arena->capacity = generation_bytes ? generation_bytes : CWIST_ARENA_DEFAULT_GENERATION_BYTES;
36 arena->base = (uint8_t *)malloc(arena->capacity);
37 if (!arena->base) {
38 free(arena);
39 return NULL;
40 }
41 return arena;
42}
43
44void *cwist_arena_alloc(cwist_arena_t *arena, size_t size) {
45 if (!arena || !size || !arena->base) return NULL;
46 if (size > SIZE_MAX - 15u) return NULL;
47 size = (size + 15u) & ~15u;
48 if (size > arena->capacity - arena->used) return NULL;
49 void *ptr = arena->base + arena->used;
50 arena->used += size;
51 return ptr;
52}
53
54bool cwist_arena_owns(const cwist_arena_t *arena, const void *ptr) {
55 if (!arena || !ptr || !arena->base) return false;
56 const uint8_t *p = (const uint8_t *)ptr;
57 return p >= arena->base && p < arena->base + arena->capacity;
58}
59
61 if (!arena) return;
62 free(arena->base);
63 free(arena);
64}
65#else
66#include <pthread.h>
67#include <stdint.h>
68#include <stdlib.h>
69
70#include <ttak/mem/arena_helper.h>
71#include <ttak/mem/mem.h>
72#include <ttak/timing/timing.h>
73
75#define CWIST_ARENA_CACHE_MAX 8
76
77struct cwist_arena {
78 ttak_arena_generation_t gen;
79 size_t generation_bytes;
80 pthread_t owner_tid;
81};
82
86typedef struct cwist_arena_tls {
87 ttak_arena_env_t env;
88 void *bufs[CWIST_ARENA_CACHE_MAX];
89 size_t buf_count;
90 cwist_arena_t *structs[CWIST_ARENA_CACHE_MAX];
91 size_t struct_count;
92 uint32_t epoch_seq;
93} cwist_arena_tls_t;
94
95#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L)
96static _Thread_local cwist_arena_tls_t *t_arena_tls = NULL;
97#elif defined(__GNUC__) || defined(__clang__)
98static __thread cwist_arena_tls_t *t_arena_tls = NULL;
99#endif
100
101static pthread_key_t g_arena_tls_key;
102static pthread_once_t g_arena_tls_once = PTHREAD_ONCE_INIT;
103
108static void cwist_arena_tls_destroy(void *ptr) {
109 cwist_arena_tls_t *tls = (cwist_arena_tls_t *)ptr;
110 if (!tls) return;
111#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__GNUC__) || \
112 defined(__clang__)
113 t_arena_tls = NULL;
114#endif
115 for (size_t i = 0; i < tls->buf_count; i++) {
116 ttak_mem_free(tls->bufs[i]);
117 }
118 for (size_t i = 0; i < tls->struct_count; i++) {
119 free(tls->structs[i]);
120 }
121 ttak_arena_env_destroy(&tls->env);
122 free(tls);
123}
124
125static void cwist_arena_tls_key_init(void) {
126 pthread_key_create(&g_arena_tls_key, cwist_arena_tls_destroy);
127}
128
133static inline cwist_arena_tls_t *cwist_arena_tls_get(void) {
134#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__GNUC__) || \
135 defined(__clang__)
136 if (__builtin_expect(t_arena_tls != NULL, 1)) {
137 return t_arena_tls;
138 }
139#endif
140
141 if (pthread_once(&g_arena_tls_once, cwist_arena_tls_key_init) != 0) {
142 return NULL;
143 }
144 cwist_arena_tls_t *tls = (cwist_arena_tls_t *)pthread_getspecific(g_arena_tls_key);
145 if (tls) {
146#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__GNUC__) || \
147 defined(__clang__)
148 t_arena_tls = tls;
149#endif
150 return tls;
151 }
152
153 tls = (cwist_arena_tls_t *)calloc(1, sizeof(cwist_arena_tls_t));
154 if (!tls) return NULL;
155
156 ttak_arena_env_config_t config;
157 ttak_arena_env_config_init(&config);
158 config.generation_bytes = CWIST_ARENA_DEFAULT_GENERATION_BYTES;
159 if (!ttak_arena_env_init(&tls->env, &config)) {
160 free(tls);
161 return NULL;
162 }
163 if (pthread_setspecific(g_arena_tls_key, tls) != 0) {
164 ttak_arena_env_destroy(&tls->env);
165 free(tls);
166 return NULL;
167 }
168#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__GNUC__) || \
169 defined(__clang__)
170 t_arena_tls = tls;
171#endif
172 return tls;
173}
174
175cwist_arena_t *cwist_arena_create(size_t generation_bytes) {
176 cwist_arena_tls_t *tls = cwist_arena_tls_get();
177 if (!tls) return NULL;
178
179 size_t bytes = generation_bytes ? generation_bytes : CWIST_ARENA_DEFAULT_GENERATION_BYTES;
180
181 /* Only default-sized buffers participate in the recycle cache. */
182 void *buffer = NULL;
183 if (bytes == CWIST_ARENA_DEFAULT_GENERATION_BYTES && tls->buf_count > 0) {
184 buffer = tls->bufs[--tls->buf_count];
185 }
186 if (!buffer) {
187 buffer = ttak_mem_alloc_with_flags_raw(bytes, __TTAK_UNSAFE_MEM_FOREVER__,
188 ttak_get_tick_count(), tls->env.config.alloc_flags);
189 if (!buffer) return NULL;
190 }
191
192 cwist_arena_t *arena = tls->struct_count > 0
193 ? tls->structs[--tls->struct_count]
194 : (cwist_arena_t *)calloc(1, sizeof(cwist_arena_t));
195 if (!arena) {
197 tls->buf_count < CWIST_ARENA_CACHE_MAX) {
198 tls->bufs[tls->buf_count++] = buffer;
199 } else {
200 ttak_mem_free(buffer);
201 }
202 return NULL;
203 }
204
205 arena->gen.base = buffer;
206 arena->gen.capacity = bytes;
207 arena->gen.used = 0;
208 arena->gen.epoch_id = ++tls->epoch_seq;
209 arena->generation_bytes = bytes;
210 arena->owner_tid = pthread_self();
211 return arena;
212}
213
214void *cwist_arena_alloc(cwist_arena_t *arena, size_t size) {
215 if (!arena || !size || !arena->gen.base) return NULL;
216 /* Plain 16-byte-aligned bump over the generation buffer. Skipping
217 * ttak_arena_generation_claim avoids its per-claim cache-line padding
218 * and scatter offset; exhaustion still returns NULL so callers fall
219 * back to the heap exactly as before. */
220 if (size > SIZE_MAX - 15u) return NULL;
221 size = (size + 15u) & ~15u;
222 if (size > arena->gen.capacity - arena->gen.used) return NULL;
223 void *ptr = (uint8_t *)arena->gen.base + arena->gen.used;
224 arena->gen.used += size;
225 return ptr;
226}
227
228bool cwist_arena_owns(const cwist_arena_t *arena, const void *ptr) {
229 if (!arena || !ptr || !arena->gen.base) return false;
230 const uint8_t *p = (const uint8_t *)ptr;
231 const uint8_t *base = (const uint8_t *)arena->gen.base;
232 return p >= base && p < base + arena->gen.capacity;
233}
234
236 if (!arena) return;
237 void *buffer = arena->gen.base;
238 size_t bytes = arena->generation_bytes;
239 bool default_sized = (bytes == CWIST_ARENA_DEFAULT_GENERATION_BYTES);
240
241 /* If destroyed on a foreign thread (e.g. cross-thread async completion),
242 * release directly to avoid polluting foreign thread-local caches. */
243 bool is_owner = pthread_equal(arena->owner_tid, pthread_self());
244 cwist_arena_tls_t *tls = is_owner ? cwist_arena_tls_get() : NULL;
245
246 if (tls && tls->struct_count < CWIST_ARENA_CACHE_MAX) {
247 /* Recycle the arena struct itself along with the buffer. */
248 tls->structs[tls->struct_count++] = arena;
249 } else {
250 free(arena);
251 }
252
253 if (!buffer) return;
254 if (tls && default_sized && tls->buf_count < CWIST_ARENA_CACHE_MAX) {
255 /* Recycle: the next request reuses this buffer with zero GC work. */
256 tls->bufs[tls->buf_count++] = buffer;
257 } else {
258 ttak_mem_free(buffer);
259 }
260}
261#endif /* __EMSCRIPTEN__ / __wasi__ */
void * cwist_arena_alloc(cwist_arena_t *arena, size_t size)
Bump-allocate size bytes from the active generation.
Definition arena.c:44
bool cwist_arena_owns(const cwist_arena_t *arena, const void *ptr)
Check whether ptr lies inside the active generation buffer.
Definition arena.c:54
void cwist_arena_destroy(cwist_arena_t *arena)
Retire the generation and destroy the arena, releasing every allocation made from it at once.
Definition arena.c:60
cwist_arena_t * cwist_arena_create(size_t generation_bytes)
Create an arena backed by a recycled thread-local generation buffer.
Definition arena.c:32
Request-scoped arena allocator backed by libttak arena generations.
struct cwist_arena cwist_arena_t
Definition arena.h:22
#define CWIST_ARENA_DEFAULT_GENERATION_BYTES
Default capacity of one arena generation buffer. A full HTTP/1.1 request+response pair (headers,...
Definition arena.h:20
Target capability macros shared by all CWIST sources.