1
0
mirror of https://github.com/janet-lang/janet synced 2025-05-06 01:14:13 +00:00

Fix linux issues with epoll on normal files.

We use the selfpipe trick if epoll fails with EPERM when trying to
register a file descriptor.
This commit is contained in:
Calvin Rose 2021-07-25 21:47:52 -05:00
parent 030dd747e9
commit eb84200f28
4 changed files with 95 additions and 60 deletions

View File

@ -943,11 +943,11 @@ Janet janet_disasm(JanetFuncDef *def) {
} }
JANET_CORE_FN(cfun_asm, JANET_CORE_FN(cfun_asm,
"(asm assembly)", "(asm assembly)",
"Returns a new function that is the compiled result of the assembly.\n" "Returns a new function that is the compiled result of the assembly.\n"
"The syntax for the assembly can be found on the Janet website, and should correspond\n" "The syntax for the assembly can be found on the Janet website, and should correspond\n"
"to the return value of disasm. Will throw an\n" "to the return value of disasm. Will throw an\n"
"error on invalid assembly.") { "error on invalid assembly.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetAssembleResult res; JanetAssembleResult res;
res = janet_asm(argv[0], 0); res = janet_asm(argv[0], 0);
@ -958,23 +958,23 @@ JANET_CORE_FN(cfun_asm,
} }
JANET_CORE_FN(cfun_disasm, JANET_CORE_FN(cfun_disasm,
"(disasm func &opt field)", "(disasm func &opt field)",
"Returns assembly that could be used to compile the given function. " "Returns assembly that could be used to compile the given function. "
"func must be a function, not a c function. Will throw on error on a badly " "func must be a function, not a c function. Will throw on error on a badly "
"typed argument. If given a field name, will only return that part of the function assembly. " "typed argument. If given a field name, will only return that part of the function assembly. "
"Possible fields are:\n\n" "Possible fields are:\n\n"
"* :arity - number of required and optional arguments.\n" "* :arity - number of required and optional arguments.\n"
"* :min-arity - minimum number of arguments function can be called with.\n" "* :min-arity - minimum number of arguments function can be called with.\n"
"* :max-arity - maximum number of arguments function can be called with.\n" "* :max-arity - maximum number of arguments function can be called with.\n"
"* :vararg - true if function can take a variable number of arguments.\n" "* :vararg - true if function can take a variable number of arguments.\n"
"* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n" "* :bytecode - array of parsed bytecode instructions. Each instruction is a tuple.\n"
"* :source - name of source file that this function was compiled from.\n" "* :source - name of source file that this function was compiled from.\n"
"* :name - name of function.\n" "* :name - name of function.\n"
"* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n" "* :slotcount - how many virtual registers, or slots, this function uses. Corresponds to stack space used by function.\n"
"* :constants - an array of constants referenced by this function.\n" "* :constants - an array of constants referenced by this function.\n"
"* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n" "* :sourcemap - a mapping of each bytecode instruction to a line and column in the source file.\n"
"* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n" "* :environments - an internal mapping of which enclosing functions are referenced for bindings.\n"
"* :defs - other function definitions that this function may instantiate.\n") { "* :defs - other function definitions that this function may instantiate.\n") {
janet_arity(argc, 1, 2); janet_arity(argc, 1, 2);
JanetFunction *f = janet_getfunction(argv, 0); JanetFunction *f = janet_getfunction(argv, 0);
if (argc == 2) { if (argc == 2) {

View File

@ -851,9 +851,9 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp timeout);
int janet_loop_done(void) { int janet_loop_done(void) {
return !(janet_vm.listener_count || return !(janet_vm.listener_count ||
(janet_vm.spawn.head != janet_vm.spawn.tail) || (janet_vm.spawn.head != janet_vm.spawn.tail) ||
janet_vm.tq_count || janet_vm.tq_count ||
janet_vm.extra_listeners); janet_vm.extra_listeners);
} }
JanetFiber *janet_loop1(void) { JanetFiber *janet_loop1(void) {
@ -1082,6 +1082,23 @@ static int make_epoll_events(int mask) {
return events; return events;
} }
static void janet_epoll_sync_callback(JanetEVGenericMessage msg) {
JanetListenerState *state = msg.argp;
JanetAsyncStatus status1 = JANET_ASYNC_STATUS_NOT_DONE;
JanetAsyncStatus status2 = JANET_ASYNC_STATUS_NOT_DONE;
if (state->stream->_mask & JANET_ASYNC_LISTEN_WRITE)
status1 = state->machine(state, JANET_ASYNC_EVENT_WRITE);
if (state->stream->_mask & JANET_ASYNC_LISTEN_WRITE)
status2 = state->machine(state, JANET_ASYNC_EVENT_READ);
if (status1 == JANET_ASYNC_STATUS_DONE ||
status2 == JANET_ASYNC_STATUS_DONE) {
janet_unlisten(state, 0);
} 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) { JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user) {
int is_first = !(stream->state); int is_first = !(stream->state);
@ -1095,8 +1112,22 @@ JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, in
status = epoll_ctl(janet_vm.epoll, op, stream->handle, &ev); status = epoll_ctl(janet_vm.epoll, op, stream->handle, &ev);
} while (status == -1 && errno == EINTR); } while (status == -1 && errno == EINTR);
if (status == -1) { if (status == -1) {
janet_unlisten_impl(state, 0); if (errno == EPERM) {
janet_panicv(janet_ev_lasterr()); /* Couldn't add to event loop, so assume that it completes
* synchronously. In that case, fire the completion
* event manually, since this should be a read or write
* event to a file. So we just post a custom event to do the read/write
* asap. */
/* Use flag to indicate state is not registered in epoll */
state->_mask |= (1 << JANET_ASYNC_EVENT_COMPLETE);
JanetEVGenericMessage msg = {0};
msg.argp = state;
janet_ev_post_event(NULL, janet_epoll_sync_callback, msg);
} else {
/* Unexpected error */
janet_unlisten_impl(state, 0);
janet_panicv(janet_ev_lasterr());
}
} }
return state; return state;
} }
@ -1105,17 +1136,20 @@ 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)) {
int is_last = (state->_next == NULL && stream->state == state); /* Use flag to indicate state is not registered in epoll */
int op = is_last ? EPOLL_CTL_DEL : EPOLL_CTL_MOD; if (!(state->_mask & (1 << JANET_ASYNC_EVENT_COMPLETE))) {
struct epoll_event ev; int is_last = (state->_next == NULL && stream->state == state);
ev.events = make_epoll_events(stream->_mask & ~state->_mask); int op = is_last ? EPOLL_CTL_DEL : EPOLL_CTL_MOD;
ev.data.ptr = stream; struct epoll_event ev;
int status; ev.events = make_epoll_events(stream->_mask & ~state->_mask);
do { ev.data.ptr = stream;
status = epoll_ctl(janet_vm.epoll, op, stream->handle, &ev); int status;
} while (status == -1 && errno == EINTR); do {
if (status == -1) { status = epoll_ctl(janet_vm.epoll, op, stream->handle, &ev);
janet_panicv(janet_ev_lasterr()); } while (status == -1 && errno == EINTR);
if (status == -1) {
janet_panicv(janet_ev_lasterr());
}
} }
} }
/* Destroy state machine and free memory */ /* Destroy state machine and free memory */
@ -1350,15 +1384,15 @@ void janet_ev_post_event(JanetVM *vm, JanetCallback cb, JanetEVGenericMessage ms
event->msg = msg; event->msg = msg;
event->cb = cb; event->cb = cb;
janet_assert(PostQueuedCompletionStatus(iocp, janet_assert(PostQueuedCompletionStatus(iocp,
sizeof(JanetSelfPipeEvent), sizeof(JanetSelfPipeEvent),
0, 0,
(LPOVERLAPPED) event), (LPOVERLAPPED) event),
"failed to post completion event"); "failed to post completion event");
#else #else
JanetSelfPipeEvent event; JanetSelfPipeEvent event;
event.msg = msg; event.msg = msg;
event.cb = cb; event.cb = cb;
int fd = vm->selfpipe; int fd = vm->selfpipe[1];
/* handle a bit of back pressure before giving up. */ /* handle a bit of back pressure before giving up. */
int tries = 4; int tries = 4;
while (tries > 0) { while (tries > 0) {

View File

@ -1542,10 +1542,10 @@ static JanetPeg *compile_peg(Janet x) {
*/ */
JANET_CORE_FN(cfun_peg_compile, JANET_CORE_FN(cfun_peg_compile,
"(peg/compile peg)", "(peg/compile peg)",
"Compiles a peg source data structure into a <core/peg>. This will speed up matching " "Compiles a peg source data structure into a <core/peg>. This will speed up matching "
"if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to suppliment " "if the same peg will be used multiple times. Will also use `(dyn :peg-grammar)` to suppliment "
"the grammar of the peg for otherwise undefined peg keywords.") { "the grammar of the peg for otherwise undefined peg keywords.") {
janet_fixarity(argc, 1); janet_fixarity(argc, 1);
JanetPeg *peg = compile_peg(argv[0]); JanetPeg *peg = compile_peg(argv[0]);
return janet_wrap_abstract(peg); return janet_wrap_abstract(peg);
@ -1609,17 +1609,17 @@ static void peg_call_reset(PegCall *c) {
} }
JANET_CORE_FN(cfun_peg_match, JANET_CORE_FN(cfun_peg_match,
"(peg/match peg text &opt start & args)", "(peg/match peg text &opt start & args)",
"Match a Parsing Expression Grammar to a byte string and return an array of captured values. " "Match a Parsing Expression Grammar to a byte string and return an array of captured values. "
"Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.") { "Returns nil if text does not match the language defined by peg. The syntax of PEGs is documented on the Janet website.") {
PegCall c = peg_cfun_init(argc, argv, 0); PegCall c = peg_cfun_init(argc, argv, 0);
const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + c.start); const uint8_t *result = peg_rule(&c.s, c.s.bytecode, c.bytes.bytes + c.start);
return result ? janet_wrap_array(c.s.captures) : janet_wrap_nil(); return result ? janet_wrap_array(c.s.captures) : janet_wrap_nil();
} }
JANET_CORE_FN(cfun_peg_find, JANET_CORE_FN(cfun_peg_find,
"(peg/find peg text &opt start & args)", "(peg/find peg text &opt start & args)",
"Find first index where the peg matches in text. Returns an integer, or nil if not found.") { "Find first index where the peg matches in text. Returns an integer, or nil if not found.") {
PegCall c = peg_cfun_init(argc, argv, 0); PegCall c = peg_cfun_init(argc, argv, 0);
for (int32_t i = c.start; i < c.bytes.len; i++) { for (int32_t i = c.start; i < c.bytes.len; i++) {
peg_call_reset(&c); peg_call_reset(&c);
@ -1630,8 +1630,8 @@ JANET_CORE_FN(cfun_peg_find,
} }
JANET_CORE_FN(cfun_peg_find_all, JANET_CORE_FN(cfun_peg_find_all,
"(peg/find-all peg text &opt start & args)", "(peg/find-all peg text &opt start & args)",
"Find all indexes where the peg matches in text. Returns an array of integers.") { "Find all indexes where the peg matches in text. Returns an array of integers.") {
PegCall c = peg_cfun_init(argc, argv, 0); PegCall c = peg_cfun_init(argc, argv, 0);
JanetArray *ret = janet_array(0); JanetArray *ret = janet_array(0);
for (int32_t i = c.start; i < c.bytes.len; i++) { for (int32_t i = c.start; i < c.bytes.len; i++) {
@ -1671,15 +1671,15 @@ static Janet cfun_peg_replace_generic(int32_t argc, Janet *argv, int only_one) {
} }
JANET_CORE_FN(cfun_peg_replace_all, JANET_CORE_FN(cfun_peg_replace_all,
"(peg/replace-all peg repl text &opt start & args)", "(peg/replace-all peg repl text &opt start & args)",
"Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.") { "Replace all matches of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement.") {
return cfun_peg_replace_generic(argc, argv, 0); return cfun_peg_replace_generic(argc, argv, 0);
} }
JANET_CORE_FN(cfun_peg_replace, JANET_CORE_FN(cfun_peg_replace,
"(peg/replace peg repl text &opt start & args)", "(peg/replace peg repl text &opt start & args)",
"Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. " "Replace first match of peg in text with repl, returning a new buffer. The peg does not need to make captures to do replacement. "
"If no matches are found, returns the input string in a new buffer.") { "If no matches are found, returns the input string in a new buffer.") {
return cfun_peg_replace_generic(argc, argv, 1); return cfun_peg_replace_generic(argc, argv, 1);
} }

View File

@ -553,7 +553,8 @@ void janet_core_cfuns(JanetTable *env, const char *regprefix, const JanetReg *cf
} }
void janet_core_def_sm(JanetTable *env, const char *name, Janet x, const void *p, const void *sf, int32_t sl) { void janet_core_def_sm(JanetTable *env, const char *name, Janet x, const void *p, const void *sf, int32_t sl) {
(void) sf, sl; (void) sf;
(void) sl;
janet_core_def(env, name, x, p); janet_core_def(env, name, x, p);
} }