ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
async.c
Go to the documentation of this file.
7#include <ascii-chat/common.h>
9#include <ascii-chat/buffer.h>
12
13#include <libplatform/cond.h>
15
18typedef struct async_packet {
20 void *data;
21 size_t size;
22 bool pooled;
24
25#define ASYNC_RECEIVE_POOL_CAPACITY (256u * 1024u)
26#define ASYNC_RECEIVE_POOL_SIZE 64u
27
62
63static void recycle_packet(async_tcp_t *tcp, async_packet_t *packet) {
64 packet->data = NULL;
65 packet->size = 0;
66 packet->pooled = false;
67 packet->next = tcp->recycled;
68 tcp->recycled = packet;
69}
70
71static asciichat_error_t async_sendv(acip_transport_t *transport, const socket_buffer_t *buffers, size_t count) {
72 async_tcp_t *tcp = transport->impl_data;
73 /* The outbound worker is the sole writer, but the close callback can clear
74 * tcp->stream and destroy its event stream concurrently. Keep the lifetime
75 * guard through the write so the stream cannot disappear between the state
76 * check and platform_event_stream_write(). */
77 mutex_lock(&tcp->mutex);
78 platform_event_stream_t *stream = tcp->stream;
79 if (!stream || atomic_load_bool(&tcp->closed) || atomic_load_bool(&tcp->interrupted)) {
80 mutex_unlock(&tcp->mutex);
81 return SET_ERRNO(ERROR_NETWORK, "Async transport closed");
82 }
84 asciichat_error_t result = acip_packet_plan(transport->crypto_ctx, buffers, count, &plan);
85 if (result != ASCIICHAT_OK) {
86 mutex_unlock(&tcp->mutex);
87 return result;
88 }
89 if (!plan.encrypt) {
90 result = platform_event_stream_write(stream, buffers, count, NULL, NULL);
91 } else {
93 mutex_unlock(&tcp->mutex);
94 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate async ciphertext");
95 }
96 uint8_t *ciphertext = tcp->ciphertext.data;
97 size_t length = 0;
98 packet_header_t outer;
99 result = acip_packet_encrypt(transport->crypto_ctx, &plan, plan.domain, ciphertext, plan.ciphertext_capacity,
100 &outer, &length);
101 if (result == ERROR_MEMORY) {
102 mutex_unlock(&tcp->mutex);
103 return result;
104 }
105 if (result == ASCIICHAT_OK) {
106 socket_buffer_t wire[] = {{&outer, sizeof(outer)}, {ciphertext, length}};
107 // The event stream copies the vectors before returning.
108 result = platform_event_stream_write(stream, wire, 2, NULL, NULL);
109 }
110 }
111 if (result != ASCIICHAT_OK) {
112 // Encryption sequence may already have advanced. Never continue this stream.
113 atomic_store_bool(&tcp->closed, true);
115 if (result != ERROR_CRYPTO)
116 result = SET_ERRNO(ERROR_NETWORK, "Async packet transmission failed");
117 }
118 mutex_unlock(&tcp->mutex);
119 return result;
120}
121
122static asciichat_error_t async_send(acip_transport_t *transport, const void *data, size_t length) {
123 socket_buffer_t buffer = {data, length};
124 return async_sendv(transport, &buffer, 1);
125}
126
127static asciichat_error_t async_recv(acip_transport_t *transport, void **data, size_t *length, void **allocation) {
128 async_tcp_t *tcp = transport->impl_data;
129 if (!tcp->queued_receive || !data || !length || !allocation)
130 return SET_ERRNO(ERROR_INVALID_STATE, "Async server receives through packet callbacks");
131 *data = *allocation = NULL;
132 *length = 0;
133 /* A pooled buffer retains one transport-owned reference while the caller
134 * holds the shared reference returned below. Recycle it only after the
135 * caller has released that reference, without taking the buffer-domain lock.
136 */
137 async_packet_t **held_link = &tcp->held;
138 while (*held_link) {
139 async_packet_t *held = *held_link;
140 if (!buffer_is_unique(held->data)) {
141 held_link = &held->next;
142 continue;
143 }
144 *held_link = held->next;
145 void *reusable = held->data;
146 recycle_packet(tcp, held);
147 if (!ringbuffer_write(tcp->receive_free, &reusable)) {
148 buffer_release(reusable);
149 continue;
150 }
151 }
152 uint64_t start = time_get_ns();
153 uint64_t timeout = transport->receive_timeout_ns ? transport->receive_timeout_ns : NS_PER_SEC_INT;
154 if (!tcp->receive_lock_held) {
155 mutex_lock(&tcp->receive_mutex);
156 tcp->receive_lock_held = true;
157 }
158 while (atomic_load_u64(&tcp->received_count) == 0 && !atomic_load_bool(&tcp->closed)) {
159 if (atomic_load_bool(&tcp->interrupted)) {
161 mutex_unlock(&tcp->receive_mutex);
162 tcp->receive_lock_held = false;
163 return SET_ERRNO(ERROR_NETWORK, "Async receive interrupted");
164 }
165 uint64_t elapsed = time_get_ns() - start;
166 if (elapsed >= timeout) {
167 mutex_unlock(&tcp->receive_mutex);
168 tcp->receive_lock_held = false;
169 // Short receive deadlines drive polling; the enclosing operation owns its final timeout.
170 bool previous = errno_notifications_suppress(true);
171 asciichat_error_t result = SET_ERRNO(ERROR_NETWORK_TIMEOUT, "Async receive deadline expired");
173 return result;
174 }
175 uint64_t wait = timeout - elapsed;
176 if (wait > 50 * NS_PER_MS_INT)
177 wait = 50 * NS_PER_MS_INT;
178 cond_timedwait(&tcp->received, &tcp->receive_mutex, wait);
179 }
180 async_packet_t *packet = NULL;
181 if (!ringbuffer_read(tcp->receive_queue, &packet) || !packet) {
182 mutex_unlock(&tcp->receive_mutex);
183 tcp->receive_lock_held = false;
184 return SET_ERRNO(ERROR_NETWORK, "Async peer disconnected");
185 }
186 atomic_fetch_sub_u64(&tcp->received_bytes, packet->size);
187 atomic_fetch_sub_u64(&tcp->received_count, 1);
188 packet_header_t header;
189 memcpy(&header, packet->data, sizeof(header));
190 packet_envelope_t envelope = {.type = NET_TO_HOST_U16(header.type),
191 .data = (uint8_t *)packet->data + sizeof(header),
192 .len = packet->size - sizeof(header),
193 .allocated_buffer = packet->data,
194 .allocated_size = packet->size};
196 if (envelope.type == PACKET_TYPE_ENCRYPTED) {
197 result = packet_decrypt_envelope(&envelope, transport->crypto_ctx);
198 if (result == ASCIICHAT_OK) {
199 memcpy(&header, envelope.allocated_buffer, sizeof(header));
200 if (NET_TO_HOST_U64(header.magic) != PACKET_MAGIC ||
201 (envelope.len && asciichat_crc32(envelope.data, envelope.len) != NET_TO_HOST_U32(header.crc32)))
202 result = SET_ERRNO(ERROR_NETWORK_PROTOCOL, "Invalid decrypted async packet");
203 }
204 } else if (transport->crypto_ctx && transport->crypto_ctx->encrypt_data &&
205 !packet_is_initial_handshake_type(envelope.type)) {
206 result = SET_ERRNO(ERROR_CRYPTO, "Unexpected plaintext packet");
207 }
208 if (result == ASCIICHAT_OK && envelope.consumed) {
209 if (packet->pooled) {
210 if (envelope.allocated_buffer != packet->data)
211 buffer_release(envelope.allocated_buffer);
212 ringbuffer_write(tcp->receive_free, &packet->data);
213 } else {
214 buffer_release(envelope.allocated_buffer);
215 }
216 recycle_packet(tcp, packet);
218 }
219 if (result != ASCIICHAT_OK) {
220 if (packet->pooled) {
221 if (envelope.allocated_buffer != packet->data)
222 buffer_release(envelope.allocated_buffer);
223 ringbuffer_write(tcp->receive_free, &packet->data);
224 } else {
225 buffer_release(envelope.allocated_buffer);
226 }
227 recycle_packet(tcp, packet);
228 mutex_unlock(&tcp->receive_mutex);
229 tcp->receive_lock_held = false;
230 mutex_lock(&tcp->mutex);
232 mutex_unlock(&tcp->mutex);
233 return result;
234 }
235 if (packet->pooled) {
236 if (!buffer_share(envelope.allocated_buffer)) {
237 ringbuffer_write(tcp->receive_free, &packet->data);
238 recycle_packet(tcp, packet);
239 mutex_unlock(&tcp->receive_mutex);
240 tcp->receive_lock_held = false;
241 return SET_ERRNO(ERROR_MEMORY, "Cannot retain async receive buffer");
242 }
243 packet->next = tcp->held;
244 tcp->held = packet;
245 } else {
246 recycle_packet(tcp, packet);
247 }
248 *data = *allocation = envelope.allocated_buffer;
249 *length = envelope.len + sizeof(header);
250 return ASCIICHAT_OK;
251}
252
253static asciichat_error_t async_close(acip_transport_t *transport) {
254 async_tcp_t *tcp = transport->impl_data;
255 mutex_lock(&tcp->mutex);
256 atomic_store_bool(&tcp->closed, true);
258 mutex_unlock(&tcp->mutex);
259 return ASCIICHAT_OK;
260}
261
263 async_tcp_t *tcp = transport->impl_data;
264 mutex_lock(&tcp->mutex);
265 atomic_store_bool(&tcp->closed, true);
267 tcp->stream = NULL;
268 mutex_unlock(&tcp->mutex);
269}
270
271static bool async_connected(acip_transport_t *transport) {
272 async_tcp_t *tcp = transport->impl_data;
273 bool connected = !atomic_load_bool(&tcp->closed) && !atomic_load_bool(&tcp->interrupted);
274 return connected;
275}
276static acip_transport_type_t async_type(acip_transport_t *transport) {
277 (void)transport;
278 return ACIP_TRANSPORT_TCP;
279}
280static socket_t async_socket(acip_transport_t *transport) {
281 (void)transport;
282 return INVALID_SOCKET_VALUE;
283}
284static bool async_pending(acip_transport_t *transport) {
285 async_tcp_t *tcp = transport->impl_data;
286 bool pending = atomic_load_u64(&tcp->received_count) != 0 || atomic_load_bool(&tcp->closed) ||
287 atomic_load_bool(&tcp->interrupted);
288 return pending;
289}
290static void async_destroy(acip_transport_t *transport) {
291 async_tcp_t *tcp = transport->impl_data;
292 mutex_lock(&tcp->mutex);
294 mutex_unlock(&tcp->mutex);
295 if (tcp->queued_receive && !tcp->loop) {
296 mutex_lock(&tcp->mutex);
297 if (tcp->stream)
299 mutex_unlock(&tcp->mutex);
300 }
301 if (tcp->loop) {
303 if (tcp->thread_started)
304 thread_join(&tcp->thread, NULL);
305 else
308 }
309 if (tcp->queued_receive) {
311 async_packet_t *packet = NULL;
312 while (tcp->receive_queue && ringbuffer_read(tcp->receive_queue, &packet)) {
313 if (packet->pooled) {
314 if (!ringbuffer_write(tcp->receive_free, &packet->data))
315 buffer_release(packet->data);
316 } else {
317 buffer_release(packet->data);
318 }
319 SAFE_FREE(packet);
320 }
322 tcp->receive_queue = NULL;
323 while (tcp->held) {
324 packet = tcp->held;
325 tcp->held = packet->next;
326 buffer_release(packet->data);
327 SAFE_FREE(packet);
328 }
329 if (tcp->receive_free) {
330 void *reusable = NULL;
331 while (ringbuffer_read(tcp->receive_free, &reusable))
332 buffer_release(reusable);
334 tcp->receive_free = NULL;
335 }
336 while (tcp->recycled) {
337 async_packet_t *packet = tcp->recycled;
338 tcp->recycled = packet->next;
339 buffer_release(packet->data);
340 SAFE_FREE(packet);
341 }
342 cond_destroy(&tcp->received);
343 }
344 cond_destroy(&tcp->writable);
346 mutex_destroy(&tcp->receive_mutex);
347 mutex_destroy(&tcp->mutex);
348}
349static void async_interrupt(acip_transport_t *transport) {
350 async_tcp_t *tcp = transport->impl_data;
351 atomic_store_bool(&tcp->interrupted, true);
352}
353
354static size_t async_queued_bytes(acip_transport_t *transport) {
355 return acip_async_tcp_transport_queued(transport);
356}
357
358static asciichat_error_t async_wait_writable(acip_transport_t *transport, uint64_t timeout_ns) {
359 async_tcp_t *tcp = transport->impl_data;
360 mutex_lock(&tcp->mutex);
361 while (atomic_load_u64(&tcp->queued_wire_bytes) != 0 && !atomic_load_bool(&tcp->closed) &&
362 !atomic_load_bool(&tcp->interrupted)) {
363 if (cond_timedwait(&tcp->writable, &tcp->mutex, timeout_ns) == ERROR_NETWORK_TIMEOUT)
364 break;
365 }
366 bool writable = atomic_load_u64(&tcp->queued_wire_bytes) == 0;
367 bool closed = atomic_load_bool(&tcp->closed) || atomic_load_bool(&tcp->interrupted);
368 mutex_unlock(&tcp->mutex);
369 if (closed)
370 return SET_ERRNO(ERROR_NETWORK, "Async transport closed while waiting for writable output");
371 return writable ? ASCIICHAT_OK : ERROR_NETWORK_TIMEOUT;
372}
373
374static const acip_transport_methods_t async_methods = {.send = async_send,
375 .reserved_bytes = SOCKET_STREAM_SEND_RESERVE,
376 .queued_bytes = async_queued_bytes,
377 .wait_writable = async_wait_writable,
378 .sendv = async_sendv,
379 .recv = async_recv,
380 .close = async_close,
381 .interrupt = async_interrupt,
382 .get_type = async_type,
383 .get_socket = async_socket,
384 .is_connected = async_connected,
385 .has_pending_data = async_pending,
386 .destroy_impl = async_destroy};
387
389 return transport && transport->methods == &async_methods;
390}
391
393 async_tcp_t *tcp = transport->impl_data;
394 return (size_t)atomic_load_u64(&tcp->queued_wire_bytes);
395}
396
397static void queue_changed(void *context, size_t bytes) {
398 async_tcp_t *tcp = context;
399 atomic_store_u64(&tcp->queued_wire_bytes, bytes);
400 /* The event backend may invoke this callback synchronously from
401 * platform_event_stream_write(), while async_sendv() holds the lifetime
402 * guard. Condition-variable notifications do not require owning the
403 * associated mutex; taking it here would recurse into tcp->mutex. */
404 cond_broadcast(&tcp->writable);
405}
406
408 acip_transport_t *transport = SAFE_CALLOC(1, sizeof(*transport), acip_transport_t *);
409 async_tcp_t *tcp = SAFE_CALLOC(1, sizeof(*tcp), async_tcp_t *);
410 if (mutex_init(&tcp->mutex, "tcp_packet_io_lifetime_guard") != ASCIICHAT_OK) {
411 SAFE_FREE(tcp);
412 SAFE_FREE(transport);
413 return NULL;
414 }
415 if (mutex_init(&tcp->receive_mutex, "tcp_receive_queue") != ASCIICHAT_OK) {
416 mutex_destroy(&tcp->mutex);
417 SAFE_FREE(tcp);
418 SAFE_FREE(transport);
419 return NULL;
420 }
421 if (cond_init(&tcp->writable, "tcp_output_writable") != ASCIICHAT_OK) {
422 mutex_destroy(&tcp->receive_mutex);
423 mutex_destroy(&tcp->mutex);
424 SAFE_FREE(tcp);
425 SAFE_FREE(transport);
426 return NULL;
427 }
428 tcp->receive_queue = ringbuffer_create(sizeof(async_packet_t *), 4096);
429 if (!tcp->receive_queue) {
431 cond_destroy(&tcp->writable);
432 mutex_destroy(&tcp->receive_mutex);
433 mutex_destroy(&tcp->mutex);
434 SAFE_FREE(tcp);
435 SAFE_FREE(transport);
436 return NULL;
437 }
438 tcp->stream = stream;
439 transport->methods = &async_methods;
440 transport->impl_data = tcp;
441 transport->crypto_ctx = crypto;
442 transport->stats_peer = stats_runtime_peer_open("TCP-async");
443 if (acip_outbound_create(transport) != ASCIICHAT_OK) {
444 acip_transport_destroy(transport);
445 return NULL;
446 }
447 platform_event_stream_observe_queue(stream, queue_changed, tcp);
448 return transport;
449}
450
451static asciichat_error_t client_packet(const void *data, size_t length, void *arg) {
452 async_tcp_t *tcp = arg;
453 uint64_t queued_bytes = atomic_load_u64(&tcp->received_bytes);
454 if (atomic_load_u64(&tcp->received_count) >= 4096 || length > 16 * 1024 * 1024 - queued_bytes) {
455 return SET_ERRNO(ERROR_NETWORK_SIZE, "Async client receive queue full");
456 }
457 async_packet_t *packet = tcp->recycled;
458 if (packet)
459 tcp->recycled = packet->next;
460 else
461 packet = SAFE_CALLOC(1, sizeof(*packet), async_packet_t *);
462 if (length <= ASYNC_RECEIVE_POOL_CAPACITY && tcp->receive_free && ringbuffer_read(tcp->receive_free, &packet->data))
463 packet->pooled = true;
464 else
465 packet->data = buffer_alloc(NULL, length);
466 packet->size = length;
467 if (!packet->data) {
468 recycle_packet(tcp, packet);
469 return SET_ERRNO(ERROR_MEMORY, "Cannot allocate async receive packet");
470 }
471 memcpy(packet->data, data, length);
472 if (!ringbuffer_write(tcp->receive_queue, &packet)) {
473 if (!packet->pooled)
474 buffer_release(packet->data);
475 else
476 ringbuffer_write(tcp->receive_free, &packet->data);
477 recycle_packet(tcp, packet);
478 return SET_ERRNO(ERROR_NETWORK_SIZE, "Async client receive queue full");
479 }
480 bool was_empty = atomic_load_u64(&tcp->received_count) == 0;
481 atomic_fetch_add_u64(&tcp->received_bytes, length);
482 atomic_fetch_add_u64(&tcp->received_count, 1);
483 tcp->packet_received = true;
484 if (was_empty)
485 cond_signal(&tcp->received);
486 return ASCIICHAT_OK;
487}
488static void client_read(platform_event_stream_t *stream, const void *data, size_t length, void *arg) {
489 async_tcp_t *tcp = arg;
490 bool partial = packet_decoder_pending(&tcp->decoder);
491 tcp->packet_received = false;
492 asciichat_error_t result = packet_decoder_feed(&tcp->decoder, data, length, client_packet, tcp);
493 if (result != ASCIICHAT_OK)
494 platform_event_stream_close(stream, result);
495 else if (!packet_decoder_pending(&tcp->decoder))
497 else if (!partial || tcp->packet_received)
498 platform_event_stream_deadline(stream, 30000);
499}
500static void client_closed(void *arg, asciichat_error_t reason) {
501 (void)reason;
502 async_tcp_t *tcp = arg;
503 mutex_lock(&tcp->mutex);
504 atomic_store_bool(&tcp->closed, true);
505 tcp->stream = NULL;
506 platform_event_loop_t *loop = tcp->loop;
507 acip_transport_t *destroy_on_close = tcp->destroy_on_close;
508 mutex_unlock(&tcp->mutex);
509 cond_broadcast(&tcp->received);
510 if (loop)
512 if (destroy_on_close)
513 acip_transport_destroy(destroy_on_close);
514}
515static void client_tick(void *arg) {
516 async_tcp_t *tcp = arg;
517 if (atomic_load_bool(&tcp->interrupted))
519}
520static void *client_loop(void *arg) {
521 async_tcp_t *tcp = arg;
523 return NULL;
524}
526 acip_transport_t *transport = acip_async_tcp_transport_create(NULL, crypto);
527 if (!transport)
528 return NULL;
529 async_tcp_t *tcp = transport->impl_data;
530 if (cond_init(&tcp->received, "async_tcp_receive") != ASCIICHAT_OK) {
531 acip_transport_destroy(transport);
532 return NULL;
533 }
534 tcp->queued_receive = true;
536 if (!tcp->receive_free)
537 goto client_pool_failure;
538 for (size_t i = 0; i < ASYNC_RECEIVE_POOL_SIZE; i++) {
539 void *pool_buffer = buffer_alloc(NULL, ASYNC_RECEIVE_POOL_CAPACITY);
540 if (!pool_buffer)
541 goto client_pool_failure;
542 if (!ringbuffer_write(tcp->receive_free, &pool_buffer)) {
543 buffer_release(pool_buffer);
544 goto client_pool_failure;
545 }
546 }
548 acip_transport_destroy(transport);
549 return NULL;
550 }
551 asciichat_error_t result = platform_event_stream_adopt(tcp->loop, socket, &tcp->stream);
552 if (result == ASCIICHAT_OK) {
553 platform_event_stream_observe_queue(tcp->stream, queue_changed, tcp);
554 result = platform_event_stream_start(tcp->stream, client_read, client_closed, tcp);
555 }
556 if (result == ASCIICHAT_OK)
557 result = platform_event_loop_tick(tcp->loop, 50, client_tick, tcp);
558 if (result == ASCIICHAT_OK)
559 result = platform_thread_create(&tcp->thread, "tcp_io", client_loop, tcp);
560 if (result != ASCIICHAT_OK) {
561 acip_transport_destroy(transport);
562 return NULL;
563 }
564 tcp->thread_started = true;
565 if (acip_outbound_create(transport) != ASCIICHAT_OK) {
566 acip_transport_destroy(transport);
567 return NULL;
568 }
569 return transport;
570
571client_pool_failure:
572 acip_transport_destroy(transport);
573 return NULL;
574}
575
577 acip_transport_t *transport = acip_async_tcp_transport_create(stream, NULL);
578 if (!transport)
579 return NULL;
580 async_tcp_t *tcp = transport->impl_data;
581 if (cond_init(&tcp->received, "accepted_receive") != ASCIICHAT_OK) {
583 tcp->stream = NULL;
584 atomic_store_bool(&tcp->closed, true);
585 acip_transport_destroy(transport);
586 return NULL;
587 }
588 tcp->queued_receive = true;
589 asciichat_error_t result = platform_event_stream_start(stream, client_read, client_closed, tcp);
590 if (result != ASCIICHAT_OK) {
591 // The stream retains its callbacks on startup failure; release their state after close completes.
592 tcp->destroy_on_close = transport;
593 platform_event_stream_close(stream, result);
594 return NULL;
595 }
596 return transport;
597}
bool errno_notifications_suppress(bool suppress)
#define SET_ERRNO(code,...)
acip_transport_t * acip_async_tcp_transport_create(platform_event_stream_t *stream, crypto_context_t *crypto)
Definition async.c:407
#define ASYNC_RECEIVE_POOL_SIZE
Definition async.c:26
#define ASYNC_RECEIVE_POOL_CAPACITY
Definition async.c:25
size_t acip_async_tcp_transport_queued(acip_transport_t *transport)
Definition async.c:392
struct async_packet async_packet_t
void acip_async_tcp_transport_detach(acip_transport_t *transport)
Definition async.c:262
acip_transport_t * acip_async_tcp_client_create(socket_t *socket, crypto_context_t *crypto)
Definition async.c:525
bool acip_async_tcp_transport_is(const acip_transport_t *transport)
Definition async.c:388
acip_transport_t * acip_async_tcp_accepted_create(platform_event_stream_t *stream)
Definition async.c:576
asciichat_error_t acip_outbound_create(acip_transport_t *transport)
bool buffer_is_unique(const void *data)
Definition buffer.c:317
asciichat_error_t buffer_workspace_reserve(buffer_workspace_t *workspace, buffer_domain_t *domain, size_t capacity)
Definition buffer.c:356
void * buffer_alloc(buffer_domain_t *domain, size_t size)
Definition buffer.c:274
void buffer_workspace_destroy(buffer_workspace_t *workspace)
Definition buffer.c:374
const void * buffer_share(const void *data)
Definition buffer.c:304
#define buffer_release(data)
Definition buffer.h:91
⚙️ Common definitions, error codes, macros, and types shared throughout the application
Hardware-Accelerated CRC32 Checksum Computation.
🔄 Network byte order conversion helpers
#define NET_TO_HOST_U64(val)
Definition endian.h:228
#define NET_TO_HOST_U16(val)
Definition endian.h:111
#define NET_TO_HOST_U32(val)
Definition endian.h:81
asciichat_error_t platform_event_loop_destroy(platform_event_loop_t *loop)
Definition event.c:268
void platform_event_stream_close(platform_event_stream_t *stream, asciichat_error_t reason)
Definition event.c:120
void platform_event_stream_request_close(platform_event_stream_t *stream, asciichat_error_t reason)
Definition event.c:625
asciichat_error_t platform_event_stream_write(platform_event_stream_t *stream, const socket_buffer_t *buffers, size_t count, platform_event_write_fn done, void *arg)
Definition event.c:637
asciichat_error_t platform_event_stream_adopt(platform_event_loop_t *loop, socket_t *socket, platform_event_stream_t **out)
Definition event.c:410
asciichat_error_t platform_event_stream_start(platform_event_stream_t *stream, platform_event_read_fn read, platform_event_close_fn close, void *arg)
Definition event.c:326
void platform_event_loop_stop(platform_event_loop_t *loop)
Definition event.c:257
asciichat_error_t platform_event_loop_run(platform_event_loop_t *loop)
Definition event.c:234
asciichat_error_t platform_event_loop_create(platform_event_loop_t **out)
Definition event.c:205
void platform_event_stream_observe_queue(platform_event_stream_t *stream, platform_event_queue_fn observer, void *context)
Definition event.c:712
asciichat_error_t platform_event_loop_tick(platform_event_loop_t *loop, uint64_t interval, platform_event_task_fn fn, void *arg)
Definition event.c:296
asciichat_error_t platform_event_stream_deadline(platform_event_stream_t *stream, uint64_t timeout_ms)
Definition event.c:357
struct platform_event_loop platform_event_loop_t
Definition event.h:11
struct platform_event_stream platform_event_stream_t
Definition event.h:12
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
unsigned long long uint64_t
Definition common.h:63
unsigned char uint8_t
Definition common.h:60
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ERROR_NETWORK_INCOMPLETE
Definition error_codes.h:90
@ ERROR_NETWORK
Definition error_codes.h:72
@ ERROR_NETWORK_PROTOCOL
Definition error_codes.h:76
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_CRYPTO
Definition error_codes.h:93
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:75
#define NS_PER_SEC_INT
Definition time.h:169
#define NS_PER_MS_INT
Definition time.h:168
asciichat_error_t packet_decrypt_envelope(packet_envelope_t *envelope, void *crypto_ctx)
Send a clear console packet.
Definition packet.c:665
#define PACKET_MAGIC
Packet magic number (alias for MAGIC_PACKET_VALID).
Definition packet.h:270
@ PACKET_TYPE_ENCRYPTED
Encrypted packet (after handshake completion).
Definition packet.h:355
ringbuffer_t * ringbuffer_create(size_t element_size, size_t capacity)
Create a new ring buffer.
Definition ringbuffer.c:34
void ringbuffer_destroy(ringbuffer_t *rb)
Destroy a ring buffer and free its memory.
Definition ringbuffer.c:61
bool ringbuffer_read(ringbuffer_t *rb, void *data)
Try to read an element from the ring buffer (non-blocking).
Definition ringbuffer.c:93
bool ringbuffer_write(ringbuffer_t *rb, const void *data)
Try to write an element to the ring buffer (non-blocking).
Definition ringbuffer.c:71
struct acip_transport acip_transport_t
Forward declaration of ACIP transport.
Definition host.h:506
#define asciichat_crc32(data, len)
Main CRC32 dispatcher macro - use this in application code.
Definition crc32.h:147
int socket_t
acip_transport_type_t
Transport method table (virtual function table).
@ ACIP_TRANSPORT_TCP
Raw TCP socket.
bool packet_decoder_pending(const packet_decoder_t *decoder)
void packet_decoder_reset(packet_decoder_t *decoder)
asciichat_error_t packet_decoder_feed(packet_decoder_t *decoder, const void *data, size_t length, packet_decoder_fn emit, void *arg)
✅ Safe Integer Arithmetic and Overflow Detection
asciichat_error_t acip_packet_plan(crypto_context_t *crypto, const socket_buffer_t *buffers, size_t count, acip_packet_plan_t *plan)
Definition prepare.c:9
asciichat_error_t acip_packet_encrypt(crypto_context_t *crypto, const acip_packet_plan_t *plan, buffer_domain_t *domain, void *ciphertext, size_t capacity, packet_header_t *header, size_t *length)
The useful part of acip packet plan is in buffers, count, plaintext_size, ciphertext_capacity: togeth...
Definition prepare.c:64
Lock-Free Ring Buffer and Frame Buffer Management.
stats_peer_t * stats_runtime_peer_open(const char *transport)
Definition runtime.c:152
size_t ciphertext_capacity
Definition prepare.h:20
buffer_domain_t * domain
Borrowed control reserve for handshake/error traffic; NULL otherwise.
Definition prepare.h:25
Transport method table (virtual function table).
void * impl_data
Transport-specific state.
stats_peer_t * stats_peer
Owned statistics handle, destroyed with transport.
const acip_transport_methods_t * methods
Method table (virtual functions).
crypto_context_t * crypto_ctx
Optional encryption context.
uint64_t receive_timeout_ns
Optional TCP receive timeout override; zero uses the normal polling timeout.
void * data
Definition async.c:20
bool pooled
Definition async.c:22
size_t size
Definition async.c:21
struct async_packet * next
Definition async.c:19
bool queued_receive
Definition async.c:36
bool thread_started
Definition async.c:35
atomic_t closed
Definition async.c:32
ringbuffer_t * receive_queue
Definition async.c:42
packet_decoder_t decoder
Definition async.c:41
async_packet_t * recycled
Definition async.c:45
atomic_t received_count
Definition async.c:49
async_packet_t * head
Definition async.c:44
buffer_workspace_t ciphertext
Definition async.c:50
platform_event_loop_t * loop
Definition async.c:33
size_t recycled_count
Definition async.c:47
acip_transport_t * destroy_on_close
Definition async.c:37
cond_t received
Definition async.c:38
ringbuffer_t * receive_free
Definition async.c:43
mutex_t mutex
Definition async.c:30
atomic_t interrupted
Definition async.c:53
bool receive_lock_held
Definition async.c:39
async_packet_t * tail
Definition async.c:44
async_packet_t * held
Definition async.c:46
platform_event_stream_t * stream
Definition async.c:29
bool packet_received
Definition async.c:52
cond_t writable
Definition async.c:40
mutex_t receive_mutex
Definition async.c:31
atomic_t queued_wire_bytes
Definition async.c:51
atomic_t received_bytes
Definition async.c:48
platform_thread_t thread
Definition async.c:34
Owns the temporary storage used by buffer workspace. The data pointers and size or cursor fields desc...
Definition buffer.h:102
Endpoint cryptographic state for handshake, packet protection, authentication, and rekeying.
When code needs to work with packet decoder, it keeps header_used, packet, used, size here instead of...
Packet envelope containing received packet data.
Definition packet.h:1185
Fixed prefix shared by every ACIP packet on the wire.
Definition packet.h:628
Audio ring buffer for real-time audio streaming.
Definition ringbuffer.h:90
void acip_transport_destroy(acip_transport_t *transport)
Destroy transport and free all resources.
⏱️ High-precision timing utilities using sokol_time.h and uthash