19#if !defined(CWIST_PLATFORM_HOSTED)
34 if (!arena)
return NULL;
36 arena->base = (uint8_t *)malloc(arena->capacity);
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;
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;
70#include <ttak/mem/arena_helper.h>
71#include <ttak/mem/mem.h>
72#include <ttak/timing/timing.h>
75#define CWIST_ARENA_CACHE_MAX 8
78 ttak_arena_generation_t gen;
79 size_t generation_bytes;
86typedef struct cwist_arena_tls {
88 void *bufs[CWIST_ARENA_CACHE_MAX];
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;
101static pthread_key_t g_arena_tls_key;
102static pthread_once_t g_arena_tls_once = PTHREAD_ONCE_INIT;
108static void cwist_arena_tls_destroy(
void *ptr) {
109 cwist_arena_tls_t *tls = (cwist_arena_tls_t *)ptr;
111#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__GNUC__) || \
115 for (
size_t i = 0; i < tls->buf_count; i++) {
116 ttak_mem_free(tls->bufs[i]);
118 for (
size_t i = 0; i < tls->struct_count; i++) {
119 free(tls->structs[i]);
121 ttak_arena_env_destroy(&tls->env);
125static void cwist_arena_tls_key_init(
void) {
126 pthread_key_create(&g_arena_tls_key, cwist_arena_tls_destroy);
133static inline cwist_arena_tls_t *cwist_arena_tls_get(
void) {
134#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__GNUC__) || \
136 if (__builtin_expect(t_arena_tls != NULL, 1)) {
141 if (pthread_once(&g_arena_tls_once, cwist_arena_tls_key_init) != 0) {
144 cwist_arena_tls_t *tls = (cwist_arena_tls_t *)pthread_getspecific(g_arena_tls_key);
146#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__GNUC__) || \
153 tls = (cwist_arena_tls_t *)calloc(1,
sizeof(cwist_arena_tls_t));
154 if (!tls)
return NULL;
156 ttak_arena_env_config_t config;
157 ttak_arena_env_config_init(&config);
159 if (!ttak_arena_env_init(&tls->env, &config)) {
163 if (pthread_setspecific(g_arena_tls_key, tls) != 0) {
164 ttak_arena_env_destroy(&tls->env);
168#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) || defined(__GNUC__) || \
176 cwist_arena_tls_t *tls = cwist_arena_tls_get();
177 if (!tls)
return NULL;
184 buffer = tls->bufs[--tls->buf_count];
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;
193 ? tls->structs[--tls->struct_count]
197 tls->buf_count < CWIST_ARENA_CACHE_MAX) {
198 tls->bufs[tls->buf_count++] = buffer;
200 ttak_mem_free(buffer);
205 arena->gen.base = buffer;
206 arena->gen.capacity = bytes;
208 arena->gen.epoch_id = ++tls->epoch_seq;
209 arena->generation_bytes = bytes;
210 arena->owner_tid = pthread_self();
215 if (!arena || !size || !arena->gen.base)
return NULL;
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;
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;
237 void *buffer = arena->gen.base;
238 size_t bytes = arena->generation_bytes;
243 bool is_owner = pthread_equal(arena->owner_tid, pthread_self());
244 cwist_arena_tls_t *tls = is_owner ? cwist_arena_tls_get() : NULL;
246 if (tls && tls->struct_count < CWIST_ARENA_CACHE_MAX) {
248 tls->structs[tls->struct_count++] = arena;
254 if (tls && default_sized && tls->buf_count < CWIST_ARENA_CACHE_MAX) {
256 tls->bufs[tls->buf_count++] = buffer;
258 ttak_mem_free(buffer);
void * cwist_arena_alloc(cwist_arena_t *arena, size_t size)
Bump-allocate size bytes from the active generation.
bool cwist_arena_owns(const cwist_arena_t *arena, const void *ptr)
Check whether ptr lies inside the active generation buffer.
void cwist_arena_destroy(cwist_arena_t *arena)
Retire the generation and destroy the arena, releasing every allocation made from it at once.
cwist_arena_t * cwist_arena_create(size_t generation_bytes)
Create an arena backed by a recycled thread-local generation buffer.
Request-scoped arena allocator backed by libttak arena generations.
struct cwist_arena cwist_arena_t
#define CWIST_ARENA_DEFAULT_GENERATION_BYTES
Default capacity of one arena generation buffer. A full HTTP/1.1 request+response pair (headers,...