Branch :
/* kc3
* Copyright from 2022 to 2026 kmx.io <contact@kmx.io>
*
* Permission is hereby granted to use this software granted the above
* copyright notice and this permission paragraph are included in all
* copies and substantial portions of this software.
*
* THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
* PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
* AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
* THIS SOFTWARE.
*/
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include "../libkc3/assert.h"
#include "../libkc3/sym.h"
#include "../libkc3/tag.h"
#include "kqueue.h"
s64 kc3_kqueue (void)
{
s32 e;
s32 r;
if ((r = kqueue()) < 0) {
e = errno;
err_write_1("kc3_kqueue: kqueue: ");
err_puts(strerror(e));
assert(! "kc3_kqueue: kqueue");
}
return r;
}
s64 kc3_kqueue_add (s64 kqfd, s64 fd, s_tag *timeout, s_tag **udata)
{
s32 e;
struct kevent events[2];
s32 nevents = 1;
s32 r;
void *stored_udata;
memset(events, 0, sizeof(events));
stored_udata = udata ? *udata : NULL;
events[0].ident = fd;
events[0].filter = EVFILT_READ;
events[0].flags = EV_ADD | EV_ONESHOT;
events[0].data = SOMAXCONN;
events[0].udata = stored_udata;
if (timeout && timeout->type == TAG_TIME) {
s_time *t = &timeout->data.time;
s64 timeout_ms = t->tv_sec * 1000 + t->tv_nsec / 1000000;
events[1].ident = fd;
events[1].filter = EVFILT_TIMER;
events[1].flags = EV_ADD | EV_ONESHOT;
events[1].data = timeout_ms;
events[1].udata = stored_udata;
nevents = 2;
}
if ((r = kevent(kqfd, events, nevents, NULL, 0, NULL)) < 0) {
e = errno;
err_write_1("kc3_kqueue_add: kevent: ");
err_puts(strerror(e));
assert(! "kc3_kqueue_add: kevent");
}
return r;
}
s64 kc3_kqueue_delete (s64 kqfd, s64 fd, s_tag *filter)
{
struct kevent event = {0};
event.ident = fd;
event.flags = EV_DELETE;
if (filter && filter->type == TAG_PSYM) {
if (filter->data.psym == &g_sym_read)
event.filter = EVFILT_READ;
else if (filter->data.psym == &g_sym_timer)
event.filter = EVFILT_TIMER;
else {
err_write_1("kc3_kqueue_delete: invalid filter: ");
err_inspect_sym(filter->data.psym);
err_write_1("\n");
return -1;
}
}
else {
err_puts("kc3_kqueue_delete: filter must be a symbol");
return -1;
}
return kevent(kqfd, &event, 1, NULL, 0, NULL);
}
s_tag * kc3_kqueue_poll (s64 kqfd, s_tag *timeout, s_tag *dest)
{
s32 e;
struct kevent event = {0};
const s_sym *event_type;
s_timespec *p = NULL;
s32 r;
s_timespec timespec = {0};
s_tag *udata;
if (timeout && timeout->type != TAG_VOID) {
switch (timeout->type) {
case TAG_TIME:
timespec.tv_sec = timeout->data.time.tv_sec;
timespec.tv_nsec = timeout->data.time.tv_nsec;
p = ×pec;
break;
default:
err_puts("kc3_kqueue_poll: invalid timeout");
assert(! "kc3_kqueue_poll: invalid timeout");
return NULL;
}
}
if ((r = kevent(kqfd, NULL, 0, &event, 1, p)) < 0) {
e = errno;
err_write_1("kc3_kqueue: kevent: ");
err_puts(strerror(e));
assert(! "kc3_kqueue_poll: kevent");
return tag_init_void(dest);
}
if (r > 0) {
udata = event.udata;
if (false) {
err_write_1("kc3_kqueue_poll: udata=");
err_inspect_uw_hexadecimal((uw) udata);
if (udata) {
err_write_1(" type=");
err_inspect_u8((u8) udata->type);
}
err_write_1("\n");
}
if (event.filter == EVFILT_TIMER)
event_type = &g_sym_timer;
else if (event.flags & EV_EOF)
event_type = &g_sym_eof;
else
event_type = &g_sym_read;
if (! tag_init_ptuple(dest, 3))
return NULL;
if (! tag_init_s64(dest->data.ptuple->tag, event.ident) ||
! tag_init_psym(dest->data.ptuple->tag + 1, event_type)) {
tag_clean(dest);
return NULL;
}
if (udata) {
if (! tag_init_copy(dest->data.ptuple->tag + 2, udata)) {
tag_clean(dest);
return NULL;
}
}
else
tag_init_void(dest->data.ptuple->tag + 2);
return dest;
}
return tag_init_void(dest);
}