ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
buffer.h File Reference

Go to the source code of this file.

Data Structures

struct  buffer_domain_config_t
struct  buffer_stats_t
struct  buffer_workspace_t
 Owns the temporary storage used by buffer workspace. The data pointers and size or cursor fields describe the live portion of the allocation; callers must use the type's cleanup path when the buffer or queue is no longer needed. More...

Macros

#define BUFFER_DEFAULT_ALIGNMENT   64u
#define BUFFER_PROCESS_LIMIT   ((size_t)512 * 1024 * 1024)
#define BUFFER_CONTROL_RESERVE   ((size_t)8 * 1024 * 1024)
#define BUFFER_DOMAIN_LIMIT   (BUFFER_PROCESS_LIMIT - BUFFER_CONTROL_RESERVE)
#define buffer_release(data)

Typedefs

typedef struct buffer_domain buffer_domain_t

Functions

asciichat_error_t buffer_domain_create (const buffer_domain_config_t *config, buffer_domain_t **out)
void buffer_domain_close (buffer_domain_t **domain)
void buffer_domain_trim (buffer_domain_t *domain)
void buffer_domain_stats (buffer_domain_t *domain, buffer_stats_t *out)
void buffer_domain_log_stats (buffer_domain_t *domain, const char *name)
size_t buffer_process_reserved (void)
asciichat_error_t buffer_budget_reserve (buffer_budget_t *budget, size_t bytes)
void buffer_budget_release (buffer_budget_t *budget)
asciichat_error_t buffer_acquire (buffer_domain_t *domain, size_t size, size_t alignment, void **out)
void * buffer_alloc (buffer_domain_t *domain, size_t size)
size_t buffer_capacity (const void *data)
size_t buffer_size (const void *data)
asciichat_error_t buffer_set_size (void *data, size_t size)
const void * buffer_share (const void *data)
bool buffer_is_unique (const void *data)
void buffer_unref (const void *data)
asciichat_error_t buffer_workspace_reserve (buffer_workspace_t *workspace, buffer_domain_t *domain, size_t capacity)
void buffer_workspace_destroy (buffer_workspace_t *workspace)
void buffer_system_init (void)
void buffer_system_cleanup (void)
buffer_domain_t * buffer_default_domain (void)
buffer_domain_t * buffer_control_domain (void)

Macro Definition Documentation

◆ BUFFER_CONTROL_RESERVE

#define BUFFER_CONTROL_RESERVE   ((size_t)8 * 1024 * 1024)

Definition at line 28 of file buffer.h.

Referenced by buffer_system_init().

◆ BUFFER_DEFAULT_ALIGNMENT

#define BUFFER_DEFAULT_ALIGNMENT   64u

Definition at line 26 of file buffer.h.

Referenced by buffer_acquire(), buffer_alloc(), buffer_workspace_reserve(), and image_new().

◆ BUFFER_DOMAIN_LIMIT

#define BUFFER_DOMAIN_LIMIT   (BUFFER_PROCESS_LIMIT - BUFFER_CONTROL_RESERVE)

Definition at line 29 of file buffer.h.

Referenced by buffer_domain_create().

◆ BUFFER_PROCESS_LIMIT

#define BUFFER_PROCESS_LIMIT   ((size_t)512 * 1024 * 1024)

Definition at line 27 of file buffer.h.

◆ buffer_release

#define buffer_release ( data)
Value:
do { \
__typeof__(&(data)) buffer_slot__ = &(data); \
buffer_unref(*buffer_slot__); \
*buffer_slot__ = NULL; \
} while (0)

Release and clear a typed pointer lvalue; evaluates the lvalue once.

Definition at line 91 of file buffer.h.

91#define buffer_release(data) \
92 do { \
93 __typeof__(&(data)) buffer_slot__ = &(data); \
94 buffer_unref(*buffer_slot__); \
95 *buffer_slot__ = NULL; \
96 } while (0)

Referenced by acip_async_tcp_client_create(), acip_client_receive_and_dispatch(), acip_packet_encrypt(), acip_packet_seal(), acip_security_decrypt(), acip_security_flush(), acip_send_audio_batch(), acip_send_audio_opus_batch(), acip_send_error(), acip_send_image_frame_h265(), acip_send_remote_log(), acip_server_receive_and_dispatch(), audio_ring_buffer_destroy(), av_send_audio_opus_batch(), browser_security_output(), browser_security_packet(), buffer_workspace_destroy(), buffer_workspace_reserve(), client_decrypt_packet(), client_encrypt_packet(), client_handle_crypto_parameters(), compress_data(), crypto_record_open_sequence(), crypto_record_seal_into_domain(), framebuffer_clear(), framebuffer_read_frame(), framebuffer_write_frame(), framebuffer_write_multi_frame(), image_destroy(), mixer_create_channels(), mixer_destroy(), packet_decoder_reset(), packet_decrypt_envelope(), packet_queue_free_packet(), packet_queue_push_copy(), packet_queue_push_shared(), packet_receive(), packet_receive_via_transport(), peer_crypto_destroy(), peer_crypto_output_pop(), peer_crypto_receive(), quic_connection_destroy(), quic_connection_enable_security(), quic_connection_submit(), quic_connection_submit_datagram(), quic_datagram_read(), receive_packet_secure_with_timeout(), send_image_frame_packet(), send_packet_secure(), video_frame_buffer_create(), video_frame_buffer_destroy(), websocket_transport_send(), and websocket_transport_write_pending().

Typedef Documentation

◆ buffer_domain_t

Owned, aligned payloads for synchronous and asynchronous media pipelines.

A returned payload pointer is an ownership token. Only pointers returned by this API may be released or queried; interior pointers are borrowed views. Capacity, allocation origin and domain lifetime are private implementation details. Ordinary objects continue to use SAFE_MALLOC/SAFE_FREE.

Buffers are exclusive until buffer_share() is called. Shared buffers must be treated as immutable. Each owner releases exactly once. Transfer a pointer by clearing the source after a successful handoff; failure preserves it.

Domains serialize allocation, return and trim. No allocator operation is suitable for a real-time audio callback: reserve and touch storage first.

Definition at line 24 of file buffer.h.

Function Documentation

◆ buffer_acquire()

asciichat_error_t buffer_acquire ( buffer_domain_t * domain,
size_t size,
size_t alignment,
void ** out )

NULL domain selects the default process domain. Failure leaves *out NULL.

Definition at line 178 of file buffer.c.

178 {
179 if (!out)
180 return SET_ERRNO(ERROR_INVALID_PARAM, "Buffer output is NULL");
181 *out = NULL;
182 if (!alignment)
183 alignment = BUFFER_DEFAULT_ALIGNMENT;
184 if (alignment < sizeof(void *) || (alignment & (alignment - 1)) || alignment > 4096)
185 return SET_ERRNO(ERROR_INVALID_PARAM, "Buffer alignment must be a power of two between pointer size and 4096");
186 if (!domain)
187 domain = buffer_default_domain();
188 if (!domain)
189 return SET_ERRNO(ERROR_INVALID_STATE, "Buffer system is unavailable");
190
191 size_t capacity = BUFFER_MIN_CAPACITY;
192 unsigned bin = 0;
193 while (capacity < size && bin + 1 < BUFFER_BINS) {
194 capacity *= 2;
195 bin++;
196 }
197 if (capacity < size)
198 capacity = size;
199 size_t overhead = sizeof(buffer_header_t) + sizeof(buffer_header_t *) + alignment - 1;
200 if (capacity > SIZE_MAX - overhead)
201 return SET_ERRNO(ERROR_INVALID_PARAM, "Buffer capacity overflow");
202 size_t charge = capacity + overhead;
203
204 mutex_lock(&domain->mutex);
205 if (domain->closing) {
206 mutex_unlock(&domain->mutex);
207 return SET_ERRNO(ERROR_INVALID_STATE, "Buffer domain is closing");
208 }
209 buffer_header_t **link = &domain->bins[bin];
210 buffer_header_t *node = NULL;
211 if (capacity <= BUFFER_MAX_CACHED) {
212 while (*link) {
213 if ((*link)->alignment >= alignment) {
214 node = *link;
215 *link = node->next;
216 domain->stats.cached_bytes -= node->charge;
217 domain->stats.hits++;
218 break;
219 }
220 link = &(*link)->next;
221 }
222 }
223 if (!node) {
224 if (charge > domain->config.limit_bytes || domain->stats.reserved_bytes > domain->config.limit_bytes - charge) {
225 /* A miss may reclaim idle capacity, but a release never scans a pool. */
226 trim_locked(domain, true);
227 }
228 bool fits =
229 charge <= domain->config.limit_bytes && domain->stats.reserved_bytes <= domain->config.limit_bytes - charge;
230 bool reserved = fits && process_reserve(charge, domain->config.control);
231 if (fits && !reserved && domain->stats.cached_bytes) {
232 trim_locked(domain, true);
233 reserved = process_reserve(charge, domain->config.control);
234 }
235 if (!reserved) {
236 domain->stats.failures++;
237 mutex_unlock(&domain->mutex);
238 return SET_ERRNO(ERROR_MEMORY, "Buffer budget exhausted requesting %zu bytes", size);
239 }
240 node = ALLOC_MALLOC(charge);
241 if (!node) {
242 atomic_fetch_sub_u64(&process_bytes, charge);
243 domain->stats.failures++;
244 mutex_unlock(&domain->mutex);
245 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate %zu buffer bytes", charge);
246 }
247 memset(node, 0, sizeof(*node));
248 node->allocation = node;
249 uintptr_t start = (uintptr_t)(node + 1) + sizeof(buffer_header_t *);
250 node->data = (void *)((start + alignment - 1) & ~(uintptr_t)(alignment - 1));
251 ((buffer_header_t **)node->data)[-1] = node;
252 node->domain = domain;
253 node->capacity = capacity;
254 node->charge = charge;
255 node->alignment = alignment;
256 node->bin = bin;
257 domain->stats.reserved_bytes += charge;
258 domain->stats.allocations++;
259 }
260 node->next = NULL;
261 BUFFER_UNPOISON(node->data, node->capacity);
262 atomic_store_u64(&node->references, 1);
263 node->length = size;
264 domain->stats.outstanding++;
265 domain->stats.live_bytes += node->charge;
266 domain->stats.requested_bytes += size;
267 if (domain->stats.reserved_bytes > domain->stats.peak_bytes)
268 domain->stats.peak_bytes = domain->stats.reserved_bytes;
269 *out = node->data;
270 mutex_unlock(&domain->mutex);
271 return ASCIICHAT_OK;
272}
#define SET_ERRNO(code,...)
#define BUFFER_MIN_CAPACITY
Definition buffer.c:25
buffer_domain_t * buffer_default_domain(void)
Definition buffer.c:423
#define BUFFER_BINS
Definition buffer.c:24
#define BUFFER_MAX_CACHED
Definition buffer.c:26
struct buffer_header buffer_header_t
#define BUFFER_UNPOISON(p, n)
Definition buffer.c:20
#define BUFFER_DEFAULT_ALIGNMENT
Definition buffer.h:26
#define ALLOC_MALLOC(size)
Definition common.h:253
@ ERROR_INVALID_STATE
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_INVALID_PARAM
static bool size_t gotel_query_t * out
Definition queries.h:8
buffer_stats_t stats
Definition buffer.c:55
bool closing
Definition buffer.c:57
mutex_t mutex
Definition buffer.c:53
buffer_header_t * bins[17]
Definition buffer.c:56
buffer_domain_config_t config
Definition buffer.c:54
size_t capacity
Definition buffer.c:35
void * allocation
Definition buffer.c:33
size_t charge
Definition buffer.c:37
struct buffer_header * next
Definition buffer.c:31
buffer_domain_t * domain
Definition buffer.c:32
unsigned bin
Definition buffer.c:41
size_t alignment
Definition buffer.c:38
size_t length
Definition buffer.c:36
void * data
Definition buffer.c:34
atomic_t references
Definition buffer.c:39
size_t live_bytes
Definition buffer.h:42
uint64_t hits
Definition buffer.h:47
size_t outstanding
Definition buffer.h:46
size_t reserved_bytes
Definition buffer.h:41
size_t peak_bytes
Definition buffer.h:45
size_t requested_bytes
Definition buffer.h:43
size_t cached_bytes
Definition buffer.h:44
uint64_t failures
Definition buffer.h:49
uint64_t allocations
Definition buffer.h:48

References buffer_header::alignment, ALLOC_MALLOC, buffer_header::allocation, buffer_stats_t::allocations, ASCIICHAT_OK, buffer_header::bin, buffer_domain::bins, BUFFER_BINS, BUFFER_DEFAULT_ALIGNMENT, buffer_default_domain(), BUFFER_MAX_CACHED, BUFFER_MIN_CAPACITY, BUFFER_UNPOISON, buffer_stats_t::cached_bytes, buffer_header::capacity, buffer_header::charge, buffer_domain::closing, buffer_domain::config, buffer_domain_config_t::control, buffer_header::data, buffer_header::domain, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, ERROR_MEMORY, buffer_stats_t::failures, buffer_stats_t::hits, buffer_header::length, buffer_domain_config_t::limit_bytes, buffer_stats_t::live_bytes, buffer_domain::mutex, buffer_header::next, out, buffer_stats_t::outstanding, buffer_stats_t::peak_bytes, buffer_header::references, buffer_stats_t::requested_bytes, buffer_stats_t::reserved_bytes, SET_ERRNO, and buffer_domain::stats.

Referenced by buffer_alloc(), and buffer_workspace_reserve().

◆ buffer_alloc()

◆ buffer_budget_release()

void buffer_budget_release ( buffer_budget_t * budget)

Definition at line 88 of file buffer.c.

88 {
89 if (budget && budget->bytes) {
90 atomic_fetch_sub_u64(&process_bytes, budget->bytes);
91 budget->bytes = 0;
92 }
93}
size_t bytes
Definition budget.h:9

References buffer_budget_t::bytes.

◆ buffer_budget_reserve()

asciichat_error_t buffer_budget_reserve ( buffer_budget_t * budget,
size_t bytes )

Budget lease for platform storage that cannot be recycled (e.g. pinned mappings). Zero-initialize, reserve before allocation, release only after disposing storage. A failed reservation preserves the lease. Never copy a live lease.

Definition at line 79 of file buffer.c.

79 {
80 if (!budget || budget->bytes || !bytes)
81 return SET_ERRNO(ERROR_INVALID_PARAM, "Expected an empty buffer budget lease");
82 if (!process_reserve(bytes, false))
83 return SET_ERRNO(ERROR_MEMORY, "Process buffer budget exhausted reserving platform storage");
84 budget->bytes = bytes;
85 return ASCIICHAT_OK;
86}

References ASCIICHAT_OK, buffer_budget_t::bytes, ERROR_INVALID_PARAM, ERROR_MEMORY, and SET_ERRNO.

◆ buffer_capacity()

◆ buffer_control_domain()

buffer_domain_t * buffer_control_domain ( void )

Small, bounded control messages may use the reserved process allowance.

Definition at line 436 of file buffer.c.

436 {
438 return control_domain;
439}
void buffer_system_init(void)
Definition buffer.c:400

References buffer_system_init().

Referenced by acip_packet_plan(), acip_send_error(), crypto_record_seal_into_domain(), packet_receive(), stats_logger_thread(), and websocket_transport_send().

◆ buffer_default_domain()

buffer_domain_t * buffer_default_domain ( void )

Definition at line 423 of file buffer.c.

423 {
425 return default_domain;
426}

References buffer_system_init().

Referenced by buffer_acquire(), and stats_logger_thread().

◆ buffer_domain_close()

void buffer_domain_close ( buffer_domain_t ** domain)

Stop admissions and trim cached storage. Outstanding buffers keep the domain alive. Join users of the domain pointer before closing; outstanding payloads may be released afterwards, from any thread. Clears the caller's domain pointer.

Definition at line 156 of file buffer.c.

156 {
157 if (!slot || !*slot)
158 return;
159 buffer_domain_t *domain = *slot;
160 *slot = NULL;
161 mutex_lock(&domain->mutex);
162 domain->closing = true;
163 trim_locked(domain, true);
164 bool done = domain->stats.outstanding == 0;
165 mutex_unlock(&domain->mutex);
166 if (done)
167 domain_dispose(domain);
168}
struct buffer_domain buffer_domain_t
Definition buffer.h:24

References buffer_domain::closing, buffer_domain::mutex, buffer_stats_t::outstanding, and buffer_domain::stats.

Referenced by buffer_system_cleanup(), buffer_system_init(), and packet_queue_destroy().

◆ buffer_domain_create()

asciichat_error_t buffer_domain_create ( const buffer_domain_config_t * config,
buffer_domain_t ** out )

Create a domain under the shared process budget.

Definition at line 125 of file buffer.c.

125 {
126 if (!out)
127 return SET_ERRNO(ERROR_INVALID_PARAM, "Buffer domain output is NULL");
128 *out = NULL;
129 buffer_domain_config_t defaults = {BUFFER_DOMAIN_LIMIT, 32 * 1024 * 1024, 5000000000ULL, false};
130 if (!process_reserve(sizeof(buffer_domain_t), config && config->control))
131 return SET_ERRNO(ERROR_MEMORY, "Process buffer budget exhausted creating domain");
132 buffer_domain_t *domain = ALLOC_CALLOC(1, sizeof(*domain));
133 if (!domain) {
134 atomic_fetch_sub_u64(&process_bytes, sizeof(*domain));
135 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate buffer domain");
136 }
137 domain->config = config ? *config : defaults;
138 if (!domain->config.limit_bytes)
140 if (mutex_init(&domain->mutex,
141 domain->config.control ? "reserved_control_buffer_pool" : "shared_media_buffer_pool") != 0) {
142 atomic_fetch_sub_u64(&process_bytes, sizeof(*domain));
143 SAFE_FREE(domain);
144 return SET_ERRNO(ERROR_THREAD, "Cannot initialize buffer domain mutex");
145 }
146 *out = domain;
147 return ASCIICHAT_OK;
148}
#define BUFFER_DOMAIN_LIMIT
Definition buffer.h:29
#define SAFE_FREE(ptr)
Definition common.h:377
#define ALLOC_CALLOC(count, size)
Definition common.h:254
@ ERROR_THREAD

References ALLOC_CALLOC, ASCIICHAT_OK, BUFFER_DOMAIN_LIMIT, buffer_domain::config, buffer_domain_config_t::control, ERROR_INVALID_PARAM, ERROR_MEMORY, ERROR_THREAD, buffer_domain_config_t::limit_bytes, buffer_domain::mutex, out, SAFE_FREE, and SET_ERRNO.

Referenced by buffer_system_init(), and packet_queue_create_config().

◆ buffer_domain_log_stats()

void buffer_domain_log_stats ( buffer_domain_t * domain,
const char * name )

Definition at line 392 of file buffer.c.

392 {
393 buffer_stats_t stats;
394 buffer_domain_stats(domain, &stats);
395 log_debug("Buffers %s: reserved=%zu live=%zu requested=%zu cached=%zu outstanding=%zu hits=%llu failures=%llu",
396 name ? name : "default", stats.reserved_bytes, stats.live_bytes, stats.requested_bytes, stats.cached_bytes,
397 stats.outstanding, (unsigned long long)stats.hits, (unsigned long long)stats.failures);
398}
void buffer_domain_stats(buffer_domain_t *domain, buffer_stats_t *out)
Definition buffer.c:381
#define log_debug(...)
Log a DEBUG message.
Definition log/log.h:595

References buffer_domain_stats(), buffer_stats_t::cached_bytes, buffer_stats_t::failures, buffer_stats_t::hits, buffer_stats_t::live_bytes, log_debug, buffer_stats_t::outstanding, buffer_stats_t::requested_bytes, and buffer_stats_t::reserved_bytes.

Referenced by stats_logger_thread().

◆ buffer_domain_stats()

void buffer_domain_stats ( buffer_domain_t * domain,
buffer_stats_t * out )

Definition at line 381 of file buffer.c.

381 {
382 if (!out)
383 return;
384 memset(out, 0, sizeof(*out));
385 if (!domain)
386 return;
387 mutex_lock(&domain->mutex);
388 *out = domain->stats;
389 mutex_unlock(&domain->mutex);
390}

References buffer_domain::mutex, out, and buffer_domain::stats.

Referenced by buffer_domain_log_stats().

◆ buffer_domain_trim()

void buffer_domain_trim ( buffer_domain_t * domain)

Maintenance operation; never called implicitly from the steady-state release path.

Definition at line 170 of file buffer.c.

170 {
171 if (!domain)
172 return;
173 mutex_lock(&domain->mutex);
174 trim_locked(domain, false);
175 mutex_unlock(&domain->mutex);
176}

References buffer_domain::mutex.

Referenced by stats_logger_thread().

◆ buffer_is_unique()

bool buffer_is_unique ( const void * data)

Test exclusive ownership while retaining a reference. Only the owner may publish new references while using this result to modify/recycle storage.

Definition at line 317 of file buffer.c.

317 {
318 return data && atomic_load_u64(&header(data)->references) == 1;
319}

Referenced by packet_decrypt_envelope().

◆ buffer_process_reserved()

size_t buffer_process_reserved ( void )

Definition at line 75 of file buffer.c.

75 {
76 return (size_t)atomic_load_u64(&process_bytes);
77}

◆ buffer_set_size()

asciichat_error_t buffer_set_size ( void * data,
size_t size )

Change logical length within capacity. Requires exclusive ownership.

Definition at line 287 of file buffer.c.

287 {
288 if (!data || size > buffer_capacity(data))
289 return SET_ERRNO(ERROR_INVALID_PARAM, "Buffer length exceeds capacity");
290 buffer_header_t *node = header(data);
291 buffer_domain_t *domain = node->domain;
292 mutex_lock(&domain->mutex);
293 if (atomic_load_u64(&node->references) != 1) {
294 mutex_unlock(&domain->mutex);
295 return SET_ERRNO(ERROR_INVALID_STATE, "Cannot resize shared buffer");
296 }
297 domain->stats.requested_bytes -= node->length;
298 node->length = size;
299 domain->stats.requested_bytes += size;
300 mutex_unlock(&domain->mutex);
301 return ASCIICHAT_OK;
302}
size_t buffer_capacity(const void *data)
Definition buffer.c:280

References ASCIICHAT_OK, buffer_capacity(), buffer_header::domain, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, buffer_header::length, buffer_domain::mutex, buffer_header::references, buffer_stats_t::requested_bytes, SET_ERRNO, and buffer_domain::stats.

Referenced by compress_workspace().

◆ buffer_share()

const void * buffer_share ( const void * data)

Acquire another immutable reference while an existing reference is held.

Definition at line 304 of file buffer.c.

304 {
305 if (!data)
306 return NULL;
307 buffer_header_t *node = header(data);
308 uint64_t references = atomic_load_u64(&node->references);
309 while (references && references != SIZE_MAX) {
310 if (atomic_cas_u64(&node->references, &references, references + 1))
311 return data;
312 }
313 SET_ERRNO(ERROR_INVALID_STATE, "Invalid buffer reference count");
314 return NULL;
315}
unsigned long long uint64_t
Definition common.h:63

References ERROR_INVALID_STATE, buffer_header::references, and SET_ERRNO.

Referenced by packet_queue_push_shared().

◆ buffer_size()

size_t buffer_size ( const void * data)

Definition at line 283 of file buffer.c.

283 {
284 return data ? header(data)->length : 0;
285}

◆ buffer_system_cleanup()

void buffer_system_cleanup ( void )

Definition at line 428 of file buffer.c.

428 {
429 if (lifecycle_shutdown(&default_lifecycle)) {
430 NAMED_UNREGISTER(&process_bytes);
431 buffer_domain_close(&default_domain);
432 buffer_domain_close(&control_domain);
433 }
434}
void buffer_domain_close(buffer_domain_t **slot)
Definition buffer.c:156
#define NAMED_UNREGISTER(ptr)
Unregister a pointer.

References buffer_domain_close(), and NAMED_UNREGISTER.

Referenced by asciichat_shared_destroy(), and mirror_cleanup().

◆ buffer_system_init()

void buffer_system_init ( void )

Application startup/shutdown, serialized against starting/stopping workers.

Definition at line 400 of file buffer.c.

400 {
401 for (;;) {
402 if (lifecycle_init_once(&default_lifecycle))
403 break;
404 if (lifecycle_is_initialized(&default_lifecycle) || lifecycle_is_dead(&default_lifecycle))
405 return;
406 // Another caller must finish publishing both domains before allocations use them.
407 platform_sleep_us(100);
408 }
409 if (buffer_domain_create(NULL, &default_domain) != ASCIICHAT_OK) {
410 lifecycle_init_abort(&default_lifecycle);
411 return;
412 }
413 buffer_domain_config_t control = {BUFFER_CONTROL_RESERVE, 1024 * 1024, 5000000000ULL, true};
414 if (buffer_domain_create(&control, &control_domain) != ASCIICHAT_OK) {
415 buffer_domain_close(&default_domain);
416 lifecycle_init_abort(&default_lifecycle);
417 return;
418 }
419 NAMED_REGISTER_ATOMIC(&process_bytes, "buffer.process_reserved_bytes", NULL);
420 lifecycle_init_commit(&default_lifecycle);
421}
asciichat_error_t buffer_domain_create(const buffer_domain_config_t *config, buffer_domain_t **out)
Definition buffer.c:125
#define BUFFER_CONTROL_RESERVE
Definition buffer.h:28
#define NAMED_REGISTER_ATOMIC(a, name, parent_ptr)
Register an atomic_t with automatic format specifier.

References ASCIICHAT_OK, BUFFER_CONTROL_RESERVE, buffer_domain_close(), buffer_domain_create(), and NAMED_REGISTER_ATOMIC.

Referenced by asciichat_shared_init(), buffer_control_domain(), and buffer_default_domain().

◆ buffer_unref()

void buffer_unref ( const void * data)

Low-level release for ownership passed by value. Prefer buffer_release().

Definition at line 321 of file buffer.c.

321 {
322 if (!data)
323 return;
324 buffer_header_t *node = header(data);
325 buffer_domain_t *domain = node->domain;
326 uint64_t references = atomic_load_u64(&node->references);
327 while (references && !atomic_cas_u64(&node->references, &references, references - 1)) {
328 }
329 if (!references) {
330 SET_ERRNO(ERROR_INVALID_STATE, "Buffer has already been released");
331 return;
332 }
333 if (references > 1) {
334 return;
335 }
336 mutex_lock(&domain->mutex);
337 domain->stats.outstanding--;
338 domain->stats.live_bytes -= node->charge;
339 domain->stats.requested_bytes -= node->length;
340 bool done = domain->closing && !domain->stats.outstanding;
341 if (!domain->closing && node->capacity <= BUFFER_MAX_CACHED && node->charge <= domain->config.cache_bytes &&
342 domain->stats.cached_bytes <= domain->config.cache_bytes - node->charge) {
343 node->returned_at = time_get_ns();
344 BUFFER_POISON(node->data, node->capacity);
345 node->next = domain->bins[node->bin];
346 domain->bins[node->bin] = node;
347 domain->stats.cached_bytes += node->charge;
348 } else {
349 dispose(domain, node);
350 }
351 mutex_unlock(&domain->mutex);
352 if (done)
353 domain_dispose(domain);
354}
#define BUFFER_POISON(p, n)
Definition buffer.c:19
uint64_t returned_at
Definition buffer.c:40

References buffer_header::bin, buffer_domain::bins, BUFFER_MAX_CACHED, BUFFER_POISON, buffer_domain_config_t::cache_bytes, buffer_stats_t::cached_bytes, buffer_header::capacity, buffer_header::charge, buffer_domain::closing, buffer_domain::config, buffer_header::data, buffer_header::domain, ERROR_INVALID_STATE, buffer_header::length, buffer_stats_t::live_bytes, buffer_domain::mutex, buffer_header::next, buffer_stats_t::outstanding, buffer_header::references, buffer_stats_t::requested_bytes, buffer_header::returned_at, SET_ERRNO, and buffer_domain::stats.

◆ buffer_workspace_destroy()

void buffer_workspace_destroy ( buffer_workspace_t * workspace)

Definition at line 374 of file buffer.c.

374 {
375 if (workspace) {
376 buffer_release(workspace->data);
377 workspace->length = 0;
378 }
379}
#define buffer_release(data)
Definition buffer.h:91

References buffer_release, buffer_workspace_t::data, and buffer_workspace_t::length.

Referenced by compression_workspace_destroy().

◆ buffer_workspace_reserve()

asciichat_error_t buffer_workspace_reserve ( buffer_workspace_t * workspace,
buffer_domain_t * domain,
size_t capacity )

Preserves the previous workspace on failure; retains capacity between frames.

Definition at line 356 of file buffer.c.

356 {
357 if (!workspace)
358 return SET_ERRNO(ERROR_INVALID_PARAM, "Workspace is NULL");
359 if (workspace->length > buffer_capacity(workspace->data))
360 return SET_ERRNO(ERROR_INVALID_STATE, "Workspace length exceeds its storage");
361 if (capacity <= buffer_capacity(workspace->data))
362 return ASCIICHAT_OK;
363 void *next = NULL;
364 asciichat_error_t result = buffer_acquire(domain, capacity, BUFFER_DEFAULT_ALIGNMENT, &next);
365 if (result != ASCIICHAT_OK)
366 return result;
367 if (workspace->data && workspace->length)
368 memcpy(next, workspace->data, workspace->length);
369 buffer_release(workspace->data);
370 workspace->data = next;
371 return ASCIICHAT_OK;
372}
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44

References ASCIICHAT_OK, buffer_acquire(), buffer_capacity(), BUFFER_DEFAULT_ALIGNMENT, buffer_release, buffer_workspace_t::data, ERROR_INVALID_PARAM, ERROR_INVALID_STATE, buffer_workspace_t::length, and SET_ERRNO.

Referenced by compress_workspace(), packet_decode_frame_data_workspace(), packet_decoder_feed(), and threaded_send_image_frame_encoded().