ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
event.c
Go to the documentation of this file.
2#include <ascii-chat/common.h>
3#include <uv.h>
4#include <string.h>
5#include <ascii-chat/buffer.h>
6
7#define EVENT_COMMAND_LIMIT 1024
8#define EVENT_WRITE_LIMIT (16U * 1024U * 1024U)
9#define EVENT_WRITE_COUNT_LIMIT 4096
10#define EVENT_READ_SIZE 65536
11
12typedef struct {
14 void *arg;
18
34
70
82
95
96static asciichat_error_t event_error(int status, const char *operation) {
97 return SET_ERRNO(ERROR_NETWORK, "%s: %s", operation, uv_strerror(status));
98}
99
100static void flush_output(uv_async_t *output);
101static void write_complete(uv_write_t *request, int status);
102
103static void stream_closed(uv_handle_t *handle) {
104 platform_event_stream_t *stream = handle->data;
105 if (--stream->handles)
106 return;
107 if (stream->close)
109 while (stream->recycled) {
110 event_write_t *write = stream->recycled;
111 stream->recycled = write->next;
112 socket_send_buffer_free(&write->storage);
113 SAFE_FREE(write);
114 }
115 uv_mutex_destroy(&stream->mutex);
116 --stream->owner->streams;
118}
119
121 if (!stream)
122 return;
123 uv_mutex_lock(&stream->mutex);
124 if (stream->closing) {
125 uv_mutex_unlock(&stream->mutex);
126 return;
127 }
128 stream->closing = true;
129 event_write_t *pending = stream->first;
130 stream->first = stream->last = NULL;
131 uv_mutex_unlock(&stream->mutex);
132 while (pending) {
133 event_write_t *next = pending->next;
134 uv_mutex_lock(&stream->mutex);
135 stream->queued -= pending->size;
138 --stream->writes;
139 uv_mutex_unlock(&stream->mutex);
140 if (pending->done)
141 pending->done(pending->arg, ERROR_NETWORK);
142 socket_send_buffer_free(&pending->storage);
143 SAFE_FREE(pending);
144 pending = next;
145 }
146 stream->reason = reason;
147 // Submitted uv_write requests complete during TCP close. A raw zerocopy
148 // submission has no libuv request; discard its mapping without recycling it.
150 write_complete(&stream->active->request, UV_ECANCELED);
151 uv_timer_stop(&stream->output_timer);
152 uv_close((uv_handle_t *)&stream->output_timer, stream_closed);
153 uv_read_stop((uv_stream_t *)&stream->tcp);
154 uv_timer_stop(&stream->timer);
155 uv_close((uv_handle_t *)&stream->timer, stream_closed);
156 uv_close((uv_handle_t *)&stream->tcp, stream_closed);
157 uv_close((uv_handle_t *)&stream->output, stream_closed);
158}
159
160static void listener_closed(uv_handle_t *handle) {
161 platform_event_listener_t *listener = handle->data;
162 SAFE_FREE(listener);
163}
164
166 if (listener && !uv_is_closing((uv_handle_t *)&listener->tcp))
167 uv_close((uv_handle_t *)&listener->tcp, listener_closed);
168}
169
170static void stop_handle(uv_handle_t *handle, void *arg) {
171 platform_event_loop_t *loop = arg;
172 if (uv_is_closing(handle))
173 return;
174 if (handle->type == UV_TCP) {
175 if (*(bool *)handle->data)
176 platform_event_listener_close(handle->data);
177 else
179 } else if (handle == (uv_handle_t *)&loop->wake || handle == (uv_handle_t *)&loop->tick) {
180 uv_close(handle, NULL);
181 }
182}
183
184static void wake_loop(uv_async_t *wake) {
185 platform_event_loop_t *loop = wake->data;
186 // Bound each batch so a producer cannot starve socket events and deadlines.
187 for (size_t i = 0; i < EVENT_COMMAND_LIMIT; ++i) {
188 uv_mutex_lock(&loop->mutex);
189 if (!loop->count) {
190 bool stopping = loop->stopping;
191 uv_mutex_unlock(&loop->mutex);
192 if (stopping)
193 uv_walk(&loop->uv, stop_handle, loop);
194 return;
195 }
196 event_command_t command = loop->commands[loop->head];
197 loop->head = (loop->head + 1) % EVENT_COMMAND_LIMIT;
198 --loop->count;
199 uv_mutex_unlock(&loop->mutex);
200 command.fn(command.arg);
201 }
202 uv_async_send(wake);
203}
204
206 if (!out)
207 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing event loop output");
208 *out = NULL;
209 platform_event_loop_t *loop = SAFE_CALLOC(1, sizeof(*loop), platform_event_loop_t *);
210 int status = uv_loop_init(&loop->uv);
211 if (status < 0) {
212 SAFE_FREE(loop);
213 return event_error(status, "Initialize event loop");
214 }
215 status = uv_mutex_init(&loop->mutex);
216 if (status < 0) {
217 uv_loop_close(&loop->uv);
218 SAFE_FREE(loop);
219 return event_error(status, "Initialize event queue");
220 }
221 status = uv_async_init(&loop->uv, &loop->wake, wake_loop);
222 if (status < 0) {
223 uv_mutex_destroy(&loop->mutex);
224 uv_loop_close(&loop->uv);
225 SAFE_FREE(loop);
226 return event_error(status, "Initialize event wakeup");
227 }
228 loop->wake.data = loop;
229 loop->uv.data = loop;
230 *out = loop;
231 return ASCIICHAT_OK;
232}
233
235 if (!loop)
236 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing event loop");
237 uv_run(&loop->uv, UV_RUN_DEFAULT);
238 return ASCIICHAT_OK;
239}
240
242 if (!loop || !fn)
243 return SET_ERRNO(ERROR_INVALID_PARAM, "Missing event command");
244 uv_mutex_lock(&loop->mutex);
245 if (loop->stopping || loop->count == EVENT_COMMAND_LIMIT) {
246 uv_mutex_unlock(&loop->mutex);
247 return SET_ERRNO(ERROR_INVALID_STATE, "Event queue stopped or full");
248 }
249 loop->commands[(loop->head + loop->count) % EVENT_COMMAND_LIMIT] = (event_command_t){fn, arg};
250 ++loop->count;
251 // Keep the wake handle alive until this send has finished.
252 uv_async_send(&loop->wake);
253 uv_mutex_unlock(&loop->mutex);
254 return ASCIICHAT_OK;
255}
256
258 if (!loop)
259 return;
260 uv_mutex_lock(&loop->mutex);
261 if (!loop->stopping) {
262 loop->stopping = true;
263 uv_async_send(&loop->wake);
264 }
265 uv_mutex_unlock(&loop->mutex);
266}
267
269 if (!loop)
270 return ASCIICHAT_OK;
271 int status = uv_loop_close(&loop->uv);
272 if (status < 0)
273 return event_error(status, "Destroy event loop (stop and join first)");
274 uv_mutex_destroy(&loop->mutex);
275 SAFE_FREE(loop);
276 return ASCIICHAT_OK;
277}
278
279const char *platform_event_backend(void) {
280#ifdef _WIN32
281 return "iocp";
282#elif defined(__linux__)
283 return "epoll";
284#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
285 return "kqueue";
286#else
287 return "libuv";
288#endif
289}
290
291static void tick_loop(uv_timer_t *timer) {
292 platform_event_loop_t *loop = timer->data;
293 loop->tick_fn(loop->tick_arg);
294}
295
297 void *arg) {
298 if (!loop || !interval || !fn || loop->tick_initialized)
299 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid event loop tick");
300 int status = uv_timer_init(&loop->uv, &loop->tick);
301 if (status < 0)
302 return event_error(status, "Initialize loop timer");
303 loop->tick_initialized = true;
304 loop->tick.data = loop;
305 loop->tick_fn = fn;
306 loop->tick_arg = arg;
307 status = uv_timer_start(&loop->tick, tick_loop, interval, interval);
308 return status < 0 ? event_error(status, "Start loop timer") : ASCIICHAT_OK;
309}
310
311static void allocate_read(uv_handle_t *handle, size_t suggested, uv_buf_t *buffer) {
312 (void)suggested;
313 platform_event_stream_t *stream = handle->data;
314 *buffer = uv_buf_init(stream->input, sizeof(stream->input));
315}
316
317static void read_stream(uv_stream_t *handle, ssize_t count, const uv_buf_t *buffer) {
318 platform_event_stream_t *stream = handle->data;
319 if (count < 0) {
321 } else if (count && !stream->closing && stream->read) {
322 stream->read(stream, buffer->base, (size_t)count, stream->arg);
323 }
324}
325
327 platform_event_close_fn close, void *arg) {
328 if (!stream || stream->closing || !read)
329 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid stream callbacks");
330 // Retain cleanup callbacks even when configuring or starting reads fails.
331 stream->read = read;
332 stream->close = close;
333 stream->arg = arg;
334 int send_buffer = SOCKET_STREAM_SEND_BUFFER;
335 int status = uv_send_buffer_size((uv_handle_t *)&stream->tcp, &send_buffer);
336 if (status < 0)
337 return event_error(status, "Bound stream send buffer");
339}
340
342 if (stream && !stream->closing)
343 uv_read_stop((uv_stream_t *)&stream->tcp);
344}
345
347 if (!stream || stream->closing || !stream->read)
348 return SET_ERRNO(ERROR_INVALID_STATE, "Stream cannot resume");
349 int status = uv_read_start((uv_stream_t *)&stream->tcp, allocate_read, read_stream);
350 return status < 0 ? event_error(status, "Start stream read") : ASCIICHAT_OK;
351}
352
353static void deadline_expired(uv_timer_t *timer) {
355}
356
358 if (!stream || stream->closing)
359 return SET_ERRNO(ERROR_INVALID_STATE, "Stream is closing");
360 uv_timer_stop(&stream->timer);
361 int status = timeout_ms ? uv_timer_start(&stream->timer, deadline_expired, timeout_ms, 0) : 0;
362 return status < 0 ? event_error(status, "Set stream deadline") : ASCIICHAT_OK;
363}
364
365static platform_event_stream_t *create_stream(uv_loop_t *loop) {
367 int status = uv_mutex_init(&stream->mutex);
368 if (status < 0) {
370 return NULL;
371 }
372 status = uv_tcp_init(loop, &stream->tcp);
373 if (status < 0) {
374 uv_mutex_destroy(&stream->mutex);
376 return NULL;
377 }
378 stream->tcp.data = stream;
379 stream->owner = loop->data;
380 stream->zerocopy = true;
381 ++stream->owner->streams;
382 stream->handles = 1;
383 status = uv_timer_init(loop, &stream->timer);
384 if (status < 0) {
385 uv_close((uv_handle_t *)&stream->tcp, stream_closed);
386 return NULL;
387 }
388 stream->timer.data = stream;
389 ++stream->handles;
390 status = uv_timer_init(loop, &stream->output_timer);
391 if (status < 0) {
392 uv_close((uv_handle_t *)&stream->timer, stream_closed);
393 uv_close((uv_handle_t *)&stream->tcp, stream_closed);
394 return NULL;
395 }
396 stream->output_timer.data = stream;
397 ++stream->handles;
398 status = uv_async_init(loop, &stream->output, flush_output);
399 if (status < 0) {
400 uv_close((uv_handle_t *)&stream->output_timer, stream_closed);
401 uv_close((uv_handle_t *)&stream->timer, stream_closed);
402 uv_close((uv_handle_t *)&stream->tcp, stream_closed);
403 return NULL;
404 }
405 stream->output.data = stream;
406 ++stream->handles;
407 return stream;
408}
409
412 if (!loop || !socket || !out)
413 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid socket adoption");
414 *out = NULL;
415 platform_event_stream_t *stream = create_stream(&loop->uv);
416 if (!stream)
417 return SET_ERRNO(ERROR_MEMORY, "Cannot initialize event stream");
418 int status = uv_tcp_open(&stream->tcp, (uv_os_sock_t)*socket);
419 if (status < 0) {
421 return event_error(status, "Adopt TCP stream");
422 }
423 *socket = INVALID_SOCKET_VALUE;
424 uv_tcp_nodelay(&stream->tcp, 1);
425 uv_tcp_keepalive(&stream->tcp, 1, 60);
426 *out = stream;
427 return ASCIICHAT_OK;
428}
429
430static void accepted(uv_stream_t *handle, int status) {
431 if (status < 0)
432 return;
433 platform_event_listener_t *listener = handle->data;
434 platform_event_stream_t *stream = create_stream(handle->loop);
435 if (!stream)
436 return;
437 status = uv_accept(handle, (uv_stream_t *)&stream->tcp);
438 if (status < 0) {
440 return;
441 }
442 uv_tcp_nodelay(&stream->tcp, 1);
443 uv_tcp_keepalive(&stream->tcp, 1, 60);
444 if (stream->owner->streams > 512) {
446 return;
447 }
448 listener->accept(stream, listener->arg);
449}
450
452 void *arg, platform_event_listener_t **out) {
453 if (!loop || !socket || !accept || !out)
454 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid event listener");
455 *out = NULL;
456 platform_event_listener_t *listener = SAFE_CALLOC(1, sizeof(*listener), platform_event_listener_t *);
457 int status = uv_tcp_init(&loop->uv, &listener->tcp);
458 if (status < 0) {
459 SAFE_FREE(listener);
460 return event_error(status, "Initialize listener");
461 }
462 listener->tcp.data = listener;
463 listener->listener = true;
464 listener->accept = accept;
465 listener->arg = arg;
466 status = uv_tcp_open(&listener->tcp, (uv_os_sock_t)*socket);
467 if (status < 0) {
468 uv_close((uv_handle_t *)&listener->tcp, listener_closed);
469 return event_error(status, "Adopt listener");
470 }
471 *socket = INVALID_SOCKET_VALUE;
472 status = uv_listen((uv_stream_t *)&listener->tcp, 128, accepted);
473 if (status < 0) {
474 // Adoption has succeeded: the loop owns the descriptor even if listen fails.
475 uv_close((uv_handle_t *)&listener->tcp, listener_closed);
476 return event_error(status, "Listen");
477 }
478 *out = listener;
479 return ASCIICHAT_OK;
480}
481
482static void write_complete(uv_write_t *request, int status) {
483 event_write_t *write = request->data;
486 void *context = write->arg;
487 uv_timer_stop(&stream->output_timer);
488 stream->active = NULL;
489 uv_mutex_lock(&stream->mutex);
490 stream->queued -= write->size;
493 --stream->writes;
494 bool recycle =
495 !stream->closing && stream->recycled_count < 8 && !write->storage.mapped && write->storage.capacity <= 256 * 1024;
496 if (recycle) {
497 write->next = stream->recycled;
498 stream->recycled = write;
500 }
501 uv_mutex_unlock(&stream->mutex);
502 if (done)
503 done(context, status < 0 ? ERROR_NETWORK : ASCIICHAT_OK);
504 if (!recycle) {
505 socket_send_buffer_free(&write->storage);
506 SAFE_FREE(write);
507 }
508 if (status < 0)
510 else if (!stream->closing)
511 uv_async_send(&stream->output);
512}
513
514static socket_t stream_socket(platform_event_stream_t *stream) {
515 uv_os_fd_t descriptor;
516 if (uv_fileno((uv_handle_t *)&stream->tcp, &descriptor) < 0)
517 return INVALID_SOCKET_VALUE;
518 return (socket_t)(uintptr_t)descriptor;
519}
520
521static void submit_output(platform_event_stream_t *stream) {
522 event_write_t *write = stream->active;
523 uv_mutex_lock(&stream->mutex);
524 bool zerocopy = stream->zerocopy;
525 uv_mutex_unlock(&stream->mutex);
526 if (zerocopy && write->storage.mapped && write->size - write->offset >= SOCKET_ZEROCOPY_THRESHOLD) {
527 size_t sent = 0;
528 bool supported;
529 asciichat_error_t result = socket_zerocopy_try(stream_socket(stream), &write->storage, write->offset,
530 write->size - write->offset, &sent, &supported);
531 if (result != ASCIICHAT_OK) {
532 write_complete(&write->request, UV_EIO);
533 return;
534 }
535 if (!supported) {
536 uv_mutex_lock(&stream->mutex);
537 stream->zerocopy = false;
538 uv_mutex_unlock(&stream->mutex);
539 }
540 if (sent) {
541 write->offset += sent;
542 write->waiting = true;
543 return;
544 }
545 }
546 // No kernel-pinned submission is outstanding here. libuv handles readiness,
547 // partial writes, and completion on every platform for the remaining bytes.
548 uv_buf_t buffer =
549 uv_buf_init((char *)write->storage.data + write->offset, (unsigned int)(write->size - write->offset));
550 int status = uv_write(&write->request, (uv_stream_t *)&stream->tcp, &buffer, 1, write_complete);
551 if (status < 0)
552 write_complete(&write->request, status);
553}
554
555static void output_progress(uv_timer_t *timer) {
556 platform_event_stream_t *stream = timer->data;
557 event_write_t *write = stream->active;
558 if (!write || stream->closing)
559 return;
560 if (uv_now(stream->tcp.loop) - write->started >= 30000) {
562 return;
563 }
564 if (!write->waiting)
565 return;
566 bool completed, copied;
567 asciichat_error_t result = socket_zerocopy_poll(stream_socket(stream), &completed, &copied);
568 if (result != ASCIICHAT_OK) {
570 return;
571 }
572 if (!completed)
573 return;
574 write->waiting = false;
575 if (copied) {
576 uv_mutex_lock(&stream->mutex);
577 stream->zerocopy = false;
578 uv_mutex_unlock(&stream->mutex);
579 }
580 if (write->offset == write->size)
581 write_complete(&write->request, 0);
582 else
583 submit_output(stream);
584}
585
586static void flush_output(uv_async_t *output) {
587 platform_event_stream_t *stream = output->data;
588 uv_mutex_lock(&stream->mutex);
591 uv_mutex_unlock(&stream->mutex);
593 return;
594 }
595 if (stream->closing || stream->active || !stream->first) {
596 uv_mutex_unlock(&stream->mutex);
597 return;
598 }
599 event_write_t *write = stream->first;
600 stream->first = write->next;
601 if (!stream->first)
602 stream->last = NULL;
603 stream->active = write;
604 uv_mutex_unlock(&stream->mutex);
605 write->started = uv_now(stream->tcp.loop);
606 // Only copy-avoidance submissions need completion probing. Ordinary writes
607 // use this timer solely for their deadline.
608 uint64_t interval = write->storage.mapped ? 5 : 30000;
609 int status = uv_timer_start(&stream->output_timer, output_progress, interval, interval);
610 if (status < 0) {
611 write_complete(&write->request, status);
612 return;
613 }
614 submit_output(stream);
615}
616
618 if (!stream)
619 return;
620 uv_mutex_lock(&stream->mutex);
622 uv_mutex_unlock(&stream->mutex);
623}
624
626 if (!stream)
627 return;
628 uv_mutex_lock(&stream->mutex);
630 stream->close_requested = true;
631 stream->requested_reason = reason;
632 uv_async_send(&stream->output);
633 }
634 uv_mutex_unlock(&stream->mutex);
635}
636
638 size_t count, platform_event_write_fn done, void *arg) {
639 if (!stream || !buffers || !count || count > SOCKET_IOV_MAX)
640 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid asynchronous write");
641 size_t size = 0;
642 for (size_t i = 0; i < count; ++i) {
643 if ((!buffers[i].data && buffers[i].len) || buffers[i].len > EVENT_WRITE_LIMIT - size)
644 return SET_ERRNO(ERROR_NETWORK_SIZE, "Asynchronous write too large");
645 size += buffers[i].len;
646 }
647 uv_mutex_lock(&stream->mutex);
650 uv_mutex_unlock(&stream->mutex);
651 return SET_ERRNO(ERROR_NETWORK_SIZE, "Asynchronous output queue full");
652 }
653 bool mapped = stream->zerocopy && size >= SOCKET_ZEROCOPY_THRESHOLD;
654 event_write_t **cached = &stream->recycled;
655 while (*cached && (mapped || (*cached)->storage.capacity < size))
656 cached = &(*cached)->next;
657 event_write_t *write = *cached;
658 socket_send_buffer_t storage = {0};
659 if (write) {
660 *cached = write->next;
662 storage = write->storage;
663 } else {
664 write = SAFE_MALLOC(sizeof(*write), event_write_t *);
665 }
666 *write = (event_write_t){.stream = stream, .done = done, .arg = arg, .size = size, .storage = storage};
667 if (!write->storage.data && mapped) {
668 if (socket_send_buffer_alloc(size, &write->storage) != ASCIICHAT_OK) {
669 SAFE_FREE(write);
670 uv_mutex_unlock(&stream->mutex);
671 return SET_ERRNO(ERROR_MEMORY, "Async copy avoidance exceeds process buffer budget");
672 }
673 } else if (!write->storage.data) {
674 write->storage.data = buffer_alloc(NULL, size);
675 write->storage.capacity = size;
676 }
677 if (!write->storage.data) {
678 SAFE_FREE(write);
679 uv_mutex_unlock(&stream->mutex);
680 return SET_ERRNO(ERROR_MEMORY, "Async output exceeds process buffer budget");
681 }
682 write->request.data = write;
683 size_t offset = 0;
684 for (size_t i = 0; i < count; ++i) {
685 if (buffers[i].len)
686 memcpy((char *)write->storage.data + offset, buffers[i].data, buffers[i].len);
687 offset += buffers[i].len;
688 }
689 stream->queued += size;
692 ++stream->writes;
693 if (stream->last)
694 stream->last->next = write;
695 else
696 stream->first = write;
697 stream->last = write;
698 uv_async_send(&stream->output);
699 uv_mutex_unlock(&stream->mutex);
700 return ASCIICHAT_OK;
701}
702
704 if (!stream)
705 return 0;
706 uv_mutex_lock((uv_mutex_t *)&stream->mutex);
707 size_t bytes = stream->queued;
708 uv_mutex_unlock((uv_mutex_t *)&stream->mutex);
709 return bytes;
710}
711
713 void *context) {
714 if (!stream)
715 return;
716 uv_mutex_lock(&stream->mutex);
717 stream->queue_changed = observer;
718 stream->queue_context = context;
719 if (observer)
720 observer(context, stream->queued);
721 uv_mutex_unlock(&stream->mutex);
722}
723
741
747typedef struct {
748 uv_work_t request;
750 void *arg;
757
758static void loop_work_run(uv_work_t *request) {
759 loop_work_t *work = request->data;
760 work->work(work->arg);
761}
762static void loop_work_done(uv_work_t *request, int status) {
763 (void)status;
764 loop_work_t *work = request->data;
765 work->done(work->arg);
766 SAFE_FREE(work);
767}
769 platform_event_task_fn done, void *arg) {
770 if (!loop || !run || !done)
771 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid event worker");
772 loop_work_t *work = SAFE_CALLOC(1, sizeof(*work), loop_work_t *);
773 work->request.data = work;
774 work->work = run;
775 work->done = done;
776 work->arg = arg;
777 int status = uv_queue_work(&loop->uv, &work->request, loop_work_run, loop_work_done);
778 if (status < 0) {
779 SAFE_FREE(work);
780 return event_error(status, "Queue loop work");
781 }
782 return ASCIICHAT_OK;
783}
784
785static void run_work(uv_work_t *request) {
786 event_work_t *work = request->data;
787 work->run(work->arg);
788}
789
790static void finish_work(uv_work_t *request, int status) {
791 (void)status;
792 event_work_t *work = request->data;
793 platform_event_stream_t *stream = work->stream;
794 stream->working = false;
795 work->done(stream, work->arg, stream->closing);
796 SAFE_FREE(work);
797 // The extra reference keeps application state alive through worker completion.
798 stream_closed((uv_handle_t *)&stream->tcp);
799}
800
802 platform_event_work_done_fn done, void *arg) {
803 if (!stream || stream->closing || stream->working || !run || !done)
804 return SET_ERRNO(ERROR_INVALID_STATE, "Stream work already active or closing");
805 event_work_t *work = SAFE_CALLOC(1, sizeof(*work), event_work_t *);
806 work->stream = stream;
807 work->run = run;
808 work->done = done;
809 work->arg = arg;
810 work->work.data = work;
812 int status = uv_queue_work(stream->tcp.loop, &work->work, run_work, finish_work);
813 if (status < 0) {
814 SAFE_FREE(work);
815 return event_error(status, "Queue stream work");
816 }
817 stream->working = true;
818 ++stream->handles;
819 return ASCIICHAT_OK;
820}
821
823 socklen_t *length) {
824 if (!stream || !address || !length)
825 return SET_ERRNO(ERROR_INVALID_PARAM, "Invalid peer address output");
826 int size = (int)*length;
827 int status = uv_tcp_getpeername(&stream->tcp, (struct sockaddr *)address, &size);
828 *length = (socklen_t)size;
829 return status < 0 ? event_error(status, "Get stream peer") : ASCIICHAT_OK;
830}
#define SET_ERRNO(code,...)
void * buffer_alloc(buffer_domain_t *domain, size_t size)
Definition buffer.c:274
⚙️ Common definitions, error codes, macros, and types shared throughout the application
asciichat_error_t platform_event_stream_resume(platform_event_stream_t *stream)
Definition event.c:346
size_t platform_event_stream_queued_bytes(const platform_event_stream_t *stream)
Definition event.c:703
asciichat_error_t platform_event_loop_destroy(platform_event_loop_t *loop)
Definition event.c:268
asciichat_error_t platform_event_loop_post(platform_event_loop_t *loop, platform_event_task_fn fn, void *arg)
Definition event.c:241
struct event_write event_write_t
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
#define EVENT_COMMAND_LIMIT
Definition event.c:7
asciichat_error_t platform_event_stream_peer(platform_event_stream_t *stream, struct sockaddr_storage *address, socklen_t *length)
Definition event.c:822
asciichat_error_t platform_event_listen(platform_event_loop_t *loop, socket_t *socket, platform_event_accept_fn accept, void *arg, platform_event_listener_t **out)
Definition event.c:451
asciichat_error_t platform_event_stream_adopt(platform_event_loop_t *loop, socket_t *socket, platform_event_stream_t **out)
Definition event.c:410
#define EVENT_READ_SIZE
Definition event.c:10
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_stream_set_zerocopy(platform_event_stream_t *stream, bool enabled)
Definition event.c:617
void platform_event_loop_stop(platform_event_loop_t *loop)
Definition event.c:257
#define EVENT_WRITE_LIMIT
Definition event.c:8
asciichat_error_t platform_event_stream_work(platform_event_stream_t *stream, platform_event_work_fn run, platform_event_work_done_fn done, void *arg)
Definition event.c:801
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
asciichat_error_t platform_event_loop_work(platform_event_loop_t *loop, platform_event_task_fn run, platform_event_task_fn done, void *arg)
Definition event.c:768
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
void platform_event_listener_close(platform_event_listener_t *listener)
Definition event.c:165
const char * platform_event_backend(void)
Definition event.c:279
void platform_event_stream_pause(platform_event_stream_t *stream)
Definition event.c:341
#define EVENT_WRITE_COUNT_LIMIT
Definition event.c:9
asciichat_error_t platform_event_stream_deadline(platform_event_stream_t *stream, uint64_t timeout_ms)
Definition event.c:357
struct platform_event_listener platform_event_listener_t
Definition event.h:13
struct platform_event_loop platform_event_loop_t
Definition event.h:11
void(* platform_event_work_fn)(void *)
Definition event.h:20
void(* platform_event_accept_fn)(platform_event_stream_t *, void *)
Definition event.h:19
void(* platform_event_close_fn)(void *, asciichat_error_t)
Definition event.h:16
struct platform_event_stream platform_event_stream_t
Definition event.h:12
void(* platform_event_work_done_fn)(platform_event_stream_t *, void *, bool closing)
Definition event.h:21
void(* platform_event_task_fn)(void *arg)
Definition event.h:14
void(* platform_event_queue_fn)(void *, size_t)
Definition event.h:18
void(* platform_event_write_fn)(void *, asciichat_error_t)
Definition event.h:17
void(* platform_event_read_fn)(platform_event_stream_t *, const void *, size_t, void *)
Definition event.h:15
bool enabled
Is filtering active?
Definition grep.c:62
#define SAFE_FREE(ptr)
Definition common.h:377
#define SAFE_MALLOC(size, cast)
Definition common.h:265
#define SAFE_CALLOC(count, size, cast)
Definition common.h:275
unsigned long long uint64_t
Definition common.h:63
asciichat_error_t
Error and exit codes - unified status values (0-255).
Definition error_codes.h:44
@ ERROR_INVALID_STATE
@ ERROR_NETWORK
Definition error_codes.h:72
@ ERROR_MEMORY
Definition error_codes.h:51
@ ASCIICHAT_OK
Definition error_codes.h:46
@ ERROR_NETWORK_SIZE
Definition error_codes.h:77
@ ERROR_INVALID_PARAM
@ ERROR_NETWORK_TIMEOUT
Definition error_codes.h:75
int socket_t
static bool size_t gotel_query_t * out
Definition queries.h:8
🔤 String Manipulation and Shell Escaping Utilities
void * arg
Definition event.c:14
platform_event_task_fn fn
Definition event.c:13
event command keeps the values that move through the subsystem: fn, arg. Some members are pointers in...
Definition event.c:729
platform_event_work_fn run
Definition event.c:732
platform_event_stream_t * stream
Definition event.c:731
void * arg
Definition event.c:734
uv_work_t work
Definition event.c:730
platform_event_work_done_fn done
Definition event.c:733
socket_send_buffer_t storage
Definition event.c:90
size_t size
Definition event.c:88
uv_write_t request
Definition event.c:84
size_t offset
Definition event.c:91
bool waiting
Definition event.c:93
platform_event_write_fn done
Definition event.c:86
void * arg
Definition event.c:87
uint64_t started
Definition event.c:92
struct event_write * next
Definition event.c:89
platform_event_stream_t * stream
Definition event.c:85
This is the handoff point for event work. Callers fill work, stream, run, done before passing it on; ...
Definition event.c:747
platform_event_task_fn done
Definition event.c:749
void * arg
Definition event.c:750
uv_work_t request
Definition event.c:748
platform_event_task_fn work
Definition event.c:749
platform event stream is intentionally a small, concrete record. Fields such as listener,...
Definition event.c:76
platform_event_accept_fn accept
Definition event.c:79
event_command_t commands[1024]
Definition event.c:30
uv_loop_t uv
Definition event.c:22
uv_mutex_t mutex
Definition event.c:29
uv_async_t wake
Definition event.c:23
platform_event_task_fn tick_fn
Definition event.c:25
void * tick_arg
Definition event.c:26
bool tick_initialized
Definition event.c:27
uv_timer_t tick
Definition event.c:24
The useful part of event write is in request, stream, done, arg: together they describe what is pendi...
Definition event.c:43
size_t recycled_count
Definition event.c:53
struct event_write * recycled
Definition event.c:52
struct event_write * active
Definition event.c:51
platform_event_queue_fn queue_changed
Definition event.c:59
asciichat_error_t reason
Definition event.c:67
char input[65536]
Definition event.c:68
void * queue_context
Definition event.c:60
struct event_write * first
Definition event.c:51
platform_event_close_fn close
Definition event.c:56
unsigned handles
Definition event.c:62
platform_event_loop_t * owner
Definition event.c:45
uv_timer_t output_timer
Definition event.c:48
uv_async_t output
Definition event.c:49
uv_mutex_t mutex
Definition event.c:50
platform_event_read_fn read
Definition event.c:55
asciichat_error_t requested_reason
Definition event.c:66
uv_timer_t timer
Definition event.c:47
struct event_write * last
Definition event.c:51