1
0
mirror of https://github.com/janet-lang/janet synced 2025-11-10 12:33:08 +00:00

Compare commits

..

77 Commits

Author SHA1 Message Date
Calvin Rose
9b4b24edf7 Prepare for 1.17.2 release. 2021-09-18 13:42:26 -05:00
Calvin Rose
8b10a5fb7c Format and update CHANGELOG.md 2021-09-18 13:40:32 -05:00
Calvin Rose
b0d0d9cad2 Address #809 - treat first docstring line different from others.
Only do this if the docstring starts with an open parentheses.
2021-09-18 12:41:11 -05:00
Calvin Rose
9abee3f29a Add semi-colon. 2021-09-17 19:20:59 -05:00
Calvin Rose
bf9b6b1301 Avoid including windows.h in janet.h for JanetOSMutex. 2021-09-17 16:59:50 -05:00
Calvin Rose
8cd57025a0 Add makefile var to fix jpm to a tag/branch 2021-09-17 16:38:11 -05:00
Calvin Rose
faf60b6b1f Pass DESTDIR directly to jpm bootstrap script. 2021-09-16 18:36:29 -05:00
Calvin Rose
da2c1be49c Fix #801 threaded abstract cyclic references in marshalling.
We forgot to mark threaded abstract types as "seen" when marshalling so
we would mistakenly marshal them twice. This messed up unmarshalling.
2021-09-14 21:12:02 -05:00
Calvin Rose
92c02449f4 Merge pull request #800 from marler8997/fixUbInGC
add NULL check in gc.c to avoid UB
2021-09-14 16:04:12 -05:00
Jonathan Marler
e381622a9a add NULL check in gc.c to avoid UB
After the UB was fixed in value.c, I tried running the build again and encoutered another instance of UB in gc.c.  With this fixed I can now build janet with ubsan enabled, meaning there's no more UB encountered in janet_boot during the build.
2021-09-11 19:50:52 -06:00
bakpakin
b799223ebc Merge branch 'master' of github.com:janet-lang/janet 2021-09-11 10:34:22 -05:00
bakpakin
40ef224a95 Update test code. 2021-09-11 10:34:08 -05:00
Calvin Rose
a4c20b6e1c Merge pull request #797 from sogaiu/math-nan-doc-tweak
Tweak math/nan docstring
2021-09-11 09:13:38 -05:00
sogaiu
e6ee867f72 Tweak math/nan docstring 2021-09-11 16:54:35 +09:00
bakpakin
468a31f515 Address #795 - add NULL check to avoid UB 2021-09-09 07:20:26 -05:00
bakpakin
4d746794cc Merge branch 'bindport' 2021-09-07 22:45:22 -05:00
bakpakin
02d2a66ef2 Merge branch 'master' of github.com:janet-lang/janet 2021-09-07 22:44:59 -05:00
Calvin Rose
4638baf545 Merge pull request #790 from llmII/feature-getsockname-getpeername
Work in progress - more socket functions
2021-09-07 22:44:40 -05:00
bakpakin
2be23d3768 Fix meson build. 2021-09-07 22:11:45 -05:00
bakpakin
b39b1746ba Support bindport. 2021-09-07 21:59:17 -05:00
bakpakin
24f97510b0 Fix incorrect code that created socket twice. 2021-09-07 20:51:33 -05:00
llmII
325d5399fa Code cleanup and attribution set.
All that is left is to test unix sockets.
2021-09-07 20:00:00 -05:00
Calvin Rose
d8f6fbf594 Merge pull request #789 from llmII/feature-bind-connect
Add bind option to net/connect
2021-09-07 18:47:17 -05:00
llmII
21b3e4052c Don't print stuff in tests, CI looks wacky 2021-09-07 17:21:10 -05:00
llmII
bf2928805e Had an extra plen definition, removed. 2021-09-07 17:03:34 -05:00
llmII
7d2bf334c8 Fix incorrect error when argv[3] is null
The `janet_get_addrinfo` function retained code that was meant for
compliance with 3 separate function signatures under a single function
name. Changing things to be a single function signature was broken until
the code pertaining to the aforementioned was stripped out.
2021-09-07 16:11:37 -05:00
llmII
7446802a70 Quit trying to make it 3 different functions
Prior commits was an attempt to make this one function adhere to 3
different function signatures! This puts an end to that and makes it
where it's a single function signature and if one wants to use the 4th
argument they'll need to explicitly set the 3rd argument (to nil for
default).
2021-09-07 14:56:13 -05:00
llmII
077bf5ebae Create test case for localname/peername 2021-09-07 07:12:43 -05:00
llmII
c9bef39f96 Make net/connect special
Keeps net/listen from being affected by changes necessary to make bind
on connect work (while keeping from breaking the API).
2021-09-07 05:40:48 -05:00
llmII
3740eadb7d Seeing if this fixes the last warnings for Windows 2021-09-06 19:57:56 -05:00
llmII
e29fa66a74 More Windows fixes 2021-09-06 19:42:45 -05:00
llmII
ca5406c8e4 More windows fixes
MSVC's output via appveyor is a little lacking in indication of all
issues so I'm hitting them as I can find them.
2021-09-06 19:31:16 -05:00
llmII
7217caacd1 Attempting some more windows related fixes. 2021-09-06 19:26:33 -05:00
Calvin Rose
8081082251 Merge pull request #785 from llmII/feature-kqueue
Add kqueue support to Janet
2021-09-06 18:42:05 -05:00
llmII
1597ca0de5 Cleanup code a bit
Inconsistent indentation and such fixed, superfluous newlines removed,
documentation of new functions.
2021-09-06 18:32:23 -05:00
llmII
8c938ceff9 Fix for Windows (possibly)
Windows does not have <arpa/inet.h> so only include it when not Windows.
2021-09-06 17:48:17 -05:00
llmII
65a6945ea5 Finalize peername and localname
Both now do the right thing and give back all information (host and
port) when possible as a tuple of (host port).
2021-09-06 17:35:49 -05:00
llmII
02640812af Add getsockname (net/localname) 2021-09-06 17:01:09 -05:00
llmII
ba761d5c35 Work in progress - more socket functions
When this is complete we'll have getpeername, getsockname and possibly
getpeerid in the net/* API.
2021-09-06 16:15:01 -05:00
llmII
48a3b1f07f Enable kqueue on MacOS
Make sure JANET_EV_KQUEUE is defined when JANET_APPLE is defined unless
disabled by configuration.
2021-09-06 16:01:06 -05:00
llmII
4370cb77e7 Update documentation.
Documenting the new bindhost parameter to net/connect.
2021-09-06 10:54:11 -05:00
llmII
470e8f6fc7 Reused address info struct incorrectly
Wrongly assumed that `ai` was done being used before binding, it's not,
so create a separate address info for binding...
2021-09-06 10:44:23 -05:00
llmII
b270d88427 More off by one error fixes 2021-09-06 10:12:36 -05:00
llmII
66ce247129 Fixing off by one indexing errors... 2021-09-06 10:01:16 -05:00
llmII
6ad016c587 Check type when getting socket type
janet_get_sockettype expects a keyword but we're making it optional that
the call to the functions that use it with arity >=3 will be guaranteed
to have it as a keyword value! If it's not a keyword then it's the same
as NULL.
2021-09-06 09:53:53 -05:00
llmII
532dac1b95 Check type instead of value
Primarily because trying to check the value results in a panic when the
value is not the type of value requested from the API. Also probably
cheaper and the previous idea of just getting the value then comparing
was pretty stupid (needed a string comparison... and was going to do
pointer comparison).
2021-09-06 09:48:29 -05:00
llmII
2a4bcc262f Don't bind when address info doesn't exist
Simple logic issue, something overlooked.
2021-09-06 09:06:40 -05:00
llmII
1ce2361daf Better error message in connect
Quick thing to help check when failing tests.
2021-09-06 09:02:56 -05:00
llmII
6e8584e8e0 Add bind option to net/connect
This will allow us to set the address we use for outgoing connections.

Builds, haven't checked it passes current tests, haven't checked it
actually works either.
2021-09-06 08:54:24 -05:00
llmII
121aa91139 Fixes for NetBSD (again)
Minimum interval for a timer must be 1 or more (or we get EINVAL) and
Janet fails tests and halts events that the programmer may still be
interested in.
2021-09-05 21:48:53 -05:00
llmII
bbc07c72d3 More NetBSD fixes
A comptime known value of 0 for data in EV_SET with EVFILT_TIMER causes
a complete compilation failure (fails to link). This fixes it by making
it a 1 instead of a 0 for amount of milliseconds in the interval to wait
under NetBSD.
2021-09-05 21:17:41 -05:00
llmII
43b48fdbea More fixes for NetBSD
Kills compiler warnings with regards to implicit conversion of intptr_t
to void*
2021-09-05 21:01:24 -05:00
llmII
604f97aba1 Fixes for BSD where BSD != FreeBSD
NetBSD and OpenBSD lack NOTE_ABSTIME and NOTE_MSECONDS, so we define
those and create a macro that we use for all timeout values in EV_TIMER
events that will on all BSD excepting FreeBSD change an absolute time
into an interval.
2021-09-05 20:48:42 -05:00
Calvin Rose
dc980081cd Fix #783 - change docstring for x86-64 to x64. 2021-09-05 12:32:33 -05:00
llmII
981f03fef3 Remove comments regarding NetBSD breakage
Since those no longer should apply, don't keep them around.
2021-09-05 10:45:45 -05:00
llmII
d40133dc72 NetBSD support
Checking throught NetBSD's man pages, excepting for NetBSD-current,
NetBSD uses `intptr_t` as the type for `.udata`. This change allows for
`.udata` to match whatever type (by cast) the underlying system uses.
2021-09-05 07:30:22 -05:00
llmII
c9fa586fce Code style fixes (pt 2).
Missed some.
2021-09-04 09:37:07 -05:00
llmII
b847a7d90b Code style fixes.
Pretty obvious I thought control statements were glued to their opening
parenthesis at first and then I realized not and voila, a bundle of
mixed style. Hopefully this fixes all of it.
2021-09-04 09:34:47 -05:00
llmII
8b67108dc8 Complete kqueue feature
From this point things should be bug fixes or code formatting most
likely.

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

All tests ran with `gmake test` now pass. `valgrind` with FreeBSD does
not support forking so `gmake valtest` fails once child processes are
started. Determined not an issue, can't fix valgrind.
2021-09-04 08:23:03 -05:00
llmII
b559f9625a Timeout is an absolute time, not an interval.
Fixes the wrong assumption, passing all tests at that point.
2021-09-04 08:02:50 -05:00
llmII
1736c9b0f8 Handle null state, don't read/write on error
Need to guard against errors when reading/writing probably, if there is
an error, forgo those events.

Guard against null state (and the byproduct, a segfault), check if the
state is null before utilizing it.
2021-09-03 23:22:07 -05:00
llmII
4fb2d8d318 Logical error regarding length fixed. 2021-09-03 22:41:59 -05:00
llmII
95891eb0a5 Fix incorrect use of EV_SET on pipes (part 2)
Forgot that we use nearly the same routine for adding and deletion...
can't go around deleting things we haven't put into the changeset!
2021-09-03 22:33:28 -05:00
llmII
c133443eb7 Fix incorrect use of EV_SET on pipes.
It would seem adding a read and a write event filter on a pipe which is
unidirectional might just be a bad idea.
2021-09-03 19:44:27 -05:00
llmII
8f0641f36c Disabling superfluous code
The code in question may be checking things in an erroneous manner?
2021-09-03 17:18:40 -05:00
llmII
f48dbde736 Better exit (error?) message
The prior calls to exit(-1) were wrong anyway and they may at this point
be hindering figuring out what's going on in suite 9.
2021-09-03 17:13:54 -05:00
llmII
f2e4c1ae9a Forgot a semicolon... 2021-09-03 16:31:20 -05:00
llmII
a4aef38cc0 More typo and syntax fixes. 2021-09-03 16:29:39 -05:00
llmII
b445ecde51 Add kqueue option to meson, janetconf, fix typoes 2021-09-03 16:23:15 -05:00
llmII
a209a01284 Add kqueue support to Janet
Note that this is a work in progress and simply a first attempt at
getting some code into place before being able to test it. This code
follows of sorts both the poll and epoll sections of the codebase hoping
to achieve the exact same.
2021-09-03 14:33:47 -05:00
Calvin Rose
7037532943 Errored threads always emit stacktrace or supervisor event.
That way, it is much harder to swallow errors. Error swallowing behavior
would have to be done explicitly by wrapping fibers with `protect` or
`try`.
2021-09-01 21:05:05 -05:00
Calvin Rose
bb405ee1aa Address #778
Relax check that number of closure environments in a function matches
that of the def.

The def could be partially constructed, and so there may be a false
negative. The runtime will check that this is consistent, and the
garbage collector should handle when this constraint is not kept.
2021-08-31 22:58:44 -05:00
Calvin Rose
ef23356309 Threaded supervisors return fiber->last_value instead
of the fiber itself.
2021-08-31 14:50:27 -05:00
Calvin Rose
1613e2593c Update CHANGELOG.md 2021-08-30 22:24:34 -05:00
Calvin Rose
5464a7a379 Allow passing a function to directly to ev/go.
Makes ev/call less useful but ev/go more useful. No need
to construct as many identical intermediate fibers.
2021-08-30 22:22:22 -05:00
Calvin Rose
bb1331e449 Update changelog. 2021-08-30 22:06:28 -05:00
Calvin Rose
acbebc5631 Allow passing function to ev/thread.
Convenient when there is no need to create an entire fiber.
2021-08-30 22:04:15 -05:00
18 changed files with 729 additions and 61 deletions

View File

@@ -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.

View File

@@ -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:

View File

@@ -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)

View File

@@ -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') != ''

View File

@@ -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)

View File

@@ -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)

View File

@@ -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 */

View File

@@ -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

View File

@@ -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");

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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);

View File

@@ -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"

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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)