Complete kqueue feature

From this point things should be bug fixes or code formatting most
likely.

Updated commentary (removed superfluous comments, and commented out
code). Refined commentary where it seemed important and may help whoever
comes behind me keep from making bad assumptions similar to the ones I
made.

All tests ran with `gmake test` now pass. `valgrind` with FreeBSD does
not support forking so `gmake valtest` fails once child processes are
started. Determined not an issue, can't fix valgrind.
This commit is contained in:
llmII 2021-09-04 08:23:03 -05:00
parent b559f9625a
commit 8b67108dc8
No known key found for this signature in database
GPG Key ID: E3AD2E259F58A9A0
1 changed files with 19 additions and 38 deletions

View File

@ -1571,13 +1571,9 @@ void janet_ev_deinit(void) {
*/ */
#elif defined(JANET_EV_KQUEUE) #elif defined(JANET_EV_KQUEUE)
/*
* NOTE: need to touch up janet.h & state.h to have specifics for kqueue.
*/
/* TODO: make this available be we using kqueue or epoll, instead of /* TODO: make this available be we using kqueue or epoll, instead of
* redefinining it for kqueue and epoll separately. * redefinining it for kqueue and epoll separately? */
*/
static JanetTimestamp ts_now(void) { static JanetTimestamp ts_now(void) {
struct timespec now; struct timespec now;
janet_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &now), "failed to get time"); janet_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &now), "failed to get time");
@ -1587,39 +1583,30 @@ static JanetTimestamp ts_now(void) {
} }
void add_kqueue_events(const struct kevent *events, int length) { void add_kqueue_events(const struct kevent *events, int length) {
/* NOTE: status should equal the amount of events *added*. This number /* NOTE: Status should be equal to the amount of events added, which isn't
* isn't always known if deletions or modifications occur. We also can't * always known since deletions or modifications occur. Can't use the
* use an event list for it to report to us what failed otherwise we may * eventlist argument for it to report to us what failed otherwise we may
* poll in events to handle! So we'll pretend it atomic, can either * poll in events to handle! This code assumes atomicity, that kqueue can
* succeed, or fail, nothing inbetween, without violating constraints in * either succeed or fail, but never partially (which is seemingly how it
* which case we exit. * works in practice). When encountering an "inbetween" state we currently
* just panic!
* *
* The example code on the manual page shows a check against the event * The FreeBSD man page kqueue(2) shows a check through the change list to
* added itself for the error? Maybe we should do this? The description of * check if kqueue had an error with any of the events being pushed to
* the kevent call doesn't really say that that is where to check the * change. Maybe we should do this, even tho the man page also doesn't
* error... Trying it anyway... */ * note that kqueue actually does this. We do not do this at this time. */
int status; int status;
status = kevent(janet_vm.kq, events, length, NULL, 0, NULL); status = kevent(janet_vm.kq, events, length, NULL, 0, NULL);
if(status == -1 && errno != EINTR) if(status == -1 && errno != EINTR)
janet_panicv(janet_ev_lasterr()); janet_panicv(janet_ev_lasterr());
/* Disabling this for now, probably is wrong, haven't seen it done elsewhere
for(int i = 0; i < length; i++) {
if((events[i].flags & EV_ERROR) && events[i].data != EINTR) {
janet_panicv(janet_ev_lasterr());
}
}*/
} }
JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user) { JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user) {
JanetListenerState *state = janet_listen_impl(stream, behavior, mask, size, user); JanetListenerState *state = janet_listen_impl(stream, behavior, mask, size, user);
struct kevent kev[2]; struct kevent kev[2];
/* NOTE: NetBSD uses a different type for udata, might not work there or
* may warn! */
/* Disabled, may be incorrect
EV_SET(&kev[0], stream->handle, EVFILT_READ, EV_ADD | (state->stream->_mask & JANET_ASYNC_LISTEN_READ ? EV_ENABLE : EV_DISABLE), 0, 0, stream);
EV_SET(&kev[1], stream->handle, EVFILT_WRITE, EV_ADD | (state->stream->_mask & JANET_ASYNC_LISTEN_WRITE ? EV_ENABLE : EV_DISABLE), 0, 0, stream);
*/
/* NOTE: NetBSD uses a different type for udata, might not work there or
* may warn/fail to compile (see wahern/cqueues)! */
int length = 0; int length = 0;
if (state->stream->_mask & JANET_ASYNC_LISTEN_READ) { if (state->stream->_mask & JANET_ASYNC_LISTEN_READ) {
EV_SET(&kev[length], stream->handle, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, stream); EV_SET(&kev[length], stream->handle, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, stream);
@ -1640,16 +1627,12 @@ JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, in
static void janet_unlisten(JanetListenerState *state, int is_gc) { static void janet_unlisten(JanetListenerState *state, int is_gc) {
JanetStream *stream = state->stream; JanetStream *stream = state->stream;
if(!(stream->flags & JANET_STREAM_CLOSED)) { if(!(stream->flags & JANET_STREAM_CLOSED)) {
/* following epoll's example, might need to clarify this there, and /* Use flag to indicate state is not registered in kqueue */
* here as to what is actually happening? */
if(!(state->_mask & (1 << JANET_ASYNC_EVENT_COMPLETE))) { if(!(state->_mask & (1 << JANET_ASYNC_EVENT_COMPLETE))) {
int is_last = (state->_next == NULL && stream->state == state); int is_last = (state->_next == NULL && stream->state == state);
int op = is_last ? EV_DELETE : EV_DISABLE | EV_ADD; int op = is_last ? EV_DELETE : EV_DISABLE | EV_ADD;
struct kevent kev[2]; struct kevent kev[2];
/* Disabled, may be incorrect
EV_SET(&kev[0], stream->handle, EVFILT_READ, op, 0, 0, stream);
EV_SET(&kev[1], stream->handle, EVFILT_WRITE, op, 0, 0, stream); EV_SET(&kev[1], stream->handle, EVFILT_WRITE, op, 0, 0, stream);
*/
int length = 0; int length = 0;
if (stream->_mask & JANET_ASYNC_EVENT_WRITE) { if (stream->_mask & JANET_ASYNC_EVENT_WRITE) {
@ -1671,12 +1654,8 @@ static void janet_unlisten(JanetListenerState *state, int is_gc) {
#define JANET_KQUEUE_MAX_EVENTS 64 #define JANET_KQUEUE_MAX_EVENTS 64
void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) { void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
/* Push a timer kevent here, always use infinite timeout. Precision is in /* Construct our timer which is a definite time on the clock, not an
* milliseconds, should it be different? is timeout a timestamp? or a * interval, in milliseconds as that is `JanetTimestamp`'s precision. */
* timeout... we're treating it as a timeout */
/* NOTE: Probably be logic errors here! */
struct kevent timer; struct kevent timer;
if (janet_vm.timer_enabled || has_timeout) { if (janet_vm.timer_enabled || has_timeout) {
EV_SET(&timer, EV_SET(&timer,
@ -1688,6 +1667,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
} }
janet_vm.timer_enabled = has_timeout; janet_vm.timer_enabled = has_timeout;
/* Poll for events */
int status; int status;
struct kevent events[JANET_KQUEUE_MAX_EVENTS]; struct kevent events[JANET_KQUEUE_MAX_EVENTS];
do { do {
@ -1696,6 +1676,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
if(status == -1) if(status == -1)
JANET_EXIT("failed to poll events"); JANET_EXIT("failed to poll events");
/* Step state machines */
for(int i = 0; i < status; i++) { for(int i = 0; i < status; i++) {
void *p = events[i].udata; void *p = events[i].udata;
if(&janet_vm.timer == p) { if(&janet_vm.timer == p) {