mirror of
https://github.com/janet-lang/janet
synced 2025-11-10 12:33:08 +00:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b4b24edf7 | ||
|
|
8b10a5fb7c | ||
|
|
b0d0d9cad2 | ||
|
|
9abee3f29a | ||
|
|
bf9b6b1301 | ||
|
|
8cd57025a0 | ||
|
|
faf60b6b1f | ||
|
|
da2c1be49c | ||
|
|
92c02449f4 | ||
|
|
e381622a9a | ||
|
|
b799223ebc | ||
|
|
40ef224a95 | ||
|
|
a4c20b6e1c | ||
|
|
e6ee867f72 | ||
|
|
468a31f515 | ||
|
|
4d746794cc | ||
|
|
02d2a66ef2 | ||
|
|
4638baf545 | ||
|
|
2be23d3768 | ||
|
|
b39b1746ba | ||
|
|
24f97510b0 | ||
|
|
325d5399fa | ||
|
|
d8f6fbf594 | ||
|
|
21b3e4052c | ||
|
|
bf2928805e | ||
|
|
7d2bf334c8 | ||
|
|
7446802a70 | ||
|
|
077bf5ebae | ||
|
|
c9bef39f96 | ||
|
|
3740eadb7d | ||
|
|
e29fa66a74 | ||
|
|
ca5406c8e4 | ||
|
|
7217caacd1 | ||
|
|
8081082251 | ||
|
|
1597ca0de5 | ||
|
|
8c938ceff9 | ||
|
|
65a6945ea5 | ||
|
|
02640812af | ||
|
|
ba761d5c35 | ||
|
|
48a3b1f07f | ||
|
|
4370cb77e7 | ||
|
|
470e8f6fc7 | ||
|
|
b270d88427 | ||
|
|
66ce247129 | ||
|
|
6ad016c587 | ||
|
|
532dac1b95 | ||
|
|
2a4bcc262f | ||
|
|
1ce2361daf | ||
|
|
6e8584e8e0 | ||
|
|
121aa91139 | ||
|
|
bbc07c72d3 | ||
|
|
43b48fdbea | ||
|
|
604f97aba1 | ||
|
|
dc980081cd | ||
|
|
981f03fef3 | ||
|
|
d40133dc72 | ||
|
|
c9fa586fce | ||
|
|
b847a7d90b | ||
|
|
8b67108dc8 | ||
|
|
b559f9625a | ||
|
|
1736c9b0f8 | ||
|
|
4fb2d8d318 | ||
|
|
95891eb0a5 | ||
|
|
c133443eb7 | ||
|
|
8f0641f36c | ||
|
|
f48dbde736 | ||
|
|
f2e4c1ae9a | ||
|
|
a4aef38cc0 | ||
|
|
b445ecde51 | ||
|
|
a209a01284 | ||
|
|
7037532943 | ||
|
|
bb405ee1aa | ||
|
|
ef23356309 | ||
|
|
1613e2593c | ||
|
|
5464a7a379 | ||
|
|
bb1331e449 | ||
|
|
acbebc5631 |
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,10 +1,20 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## 1.17.2 - 2021-09-18
|
||||
- Remove include of windows.h from janet.h. This caused issues on certain projects.
|
||||
- Fix formatting in doc-format to better handle special characters in signatures.
|
||||
- Fix some marshalling bugs.
|
||||
- Add optional Makefile target to install jpm as well.
|
||||
- Supervisor channels in threads will no longer include a wasteful copy of the fiber in every
|
||||
message across a thread.
|
||||
- Allow passing a closure to `ev/thead` as well as a whole fiber.
|
||||
- Allow passing a closure directly to `ev/go` to spawn fibers on the event loop.
|
||||
|
||||
## 1.17.1 - 2021-08-29
|
||||
- Fix docstring typos
|
||||
- Add `make install-jpm-git` to make jpm co-install simpler if using makefile.
|
||||
- Fix bugs with starting ev/threads and fiber marshling.
|
||||
- Add `make install-jpm-git` to make jpm co-install simpler if using the Makefile.
|
||||
- Fix bugs with starting ev/threads and fiber marshaling.
|
||||
|
||||
## 1.17.0 - 2021-08-21
|
||||
- Add the `-E` flag for one-liners with the `short-fn` syntax for argument passing.
|
||||
|
||||
14
Makefile
14
Makefile
@@ -36,6 +36,7 @@ JANET_PATH?=$(LIBDIR)/janet
|
||||
JANET_MANPATH?=$(PREFIX)/share/man/man1/
|
||||
JANET_PKG_CONFIG_PATH?=$(LIBDIR)/pkgconfig
|
||||
JANET_DIST_DIR?=janet-dist
|
||||
JPM_TAG?=master
|
||||
DEBUGGER=gdb
|
||||
SONAME_SETTER=-Wl,-soname,
|
||||
|
||||
@@ -284,12 +285,13 @@ install: $(JANET_TARGET) $(JANET_LIBRARY) $(JANET_STATIC_LIBRARY) build/janet.pc
|
||||
install-jpm-git: $(JANET_TARGET)
|
||||
mkdir -p build
|
||||
rm -rf build/jpm
|
||||
git clone --depth=1 https://github.com/janet-lang/jpm.git build/jpm
|
||||
cd build/jpm && PREFIX='$(DESTDIR)$(PREFIX)' \
|
||||
JANET_MANPATH='$(DESTDIR)$(JANET_MANPATH)' \
|
||||
JANET_HEADERPATH='$(DESTDIR)$(INCLUDEDIR)/janet' \
|
||||
JANET_BINPATH='$(DESTDIR)$(BINDIR)' \
|
||||
JANET_LIBPATH='$(DESTDIR)$(LIBDIR)' \
|
||||
git clone --depth=1 --branch='$(JPM_TAG)' https://github.com/janet-lang/jpm.git build/jpm
|
||||
cd build/jpm && PREFIX='$(PREFIX)' \
|
||||
DESTDIR=$(DESTDIR) \
|
||||
JANET_MANPATH='$(JANET_MANPATH)' \
|
||||
JANET_HEADERPATH='$(INCLUDEDIR)/janet' \
|
||||
JANET_BINPATH='$(BINDIR)' \
|
||||
JANET_LIBPATH='$(LIBDIR)' \
|
||||
../../$(JANET_TARGET) ./bootstrap.janet
|
||||
|
||||
uninstall:
|
||||
|
||||
@@ -10,3 +10,13 @@
|
||||
(ev/call worker :b 5)
|
||||
(ev/sleep 0.3)
|
||||
(ev/call worker :c 12)
|
||||
|
||||
(defn worker2
|
||||
[name]
|
||||
(repeat 10
|
||||
(ev/sleep 0.2)
|
||||
(print name " working")))
|
||||
|
||||
(ev/go worker2 :bob)
|
||||
(ev/go worker2 :joe)
|
||||
(ev/go worker2 :sally)
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
project('janet', 'c',
|
||||
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
|
||||
version : '1.17.1')
|
||||
version : '1.17.2')
|
||||
|
||||
# Global settings
|
||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
|
||||
@@ -73,6 +73,7 @@ conf.set('JANET_NO_REALPATH', not get_option('realpath'))
|
||||
conf.set('JANET_NO_PROCESSES', not get_option('processes'))
|
||||
conf.set('JANET_SIMPLE_GETLINE', get_option('simple_getline'))
|
||||
conf.set('JANET_EV_NO_EPOLL', not get_option('epoll'))
|
||||
conf.set('JANET_EV_NO_KQUEUE', not get_option('kqueue'))
|
||||
conf.set('JANET_NO_THREADS', get_option('threads'))
|
||||
conf.set('JANET_NO_INTERPRETER_INTERRUPT', not get_option('interpreter_interrupt'))
|
||||
if get_option('os_name') != ''
|
||||
|
||||
@@ -18,6 +18,7 @@ option('umask', type : 'boolean', value : true)
|
||||
option('realpath', type : 'boolean', value : true)
|
||||
option('simple_getline', type : 'boolean', value : false)
|
||||
option('epoll', type : 'boolean', value : false)
|
||||
option('kqueue', type : 'boolean', value : false)
|
||||
option('interpreter_interrupt', type : 'boolean', value : false)
|
||||
|
||||
option('recursion_guard', type : 'integer', min : 10, max : 8000, value : 1024)
|
||||
|
||||
@@ -2786,8 +2786,8 @@
|
||||
(def delimiters
|
||||
(if has-color
|
||||
{:underline ["\e[4m" "\e[24m"]
|
||||
:code ["\e[3;97m" "\e[39;23m"]
|
||||
:italics ["\e[3m" "\e[23m"]
|
||||
:code ["\e[97m" "\e[39m"]
|
||||
:italics ["\e[4m" "\e[24m"]
|
||||
:bold ["\e[1m" "\e[22m"]}
|
||||
{:underline ["_" "_"]
|
||||
:code ["`" "`"]
|
||||
@@ -2820,7 +2820,7 @@
|
||||
(c++)
|
||||
(- cursor x))
|
||||
|
||||
# Detection helpers - return number of characters natched
|
||||
# Detection helpers - return number of characters matched
|
||||
(defn ul? []
|
||||
(let [x (c) x1 (cn 1)]
|
||||
(and
|
||||
@@ -2954,6 +2954,14 @@
|
||||
(finish-p)
|
||||
new-indent))
|
||||
|
||||
# Handle first line specially for defn, defmacro, etc.
|
||||
(when (= (chr "(") (in str 0))
|
||||
(skipline)
|
||||
(def first-line (string/slice str 0 (- cursor 1)))
|
||||
(def fl-open (if has-color "\e[97m" ""))
|
||||
(def fl-close (if has-color "\e[39m" ""))
|
||||
(push [[(string fl-open first-line fl-close) (length first-line)]]))
|
||||
|
||||
(parse-blocks 0)
|
||||
|
||||
# Emission state
|
||||
@@ -3360,23 +3368,23 @@
|
||||
Returns a fiber that is scheduled to run the function.
|
||||
```
|
||||
[f & args]
|
||||
(ev/go (fiber/new (fn [&] (f ;args)) :tp)))
|
||||
(ev/go (fn _call [&] (f ;args))))
|
||||
|
||||
(defmacro ev/spawn
|
||||
"Run some code in a new fiber. This is shorthand for (ev/call (fn [] ;body))."
|
||||
[& body]
|
||||
~(,ev/go (fiber/new (fn _spawn [&] ,;body) :tp)))
|
||||
~(,ev/go (fn _spawn [&] ,;body)))
|
||||
|
||||
(defmacro ev/do-thread
|
||||
``Run some code in a new thread. Suspends the current fiber until the thread is complete, and
|
||||
evaluates to nil.``
|
||||
[& body]
|
||||
~(,ev/thread (fiber/new (fn _thread [&] ,;body) :t)))
|
||||
~(,ev/thread (fn _do-thread [&] ,;body)))
|
||||
|
||||
(defmacro ev/spawn-thread
|
||||
``Run some code in a new thread. Like `ev/do-thread`, but returns nil immediately.``
|
||||
[& body]
|
||||
~(,ev/thread (fiber/new (fn _thread [&] ,;body) :t) nil :n))
|
||||
~(,ev/thread (fn _spawn-thread [&] ,;body) nil :n))
|
||||
|
||||
(defmacro ev/with-deadline
|
||||
`Run a body of code with a deadline, such that if the code does not complete before
|
||||
@@ -3407,7 +3415,7 @@
|
||||
(def ,res @[])
|
||||
(,wait-for-fibers ,chan
|
||||
,(seq [[i body] :pairs bodies]
|
||||
~(,ev/go (,fiber/new (fn [] (put ,res ,i ,body)) :tp) nil ,chan)))
|
||||
~(,ev/go (fn [] (put ,res ,i ,body)) nil ,chan)))
|
||||
,res))))
|
||||
|
||||
(compwhen (dyn 'net/listen)
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
#define JANET_VERSION_MAJOR 1
|
||||
#define JANET_VERSION_MINOR 17
|
||||
#define JANET_VERSION_PATCH 1
|
||||
#define JANET_VERSION_PATCH 2
|
||||
#define JANET_VERSION_EXTRA ""
|
||||
#define JANET_VERSION "1.17.1"
|
||||
#define JANET_VERSION "1.17.2"
|
||||
|
||||
/* #define JANET_BUILD "local" */
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
/* #define JANET_OS_NAME my-custom-os */
|
||||
/* #define JANET_ARCH_NAME pdp-8 */
|
||||
/* #define JANET_EV_NO_EPOLL */
|
||||
/* #define JANET_EV_NO_KQUEUE */
|
||||
/* #define JANET_NO_INTERPRETER_INTERRUPT */
|
||||
|
||||
/* Custom vm allocator support */
|
||||
|
||||
@@ -94,19 +94,19 @@ static int32_t janet_decref(JanetAbstractHead *ab) {
|
||||
}
|
||||
|
||||
void janet_os_mutex_init(JanetOSMutex *mutex) {
|
||||
InitializeCriticalSection(mutex);
|
||||
InitializeCriticalSection((CRITICAL_SECTION *) mutex);
|
||||
}
|
||||
|
||||
void janet_os_mutex_deinit(JanetOSMutex *mutex) {
|
||||
DeleteCriticalSection(mutex);
|
||||
DeleteCriticalSection((CRITICAL_SECTION *) mutex);
|
||||
}
|
||||
|
||||
void janet_os_mutex_lock(JanetOSMutex *mutex) {
|
||||
EnterCriticalSection(mutex);
|
||||
EnterCriticalSection((CRITICAL_SECTION *) mutex);
|
||||
}
|
||||
|
||||
void janet_os_mutex_unlock(JanetOSMutex *mutex) {
|
||||
LeaveCriticalSection(mutex);
|
||||
LeaveCriticalSection((CRITICAL_SECTION *) mutex);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
274
src/core/ev.c
274
src/core/ev.c
@@ -43,6 +43,9 @@
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/timerfd.h>
|
||||
#endif
|
||||
#ifdef JANET_EV_KQUEUE
|
||||
#include <sys/event.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
@@ -510,10 +513,10 @@ void janet_ev_mark(void) {
|
||||
static int janet_channel_push(JanetChannel *channel, Janet x, int mode);
|
||||
static int janet_channel_pop(JanetChannel *channel, Janet *item, int is_choice);
|
||||
|
||||
static Janet make_supervisor_event(const char *name, JanetFiber *fiber) {
|
||||
static Janet make_supervisor_event(const char *name, JanetFiber *fiber, int threaded) {
|
||||
Janet tup[2];
|
||||
tup[0] = janet_ckeywordv(name);
|
||||
tup[1] = janet_wrap_fiber(fiber);
|
||||
tup[1] = threaded ? fiber->last_value : janet_wrap_fiber(fiber) ;
|
||||
return janet_wrap_tuple(janet_tuple_n(tup, 2));
|
||||
}
|
||||
|
||||
@@ -1209,13 +1212,17 @@ JanetFiber *janet_loop1(void) {
|
||||
Janet res;
|
||||
JanetSignal sig = janet_continue_signal(task.fiber, task.value, &res, task.sig);
|
||||
void *sv = task.fiber->supervisor_channel;
|
||||
int is_suspended = sig == JANET_SIGNAL_EVENT || sig == JANET_SIGNAL_YIELD || sig == JANET_SIGNAL_INTERRUPT;
|
||||
if (NULL == sv) {
|
||||
if (sig != JANET_SIGNAL_EVENT && sig != JANET_SIGNAL_YIELD && sig != JANET_SIGNAL_INTERRUPT) {
|
||||
if (!is_suspended) {
|
||||
janet_stacktrace(task.fiber, res);
|
||||
}
|
||||
} else if (sig == JANET_SIGNAL_OK || (task.fiber->flags & (1 << sig))) {
|
||||
JanetChannel *chan = janet_channel_unwrap(sv);
|
||||
janet_channel_push(chan, make_supervisor_event(janet_signal_names[sig], task.fiber), 2);
|
||||
janet_channel_push(chan, make_supervisor_event(janet_signal_names[sig],
|
||||
task.fiber, chan->is_threaded), 2);
|
||||
} else if (!is_suspended) {
|
||||
janet_stacktrace(task.fiber, res);
|
||||
}
|
||||
if (sig == JANET_SIGNAL_INTERRUPT) {
|
||||
/* On interrupts, return the interrupted fiber immediately */
|
||||
@@ -1563,6 +1570,208 @@ void janet_ev_deinit(void) {
|
||||
* End epoll implementation
|
||||
*/
|
||||
|
||||
#elif defined(JANET_EV_KQUEUE)
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blob/master/src/lib/kpoll.c
|
||||
* NetBSD uses intptr_t while others use void * for .udata */
|
||||
#define EV_SETx(ev, a, b, c, d, e, f) EV_SET((ev), (a), (b), (c), (d), (e), ((__typeof__((ev)->udata))(f)))
|
||||
#define JANET_KQUEUE_TF (EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT)
|
||||
|
||||
/* NOTE:
|
||||
* NetBSD doesn't like intervals less than 1 millisecond so lets make that the
|
||||
* default anywhere JANET_KQUEUE_TS will be used. */
|
||||
#ifdef __NetBSD__
|
||||
#define JANET_KQUEUE_MIN_INTERVAL 1
|
||||
#else
|
||||
#define JANET_KQUEUE_MIN_INTERVAL 0
|
||||
#endif
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#define JANET_KQUEUE_TS(timestamp) (timestamp)
|
||||
#else
|
||||
/* NOTE:
|
||||
* NetBSD and OpenBSD expect things are always intervals, so fake that we have
|
||||
* abstime capability by changing how a timestamp is used in all kqueue calls
|
||||
* and defining absent macros. Additionally NetBSD expects intervals be
|
||||
* greater than 1 millisecond, so correct all intervals to be at least 1
|
||||
* millisecond under NetBSD. */
|
||||
JanetTimestamp fix_interval(const JanetTimestamp ts) {
|
||||
return ts >= JANET_KQUEUE_MIN_INTERVAL ? ts : JANET_KQUEUE_MIN_INTERVAL;
|
||||
}
|
||||
#define JANET_KQUEUE_TS(timestamp) (fix_interval((timestamp - ts_now())))
|
||||
#define NOTE_MSECONDS 0
|
||||
#define NOTE_ABSTIME 0
|
||||
#endif
|
||||
|
||||
|
||||
/* TODO: make this available be we using kqueue or epoll, instead of
|
||||
* redefinining it for kqueue and epoll separately? */
|
||||
static JanetTimestamp ts_now(void) {
|
||||
struct timespec now;
|
||||
janet_assert(-1 != clock_gettime(CLOCK_MONOTONIC, &now), "failed to get time");
|
||||
uint64_t res = 1000 * now.tv_sec;
|
||||
res += now.tv_nsec / 1000000;
|
||||
return res;
|
||||
}
|
||||
|
||||
void add_kqueue_events(const struct kevent *events, int length) {
|
||||
/* NOTE: Status should be equal to the amount of events added, which isn't
|
||||
* always known since deletions or modifications occur. Can't use the
|
||||
* eventlist argument for it to report to us what failed otherwise we may
|
||||
* poll in events to handle! This code assumes atomicity, that kqueue can
|
||||
* either succeed or fail, but never partially (which is seemingly how it
|
||||
* works in practice). When encountering an "inbetween" state we currently
|
||||
* just panic!
|
||||
*
|
||||
* The FreeBSD man page kqueue(2) shows a check through the change list to
|
||||
* check if kqueue had an error with any of the events being pushed to
|
||||
* change. Maybe we should do this, even tho the man page also doesn't
|
||||
* note that kqueue actually does this. We do not do this at this time. */
|
||||
int status;
|
||||
status = kevent(janet_vm.kq, events, length, NULL, 0, NULL);
|
||||
if (status == -1 && errno != EINTR)
|
||||
janet_panicv(janet_ev_lasterr());
|
||||
}
|
||||
|
||||
JanetListenerState *janet_listen(JanetStream *stream, JanetListener behavior, int mask, size_t size, void *user) {
|
||||
JanetListenerState *state = janet_listen_impl(stream, behavior, mask, size, user);
|
||||
struct kevent kev[2];
|
||||
|
||||
int length = 0;
|
||||
if (state->stream->_mask & JANET_ASYNC_LISTEN_READ) {
|
||||
EV_SETx(&kev[length], stream->handle, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, stream);
|
||||
length++;
|
||||
}
|
||||
if (state->stream->_mask & JANET_ASYNC_LISTEN_WRITE) {
|
||||
EV_SETx(&kev[length], stream->handle, EVFILT_WRITE, EV_ADD | EV_ENABLE, 0, 0, stream);
|
||||
length++;
|
||||
}
|
||||
|
||||
if (length > 0) {
|
||||
add_kqueue_events(kev, length);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static void janet_unlisten(JanetListenerState *state, int is_gc) {
|
||||
JanetStream *stream = state->stream;
|
||||
if (!(stream->flags & JANET_STREAM_CLOSED)) {
|
||||
/* Use flag to indicate state is not registered in kqueue */
|
||||
if (!(state->_mask & (1 << JANET_ASYNC_EVENT_COMPLETE))) {
|
||||
int is_last = (state->_next == NULL && stream->state == state);
|
||||
int op = is_last ? EV_DELETE : EV_DISABLE | EV_ADD;
|
||||
struct kevent kev[2];
|
||||
EV_SETx(&kev[1], stream->handle, EVFILT_WRITE, op, 0, 0, stream);
|
||||
|
||||
int length = 0;
|
||||
if (stream->_mask & JANET_ASYNC_EVENT_WRITE) {
|
||||
EV_SETx(&kev[length], stream->handle, EVFILT_WRITE, op, 0, 0, stream);
|
||||
length++;
|
||||
}
|
||||
if (stream->_mask & JANET_ASYNC_EVENT_READ) {
|
||||
EV_SETx(&kev[length], stream->handle, EVFILT_READ, op, 0, 0, stream);
|
||||
length++;
|
||||
}
|
||||
|
||||
add_kqueue_events(kev, length);
|
||||
}
|
||||
}
|
||||
janet_unlisten_impl(state, is_gc);
|
||||
}
|
||||
|
||||
#define JANET_KQUEUE_TIMER_IDENT 1
|
||||
#define JANET_KQUEUE_MAX_EVENTS 64
|
||||
|
||||
void janet_loop1_impl(int has_timeout, JanetTimestamp timeout) {
|
||||
/* Construct our timer which is a definite time on the clock, not an
|
||||
* interval, in milliseconds as that is `JanetTimestamp`'s precision. */
|
||||
struct kevent timer;
|
||||
if (janet_vm.timer_enabled || has_timeout) {
|
||||
EV_SETx(&timer,
|
||||
JANET_KQUEUE_TIMER_IDENT,
|
||||
EVFILT_TIMER,
|
||||
JANET_KQUEUE_TF,
|
||||
NOTE_MSECONDS | NOTE_ABSTIME,
|
||||
JANET_KQUEUE_TS(timeout), &janet_vm.timer);
|
||||
add_kqueue_events(&timer, 1);
|
||||
}
|
||||
janet_vm.timer_enabled = has_timeout;
|
||||
|
||||
/* Poll for events */
|
||||
int status;
|
||||
struct kevent events[JANET_KQUEUE_MAX_EVENTS];
|
||||
do {
|
||||
status = kevent(janet_vm.kq, NULL, 0, events, JANET_KQUEUE_MAX_EVENTS, NULL);
|
||||
} while (status == -1 && errno == EINTR);
|
||||
if (status == -1)
|
||||
JANET_EXIT("failed to poll events");
|
||||
|
||||
/* Step state machines */
|
||||
for (int i = 0; i < status; i++) {
|
||||
void *p = (void *) events[i].udata;
|
||||
if (&janet_vm.timer == p) {
|
||||
/* Timer expired, ignore */;
|
||||
} else if (janet_vm.selfpipe == p) {
|
||||
/* Self-pipe handling */
|
||||
janet_ev_handle_selfpipe();
|
||||
} else {
|
||||
JanetStream *stream = p;
|
||||
JanetListenerState *state = stream->state;
|
||||
if (NULL != state) {
|
||||
state->event = events + i;
|
||||
JanetAsyncStatus statuses[4];
|
||||
for (int i = 0; i < 4; i++)
|
||||
statuses[i] = JANET_ASYNC_STATUS_NOT_DONE;
|
||||
|
||||
if (!(events[i].flags & EV_ERROR)) {
|
||||
if (events[i].filter == EVFILT_WRITE)
|
||||
statuses[0] = state->machine(state, JANET_ASYNC_EVENT_WRITE);
|
||||
if (events[i].filter == EVFILT_READ)
|
||||
statuses[1] = state->machine(state, JANET_ASYNC_EVENT_READ);
|
||||
if ((events[i].flags & EV_EOF) && !(events[i].data > 0))
|
||||
statuses[3] = state->machine(state, JANET_ASYNC_EVENT_HUP);
|
||||
} else {
|
||||
statuses[2] = state->machine(state, JANET_ASYNC_EVENT_ERR);
|
||||
}
|
||||
if (statuses[0] == JANET_ASYNC_STATUS_DONE ||
|
||||
statuses[1] == JANET_ASYNC_STATUS_DONE ||
|
||||
statuses[2] == JANET_ASYNC_STATUS_DONE ||
|
||||
statuses[3] == JANET_ASYNC_STATUS_DONE)
|
||||
janet_unlisten(state, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void janet_ev_init(void) {
|
||||
janet_ev_init_common();
|
||||
janet_ev_setup_selfpipe();
|
||||
janet_vm.kq = kqueue();
|
||||
janet_vm.timer_enabled = 0;
|
||||
if (janet_vm.kq == -1) goto error;
|
||||
struct kevent events[2];
|
||||
/* Don't use JANET_KQUEUE_TS here, as even under FreeBSD we use intervals
|
||||
* here. */
|
||||
EV_SETx(&events[0],
|
||||
JANET_KQUEUE_TIMER_IDENT,
|
||||
EVFILT_TIMER,
|
||||
JANET_KQUEUE_TF,
|
||||
NOTE_MSECONDS, JANET_KQUEUE_MIN_INTERVAL, &janet_vm.timer);
|
||||
EV_SETx(&events[1], janet_vm.selfpipe[0], EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, janet_vm.selfpipe);
|
||||
add_kqueue_events(events, 2);
|
||||
return;
|
||||
error:
|
||||
JANET_EXIT("failed to initialize event loop");
|
||||
}
|
||||
|
||||
void janet_ev_deinit(void) {
|
||||
janet_ev_deinit_common();
|
||||
close(janet_vm.kq);
|
||||
janet_ev_cleanup_selfpipe();
|
||||
janet_vm.kq = 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include <poll.h>
|
||||
@@ -2442,12 +2651,34 @@ JANET_CORE_FN(cfun_ev_go,
|
||||
"events occur in the newly scheduled fiber, an event will be pushed to the supervisor. "
|
||||
"If not provided, the new fiber will inherit the current supervisor.") {
|
||||
janet_arity(argc, 1, 3);
|
||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||
Janet value = argc >= 2 ? argv[1] : janet_wrap_nil();
|
||||
void *supervisor = janet_optabstract(argv, argc, 2, &janet_channel_type, janet_vm.root_fiber->supervisor_channel);
|
||||
JanetFiber *fiber;
|
||||
if (janet_checktype(argv[0], JANET_FUNCTION)) {
|
||||
/* Create a fiber for the user */
|
||||
JanetFunction *func = janet_unwrap_function(argv[0]);
|
||||
if (func->def->min_arity > 1) {
|
||||
janet_panicf("task function must accept 0 or 1 arguments");
|
||||
}
|
||||
fiber = janet_fiber(func, 64, func->def->min_arity, &value);
|
||||
fiber->flags |=
|
||||
JANET_FIBER_MASK_ERROR |
|
||||
JANET_FIBER_MASK_USER0 |
|
||||
JANET_FIBER_MASK_USER1 |
|
||||
JANET_FIBER_MASK_USER2 |
|
||||
JANET_FIBER_MASK_USER3 |
|
||||
JANET_FIBER_MASK_USER4;
|
||||
if (!janet_vm.fiber->env) {
|
||||
janet_vm.fiber->env = janet_table(0);
|
||||
}
|
||||
fiber->env = janet_table(0);
|
||||
fiber->env->proto = janet_vm.fiber->env;
|
||||
} else {
|
||||
fiber = janet_getfiber(argv, 0);
|
||||
}
|
||||
fiber->supervisor_channel = supervisor;
|
||||
janet_schedule(fiber, value);
|
||||
return argv[0];
|
||||
return janet_wrap_fiber(fiber);
|
||||
}
|
||||
|
||||
/* For ev/thread - Run an interpreter in the new thread. */
|
||||
@@ -2504,8 +2735,26 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
|
||||
JANET_MARSHAL_UNSAFE, NULL, &nextbytes);
|
||||
Janet value = janet_unmarshal(nextbytes, endbytes - nextbytes,
|
||||
JANET_MARSHAL_UNSAFE, NULL, &nextbytes);
|
||||
if (!janet_checktype(fiberv, JANET_FIBER)) janet_panicf("expected fiber, got %v", fiberv);
|
||||
JanetFiber *fiber = janet_unwrap_fiber(fiberv);
|
||||
JanetFiber *fiber;
|
||||
if (!janet_checktype(fiberv, JANET_FIBER)) {
|
||||
if (!janet_checktype(fiberv, JANET_FUNCTION)) {
|
||||
janet_panicf("expected function|fiber, got %v", fiberv);
|
||||
}
|
||||
JanetFunction *func = janet_unwrap_function(fiberv);
|
||||
if (func->def->min_arity > 1) {
|
||||
janet_panicf("thread function must accept 0 or 1 arguments");
|
||||
}
|
||||
fiber = janet_fiber(func, 64, func->def->min_arity, &value);
|
||||
fiber->flags |=
|
||||
JANET_FIBER_MASK_ERROR |
|
||||
JANET_FIBER_MASK_USER0 |
|
||||
JANET_FIBER_MASK_USER1 |
|
||||
JANET_FIBER_MASK_USER2 |
|
||||
JANET_FIBER_MASK_USER3 |
|
||||
JANET_FIBER_MASK_USER4;
|
||||
} else {
|
||||
fiber = janet_unwrap_fiber(fiberv);
|
||||
}
|
||||
fiber->supervisor_channel = janet_vm.user;
|
||||
janet_schedule(fiber, value);
|
||||
janet_loop();
|
||||
@@ -2542,9 +2791,10 @@ static JanetEVGenericMessage janet_go_thread_subr(JanetEVGenericMessage args) {
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_ev_thread,
|
||||
"(ev/thread fiber &opt value flags supervisor)",
|
||||
"Resume a (copy of a) `fiber` in a new operating system thread, optionally passing `value` "
|
||||
"to resume with. "
|
||||
"(ev/thread main &opt value flags supervisor)",
|
||||
"Run `main` in a new operating system thread, optionally passing `value` "
|
||||
"to resume with. The parameter `main` can either be a fiber, or a function that accepts "
|
||||
"0 or 1 arguments. "
|
||||
"Unlike `ev/go`, this function will suspend the current fiber until the thread is complete. "
|
||||
"If you want to run the thread without waiting for a result, pass the `:n` flag to return nil immediately. "
|
||||
"Otherwise, returns nil. Available flags:\n\n"
|
||||
@@ -2552,8 +2802,8 @@ JANET_CORE_FN(cfun_ev_thread,
|
||||
"* `:a` - don't copy abstract registry to new thread (performance optimization)\n"
|
||||
"* `:c` - don't copy cfunction registry to new thread (performance optimization)") {
|
||||
janet_arity(argc, 1, 4);
|
||||
janet_getfiber(argv, 0);
|
||||
Janet value = argc >= 2 ? argv[1] : janet_wrap_nil();
|
||||
if (!janet_checktype(argv[0], JANET_FUNCTION)) janet_getfiber(argv, 0);
|
||||
uint64_t flags = 0;
|
||||
if (argc >= 3) {
|
||||
flags = janet_getflags(argv, 2, "nac");
|
||||
|
||||
@@ -123,6 +123,8 @@ static void janet_mark_abstract(void *adata) {
|
||||
|
||||
/* Mark a bunch of items in memory */
|
||||
static void janet_mark_many(const Janet *values, int32_t n) {
|
||||
if (values == NULL)
|
||||
return;
|
||||
const Janet *end = values + n;
|
||||
while (values < end) {
|
||||
janet_mark(*values);
|
||||
|
||||
@@ -384,6 +384,7 @@ static void marshal_one_abstract(MarshalState *st, Janet x, int flags) {
|
||||
janet_abstract_incref(abstract);
|
||||
pushbyte(st, LB_THREADED_ABSTRACT);
|
||||
pushbytes(st, (uint8_t *) &abstract, sizeof(abstract));
|
||||
MARK_SEEN();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
@@ -560,9 +561,9 @@ static void marshal_one(MarshalState *st, Janet x, int flags) {
|
||||
case JANET_FUNCTION: {
|
||||
pushbyte(st, LB_FUNCTION);
|
||||
JanetFunction *func = janet_unwrap_function(x);
|
||||
pushint(st, func->def->environments_length);
|
||||
/* Mark seen before reading def */
|
||||
MARK_SEEN();
|
||||
pushint(st, func->def->environments_length);
|
||||
marshal_one_def(st, func->def, flags);
|
||||
for (int32_t i = 0; i < func->def->environments_length; i++)
|
||||
marshal_one_env(st, func->envs[i], flags + 1);
|
||||
@@ -1262,15 +1263,12 @@ static const uint8_t *unmarshal_one(
|
||||
}
|
||||
func = janet_gcalloc(JANET_MEMORY_FUNCTION, sizeof(JanetFunction) +
|
||||
len * sizeof(JanetFuncEnv));
|
||||
func->def = NULL;
|
||||
*out = janet_wrap_function(func);
|
||||
janet_v_push(st->lookup, *out);
|
||||
data = unmarshal_one_def(st, data, &def, flags + 1);
|
||||
if (def->environments_length != len) {
|
||||
janet_panicf("invalid function - env count does not match def (%d != %d)",
|
||||
len, def->environments_length);
|
||||
}
|
||||
func->def = def;
|
||||
for (int32_t i = 0; i < def->environments_length; i++) {
|
||||
for (int32_t i = 0; i < len; i++) {
|
||||
data = unmarshal_one_env(st, data, &(func->envs[i]), flags + 1);
|
||||
}
|
||||
return data;
|
||||
|
||||
@@ -375,7 +375,7 @@ void janet_lib_math(JanetTable *env) {
|
||||
JANET_CORE_DEF(env, "math/int-max", janet_wrap_number(JANET_INTMAX_DOUBLE),
|
||||
"The maximum contiguous integer represtenable by a double (-(2^53))");
|
||||
#ifdef NAN
|
||||
JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN");
|
||||
JANET_CORE_DEF(env, "math/nan", janet_wrap_number(NAN), "Not a number (IEEE-754 NaN)");
|
||||
#else
|
||||
JANET_CORE_DEF(env, "math/nan", janet_wrap_number(0.0 / 0.0), "Not a number (IEEE-754 NaN)");
|
||||
#endif
|
||||
|
||||
349
src/core/net.c
349
src/core/net.c
@@ -38,6 +38,7 @@
|
||||
#pragma comment (lib, "Mswsock.lib")
|
||||
#pragma comment (lib, "Advapi32.lib")
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
@@ -259,7 +260,8 @@ static int janet_get_sockettype(Janet *argv, int32_t argc, int32_t n) {
|
||||
}
|
||||
|
||||
/* Needs argc >= offset + 2 */
|
||||
/* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1, otherwise 0 */
|
||||
/* For unix paths, just rertuns a single sockaddr and sets *is_unix to 1,
|
||||
* otherwise 0. Also, ignores is_bind when is a unix socket. */
|
||||
static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int socktype, int passive, int *is_unix) {
|
||||
/* Unix socket support - not yet supported on windows. */
|
||||
#ifndef JANET_WINDOWS
|
||||
@@ -285,12 +287,12 @@ static struct addrinfo *janet_get_addrinfo(Janet *argv, int32_t offset, int sock
|
||||
}
|
||||
#endif
|
||||
/* Get host and port */
|
||||
const char *host = janet_getcstring(argv, offset);
|
||||
const char *port;
|
||||
char *host = (char *)janet_getcstring(argv, offset);
|
||||
char *port = NULL;
|
||||
if (janet_checkint(argv[offset + 1])) {
|
||||
port = (const char *)janet_to_string(argv[offset + 1]);
|
||||
port = (char *)janet_to_string(argv[offset + 1]);
|
||||
} else {
|
||||
port = janet_optcstring(argv, offset + 2, offset + 1, NULL);
|
||||
port = (char *)janet_optcstring(argv, offset + 2, offset + 1, NULL);
|
||||
}
|
||||
/* getaddrinfo */
|
||||
struct addrinfo *ai = NULL;
|
||||
@@ -357,16 +359,48 @@ JANET_CORE_FN(cfun_net_sockaddr,
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_net_connect,
|
||||
"(net/connect host port &opt type)",
|
||||
"(net/connect host port &opt type bindhost bindport)",
|
||||
"Open a connection to communicate with a server. Returns a duplex stream "
|
||||
"that can be used to communicate with the server. Type is an optional keyword "
|
||||
"to specify a connection type, either :stream or :datagram. The default is :stream. ") {
|
||||
janet_arity(argc, 2, 3);
|
||||
"to specify a connection type, either :stream or :datagram. The default is :stream. "
|
||||
"Bindhost is an optional string to select from what address to make the outgoing "
|
||||
"connection, with the default being the same as using the OS's preferred address. ") {
|
||||
janet_arity(argc, 2, 5);
|
||||
|
||||
/* Check arguments */
|
||||
int socktype = janet_get_sockettype(argv, argc, 2);
|
||||
int is_unix = 0;
|
||||
char *bindhost = (char *) janet_optcstring(argv, argc, 3, NULL);
|
||||
char *bindport = NULL;
|
||||
if (janet_checkint(argv[4])) {
|
||||
bindport = (char *)janet_to_string(argv[4]);
|
||||
} else {
|
||||
bindport = (char *)janet_optcstring(argv, argc, 4, NULL);
|
||||
}
|
||||
|
||||
/* Where we're connecting to */
|
||||
struct addrinfo *ai = janet_get_addrinfo(argv, 0, socktype, 0, &is_unix);
|
||||
|
||||
/* Check if we're binding address */
|
||||
struct addrinfo *binding = NULL;
|
||||
if (bindhost != NULL) {
|
||||
if (is_unix) {
|
||||
freeaddrinfo(ai);
|
||||
janet_panic("bindhost not supported for unix domain sockets");
|
||||
}
|
||||
/* getaddrinfo */
|
||||
struct addrinfo hints;
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = socktype;
|
||||
hints.ai_flags = 0;
|
||||
int status = getaddrinfo(bindhost, bindport, &hints, &binding);
|
||||
if (status) {
|
||||
freeaddrinfo(ai);
|
||||
janet_panicf("could not get address info for bindhost: %s", gai_strerror(status));
|
||||
}
|
||||
}
|
||||
|
||||
/* Create socket */
|
||||
JSock sock = JSOCKDEFAULT;
|
||||
void *addr = NULL;
|
||||
@@ -375,7 +409,9 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
if (is_unix) {
|
||||
sock = socket(AF_UNIX, socktype | JSOCKFLAGS, 0);
|
||||
if (!JSOCKVALID(sock)) {
|
||||
janet_panicf("could not create socket: %V", janet_ev_lasterr());
|
||||
Janet v = janet_ev_lasterr();
|
||||
janet_free(ai);
|
||||
janet_panicf("could not create socket: %V", v);
|
||||
}
|
||||
addr = (void *) ai;
|
||||
addrlen = sizeof(struct sockaddr_un);
|
||||
@@ -396,17 +432,42 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
}
|
||||
}
|
||||
if (NULL == addr) {
|
||||
Janet v = janet_ev_lasterr();
|
||||
if (binding) freeaddrinfo(binding);
|
||||
freeaddrinfo(ai);
|
||||
janet_panicf("could not create socket: %V", janet_ev_lasterr());
|
||||
janet_panicf("could not create socket: %V", v);
|
||||
}
|
||||
}
|
||||
|
||||
/* Bind to bindhost and bindport if given */
|
||||
if (binding) {
|
||||
struct addrinfo *rp = NULL;
|
||||
int did_bind = 0;
|
||||
for (rp = ai; rp != NULL; rp = rp->ai_next) {
|
||||
if (bind(sock, rp->ai_addr, (int) rp->ai_addrlen) == 0) {
|
||||
did_bind = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!did_bind) {
|
||||
Janet v = janet_ev_lasterr();
|
||||
freeaddrinfo(binding);
|
||||
freeaddrinfo(ai);
|
||||
JSOCKCLOSE(sock);
|
||||
janet_panicf("could not bind outgoing address: %V", v);
|
||||
} else {
|
||||
freeaddrinfo(binding);
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect to socket */
|
||||
#ifdef JANET_WINDOWS
|
||||
int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
|
||||
Janet lasterr = janet_ev_lasterr();
|
||||
freeaddrinfo(ai);
|
||||
#else
|
||||
int status = connect(sock, addr, addrlen);
|
||||
Janet lasterr = janet_ev_lasterr();
|
||||
if (is_unix) {
|
||||
janet_free(ai);
|
||||
} else {
|
||||
@@ -416,7 +477,7 @@ JANET_CORE_FN(cfun_net_connect,
|
||||
|
||||
if (status == -1) {
|
||||
JSOCKCLOSE(sock);
|
||||
janet_panicf("could not connect to socket: %V", janet_ev_lasterr());
|
||||
janet_panicf("could not connect socket: %V", lasterr);
|
||||
}
|
||||
|
||||
/* Set up the socket for non-blocking IO after connect - TODO - non-blocking connect? */
|
||||
@@ -570,6 +631,270 @@ JANET_CORE_FN(cfun_net_listen,
|
||||
}
|
||||
}
|
||||
|
||||
/* Definitions from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.h
|
||||
* SO_MAX, SA_PORT_NONE, SO_MIN, SA_ADDRSTRLEN, sa_ntoa, sa_family,
|
||||
* sa_port */
|
||||
#define SO_MAX(a, b) (((a) > (b))? (a) : (b))
|
||||
#define SA_PORT_NONE (&(in_port_t){ 0 })
|
||||
#define SO_MIN(a, b) (((a) < (b))? (a) : (b))
|
||||
#ifndef JANET_WINDOWS
|
||||
#define SA_ADDRSTRLEN SO_MAX(INET6_ADDRSTRLEN, (sizeof ((struct sockaddr_un *)0)->sun_path) + 1)
|
||||
#else
|
||||
#define SA_ADDRSTRLEN (INET6_ADDRSTRLEN + 1)
|
||||
#endif
|
||||
#define sa_ntoa(sa) sa_ntoa_((char [SA_ADDRSTRLEN]){ 0 }, SA_ADDRSTRLEN, (sa))
|
||||
#define sa_family(...) sa_family(__VA_ARGS__)
|
||||
#define sa_port(...) sa_port(__VA_ARGS__)
|
||||
#ifdef JANET_WINDOWS
|
||||
typedef short sa_family_t; /* added to silence warnings */
|
||||
typedef unsigned short in_port_t; /* added to silence warnings */
|
||||
#endif
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.h */
|
||||
union sockaddr_arg {
|
||||
struct sockaddr *sa;
|
||||
const struct sockaddr *c_sa;
|
||||
struct sockaddr_storage *ss;
|
||||
struct sockaddr_storage *c_ss;
|
||||
struct sockaddr_in *sin;
|
||||
struct sockaddr_in *c_sin;
|
||||
struct sockaddr_in6 *sin6;
|
||||
struct sockaddr_in6 *c_sin6;
|
||||
#ifndef JANET_WINDOWS
|
||||
struct sockaddr_un *sun;
|
||||
#endif
|
||||
struct sockaddr_un *c_sun;
|
||||
union sockaddr_any *any;
|
||||
union sockaddr_any *c_any;
|
||||
|
||||
void *ptr;
|
||||
void *c_ptr;
|
||||
};
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.h */
|
||||
union sockaddr_any {
|
||||
struct sockaddr sa;
|
||||
struct sockaddr_storage ss;
|
||||
struct sockaddr_in sin;
|
||||
struct sockaddr_in6 sin6;
|
||||
#ifndef JANET_WINDOWS
|
||||
struct sockaddr_un sun;
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.h */
|
||||
static inline union sockaddr_arg sockaddr_ref(void *arg) {
|
||||
return (union sockaddr_arg) {
|
||||
arg
|
||||
};
|
||||
}
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.h */
|
||||
static inline sa_family_t *(sa_family)(void *arg) {
|
||||
return &sockaddr_ref(arg).sa->sa_family;
|
||||
}
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.h */
|
||||
static inline in_port_t *(sa_port)(void *arg, const in_port_t *def, int *error) {
|
||||
switch (*sa_family(arg)) {
|
||||
case AF_INET:
|
||||
return &sockaddr_ref(arg).sin->sin_port;
|
||||
case AF_INET6:
|
||||
return &sockaddr_ref(arg).sin6->sin6_port;
|
||||
default:
|
||||
if (error)
|
||||
*error = EAFNOSUPPORT;
|
||||
|
||||
return (in_port_t *)def;
|
||||
}
|
||||
}
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.c
|
||||
* Original was dns_strlcpy */
|
||||
size_t janet_socket_strlcpy(char *dst, const char *src, size_t lim) {
|
||||
char *d = dst;
|
||||
char *e = &dst[lim];
|
||||
const char *s = src;
|
||||
|
||||
if (d < e) {
|
||||
do {
|
||||
if ('\0' == (*d++ = *s++))
|
||||
return s - src - 1;
|
||||
} while (d < e);
|
||||
|
||||
d[-1] = '\0';
|
||||
}
|
||||
|
||||
while (*s++ != '\0')
|
||||
;;
|
||||
|
||||
return s - src - 1;
|
||||
}
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.c */
|
||||
char *sa_ntop(char *dst, size_t lim, const void *src, const char *def, int *_error) {
|
||||
union sockaddr_any *any = (void *)src;
|
||||
const char *unspec = "0.0.0.0";
|
||||
char text[SA_ADDRSTRLEN];
|
||||
int error;
|
||||
|
||||
switch (*sa_family(&any->sa)) {
|
||||
case AF_INET:
|
||||
unspec = "0.0.0.0";
|
||||
|
||||
if (!inet_ntop(AF_INET, &any->sin.sin_addr, text, sizeof text))
|
||||
goto syerr;
|
||||
|
||||
break;
|
||||
case AF_INET6:
|
||||
unspec = "::";
|
||||
|
||||
if (!inet_ntop(AF_INET6, &any->sin6.sin6_addr, text, sizeof text))
|
||||
goto syerr;
|
||||
|
||||
break;
|
||||
#ifndef JANET_WINDOWS
|
||||
case AF_UNIX:
|
||||
unspec = "/nonexistent";
|
||||
|
||||
memset(text, 0, sizeof text);
|
||||
memcpy(text, any->sun.sun_path, SO_MIN(sizeof text - 1, sizeof any->sun.sun_path));
|
||||
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
error = EAFNOSUPPORT;
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (janet_socket_strlcpy(dst, text, lim) >= lim) {
|
||||
error = ENOSPC;
|
||||
|
||||
goto error;
|
||||
}
|
||||
|
||||
return dst;
|
||||
syerr:
|
||||
error = errno;
|
||||
error:
|
||||
if (_error)
|
||||
*_error = error;
|
||||
|
||||
/*
|
||||
* NOTE: Always write something in case caller ignores errors, such
|
||||
* as when caller is using the sa_ntoa() macro.
|
||||
*/
|
||||
safe_memcpy(dst, (def) ? def : unspec, lim);
|
||||
|
||||
return (char *)def;
|
||||
}
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.h */
|
||||
static inline char *sa_ntoa_(char *dst, size_t lim, const void *src) {
|
||||
return sa_ntop(dst, lim, src, NULL, &(int) {
|
||||
0
|
||||
}), dst;
|
||||
}
|
||||
|
||||
/* Definition from:
|
||||
* https://github.com/wahern/cqueues/blog/master/src/lib/socket.c
|
||||
* Originaly was lso_pushname */
|
||||
static Janet janet_so_getname(const struct sockaddr_storage *ss, socklen_t slen) {
|
||||
uint8_t *hn = NULL;
|
||||
uint16_t hp = 0;
|
||||
size_t plen = SA_ADDRSTRLEN;
|
||||
|
||||
switch (ss->ss_family) {
|
||||
case AF_INET:
|
||||
/* fall through */
|
||||
case AF_INET6:
|
||||
/* hn = hostname, hp = hostport */
|
||||
hn = (uint8_t *)sa_ntoa(ss);
|
||||
hp = ntohs(*sa_port((void *)ss, SA_PORT_NONE, NULL));
|
||||
break;
|
||||
#ifndef JANET_WINDOWS
|
||||
case AF_UNIX:
|
||||
/* support nameless sockets, linux-ism */
|
||||
if (slen > offsetof(struct sockaddr_un, sun_path)) {
|
||||
struct sockaddr_un *sun = (struct sockaddr_un *)ss;
|
||||
char *pe = (char *)sun + SO_MIN(sizeof * sun, slen);
|
||||
|
||||
while (pe > sun->sun_path && pe[-1] == '\0')
|
||||
--pe;
|
||||
|
||||
if ((plen = pe - sun->sun_path) > 0) {
|
||||
hn = (uint8_t *)sun->sun_path;
|
||||
} else {
|
||||
hn = (uint8_t *)"@";
|
||||
plen = 1;
|
||||
}
|
||||
} else {
|
||||
hn = (uint8_t *)"@";
|
||||
plen = 1;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
hn = (uint8_t *)"";
|
||||
plen = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
Janet name[2];
|
||||
int32_t len = 1;
|
||||
name[0] = janet_wrap_string(janet_cstring((const char *)hn));
|
||||
if (hp > 0) {
|
||||
len++;
|
||||
name[1] = janet_wrap_integer(hp);
|
||||
}
|
||||
|
||||
return janet_wrap_tuple(janet_tuple_n(name, len));
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_net_getsockname,
|
||||
"(net/localname stream)",
|
||||
"Gets the local address and port in a tuple in that order.") {
|
||||
janet_arity(argc, 1, 1);
|
||||
JanetStream *js = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen = sizeof ss;
|
||||
memset(&ss, 0, slen);
|
||||
|
||||
int error;
|
||||
if (0 != (error = getsockname((JSock)js->handle, (struct sockaddr *) &ss, &slen)))
|
||||
janet_panicf("Failed to get peername on fd %d, error: %s", js->handle, janet_ev_lasterr());
|
||||
|
||||
return janet_so_getname(&ss, slen);
|
||||
}
|
||||
|
||||
|
||||
JANET_CORE_FN(cfun_net_getpeername,
|
||||
"(net/peername stream)",
|
||||
"Gets the remote peer's address and port in a tuple in that order.") {
|
||||
janet_arity(argc, 1, 1);
|
||||
JanetStream *js = janet_getabstract(argv, 0, &janet_stream_type);
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen = sizeof ss;
|
||||
memset(&ss, 0, slen);
|
||||
|
||||
int error;
|
||||
if (0 != (error = getpeername((JSock)js->handle, (struct sockaddr *)&ss, &slen))) {
|
||||
janet_panicf("Failed to get peername on fd %d, error: %s", js->handle, janet_ev_lasterr());
|
||||
}
|
||||
|
||||
return janet_so_getname(&ss, slen);
|
||||
}
|
||||
|
||||
JANET_CORE_FN(cfun_stream_accept_loop,
|
||||
"(net/accept-loop stream handler)",
|
||||
"Shorthand for running a server stream that will continuously accept new connections. "
|
||||
@@ -739,6 +1064,8 @@ void janet_lib_net(JanetTable *env) {
|
||||
JANET_CORE_REG("net/flush", cfun_stream_flush),
|
||||
JANET_CORE_REG("net/connect", cfun_net_connect),
|
||||
JANET_CORE_REG("net/shutdown", cfun_net_shutdown),
|
||||
JANET_CORE_REG("net/peername", cfun_net_getpeername),
|
||||
JANET_CORE_REG("net/localname", cfun_net_getsockname),
|
||||
JANET_REG_END
|
||||
};
|
||||
janet_core_cfuns_ext(env, NULL, net_cfuns);
|
||||
|
||||
@@ -158,7 +158,7 @@ JANET_CORE_FN(os_arch,
|
||||
"(os/arch)",
|
||||
"Check the ISA that janet was compiled for. Returns one of:\n\n"
|
||||
"* :x86\n\n"
|
||||
"* :x86-64\n\n"
|
||||
"* :x64\n\n"
|
||||
"* :arm\n\n"
|
||||
"* :aarch64\n\n"
|
||||
"* :sparc\n\n"
|
||||
|
||||
@@ -171,6 +171,11 @@ struct JanetVM {
|
||||
int epoll;
|
||||
int timerfd;
|
||||
int timer_enabled;
|
||||
#elif defined(JANET_EV_KQUEUE)
|
||||
JanetHandle selfpipe[2];
|
||||
int kq;
|
||||
int timer;
|
||||
int timer_enabled;
|
||||
#else
|
||||
JanetHandle selfpipe[2];
|
||||
struct pollfd *fds;
|
||||
|
||||
@@ -37,8 +37,9 @@ static void push_traversal_node(void *lhs, void *rhs, int32_t index2) {
|
||||
node.other = (JanetGCObject *) rhs;
|
||||
node.index = 0;
|
||||
node.index2 = index2;
|
||||
if (janet_vm.traversal + 1 >= janet_vm.traversal_top) {
|
||||
size_t oldsize = janet_vm.traversal - janet_vm.traversal_base;
|
||||
int is_new = janet_vm.traversal_base == NULL;
|
||||
if (is_new || (janet_vm.traversal + 1 >= janet_vm.traversal_top)) {
|
||||
size_t oldsize = is_new ? 0 : (janet_vm.traversal - janet_vm.traversal_base);
|
||||
size_t newsize = 2 * oldsize + 1;
|
||||
if (newsize < 128) {
|
||||
newsize = 128;
|
||||
|
||||
@@ -198,6 +198,16 @@ extern "C" {
|
||||
#define JANET_EV_EPOLL
|
||||
#endif
|
||||
|
||||
/* Enable or disable kqueue on BSD */
|
||||
#if defined(JANET_BSD) && !defined(JANET_EV_NO_KQUEUE)
|
||||
#define JANET_EV_KQUEUE
|
||||
#endif
|
||||
|
||||
/* Enable or disable kqueue on Apple */
|
||||
#if defined(JANET_APPLE) && !defined(JANET_EV_NO_KQUEUE)
|
||||
#define JANET_EV_KQUEUE
|
||||
#endif
|
||||
|
||||
/* How to export symbols */
|
||||
#ifndef JANET_API
|
||||
#ifdef JANET_WINDOWS
|
||||
@@ -320,11 +330,16 @@ typedef struct {
|
||||
/* Some extra includes if EV is enabled */
|
||||
#ifdef JANET_EV
|
||||
#ifdef JANET_WINDOWS
|
||||
#ifdef JANET_NET
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
#include <windows.h>
|
||||
typedef CRITICAL_SECTION JanetOSMutex;
|
||||
typedef struct JanetDudCriticalSection {
|
||||
/* Avoid including windows.h here - instead, create a structure of the same size */
|
||||
/* Needs to be same size as crtical section see WinNT.h for CRITCIAL_SECTION definition */
|
||||
void *debug_info;
|
||||
long lock_count;
|
||||
long recursion_count;
|
||||
void *owning_thread;
|
||||
void *lock_semaphore;
|
||||
unsigned long spin_count;
|
||||
} JanetOSMutex;
|
||||
#else
|
||||
#include <pthread.h>
|
||||
typedef pthread_mutex_t JanetOSMutex;
|
||||
|
||||
@@ -151,6 +151,43 @@
|
||||
|
||||
(:close s))
|
||||
|
||||
# Test localname and peername
|
||||
(repeat 10
|
||||
(defn check-matching-names [stream &opt direction]
|
||||
"Checks that the remote agrees with the local about ip/port"
|
||||
(let [[my-ip my-port] (net/localname stream)
|
||||
[remote-ip remote-port] (net/peername stream)
|
||||
to-write (string/join
|
||||
@[my-ip (string my-port)
|
||||
remote-ip (string remote-port)]
|
||||
" ")
|
||||
buffer @""]
|
||||
(if (= direction :write)
|
||||
(do (net/write stream to-write) (net/read stream 1024 buffer))
|
||||
(do (net/read stream 1024 buffer) (net/write stream to-write)))
|
||||
(def comparison (string/split " " buffer))
|
||||
(assert (and (= my-ip (get comparison 2))
|
||||
(= (string my-port) (get comparison 3))
|
||||
(= remote-ip (get comparison 0))
|
||||
(= (string remote-port) (get comparison 1)))
|
||||
"localname does not match peername")))
|
||||
(defn names-handler
|
||||
"Simple handler for connections."
|
||||
[stream]
|
||||
(defer (:close stream)
|
||||
(check-matching-names stream)))
|
||||
|
||||
(def s (net/server "127.0.0.1" "8000" names-handler))
|
||||
(assert s "made server 1")
|
||||
|
||||
(defn test-names []
|
||||
(with [conn (net/connect "127.0.0.1" "8000")]
|
||||
(check-matching-names conn :write)))
|
||||
|
||||
(test-names)
|
||||
(test-names)
|
||||
(:close s))
|
||||
|
||||
# Create pipe
|
||||
|
||||
(var pipe-counter 0)
|
||||
|
||||
Reference in New Issue
Block a user