1
0
mirror of https://github.com/janet-lang/janet synced 2024-12-23 06:50:26 +00:00

Simpler async model that is better suited to epoll

This commit is contained in:
Calvin Rose 2023-10-06 00:37:19 -05:00
parent 66292beec9
commit 0ff8f58be8
7 changed files with 300 additions and 351 deletions

View File

@ -56,6 +56,9 @@
#ifdef JANET_EV_KQUEUE #ifdef JANET_EV_KQUEUE
#include <sys/event.h> #include <sys/event.h>
#endif #endif
#ifdef JANET_EV_POLL
#include <poll.h>
#endif
#endif #endif
typedef struct { typedef struct {
@ -179,9 +182,6 @@ static int janet_q_pop(JanetQueue *q, void *out, size_t itemsize) {
return 0; return 0;
} }
/* Forward declaration */
static void janet_unlisten(JanetListenerState *state);
/* Get current timestamp (millisecond precision) */ /* Get current timestamp (millisecond precision) */
static JanetTimestamp ts_now(void); static JanetTimestamp ts_now(void);
@ -194,8 +194,7 @@ static JanetTimestamp ts_delta(JanetTimestamp ts, double delta) {
return ts; return ts;
} }
/* Look at the next timeout value without /* Look at the next timeout value without removing it. */
* removing it. */
static int peek_timeout(JanetTimeout *out) { static int peek_timeout(JanetTimeout *out) {
if (janet_vm.tq_count == 0) return 0; if (janet_vm.tq_count == 0) return 0;
*out = janet_vm.tq[0]; *out = janet_vm.tq[0];
@ -254,102 +253,48 @@ static void add_timeout(JanetTimeout to) {
} }
} }
static int janet_listener_gc(void *p, size_t s); void janet_async_end(JanetFiber *fiber) {
static int janet_listener_mark(void *p, size_t s); if (fiber->ev_callback) {
janet_gcunroot(janet_wrap_abstract(fiber->ev_stream));
static const JanetAbstractType janet_listener_AT = { fiber->ev_callback = NULL;
"core/ev-listener", if (fiber->ev_state) {
janet_listener_gc, janet_free(fiber->ev_state);
janet_listener_mark, fiber->ev_state = NULL;
JANET_ATEND_GCMARK }
}; janet_ev_dec_refcount();
/* Create a new event listener */
static JanetListenerState *janet_listen_impl(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user) {
if (stream->flags & JANET_STREAM_CLOSED) {
janet_panic("cannot listen on closed stream");
} }
if ((mask & JANET_ASYNC_LISTEN_READ) && stream->read_state) goto bad_listen_read; }
if ((mask & JANET_ASYNC_LISTEN_WRITE) && stream->write_state) goto bad_listen_write;
janet_assert(size >= sizeof(JanetListenerState), "bad size"); void *janet_async_start(JanetFiber *fiber, JanetStream *stream, JanetAsyncMode mode, JanetEVCallback callback, size_t data_size) {
JanetListenerState *state = janet_abstract(&janet_listener_AT, size); janet_async_end(fiber); /* Clear existing callback */
state->machine = behavior; if (mode & JANET_ASYNC_LISTEN_READ) stream->read_fiber = fiber;
state->fiber = janet_vm.root_fiber; if (mode & JANET_ASYNC_LISTEN_WRITE) stream->write_fiber = fiber;
state->flags = 0; fiber->ev_callback = callback;
janet_vm.root_fiber->waiting = state; fiber->ev_stream = stream;
if (mask & JANET_ASYNC_LISTEN_READ) stream->read_state = state;
if (mask & JANET_ASYNC_LISTEN_WRITE) stream->write_state = state;
state->stream = stream;
state->event = user;
state->machine(state, JANET_ASYNC_EVENT_INIT);
janet_ev_inc_refcount(); janet_ev_inc_refcount();
state->index = janet_vm.listeners->count; janet_gcroot(janet_wrap_abstract(stream));
janet_array_push(janet_vm.listeners, janet_wrap_abstract(state)); if (data_size) {
return state; void *data = janet_malloc(data_size);
bad_listen_write: fiber->ev_state = data;
janet_panic("cannot listen for duplicate write event on stream"); return data;
bad_listen_read: } else {
janet_panic("cannot listen for duplicate read event on stream"); return NULL;
}
} }
void janet_fiber_did_resume(JanetFiber *fiber) { void janet_fiber_did_resume(JanetFiber *fiber) {
if (fiber->waiting) { janet_async_end(fiber);
janet_unlisten(fiber->waiting);
fiber->waiting = NULL;
}
}
static void janet_unlisten_impl(JanetListenerState *state) {
/* Move last listener to position of this listener - O(1) removal and keep things densely packed. */
if (state->stream) {
Janet popped = janet_array_pop(janet_vm.listeners);
janet_assert(janet_checktype(popped, JANET_ABSTRACT), "pop check");
JanetListenerState *popped_state = (JanetListenerState *) janet_unwrap_abstract(popped);
janet_vm.listeners->data[state->index] = popped;
popped_state->index = state->index;
state->index = UINT32_MAX; /* just in case */
janet_ev_dec_refcount();
if (state->stream->read_state == state) {
state->stream->read_state = NULL;
}
if (state->stream->write_state == state) {
state->stream->write_state = NULL;
}
state->stream = NULL;
}
}
static int janet_listener_gc(void *p, size_t size) {
(void) size;
JanetListenerState *state = (JanetListenerState *)p;
if (state->stream) {
janet_ev_dec_refcount();
}
if (state->machine) {
state->machine(state, JANET_ASYNC_EVENT_DEINIT);
}
return 0;
}
static int janet_listener_mark(void *p, size_t size) {
(void) size;
JanetListenerState *state = (JanetListenerState *)p;
if (state->stream) {
janet_mark(janet_wrap_abstract(state->stream));
}
if (state->fiber) {
janet_mark(janet_wrap_fiber(state->fiber));
}
state->machine(state, JANET_ASYNC_EVENT_MARK);
return 0;
} }
static void janet_stream_checktoclose(JanetStream *stream) { static void janet_stream_checktoclose(JanetStream *stream) {
if ((stream->flags & JANET_STREAM_TOCLOSE) && !stream->read_state && !stream->write_state) { if ((stream->flags & JANET_STREAM_TOCLOSE) && !stream->read_fiber && !stream->write_fiber) {
janet_stream_close(stream); janet_stream_close(stream);
} }
} }
/* Forward declaration */
static void janet_register_stream(JanetStream *stream);
static const JanetMethod ev_default_stream_methods[] = { static const JanetMethod ev_default_stream_methods[] = {
{"close", janet_cfun_stream_close}, {"close", janet_cfun_stream_close},
{"read", janet_cfun_stream_read}, {"read", janet_cfun_stream_read},
@ -363,10 +308,12 @@ JanetStream *janet_stream(JanetHandle handle, uint32_t flags, const JanetMethod
JanetStream *stream = janet_abstract(&janet_stream_type, sizeof(JanetStream)); JanetStream *stream = janet_abstract(&janet_stream_type, sizeof(JanetStream));
stream->handle = handle; stream->handle = handle;
stream->flags = flags; stream->flags = flags;
stream->read_state = NULL; stream->read_fiber = NULL;
stream->write_state = NULL; stream->write_fiber = NULL;
if (methods == NULL) methods = ev_default_stream_methods; if (methods == NULL) methods = ev_default_stream_methods;
stream->methods = methods; stream->methods = methods;
stream->index = 0;
janet_register_stream(stream);
return stream; return stream;
} }
@ -388,18 +335,30 @@ static void janet_stream_close_impl(JanetStream *stream) {
if (stream->handle != -1) { if (stream->handle != -1) {
close(stream->handle); close(stream->handle);
stream->handle = -1; stream->handle = -1;
#ifdef JANET_EV_POLL
uint32_t i = stream->index;
size_t j = janet_vm.stream_count - 1;
JanetStream *last = janet_vm.streams[j];
struct pollfd lastfd = janet_vm.fds[j + 1];
janet_vm.fds[i + 1] = lastfd;
janet_vm.streams[i] = last;
last->index = stream->index;
janet_vm.stream_count--;
#endif
} }
#endif #endif
} }
void janet_stream_close(JanetStream *stream) { void janet_stream_close(JanetStream *stream) {
if (stream->read_state) { JanetFiber *rf = stream->read_fiber;
stream->read_state->machine(stream->read_state, JANET_ASYNC_EVENT_CLOSE); JanetFiber *wf = stream->write_fiber;
janet_unlisten(stream->read_state); if (rf && rf->ev_callback) {
rf->ev_callback(rf, JANET_ASYNC_EVENT_CLOSE);
stream->read_fiber = NULL;
} }
if (stream->write_state) { if (wf && wf->ev_callback) {
stream->write_state->machine(stream->write_state, JANET_ASYNC_EVENT_CLOSE); wf->ev_callback(wf, JANET_ASYNC_EVENT_CLOSE);
janet_unlisten(stream->write_state); stream->write_fiber = NULL;
} }
janet_stream_close_impl(stream); janet_stream_close_impl(stream);
} }
@ -416,11 +375,13 @@ static int janet_stream_gc(void *p, size_t s) {
static int janet_stream_mark(void *p, size_t s) { static int janet_stream_mark(void *p, size_t s) {
(void) s; (void) s;
JanetStream *stream = (JanetStream *) p; JanetStream *stream = (JanetStream *) p;
if (NULL != stream->read_state) { JanetFiber *rf = stream->read_fiber;
janet_mark(janet_wrap_abstract(stream->read_state)); JanetFiber *wf = stream->write_fiber;
if (rf) {
janet_mark(janet_wrap_fiber(rf));
} }
if (NULL != stream->write_state) { if (wf) {
janet_mark(janet_wrap_abstract(stream->write_state)); janet_mark(janet_wrap_fiber(wf));
} }
return 0; return 0;
} }
@ -473,8 +434,8 @@ static void *janet_stream_unmarshal(JanetMarshalContext *ctx) {
} }
JanetStream *p = janet_unmarshal_abstract(ctx, sizeof(JanetStream)); JanetStream *p = janet_unmarshal_abstract(ctx, sizeof(JanetStream));
/* Can't share listening state and such across threads */ /* Can't share listening state and such across threads */
p->read_state = NULL; p->read_fiber = NULL;
p->write_state = NULL; p->write_fiber = NULL;
p->flags = (uint32_t) janet_unmarshal_int(ctx); p->flags = (uint32_t) janet_unmarshal_int(ctx);
p->methods = janet_unmarshal_ptr(ctx); p->methods = janet_unmarshal_ptr(ctx);
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
@ -593,8 +554,6 @@ void janet_ev_init_common(void) {
janet_table_init_raw(&janet_vm.active_tasks, 0); janet_table_init_raw(&janet_vm.active_tasks, 0);
janet_table_init_raw(&janet_vm.signal_handlers, 0); janet_table_init_raw(&janet_vm.signal_handlers, 0);
janet_rng_seed(&janet_vm.ev_rng, 0); janet_rng_seed(&janet_vm.ev_rng, 0);
janet_vm.listeners = janet_array(0);
janet_gcroot(janet_wrap_array(janet_vm.listeners));
#ifndef JANET_WINDOWS #ifndef JANET_WINDOWS
pthread_attr_init(&janet_vm.new_thread_attr); pthread_attr_init(&janet_vm.new_thread_attr);
pthread_attr_setdetachstate(&janet_vm.new_thread_attr, PTHREAD_CREATE_DETACHED); pthread_attr_setdetachstate(&janet_vm.new_thread_attr, PTHREAD_CREATE_DETACHED);
@ -608,7 +567,6 @@ void janet_ev_deinit_common(void) {
janet_table_deinit(&janet_vm.threaded_abstracts); janet_table_deinit(&janet_vm.threaded_abstracts);
janet_table_deinit(&janet_vm.active_tasks); janet_table_deinit(&janet_vm.active_tasks);
janet_table_deinit(&janet_vm.signal_handlers); janet_table_deinit(&janet_vm.signal_handlers);
janet_vm.listeners = NULL;
#ifndef JANET_WINDOWS #ifndef JANET_WINDOWS
pthread_attr_destroy(&janet_vm.new_thread_attr); pthread_attr_destroy(&janet_vm.new_thread_attr);
#endif #endif
@ -1554,62 +1512,25 @@ static JanetTimestamp ts_now(void) {
return res; return res;
} }
static void janet_epoll_sync_callback(JanetEVGenericMessage msg) {
JanetListenerState *state = msg.argp;
if (state->stream) {
JanetAsyncStatus status1 = JANET_ASYNC_STATUS_NOT_DONE;
JanetAsyncStatus status2 = JANET_ASYNC_STATUS_NOT_DONE;
if (state == state->stream->read_state)
status1 = state->machine(state, JANET_ASYNC_EVENT_READ);
if (state == state->stream->write_state)
status2 = state->machine(state, JANET_ASYNC_EVENT_WRITE);
if (status1 == JANET_ASYNC_STATUS_DONE ||
status2 == JANET_ASYNC_STATUS_DONE) {
janet_unlisten(state);
} else {
/* Repost event */
janet_ev_post_event(NULL, janet_epoll_sync_callback, msg);
}
}
}
/* Wait for the next event */ /* Wait for the next event */
JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user) { static void janet_register_stream(JanetStream *stream) {
JanetListenerState *state = janet_listen_impl(stream, behavior, mask, size, user); struct epoll_event ev;
if (!(stream->flags & JANET_STREAM_REGISTERED)) { ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
struct epoll_event ev; ev.data.ptr = stream;
ev.events = EPOLLIN | EPOLLOUT | EPOLLET; int status;
ev.data.ptr = stream; do {
int status; status = epoll_ctl(janet_vm.epoll, EPOLL_CTL_ADD, stream->handle, &ev);
do { } while (status == -1 && errno == EINTR);
status = epoll_ctl(janet_vm.epoll, EPOLL_CTL_ADD, stream->handle, &ev); if (status == -1) {
} while (status == -1 && errno == EINTR); if (errno == EPERM) {
if (status == -1) { /* Couldn't add to event loop, so assume that it completes
if (errno == EPERM) { * synchronously. */
/* Couldn't add to event loop, so assume that it completes stream->flags |= JANET_STREAM_UNREGISTERED;
* synchronously. In that case, fire the completion } else {
* event manually, since this should be a read or write /* Unexpected error */
* event to a file. So we just post a custom event to do the read/write janet_panicv(janet_ev_lasterr());
* asap. */
/* Use flag to indicate state is not registered in epoll */
state->flags = 1;
JanetEVGenericMessage msg = {0};
msg.argp = state;
janet_ev_post_event(NULL, janet_epoll_sync_callback, msg);
} else {
/* Unexpected error */
janet_unlisten_impl(state);
janet_panicv(janet_ev_lasterr());
}
} }
stream->flags |= JANET_STREAM_REGISTERED;
} }
return state;
}
/* Tell system we are done listening for a certain event */
static void janet_unlisten(JanetListenerState *state) {
janet_unlisten_impl(state);
} }
#define JANET_EPOLL_MAX_EVENTS 64 #define JANET_EPOLL_MAX_EVENTS 64
@ -1646,28 +1567,30 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
} else { } else {
JanetStream *stream = p; JanetStream *stream = p;
int mask = events[i].events; int mask = events[i].events;
JanetListenerState *states[2] = {stream->read_state, stream->write_state}; int has_err = mask & EPOLLERR;
for (int j = 0; j < 2; j++) { int has_hup = mask & EPOLLHUP;
JanetListenerState *state = states[j]; JanetFiber *rf = stream->read_fiber;
if (!state) continue; JanetFiber *wf = stream->write_fiber;
state->event = events + i; if (rf) {
JanetAsyncStatus status1 = JANET_ASYNC_STATUS_NOT_DONE; if (rf->ev_callback && (mask & EPOLLIN)) {
JanetAsyncStatus status2 = JANET_ASYNC_STATUS_NOT_DONE; rf->ev_callback(rf, JANET_ASYNC_EVENT_READ);
JanetAsyncStatus status3 = JANET_ASYNC_STATUS_NOT_DONE; }
JanetAsyncStatus status4 = JANET_ASYNC_STATUS_NOT_DONE; if (rf->ev_callback && has_err) {
if (mask & EPOLLOUT) rf->ev_callback(rf, JANET_ASYNC_EVENT_ERR);
status1 = state->machine(state, JANET_ASYNC_EVENT_WRITE); }
if (mask & EPOLLIN) if (rf->ev_callback && has_hup) {
status2 = state->machine(state, JANET_ASYNC_EVENT_READ); rf->ev_callback(rf, JANET_ASYNC_EVENT_HUP);
if (mask & EPOLLERR) }
status3 = state->machine(state, JANET_ASYNC_EVENT_ERR); }
if ((mask & EPOLLHUP) && !(mask & (EPOLLOUT | EPOLLIN))) if (wf) {
status4 = state->machine(state, JANET_ASYNC_EVENT_HUP); if (wf->ev_callback && (mask & EPOLLOUT)) {
if (status1 == JANET_ASYNC_STATUS_DONE || wf->ev_callback(wf, JANET_ASYNC_EVENT_WRITE);
status2 == JANET_ASYNC_STATUS_DONE || }
status3 == JANET_ASYNC_STATUS_DONE || if (wf->ev_callback && has_err) {
status4 == JANET_ASYNC_STATUS_DONE) { wf->ev_callback(wf, JANET_ASYNC_EVENT_ERR);
janet_unlisten(state); }
if (wf->ev_callback && has_hup) {
wf->ev_callback(wf, JANET_ASYNC_EVENT_HUP);
} }
} }
janet_stream_checktoclose(stream); janet_stream_checktoclose(stream);
@ -1853,9 +1776,7 @@ void janet_ev_deinit(void) {
janet_vm.kq = 0; janet_vm.kq = 0;
} }
#else #elif defined(JANET_EV_POLL)
#include <poll.h>
static JanetTimestamp ts_now(void) { static JanetTimestamp ts_now(void) {
struct timespec now; struct timespec now;
@ -1866,34 +1787,37 @@ static JanetTimestamp ts_now(void) {
} }
/* Wait for the next event */ /* Wait for the next event */
JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user) { void janet_register_stream(JanetStream *stream) {
size_t oldsize = janet_vm.listeners->capacity; struct pollfd ev = {0};
JanetListenerState *state = janet_listen_impl(stream, behavior, mask, size, user); stream->index = (uint32_t) janet_vm.stream_count;
size_t newsize = janet_vm.listeners->capacity; size_t new_count = janet_vm.stream_count + 1;
if (newsize > oldsize) { if (new_count > janet_vm.stream_capacity) {
janet_vm.fds = janet_realloc(janet_vm.fds, (newsize + 1) * sizeof(struct pollfd)); size_t new_cap = new_count * 2;
if (NULL == janet_vm.fds) { janet_vm.fds = janet_realloc(janet_vm.fds, (1 + new_cap) * sizeof(struct pollfd));
janet_vm.streams = janet_realloc(janet_vm.streams, new_cap * sizeof(JanetStream *));
if (!janet_vm.fds || !janet_vm.streams) {
JANET_OUT_OF_MEMORY; JANET_OUT_OF_MEMORY;
} }
janet_vm.stream_capacity = new_cap;
} }
struct pollfd ev;
ev.fd = stream->handle; ev.fd = stream->handle;
ev.events = 0; ev.events = POLLIN | POLLOUT;
if (stream->read_state) ev.events |= POLLIN; janet_vm.fds[janet_vm.stream_count + 1] = ev;
if (stream->write_state) ev.events |= POLLOUT; janet_vm.streams[janet_vm.stream_count] = stream;
ev.revents = 0; janet_vm.stream_count = new_count;
janet_vm.fds[state->index + 1] = ev;
return state;
}
static void janet_unlisten(JanetListenerState *state) {
if (state->stream) {
janet_vm.fds[state->index + 1] = janet_vm.fds[janet_vm.listeners->count];
}
janet_unlisten_impl(state);
} }
void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) { void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
/* set event flags */
for (size_t i = 0; i < janet_vm.stream_count; i++) {
JanetStream *stream = janet_vm.streams[i];
janet_vm.fds[i + 1].events = 0;
janet_vm.fds[i + 1].revents = 0;
if (stream->read_fiber) janet_vm.fds[i + 1].events |= POLLIN;
if (stream->write_fiber) janet_vm.fds[i + 1].events |= POLLOUT;
}
/* Poll for events */ /* Poll for events */
int ready; int ready;
do { do {
@ -1902,7 +1826,7 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
JanetTimestamp now = ts_now(); JanetTimestamp now = ts_now();
to = now > timeout ? 0 : (int)(timeout - now); to = now > timeout ? 0 : (int)(timeout - now);
} }
ready = poll(janet_vm.fds, janet_vm.listeners->count + 1, to); ready = poll(janet_vm.fds, janet_vm.stream_count + 1, to);
} while (ready == -1 && errno == EINTR); } while (ready == -1 && errno == EINTR);
if (ready == -1) { if (ready == -1) {
JANET_EXIT("failed to poll events"); JANET_EXIT("failed to poll events");
@ -1915,31 +1839,32 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
} }
/* Step state machines */ /* Step state machines */
for (int32_t i = 0; i < janet_vm.listeners->count; i++) { for (size_t i = 0; i < janet_vm.stream_count; i++) {
struct pollfd *pfd = janet_vm.fds + i + 1; struct pollfd *pfd = janet_vm.fds + i + 1;
/* Skip fds where nothing interesting happened */ JanetStream *stream = janet_vm.streams[i];
JanetListenerState *state = (JanetListenerState *) janet_unwrap_abstract(janet_vm.listeners->data[i]);
/* Normal event */
int mask = pfd->revents; int mask = pfd->revents;
JanetAsyncStatus status1 = JANET_ASYNC_STATUS_NOT_DONE; if (!mask) continue;
JanetAsyncStatus status2 = JANET_ASYNC_STATUS_NOT_DONE; int has_err = mask & POLLERR;
JanetAsyncStatus status3 = JANET_ASYNC_STATUS_NOT_DONE; int has_hup = mask & POLLHUP;
JanetAsyncStatus status4 = JANET_ASYNC_STATUS_NOT_DONE; JanetFiber *rf = stream->read_fiber;
state->event = pfd; JanetFiber *wf = stream->write_fiber;
JanetStream *stream = state->stream; if (rf) {
if (mask & POLLOUT) if (rf->ev_callback && (mask & POLLIN)) {
status1 = state->machine(state, JANET_ASYNC_EVENT_WRITE); rf->ev_callback(rf, JANET_ASYNC_EVENT_READ);
if (mask & POLLIN) } else if (rf->ev_callback && has_hup) {
status2 = state->machine(state, JANET_ASYNC_EVENT_READ); rf->ev_callback(rf, JANET_ASYNC_EVENT_HUP);
if (mask & POLLERR) } else if (rf->ev_callback && has_err) {
status3 = state->machine(state, JANET_ASYNC_EVENT_ERR); rf->ev_callback(rf, JANET_ASYNC_EVENT_ERR);
if ((mask & POLLHUP) && !(mask & (POLLIN | POLLOUT))) }
status4 = state->machine(state, JANET_ASYNC_EVENT_HUP); }
if (status1 == JANET_ASYNC_STATUS_DONE || if (wf) {
status2 == JANET_ASYNC_STATUS_DONE || if (wf->ev_callback && (mask & POLLOUT)) {
status3 == JANET_ASYNC_STATUS_DONE || wf->ev_callback(wf, JANET_ASYNC_EVENT_WRITE);
status4 == JANET_ASYNC_STATUS_DONE) { } else if (wf->ev_callback && has_hup) {
janet_unlisten(state); wf->ev_callback(wf, JANET_ASYNC_EVENT_HUP);
} else if (wf->ev_callback && has_err) {
wf->ev_callback(wf, JANET_ASYNC_EVENT_ERR);
}
} }
janet_stream_checktoclose(stream); janet_stream_checktoclose(stream);
} }
@ -1956,6 +1881,9 @@ void janet_ev_init(void) {
janet_vm.fds[0].fd = janet_vm.selfpipe[0]; janet_vm.fds[0].fd = janet_vm.selfpipe[0];
janet_vm.fds[0].events = POLLIN; janet_vm.fds[0].events = POLLIN;
janet_vm.fds[0].revents = 0; janet_vm.fds[0].revents = 0;
janet_vm.streams = NULL;
janet_vm.stream_count = 0;
janet_vm.stream_capacity = 0;
return; return;
} }
@ -1963,7 +1891,9 @@ void janet_ev_deinit(void) {
janet_ev_deinit_common(); janet_ev_deinit_common();
janet_ev_cleanup_selfpipe(); janet_ev_cleanup_selfpipe();
janet_free(janet_vm.fds); janet_free(janet_vm.fds);
janet_free(janet_vm.streams);
janet_vm.fds = NULL; janet_vm.fds = NULL;
janet_vm.streams = NULL;
} }
#endif #endif
@ -2204,7 +2134,6 @@ typedef enum {
} JanetReadMode; } JanetReadMode;
typedef struct { typedef struct {
JanetListenerState head;
int32_t bytes_left; int32_t bytes_left;
int32_t bytes_read; int32_t bytes_read;
JanetBuffer *buf; JanetBuffer *buf;
@ -2224,8 +2153,9 @@ typedef struct {
#endif #endif
} StateRead; } StateRead;
JanetAsyncStatus ev_machine_read(JanetListenerState *s, JanetAsyncEvent event) { void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
StateRead *state = (StateRead *) s; JanetStream *stream = fiber->ev_stream;
StateRead *state = (StateRead *) fiber->ev_state;
switch (event) { switch (event) {
default: default:
break; break;
@ -2233,8 +2163,9 @@ JanetAsyncStatus ev_machine_read(JanetListenerState *s, JanetAsyncEvent event) {
janet_mark(janet_wrap_buffer(state->buf)); janet_mark(janet_wrap_buffer(state->buf));
break; break;
case JANET_ASYNC_EVENT_CLOSE: case JANET_ASYNC_EVENT_CLOSE:
janet_schedule(s->fiber, janet_wrap_nil()); janet_schedule(fiber, janet_wrap_nil());
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
case JANET_ASYNC_EVENT_COMPLETE: { case JANET_ASYNC_EVENT_COMPLETE: {
/* Called when read finished */ /* Called when read finished */
@ -2307,12 +2238,15 @@ JanetAsyncStatus ev_machine_read(JanetListenerState *s, JanetAsyncEvent event) {
#else #else
case JANET_ASYNC_EVENT_ERR: { case JANET_ASYNC_EVENT_ERR: {
if (state->bytes_read) { if (state->bytes_read) {
janet_schedule(s->fiber, janet_wrap_buffer(state->buf)); janet_schedule(fiber, janet_wrap_buffer(state->buf));
} else { } else {
janet_schedule(s->fiber, janet_wrap_nil()); janet_schedule(fiber, janet_wrap_nil());
} }
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
} }
read_more:
case JANET_ASYNC_EVENT_HUP: case JANET_ASYNC_EVENT_HUP:
case JANET_ASYNC_EVENT_USER: case JANET_ASYNC_EVENT_USER:
case JANET_ASYNC_EVENT_READ: { case JANET_ASYNC_EVENT_READ: {
@ -2328,36 +2262,38 @@ JanetAsyncStatus ev_machine_read(JanetListenerState *s, JanetAsyncEvent event) {
do { do {
#ifdef JANET_NET #ifdef JANET_NET
if (state->mode == JANET_ASYNC_READMODE_RECVFROM) { if (state->mode == JANET_ASYNC_READMODE_RECVFROM) {
nread = recvfrom(s->stream->handle, buffer->data + buffer->count, read_limit, state->flags, nread = recvfrom(stream->handle, buffer->data + buffer->count, read_limit, state->flags,
(struct sockaddr *)&saddr, &socklen); (struct sockaddr *)&saddr, &socklen);
} else if (state->mode == JANET_ASYNC_READMODE_RECV) { } else if (state->mode == JANET_ASYNC_READMODE_RECV) {
nread = recv(s->stream->handle, buffer->data + buffer->count, read_limit, state->flags); nread = recv(stream->handle, buffer->data + buffer->count, read_limit, state->flags);
} else } else
#endif #endif
{ {
nread = read(s->stream->handle, buffer->data + buffer->count, read_limit); nread = read(stream->handle, buffer->data + buffer->count, read_limit);
} }
} while (nread == -1 && errno == EINTR); } while (nread == -1 && errno == EINTR);
/* Check for errors - special case errors that can just be waited on to fix */ /* Check for errors - special case errors that can just be waited on to fix */
if (nread == -1) { if (nread == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
return JANET_ASYNC_STATUS_NOT_DONE; break;
} }
/* In stream protocols, a pipe error is end of stream */ /* In stream protocols, a pipe error is end of stream */
if (errno == EPIPE && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) { if (errno == EPIPE && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) {
nread = 0; nread = 0;
} else { } else {
janet_cancel(s->fiber, janet_ev_lasterr()); janet_cancel(fiber, janet_ev_lasterr());
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
} }
} }
/* Only allow 0-length packets in recv-from. In stream protocols, a zero length packet is EOS. */ /* Only allow 0-length packets in recv-from. In stream protocols, a zero length packet is EOS. */
state->bytes_read += nread; state->bytes_read += nread;
if (state->bytes_read == 0 && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) { if (state->bytes_read == 0 && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) {
janet_schedule(s->fiber, janet_wrap_nil()); janet_schedule(fiber, janet_wrap_nil());
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
} }
/* Increment buffer counts */ /* Increment buffer counts */
@ -2378,19 +2314,22 @@ JanetAsyncStatus ev_machine_read(JanetListenerState *s, JanetAsyncEvent event) {
{ {
resume_val = janet_wrap_buffer(buffer); resume_val = janet_wrap_buffer(buffer);
} }
janet_schedule(s->fiber, resume_val); janet_schedule(fiber, resume_val);
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
} }
/* Read some more if possible */
goto read_more;
} }
break; break;
#endif #endif
} }
return JANET_ASYNC_STATUS_NOT_DONE;
} }
static void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int is_chunked, JanetReadMode mode, int flags) { static void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t nbytes, int is_chunked, JanetReadMode mode, int flags) {
StateRead *state = (StateRead *) janet_listen(stream, ev_machine_read, JanetFiber *f = janet_vm.root_fiber;
JANET_ASYNC_LISTEN_READ, sizeof(StateRead), NULL); StateRead *state = (StateRead *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_READ, ev_callback_read, sizeof(StateRead));
state->is_chunk = is_chunked; state->is_chunk = is_chunked;
state->buf = buf; state->buf = buf;
state->bytes_left = nbytes; state->bytes_left = nbytes;
@ -2401,7 +2340,7 @@ static void janet_ev_read_generic(JanetStream *stream, JanetBuffer *buf, int32_t
#else #else
state->flags = flags; state->flags = flags;
#endif #endif
ev_machine_read((JanetListenerState *) state, JANET_ASYNC_EVENT_USER); ev_callback_read(f, JANET_ASYNC_EVENT_USER);
} }
void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) { void janet_ev_read(JanetStream *stream, JanetBuffer *buf, int32_t nbytes) {
@ -2433,7 +2372,6 @@ typedef enum {
} JanetWriteMode; } JanetWriteMode;
typedef struct { typedef struct {
JanetListenerState head;
union { union {
JanetBuffer *buf; JanetBuffer *buf;
const uint8_t *str; const uint8_t *str;
@ -2453,8 +2391,9 @@ typedef struct {
#endif #endif
} StateWrite; } StateWrite;
JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event) { void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
StateWrite *state = (StateWrite *) s; JanetStream *stream = fiber->ev_stream;
StateWrite *state = (StateWrite *) fiber->ev_state;
switch (event) { switch (event) {
default: default:
break; break;
@ -2468,8 +2407,9 @@ JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event)
break; break;
} }
case JANET_ASYNC_EVENT_CLOSE: case JANET_ASYNC_EVENT_CLOSE:
janet_cancel(s->fiber, janet_cstringv("stream closed")); janet_cancel(fiber, janet_cstringv("stream closed"));
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
case JANET_ASYNC_EVENT_COMPLETE: { case JANET_ASYNC_EVENT_COMPLETE: {
/* Called when write finished */ /* Called when write finished */
@ -2540,11 +2480,13 @@ JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event)
break; break;
#else #else
case JANET_ASYNC_EVENT_ERR: case JANET_ASYNC_EVENT_ERR:
janet_cancel(s->fiber, janet_cstringv("stream err")); janet_cancel(fiber, janet_cstringv("stream err"));
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
case JANET_ASYNC_EVENT_HUP: case JANET_ASYNC_EVENT_HUP:
janet_cancel(s->fiber, janet_cstringv("stream hup")); janet_cancel(fiber, janet_cstringv("stream hup"));
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
case JANET_ASYNC_EVENT_USER: case JANET_ASYNC_EVENT_USER:
case JANET_ASYNC_EVENT_WRITE: { case JANET_ASYNC_EVENT_WRITE: {
int32_t start, len; int32_t start, len;
@ -2565,28 +2507,30 @@ JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event)
do { do {
#ifdef JANET_NET #ifdef JANET_NET
if (state->mode == JANET_ASYNC_WRITEMODE_SENDTO) { if (state->mode == JANET_ASYNC_WRITEMODE_SENDTO) {
nwrote = sendto(s->stream->handle, bytes + start, nbytes, state->flags, nwrote = sendto(stream->handle, bytes + start, nbytes, state->flags,
(struct sockaddr *) dest_abst, janet_abstract_size(dest_abst)); (struct sockaddr *) dest_abst, janet_abstract_size(dest_abst));
} else if (state->mode == JANET_ASYNC_WRITEMODE_SEND) { } else if (state->mode == JANET_ASYNC_WRITEMODE_SEND) {
nwrote = send(s->stream->handle, bytes + start, nbytes, state->flags); nwrote = send(stream->handle, bytes + start, nbytes, state->flags);
} else } else
#endif #endif
{ {
nwrote = write(s->stream->handle, bytes + start, nbytes); nwrote = write(stream->handle, bytes + start, nbytes);
} }
} while (nwrote == -1 && errno == EINTR); } while (nwrote == -1 && errno == EINTR);
/* Handle write errors */ /* Handle write errors */
if (nwrote == -1) { if (nwrote == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) break; if (errno == EAGAIN || errno == EWOULDBLOCK) break;
janet_cancel(s->fiber, janet_ev_lasterr()); janet_cancel(fiber, janet_ev_lasterr());
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
} }
/* Unless using datagrams, empty message is a disconnect */ /* Unless using datagrams, empty message is a disconnect */
if (nwrote == 0 && !dest_abst) { if (nwrote == 0 && !dest_abst) {
janet_cancel(s->fiber, janet_cstringv("disconnect")); janet_cancel(fiber, janet_cstringv("disconnect"));
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
} }
if (nwrote > 0) { if (nwrote > 0) {
@ -2597,20 +2541,21 @@ JanetAsyncStatus ev_machine_write(JanetListenerState *s, JanetAsyncEvent event)
} }
state->start = start; state->start = start;
if (start >= len) { if (start >= len) {
janet_schedule(s->fiber, janet_wrap_nil()); janet_schedule(fiber, janet_wrap_nil());
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
break;
} }
break; break;
} }
break; break;
#endif #endif
} }
return JANET_ASYNC_STATUS_NOT_DONE;
} }
static void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_abst, JanetWriteMode mode, int is_buffer, int flags) { static void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_abst, JanetWriteMode mode, int is_buffer, int flags) {
StateWrite *state = (StateWrite *) janet_listen(stream, ev_machine_write, JanetFiber *f = janet_vm.root_fiber;
JANET_ASYNC_LISTEN_WRITE, sizeof(StateWrite), NULL); StateWrite *state = (StateWrite *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_WRITE,
ev_callback_write, sizeof(StateWrite));
state->is_buffer = is_buffer; state->is_buffer = is_buffer;
state->src.buf = buf; state->src.buf = buf;
state->dest_abst = dest_abst; state->dest_abst = dest_abst;
@ -2621,7 +2566,7 @@ static void janet_ev_write_generic(JanetStream *stream, void *buf, void *dest_ab
state->flags = flags; state->flags = flags;
#endif #endif
state->start = 0; state->start = 0;
ev_machine_write((JanetListenerState *) state, JANET_ASYNC_EVENT_USER); ev_callback_write(f, JANET_ASYNC_EVENT_USER);
} }
void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf) { void janet_ev_write_buffer(JanetStream *stream, JanetBuffer *buf) {

View File

@ -40,7 +40,9 @@ static void fiber_reset(JanetFiber *fiber) {
fiber->last_value = janet_wrap_nil(); fiber->last_value = janet_wrap_nil();
#ifdef JANET_EV #ifdef JANET_EV
fiber->sched_id = 0; fiber->sched_id = 0;
fiber->waiting = NULL; fiber->ev_callback = NULL;
fiber->ev_state = NULL;
fiber->ev_stream = NULL;
fiber->supervisor_channel = NULL; fiber->supervisor_channel = NULL;
#endif #endif
janet_fiber_set_status(fiber, JANET_STATUS_NEW); janet_fiber_set_status(fiber, JANET_STATUS_NEW);

View File

@ -296,8 +296,11 @@ recur:
if (fiber->supervisor_channel) { if (fiber->supervisor_channel) {
janet_mark_abstract(fiber->supervisor_channel); janet_mark_abstract(fiber->supervisor_channel);
} }
if (fiber->waiting) { if (fiber->ev_stream) {
janet_mark_abstract(fiber->waiting); janet_mark_abstract(fiber->ev_stream);
}
if (fiber->ev_callback) {
fiber->ev_callback(fiber, JANET_ASYNC_EVENT_MARK);
} }
#endif #endif
@ -324,6 +327,11 @@ static void janet_deinit_block(JanetGCObject *mem) {
janet_free(((JanetTable *) mem)->data); janet_free(((JanetTable *) mem)->data);
break; break;
case JANET_MEMORY_FIBER: case JANET_MEMORY_FIBER:
#ifdef JANET_EV
if (((JanetFiber *)mem)->ev_state) {
janet_free(((JanetFiber *)mem)->ev_state);
}
#endif
janet_free(((JanetFiber *)mem)->data); janet_free(((JanetFiber *)mem)->data);
break; break;
case JANET_MEMORY_BUFFER: case JANET_MEMORY_BUFFER:

View File

@ -114,18 +114,19 @@ static void janet_net_socknoblock(JSock s) {
/* State machine for async connect */ /* State machine for async connect */
typedef struct { typedef struct {
JanetListenerState head;
int did_connect; int did_connect;
} NetStateConnect; } NetStateConnect;
JanetAsyncStatus net_machine_connect(JanetListenerState *s, JanetAsyncEvent event) { void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
NetStateConnect *state = (NetStateConnect *)s; JanetStream *stream = fiber->ev_stream;
NetStateConnect *state = (NetStateConnect *)fiber->ev_state;
switch (event) { switch (event) {
default: default:
return JANET_ASYNC_STATUS_NOT_DONE; break;
case JANET_ASYNC_EVENT_CLOSE: case JANET_ASYNC_EVENT_CLOSE:
janet_cancel(s->fiber, janet_cstringv("stream closed")); janet_cancel(fiber, janet_cstringv("stream closed"));
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
case JANET_ASYNC_EVENT_HUP: case JANET_ASYNC_EVENT_HUP:
case JANET_ASYNC_EVENT_ERR: case JANET_ASYNC_EVENT_ERR:
case JANET_ASYNC_EVENT_COMPLETE: case JANET_ASYNC_EVENT_COMPLETE:
@ -133,7 +134,6 @@ JanetAsyncStatus net_machine_connect(JanetListenerState *s, JanetAsyncEvent even
case JANET_ASYNC_EVENT_USER: case JANET_ASYNC_EVENT_USER:
break; break;
} }
JanetStream *stream = s->stream;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
int res = 0; int res = 0;
int size = sizeof(res); int size = sizeof(res);
@ -146,24 +146,24 @@ JanetAsyncStatus net_machine_connect(JanetListenerState *s, JanetAsyncEvent even
if (r == 0) { if (r == 0) {
if (res == 0) { if (res == 0) {
state->did_connect = 1; state->did_connect = 1;
janet_schedule(s->fiber, janet_wrap_abstract(s->stream)); janet_schedule(fiber, janet_wrap_abstract(stream));
} else { } else {
janet_cancel(s->fiber, janet_cstringv(strerror(res))); janet_cancel(fiber, janet_cstringv(strerror(res)));
stream->flags |= JANET_STREAM_TOCLOSE; stream->flags |= JANET_STREAM_TOCLOSE;
} }
} else { } else {
janet_cancel(s->fiber, janet_ev_lasterr()); janet_cancel(fiber, janet_ev_lasterr());
stream->flags |= JANET_STREAM_TOCLOSE; stream->flags |= JANET_STREAM_TOCLOSE;
} }
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
} }
static void net_sched_connect(JanetStream *stream) { static void net_sched_connect(JanetStream *stream) {
JanetListenerState *s = janet_listen(stream, net_machine_connect, JANET_ASYNC_LISTEN_WRITE, sizeof(NetStateConnect), NULL); JanetFiber *f = janet_vm.root_fiber;
NetStateConnect *state = (NetStateConnect *)s; NetStateConnect *state = (NetStateConnect *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, sizeof(NetStateConnect));
state->did_connect = 0; state->did_connect = 0;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
net_machine_connect(s, JANET_ASYNC_EVENT_USER); net_callback_connect(s, JANET_ASYNC_EVENT_USER);
#endif #endif
} }
@ -264,12 +264,12 @@ static int net_sched_accept_impl(NetStateAccept *state, Janet *err) {
#else #else
typedef struct { typedef struct {
JanetListenerState head;
JanetFunction *function; JanetFunction *function;
} NetStateAccept; } NetStateAccept;
JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event) { void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
NetStateAccept *state = (NetStateAccept *)s; JanetStream *stream = fiber->ev_stream;
NetStateAccept *state = (NetStateAccept *)fiber->ev_state;
switch (event) { switch (event) {
default: default:
break; break;
@ -278,40 +278,41 @@ JanetAsyncStatus net_machine_accept(JanetListenerState *s, JanetAsyncEvent event
break; break;
} }
case JANET_ASYNC_EVENT_CLOSE: case JANET_ASYNC_EVENT_CLOSE:
janet_schedule(s->fiber, janet_wrap_nil()); janet_schedule(fiber, janet_wrap_nil());
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
case JANET_ASYNC_EVENT_USER: case JANET_ASYNC_EVENT_USER:
case JANET_ASYNC_EVENT_READ: { case JANET_ASYNC_EVENT_READ: {
#if defined(JANET_LINUX) #if defined(JANET_LINUX)
JSock connfd = accept4(s->stream->handle, NULL, NULL, SOCK_CLOEXEC); JSock connfd = accept4(stream->handle, NULL, NULL, SOCK_CLOEXEC);
#else #else
/* On BSDs, CLOEXEC should be inherited from server socket */ /* On BSDs, CLOEXEC should be inherited from server socket */
JSock connfd = accept(s->stream->handle, NULL, NULL); JSock connfd = accept(stream->handle, NULL, NULL);
#endif #endif
if (JSOCKVALID(connfd)) { if (JSOCKVALID(connfd)) {
janet_net_socknoblock(connfd); janet_net_socknoblock(connfd);
JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE); JanetStream *stream = make_stream(connfd, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
Janet streamv = janet_wrap_abstract(stream); Janet streamv = janet_wrap_abstract(stream);
if (state->function) { if (state->function) {
JanetFiber *fiber = janet_fiber(state->function, 64, 1, &streamv); JanetFiber *sub_fiber = janet_fiber(state->function, 64, 1, &streamv);
fiber->supervisor_channel = s->fiber->supervisor_channel; sub_fiber->supervisor_channel = fiber->supervisor_channel;
janet_schedule(fiber, janet_wrap_nil()); janet_schedule(sub_fiber, janet_wrap_nil());
} else { } else {
janet_schedule(s->fiber, streamv); janet_schedule(fiber, streamv);
return JANET_ASYNC_STATUS_DONE; janet_async_end(fiber);
return;
} }
} }
break; break;
} }
} }
return JANET_ASYNC_STATUS_NOT_DONE;
} }
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) { JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
JanetListenerState *s = janet_listen(stream, net_machine_accept, JANET_ASYNC_LISTEN_READ, sizeof(NetStateAccept), NULL); JanetFiber *f = janet_vm.root_fiber;
NetStateAccept *state = (NetStateAccept *) s; NetStateAccept *state = (NetStateAccept *) janet_async_start(f, stream, JANET_ASYNC_LISTEN_READ, net_callback_accept, sizeof(NetStateAccept));
state->function = fun; state->function = fun;
net_machine_accept(s, JANET_ASYNC_EVENT_USER); net_callback_accept(f, JANET_ASYNC_EVENT_USER);
janet_await(); janet_await();
} }

View File

@ -159,7 +159,6 @@ struct JanetVM {
volatile JanetAtomicInt listener_count; /* used in signal handler, must be volatile */ volatile JanetAtomicInt listener_count; /* used in signal handler, must be volatile */
JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */ JanetTable threaded_abstracts; /* All abstract types that can be shared between threads (used in this thread) */
JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */ JanetTable active_tasks; /* All possibly live task fibers - used just for tracking */
JanetArray *listeners; /* For GC */
JanetTable signal_handlers; JanetTable signal_handlers;
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
void **iocp; void **iocp;
@ -176,6 +175,9 @@ struct JanetVM {
int timer; int timer;
int timer_enabled; int timer_enabled;
#else #else
JanetStream **streams;
size_t stream_count;
size_t stream_capacity;
pthread_attr_t new_thread_attr; pthread_attr_t new_thread_attr;
JanetHandle selfpipe[2]; JanetHandle selfpipe[2];
struct pollfd *fds; struct pollfd *fds;

View File

@ -234,6 +234,11 @@ extern "C" {
#define JANET_EV_KQUEUE #define JANET_EV_KQUEUE
#endif #endif
/* Use poll as last resort */
#if !defined(JANET_WINDOWS) && !defined(JANET_EV_EPOLL) && !defined(JANET_EV_KQUEUE)
#define JANET_EV_POLL
#endif
/* How to export symbols */ /* How to export symbols */
#ifndef JANET_EXPORT #ifndef JANET_EXPORT
#ifdef JANET_WINDOWS #ifdef JANET_WINDOWS
@ -406,12 +411,11 @@ typedef enum {
JANET_SIGNAL_USER6, JANET_SIGNAL_USER6,
JANET_SIGNAL_USER7, JANET_SIGNAL_USER7,
JANET_SIGNAL_USER8, JANET_SIGNAL_USER8,
JANET_SIGNAL_USER9 JANET_SIGNAL_USER9,
JANET_SIGNAL_INTERRUPT = JANET_SIGNAL_USER8,
JANET_SIGNAL_EVENT = JANET_SIGNAL_USER9,
} JanetSignal; } JanetSignal;
#define JANET_SIGNAL_EVENT JANET_SIGNAL_USER9
#define JANET_SIGNAL_INTERRUPT JANET_SIGNAL_USER8
/* Fiber statuses - mostly corresponds to signals. */ /* Fiber statuses - mostly corresponds to signals. */
typedef enum { typedef enum {
JANET_STATUS_DEAD, JANET_STATUS_DEAD,
@ -575,7 +579,7 @@ typedef void *JanetAbstract;
#define JANET_STREAM_CLOSED 0x1 #define JANET_STREAM_CLOSED 0x1
#define JANET_STREAM_SOCKET 0x2 #define JANET_STREAM_SOCKET 0x2
#define JANET_STREAM_REGISTERED 0x4 #define JANET_STREAM_UNREGISTERED 0x4
#define JANET_STREAM_READABLE 0x200 #define JANET_STREAM_READABLE 0x200
#define JANET_STREAM_WRITABLE 0x400 #define JANET_STREAM_WRITABLE 0x400
#define JANET_STREAM_ACCEPTABLE 0x800 #define JANET_STREAM_ACCEPTABLE 0x800
@ -583,54 +587,42 @@ typedef void *JanetAbstract;
#define JANET_STREAM_TOCLOSE 0x10000 #define JANET_STREAM_TOCLOSE 0x10000
typedef enum { typedef enum {
JANET_ASYNC_EVENT_INIT, JANET_ASYNC_EVENT_INIT = 0,
JANET_ASYNC_EVENT_MARK, JANET_ASYNC_EVENT_MARK = 1,
JANET_ASYNC_EVENT_DEINIT, JANET_ASYNC_EVENT_DEINIT = 2,
JANET_ASYNC_EVENT_CLOSE, JANET_ASYNC_EVENT_CLOSE = 3,
JANET_ASYNC_EVENT_ERR, JANET_ASYNC_EVENT_ERR = 4,
JANET_ASYNC_EVENT_HUP, JANET_ASYNC_EVENT_HUP = 5,
JANET_ASYNC_EVENT_READ, JANET_ASYNC_EVENT_READ = 6,
JANET_ASYNC_EVENT_WRITE, JANET_ASYNC_EVENT_WRITE = 7,
JANET_ASYNC_EVENT_COMPLETE, /* Used on windows for IOCP */ JANET_ASYNC_EVENT_COMPLETE = 8, /* Used on windows for IOCP */
JANET_ASYNC_EVENT_USER JANET_ASYNC_EVENT_USER = 9
} JanetAsyncEvent; } JanetAsyncEvent;
#define JANET_ASYNC_LISTEN_READ (1 << JANET_ASYNC_EVENT_READ)
#define JANET_ASYNC_LISTEN_WRITE (1 << JANET_ASYNC_EVENT_WRITE)
typedef enum { typedef enum {
JANET_ASYNC_STATUS_NOT_DONE, JANET_ASYNC_LISTEN_READ = 1,
JANET_ASYNC_STATUS_DONE JANET_ASYNC_LISTEN_WRITE,
} JanetAsyncStatus; JANET_ASYNC_LISTEN_BOTH
} JanetAsyncMode;
/* Typedefs */ /* Typedefs */
typedef struct JanetListenerState JanetListenerState;
typedef struct JanetStream JanetStream; typedef struct JanetStream JanetStream;
typedef JanetAsyncStatus(*JanetListener)(JanetListenerState *state, JanetAsyncEvent event); typedef void (*JanetEVCallback)(JanetFiber *fiber, JanetAsyncEvent event);
/* Wrapper around file descriptors and HANDLEs that can be polled. */ /* Wrapper around file descriptors and HANDLEs that can be polled. */
struct JanetStream { struct JanetStream {
JanetHandle handle; JanetHandle handle;
uint32_t flags; uint32_t flags;
JanetListenerState *read_state; uint32_t index;
JanetListenerState *write_state; JanetFiber *read_fiber;
JanetFiber *write_fiber;
const void *methods; /* Methods for this stream */ const void *methods; /* Methods for this stream */
}; };
/* Interface for state machine based event loop */ JANET_API void janet_async_end(JanetFiber *fiber);
struct JanetListenerState { JANET_API void *janet_async_start(JanetFiber *fiber, JanetStream *stream,
JanetListener machine; JanetAsyncMode mode, JanetEVCallback callback, size_t data_size);
JanetFiber *fiber;
JanetStream *stream;
void *event; /* Used to pass data from asynchronous IO event. Contents depend on both
implementation of the event loop and the particular event. */
uint32_t index; /* Used for GC and poll implentation */
uint32_t flags;
#ifdef JANET_WINDOWS
void *tag; /* Used to associate listeners with an overlapped structure */
int bytes; /* Used to track how many bytes were transfered. */
#endif
};
#endif #endif
/* Janet uses atomic integers in several places for synchronization between threads and /* Janet uses atomic integers in several places for synchronization between threads and
@ -930,7 +922,9 @@ struct JanetFiber {
* in a multi-tasking system. It would be possible to move these fields to a new * in a multi-tasking system. It would be possible to move these fields to a new
* type, say "JanetTask", that as separate from fibers to save a bit of space. */ * type, say "JanetTask", that as separate from fibers to save a bit of space. */
uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */ uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */
JanetListenerState *waiting; JanetEVCallback ev_callback; /* Call this before starting scheduled fibers */
JanetStream *ev_stream; /* which stream we are waiting on */
void *ev_state; /* Extra data for ev callback state */
void *supervisor_channel; /* Channel to push self to when complete */ void *supervisor_channel; /* Channel to push self to when complete */
#endif #endif
}; };
@ -1407,9 +1401,6 @@ JANET_API void janet_cancel(JanetFiber *fiber, Janet value);
JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig); JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig);
JANET_API void janet_schedule_soon(JanetFiber *fiber, Janet value, JanetSignal sig); JANET_API void janet_schedule_soon(JanetFiber *fiber, Janet value, JanetSignal sig);
/* Start a state machine listening for events from a stream */
JANET_API JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user);
/* Shorthand for yielding to event loop in C */ /* Shorthand for yielding to event loop in C */
JANET_NO_RETURN JANET_API void janet_await(void); JANET_NO_RETURN JANET_API void janet_await(void);
JANET_NO_RETURN JANET_API void janet_sleep_await(double sec); JANET_NO_RETURN JANET_API void janet_sleep_await(double sec);

View File

@ -19,7 +19,7 @@
(frame :source) (frame :source-line))) (frame :source) (frame :source-line)))
(if x (if x
(when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x)) (when is-verbose (eprintf "\e[32m✔\e[0m %s: %s: %v" line-info (describe e) x))
(eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x)) (do (eprintf "\e[31m✘\e[0m %s: %s: %v" line-info (describe e) x) (eflush)))
x) x)
(defmacro assert-error (defmacro assert-error