1
0
mirror of https://github.com/janet-lang/janet synced 2026-04-07 23:41:27 +00:00

Compare commits

..

65 Commits

Author SHA1 Message Date
Calvin Rose
964a800d51 More work on windows event loop code. 2020-10-06 19:07:29 -05:00
Calvin Rose
2e944931b3 Update timestamp function to work on windows. 2020-10-04 15:18:31 -05:00
Calvin Rose
db67538311 Work on getting ev support on windows. 2020-10-04 12:46:15 -05:00
Calvin Rose
307c7e00e2 Merge branch 'master' into ev 2020-10-03 11:09:21 -05:00
Calvin Rose
45feb55483 Add integer parsing to pegs. 2020-09-27 12:19:00 -05:00
Calvin Rose
0a1d902f46 Fix #477
Make the walk function preserve bracket type, which should fix
threading macro behavior when threading into bracketed expressions.
2020-09-26 13:28:29 -05:00
Calvin Rose
959a577b5f Merge branch 'master' into ev 2020-09-26 11:50:13 -05:00
Calvin Rose
b91fe8be5a Update CHANGELOG.md 2020-09-20 12:03:59 -05:00
Calvin Rose
d1f0a13ddc janet_try macro and janet_restore function.
This allows catching panics without using pcall.
2020-09-19 18:47:47 -05:00
Calvin Rose
c455bdad11 Get ready for 1.12.2 release. 2020-09-19 16:54:56 -05:00
Calvin Rose
bc1ef813c2 Fix whitespace. 2020-09-19 16:53:35 -05:00
Calvin Rose
603791c0ba Add more help text to doc macro and default repl. 2020-09-19 16:40:07 -05:00
Calvin Rose
8091b1289f Merge branch 'master' of github.com:janet-lang/janet into master 2020-09-19 16:35:43 -05:00
Calvin Rose
cc0035b1d7 Add help text to repl line. 2020-09-19 16:35:32 -05:00
Calvin Rose
ceba1ba4ee Merge pull request #472 from uvtc/patch-1
fix jpm.1 typos
2020-09-18 20:19:22 -05:00
John Gabriele
468e13501c Update jpm.1
Fix some typos.
2020-09-15 00:06:09 -04:00
Calvin Rose
32bf70571a Fix os/spawn piping on windows and free handles on errors. 2020-09-13 20:49:38 -05:00
Calvin Rose
95f4bd8e23 Merge branch 'master' into ev 2020-09-13 11:24:27 -05:00
Calvin Rose
4c9624db64 Update CHANGELOG.md 2020-09-13 11:06:49 -05:00
Calvin Rose
2cbf4d8ad1 Update documentation for thread/send and thread/receive. 2020-09-13 08:38:37 -05:00
Calvin Rose
524c9b50d4 Add windows implementation for piping. 2020-09-12 19:56:48 -05:00
Calvin Rose
d3147b661b Add :pipe to os/spawn for piping to subprocess.
Similar to Python's subprocess.PIPE, this creates and manages pipes
automatically for the caller.
2020-09-12 19:48:12 -05:00
Calvin Rose
d3182dce51 Merge branch 'master' into ev 2020-09-12 10:02:01 -05:00
Calvin Rose
8763df1cd0 Fix some typos. 2020-09-12 09:38:29 -05:00
Calvin Rose
15e05b692c Windows CI remove those pesky carriage returns. 2020-09-07 16:32:31 -05:00
Calvin Rose
191d0001f4 Add header on BSDs. 2020-09-07 15:22:18 -05:00
Calvin Rose
1a04ce33f1 Conditionally include epoll headers. 2020-09-07 13:13:28 -05:00
Calvin Rose
babfe50550 Merge branch 'master' into ev
Also add poll implementation for ev.
2020-09-07 12:52:50 -05:00
Calvin Rose
17d0b7a985 Improve import's handling of non constant args.
Be much more conservative about which arguments are cast to strings.
2020-08-27 07:40:51 -05:00
Calvin Rose
86e00e865e Merge branch 'master' into ev 2020-08-23 11:25:04 -05:00
Calvin Rose
30522bbf7d Merge branch 'master' into ev 2020-08-16 17:52:36 -05:00
Calvin Rose
fb26c9b2c4 Add ev/select and ev/rselect initial implementation.
Getting closer to a CSP implmententation. Probably
useful to move scheduling fields outside of fibers
and into an external table.
2020-08-09 00:20:27 -05:00
Calvin Rose
78ffb63429 Disallow mutlitple state machines waiting for a single fiber.
A 'select' operator will be channel based, not state machine based.
2020-08-08 07:51:46 -05:00
Calvin Rose
1213990b7d Merge branch 'master' into ev 2020-08-07 19:51:37 -05:00
Calvin Rose
cb898fabf4 Set default channel size to 0. 2020-08-03 07:57:02 -05:00
Calvin Rose
5899671d96 Merge branch 'master' into ev 2020-08-03 07:54:53 -05:00
Calvin Rose
742c5bb639 Use a common queue implementation.
Queues occur in three places, so we use a single
implementation rather than three separate ones. This also
has the result that janet_vm_spawn will not overflow in the case
of channel-heavy, IO-light operation.
2020-08-01 14:20:58 -05:00
Calvin Rose
297de01d95 Add preliminary channel implementation. 2020-08-01 13:13:58 -05:00
Calvin Rose
2eb2dddb59 Begin work on channels. 2020-07-26 23:45:25 -05:00
Calvin Rose
0403e306ed Silence warnings from some compilers. 2020-07-26 08:48:22 -05:00
Calvin Rose
d393fbf360 Merge branch 'master' into ev 2020-07-25 14:07:47 -05:00
Calvin Rose
3960d0f6de Merge branch 'master' into ev 2020-07-25 13:17:05 -05:00
Calvin Rose
553b4d9428 Add timeouts to net functions.
Further debugging of the general timeout system, as well
as having a single fiber wait on multiple state machines (select).
2020-07-19 19:41:12 -05:00
Calvin Rose
df145f4bc9 Merge branch 'master' into ev 2020-07-19 17:20:43 -05:00
Calvin Rose
cd197e8be3 Add ev/call.
This is a common operation, and making fibers manually can be tedious.
2020-07-06 19:13:32 -05:00
Calvin Rose
51cf6465ff Merge branch 'master' into ev 2020-07-06 17:22:38 -05:00
Calvin Rose
bd95f742c0 Merge branch 'master' into ev 2020-07-05 23:14:49 -05:00
Calvin Rose
9ba94d2c6b More work on timeouts and racing listeners.
When two listeners are racing to resume the same fiber, the
first should cancel out the other.
2020-07-05 17:26:17 -05:00
Calvin Rose
a4de83b3a3 Merge branch 'master' into ev 2020-07-05 10:11:23 -05:00
Calvin Rose
37a430c97c Move declarations around. 2020-07-03 13:47:48 -05:00
Calvin Rose
f264cb0b18 Merge branch 'master' into ev 2020-07-03 12:26:01 -05:00
Calvin Rose
a0abf307b4 Merge branch 'master' into ev 2020-07-03 12:14:48 -05:00
Calvin Rose
328ee94412 Merge branch 'master' into ev 2020-06-22 22:25:44 -05:00
Calvin Rose
b1a4f05b5a Explicitly disallow handler for datagram server. 2020-06-13 08:29:48 -05:00
Calvin Rose
ce2079104a Merge branch 'master' into ev 2020-06-11 19:20:51 -05:00
Calvin Rose
d64e9b6263 Remove snapcraft. 2020-06-06 10:31:16 -05:00
Calvin Rose
123710078d Add unix domain socket support to net.
Code is a bit messy, as getaddrinfo does not support
unix domain sockets directly. We require a keyword :unix
instead of the usual hostname string, and the port is the
path to unix domain socket. The UDS should support both stream
and datagram sockets.
2020-06-03 00:53:17 -05:00
Calvin Rose
ec0d0ba368 Initial UDP implementation. 2020-06-02 19:47:50 -05:00
Calvin Rose
3f434f2a44 Add backpressure capability to net. 2020-05-31 15:46:01 -05:00
Calvin Rose
71d8e6b4cd Merge branch 'master' into ev 2020-05-30 11:35:19 -05:00
Calvin Rose
a78af0a7fb Do not explicitly free state machines, instead return a status.
This makes it harder to have some kind of use after free issue.
2020-05-30 11:31:05 -05:00
Calvin Rose
117ae196fd Add net/flush.
Useful for simple TCP protocols (netrepl), which benefit from being able
to immediately send a message.
2020-05-28 19:22:38 -05:00
Calvin Rose
4c211c8dce More updates to the ev library. 2020-05-28 16:51:11 -05:00
Calvin Rose
c10d9b9d9d Merge branch 'master' into asyncio 2020-05-28 10:57:10 -05:00
Calvin Rose
b68b0a256e Start with ev module. 2020-05-28 10:39:40 -05:00
37 changed files with 2410 additions and 515 deletions

9
.gitattributes vendored
View File

@@ -1 +1,10 @@
*.janet linguist-language=Clojure
*.janet text eol=lf
*.c text eol=lf
*.h text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.build text eol=lf
*.txt text eol=lf
*.sh text eol=lf

5
.gitignore vendored
View File

@@ -32,6 +32,9 @@ lockfile.janet
# Local directory for testing
local
# Common test file I use.
temp.janet
# Emscripten
*.bc
janet.js
@@ -43,6 +46,7 @@ janet.wasm
# Generate test files
*.out
.orig
# Tools
xxd
@@ -50,6 +54,7 @@ xxd.exe
# VSCode
.vs
.clangd
# Swap files
*.swp

View File

@@ -1,6 +1,15 @@
# Changelog
All notable changes to this project will be documented in this file.
## Unreleased - ???
- Add integer parsing forms to pegs. This makes parsing many binary protocols easier.
## 1.12.2 - 2020-09-20
- Add janet\_try and janet\_restore to C API.
- Fix `os/execute` regression on windows.
- Add :pipe option to `os/spawn`.
- Fix docstring typos.
## 1.12.1 - 2020-09-07
- Make `zero?`, `one?`, `pos?`, and `neg?` polymorphic.
- Add C++ support to jpm and improve C++ interop in janet.h.

View File

@@ -97,6 +97,7 @@ JANET_CORE_SOURCES=src/core/abstract.c \
src/core/corelib.c \
src/core/debug.c \
src/core/emit.c \
src/core/ev.c \
src/core/fiber.c \
src/core/gc.c \
src/core/inttypes.c \
@@ -141,7 +142,7 @@ JANET_BOOT_OBJECTS=$(patsubst src/%.c,build/%.boot.o,$(JANET_CORE_SOURCES) $(JAN
$(JANET_BOOT_OBJECTS): $(JANET_BOOT_HEADERS)
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS)
build/%.boot.o: src/%.c $(JANET_HEADERS) $(JANET_LOCAL_HEADERS) Makefile
$(CC) $(BOOT_CFLAGS) -o $@ -c $<
build/janet_boot: $(JANET_BOOT_OBJECTS)
@@ -301,6 +302,10 @@ grammar: build/janet.tmLanguage
build/janet.tmLanguage: tools/tm_lang_gen.janet $(JANET_TARGET)
$(JANET_TARGET) $< > $@
compile-commands:
# Requires pip install copmiledb
compiledb make
clean:
-rm -rf build vgcore.* callgrind.*
-rm -rf test/install/build test/install/modpath
@@ -342,4 +347,4 @@ help:
@echo
.PHONY: clean install repl debug valgrind test \
valtest dist uninstall docs grammar format help
valtest dist uninstall docs grammar format help compile-commands

View File

@@ -102,6 +102,7 @@ exit /b 0
mkdir dist
janet.exe tools\gendoc.janet > dist\doc.html
janet.exe tools\removecr.janet dist\doc.html
janet.exe tools\removecr.janet build\janet.c
copy build\janet.c dist\janet.c
copy src\mainclient\shell.c dist\shell.c

15
examples/channel.janet Normal file
View File

@@ -0,0 +1,15 @@
(def c (ev/chan 4))
(defn writer []
(for i 0 10
(ev/sleep 0.1)
(print "writer giving item " i "...")
(ev/give c (string "item " i))))
(defn reader [name]
(forever
(print "reader " name " got " (ev/take c))))
(ev/call writer)
(each letter [:a :b :c :d :e :f :g]
(ev/call reader letter))

12
examples/evsleep.janet Normal file
View File

@@ -0,0 +1,12 @@
(defn worker
"Run for a number of iterations."
[name iterations]
(for i 0 iterations
(ev/sleep 1)
(print "worker " name " iteration " i)))
(ev/call worker :a 10)
(ev/sleep 0.2)
(ev/call worker :b 5)
(ev/sleep 0.3)
(ev/call worker :c 12)

23
examples/select.janet Normal file
View File

@@ -0,0 +1,23 @@
(def channels
(seq [:repeat 5] (ev/chan 4)))
(defn writer [c]
(for i 0 3
(def item (string i ":" (hash c)))
(ev/sleep 0.1)
(print "writer giving item " item " to " c "...")
(ev/give c item))
(print "Done!"))
(defn reader [name]
(forever
(def c (ev/select ;channels))
(print "reader " name " got " (ev/take c) " from " c)))
# Readers
(each letter [:a :b :c :d :e :f :g]
(ev/call reader letter))
# Writers
(each c channels
(ev/call writer c))

View File

@@ -6,8 +6,15 @@
(def b @"")
(print "Connection " id "!")
(while (:read stream 1024 b)
(repeat 10 (print "work for " id " ...") (ev/sleep 1))
(:write stream b)
(buffer/clear b))
(printf "Done %v!" id)))
(net/server "127.0.0.1" "8000" handler)
# Run server.
(let [server (net/server "127.0.0.1" "8000")]
(print "Starting echo server on 127.0.0.1:8000")
(forever
(if-let [conn (:accept server)]
(ev/call handler conn)
(print "no new connections"))))

5
examples/udpclient.janet Normal file
View File

@@ -0,0 +1,5 @@
(def conn (net/connect "127.0.0.1" "8009" :datagram))
(:write conn (string/format "%q" (os/cryptorand 16)))
(def x (:read conn 1024))
(pp x)

6
examples/udpserver.janet Normal file
View File

@@ -0,0 +1,6 @@
(def server (net/server "127.0.0.1" "8009" nil :datagram))
(while true
(def buf @"")
(def who (:recv-from server 1024 buf))
(printf "got %q from %v, echoing!" buf who)
(:send-to server who buf))

16
jpm.1
View File

@@ -74,6 +74,7 @@ $JANET_LIBPATH, or a reasonable default. See JANET_LIBPATH for more.
Sets the C compiler used for compiling native modules and standalone executables. Defaults
to cc.
.TP
.BR \-\-cpp\-compiler=$CXX
Sets the C++ compiler used for compiling native modules and standalone executables. Defaults
to c++..
@@ -105,7 +106,6 @@ be created in the ./build/ directory.
.TP
.BR install\ [\fBrepo...\fR]
When run with no arguments, installs all installable artifacts in the current project to
the current JANET_MODPATH for modules and JANET_BINPATH for executables and scripts. Can also
take an optional git repository URL and will install all artifacts in that repository instead.
@@ -115,7 +115,7 @@ install multiple dependencies in one command.
.TP
.BR uninstall\ [\fBname...\fR]
Uninstall a project installed with install. uninstall expects the name of the project, not the
repository url, path to installed file or executable name. The name of the project must be specified
repository url, path to installed file, or executable name. The name of the project must be specified
at the top of the project.janet file in the declare-project form. If no name is given, uninstalls
the current project if installed. Will also uninstall multiple packages in one command.
@@ -148,7 +148,7 @@ required.
List all installed packages in the current syspath.
.TP
.BR list-pkgs [\fBsearch\fR]
.BR list-pkgs\ [\fBsearch\fR]
List all package aliases in the current package listing that contain the given search string.
If no search string is given, prints the entire listing.
@@ -172,7 +172,7 @@ like make. run will run a single rule or build a single file.
List all rules that can be run via run. This is useful for exploring rules in the project.
.TP
.BR rule-tree\ [\fBroot\fR] [\fdepth\fR]
.BR rule-tree\ [\fBroot\fR]\ [\fBdepth\fR]
Show rule dependency tree in a pretty format. Optionally provide a rule to use as the tree
root, as well as a max depth to print. By default, prints the full tree for all rules. This
can be quite long, so it is recommended to give a root rule.
@@ -186,7 +186,7 @@ Show all of the paths used when installing and building artifacts.
Update the package listing by installing the 'pkgs' package. Same as jpm install pkgs
.TP
.BR quickbin [\fBentry\fR] [\fBexecutable\fR]
.BR quickbin\ [\fBentry\fR]\ [\fBexecutable\fR]
Create a standalone, statically linked executable from a Janet source file that contains a main function.
The main function is the entry point of the program and will receive command line arguments
as function arguments. The entry file can import other modules, including native C modules, and
@@ -222,7 +222,7 @@ the default location set at compile time, which can be determined with (dyn :sys
.RS
The location that jpm will use to install libraries to. Defaults to JANET_PATH, but you could
set this to a different directory if you want to. Doing so would let you import Janet modules
on the normal system path (JANET_PATH or (dyn :syspath)), but install to a different directory. It is also a more reliable way to install
on the normal system path (JANET_PATH or (dyn :syspath)), but install to a different directory. It is also a more reliable way to install.
This variable is overwritten by the --modpath=/some/path if it is provided.
.RE
@@ -238,7 +238,7 @@ variable.
.B JANET_LIBPATH
.RS
Similar to JANET_HEADERPATH, this path is where jpm will look for
libjanet.a for creating standalong executables. This does not need to be
libjanet.a for creating standalone executables. This does not need to be
set on a normal install.
If not provided, this will default to <jpm script location>/../lib.
The --libpath=/some/path option will override this variable.
@@ -257,11 +257,13 @@ The --binpath=/some/path will override this variable.
The git repository URL that contains a listing of packages. This allows installing packages with shortnames, which
is mostly a convenience. However, package dependencies can use short names, package listings
can be used to choose a particular set of dependency versions for a whole project.
.RE
.B JANET_GIT
.RS
An optional path to a git executable to use to clone git dependencies. By default, uses "git" on the current $PATH. You shouldn't need to set this
if you have a normal install of git.
.RE
.SH AUTHOR
Written by Calvin Rose <calsrose@gmail.com>

View File

@@ -20,7 +20,7 @@
project('janet', 'c',
default_options : ['c_std=c99', 'b_lundef=false', 'default_library=both'],
version : '1.12.1')
version : '1.13.0')
# Global settings
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
@@ -60,6 +60,7 @@ conf.set('JANET_NO_SOURCEMAPS', not get_option('sourcemaps'))
conf.set('JANET_NO_ASSEMBLER', not get_option('assembler'))
conf.set('JANET_NO_PEG', not get_option('peg'))
conf.set('JANET_NO_NET', not get_option('net'))
conf.set('JANET_NO_EV', not get_option('ev'))
conf.set('JANET_REDUCED_OS', get_option('reduced_os'))
conf.set('JANET_NO_TYPED_ARRAY', not get_option('typed_array'))
conf.set('JANET_NO_INT_TYPES', not get_option('int_types'))
@@ -111,6 +112,7 @@ core_src = [
'src/core/corelib.c',
'src/core/debug.c',
'src/core/emit.c',
'src/core/ev.c',
'src/core/fiber.c',
'src/core/gc.c',
'src/core/inttypes.c',

View File

@@ -12,6 +12,7 @@ option('typed_array', type : 'boolean', value : true)
option('int_types', type : 'boolean', value : true)
option('prf', type : 'boolean', value : false)
option('net', type : 'boolean', value : true)
option('ev', type : 'boolean', value : true)
option('processes', type : 'boolean', value : true)
option('umask', type : 'boolean', value : true)
option('realpath', type : 'boolean', value : true)

View File

@@ -1141,7 +1141,10 @@
:table (walk-dict f form)
:struct (table/to-struct (walk-dict f form))
:array (walk-ind f form)
:tuple (tuple/slice (walk-ind f form))
:tuple (let [x (walk-ind f form)]
(if (= :parens (tuple/type form))
(tuple/slice x)
(tuple/brackets ;x)))
form))
(undef walk-ind)
@@ -1702,7 +1705,7 @@
(print (doc-format (string "Bindings:\n\n" (string/join bindings " "))))
(print)
(print (doc-format (string "Dynamics:\n\n" (string/join dynamics " "))))
(print))
(print "\n Use (doc sym) for more information on a binding.\n"))
(defn doc*
"Get the documentation for a symbol in a given environment. Function form of doc."
@@ -2868,7 +2871,7 @@
(when (and (not *compile-only*) (or *should-repl* *no-file*))
(if-not *quiet*
(print "Janet " janet/version "-" janet/build " " (os/which) "/" (os/arch)))
(print "Janet " janet/version "-" janet/build " " (os/which) "/" (os/arch) " - '(doc)' for help"))
(flush)
(defn getprompt [p]
(def [line] (parser/where p))
@@ -2972,6 +2975,7 @@
"src/core/corelib.c"
"src/core/debug.c"
"src/core/emit.c"
"src/core/ev.c"
"src/core/fiber.c"
"src/core/gc.c"
"src/core/inttypes.c"

View File

@@ -27,10 +27,10 @@
#define JANETCONF_H
#define JANET_VERSION_MAJOR 1
#define JANET_VERSION_MINOR 12
#define JANET_VERSION_PATCH 1
#define JANET_VERSION_EXTRA ""
#define JANET_VERSION "1.12.1"
#define JANET_VERSION_MINOR 13
#define JANET_VERSION_PATCH 0
#define JANET_VERSION_EXTRA "-dev"
#define JANET_VERSION "1.13.0-dev"
/* #define JANET_BUILD "local" */
@@ -52,6 +52,7 @@
/* #define JANET_NO_NET */
/* #define JANET_NO_TYPED_ARRAY */
/* #define JANET_NO_INT_TYPES */
/* #define JANET_NO_EV */
/* #define JANET_NO_REALPATH */
/* #define JANET_NO_SYMLINKS */
/* #define JANET_NO_UMASK */
@@ -69,6 +70,7 @@
/* #define JANET_STACK_MAX 16384 */
/* #define JANET_OS_NAME my-custom-os */
/* #define JANET_ARCH_NAME pdp-8 */
/* #define JANET_EV_EPOLL */
/* Main client settings, does not affect library code */
/* #define JANET_SIMPLE_GETLINE */

View File

@@ -344,16 +344,16 @@ static const JanetReg array_cfuns[] = {
{
"array/concat", cfun_array_concat,
JDOC("(array/concat arr & parts)\n\n"
"Concatenates a variadic number of arrays (and tuples) into the first argument "
"which must an array. If any of the parts are arrays or tuples, their elements will "
"Concatenates a variable number of arrays (and tuples) into the first argument "
"which must be an array. If any of the parts are arrays or tuples, their elements will "
"be inserted into the array. Otherwise, each part in parts will be appended to arr in order. "
"Return the modified array arr.")
},
{
"array/insert", cfun_array_insert,
JDOC("(array/insert arr at & xs)\n\n"
"Insert all of xs into array arr at index at. at should be an integer "
"0 and the length of the array. A negative value for at will index from "
"Insert all xs into array arr at index at. at should be an integer between "
"0 and the length of the array. A negative value for at will index backwards from "
"the end of the array, such that inserting at -1 appends to the array. "
"Returns the array.")
},

View File

@@ -1034,6 +1034,9 @@ static void janet_load_libs(JanetTable *env) {
#ifdef JANET_THREADS
janet_lib_thread(env);
#endif
#ifdef JANET_EV
janet_lib_ev(env);
#endif
#ifdef JANET_NET
janet_lib_net(env);
#endif

1131
src/core/ev.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -37,6 +37,11 @@ static void fiber_reset(JanetFiber *fiber) {
fiber->child = NULL;
fiber->flags = JANET_FIBER_MASK_YIELD | JANET_FIBER_RESUME_NO_USEVAL | JANET_FIBER_RESUME_NO_SKIP;
fiber->env = NULL;
#ifdef JANET_EV
fiber->waiting = NULL;
fiber->timeout_index = -1;
fiber->sched_id = 0;
#endif
janet_fiber_set_status(fiber, JANET_STATUS_NEW);
}
@@ -77,6 +82,7 @@ JanetFiber *janet_fiber_reset(JanetFiber *fiber, JanetFunction *callee, int32_t
}
if (janet_fiber_funcframe(fiber, callee)) return NULL;
janet_fiber_frame(fiber)->flags |= JANET_STACKFRAME_ENTRANCE;
fiber->waiting = NULL;
return fiber;
}

View File

@@ -46,8 +46,9 @@
#define JANET_FIBER_MASK_USERN(N) (16 << (N))
#define JANET_FIBER_MASK_USER 0x3FF0
#define JANET_FIBER_RESUME_SIGNAL 0x800000
#define JANET_FIBER_STATUS_MASK 0x7F0000
#define JANET_FIBER_STATUS_MASK 0x3F0000
#define JANET_FIBER_FLAG_SCHEDULED 0x800000
#define JANET_FIBER_RESUME_SIGNAL 0x400000
#define JANET_FIBER_STATUS_OFFSET 16
#define JANET_FIBER_BREAKPOINT 0x1000000
@@ -77,4 +78,8 @@ void janet_fiber_popframe(JanetFiber *fiber);
void janet_env_maybe_detach(JanetFuncEnv *env);
int janet_env_valid(JanetFuncEnv *env);
#ifdef JANET_EV
void janet_fiber_did_resume(JanetFiber *fiber);
#endif
#endif

View File

@@ -28,6 +28,7 @@
#include "gc.h"
#include "util.h"
#include "fiber.h"
#include "vector.h"
#endif
struct JanetScratch {
@@ -400,8 +401,8 @@ void janet_collect(void) {
janet_vm_gc_interval = janet_vm_block_count * sizeof(JanetGCObject);
}
orig_rootcount = janet_vm_root_count;
#ifdef JANET_NET
janet_net_markloop();
#ifdef JANET_EV
janet_ev_mark();
#endif
for (i = 0; i < orig_rootcount; i++)
janet_mark(janet_vm_roots[i]);

View File

@@ -299,8 +299,8 @@ static Janet cfun_io_fclose(int32_t argc, Janet *argv) {
janet_panic("could not close file");
}
iof->flags |= JANET_FILE_CLOSED;
return janet_wrap_nil();
}
return janet_wrap_nil();
}
/* Seek a file */
@@ -799,6 +799,10 @@ FILE *janet_getfile(const Janet *argv, int32_t n, int *flags) {
return iof->file;
}
JanetFile *janet_makejfile(FILE *f, int flags) {
return makef(f, flags);
}
Janet janet_makefile(FILE *f, int flags) {
return janet_wrap_abstract(makef(f, flags));
}

View File

@@ -285,8 +285,8 @@ static void marshal_one_def(MarshalState *st, JanetFuncDef *def, int flags) {
}
#define JANET_FIBER_FLAG_HASCHILD (1 << 29)
#define JANET_FIBER_FLAG_HASENV (1 << 28)
#define JANET_STACKFRAME_HASENV (1 << 30)
#define JANET_FIBER_FLAG_HASENV (1 << 30)
#define JANET_STACKFRAME_HASENV (1 << 31)
/* Marshal a fiber */
static void marshal_one_fiber(MarshalState *st, JanetFiber *fiber, int flags) {
@@ -934,6 +934,8 @@ static const uint8_t *unmarshal_one_fiber(
fiber->data = NULL;
fiber->child = NULL;
fiber->env = NULL;
fiber->waiting = NULL;
fiber->timeout_index = -1;
/* Push fiber to seen stack */
janet_v_push(st->lookup, janet_wrap_fiber(fiber));
@@ -1048,6 +1050,11 @@ static const uint8_t *unmarshal_one_fiber(
fiber->maxstack = fiber_maxstack;
fiber->env = fiber_env;
int status = janet_fiber_status(fiber);
if (status < 0 || status > JANET_STATUS_ALIVE) {
janet_panic("invalid fiber status");
}
/* Return data */
*out = fiber;
return data;

File diff suppressed because it is too large Load Diff

View File

@@ -221,7 +221,8 @@ static char **os_execute_env(int32_t argc, const Janet *argv) {
return envp;
}
/* Free memory from os_execute */
/* Free memory from os_execute. Not actually needed, but doesn't pressure the GC
in the happy path. */
static void os_execute_cleanup(char **envp, const char **child_argv) {
#ifdef JANET_WINDOWS
(void) child_argv;
@@ -414,6 +415,69 @@ static Janet os_proc_kill(int32_t argc, Janet *argv) {
}
}
static void swap_handles(JanetHandle *handles) {
JanetHandle temp = handles[0];
handles[0] = handles[1];
handles[1] = temp;
}
static void close_handle(JanetHandle handle) {
#ifdef JANET_WINDOWS
CloseHandle(handle);
#else
close(handle);
#endif
}
/* Create piped file for os/execute and os/spawn. Need to be careful that we mark
the error flag if we can't create pipe and don't leak handles. *handle will be cleaned
up by the calling function. If everything goes well, *handle is owned by the calling function,
(if it is set) and the returned JanetFile owns the other end of the pipe, which will be closed
on GC or fclose. */
static JanetFile *make_pipes(JanetHandle *handle, int reverse, int *errflag) {
JanetHandle handles[2];
#ifdef JANET_WINDOWS
SECURITY_ATTRIBUTES saAttr;
memset(&saAttr, 0, sizeof(saAttr));
saAttr.nLength = sizeof(saAttr);
saAttr.bInheritHandle = TRUE;
if (!CreatePipe(handles, handles + 1, &saAttr, 0)) goto error_pipe;
if (reverse) swap_handles(handles);
/* Don't inherit the side of the pipe owned by this process */
if (!SetHandleInformation(handles[0], HANDLE_FLAG_INHERIT, 0)) goto error_set_handle_info;
*handle = handles[1];
int fd = _open_osfhandle((intptr_t) handles[0], reverse ? _O_WRONLY : _O_RDONLY);
if (fd == -1) goto error_open_osfhandle;
FILE *f = _fdopen(fd, reverse ? "w" : "r");
if (NULL == f) goto error_fdopen;
return janet_makejfile(f, reverse ? JANET_FILE_WRITE : JANET_FILE_READ);
error_fdopen:
_close(fd); /* we need to close the fake file descriptor instead of the handle, as ownership has been transfered. */
*errflag = 1;
return NULL;
error_set_handle_info:
error_open_osfhandle:
close_handle(handles[0]);
/* fallthrough */
error_pipe:
*errflag = 1;
return NULL;
#else
if (pipe(handles)) goto error_pipe;
if (reverse) swap_handles(handles);
*handle = handles[1];
FILE *f = fdopen(handles[0], reverse ? "w" : "r");
if (NULL == f) goto error_fdopen;
return janet_makejfile(f, reverse ? JANET_FILE_WRITE : JANET_FILE_READ);
error_fdopen:
close_handle(handles[0]);
/* fallthrough */
error_pipe:
*errflag = 1;
return NULL;
#endif
}
static const JanetMethod proc_methods[] = {
{"wait", os_proc_wait},
{"kill", os_proc_kill},
@@ -460,6 +524,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) {
}
/* Get environment */
int use_environ = !janet_flag_at(flags, 0);
char **envp = os_execute_env(argc, argv);
/* Get arguments */
@@ -470,6 +535,8 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) {
/* Optional stdio redirections */
JanetFile *new_in = NULL, *new_out = NULL, *new_err = NULL;
JanetHandle pipe_in = JANET_HANDLE_NONE, pipe_out = JANET_HANDLE_NONE, pipe_err = JANET_HANDLE_NONE;
int pipe_errflag = 0; /* Track errors setting up pipes */
/* Get optional redirections */
if (argc > 2) {
@@ -477,9 +544,29 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) {
Janet maybe_stdin = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("in"));
Janet maybe_stdout = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("out"));
Janet maybe_stderr = janet_dictionary_get(tab.kvs, tab.cap, janet_ckeywordv("err"));
if (!janet_checktype(maybe_stdin, JANET_NIL)) new_in = janet_getjfile(&maybe_stdin, 0);
if (!janet_checktype(maybe_stdout, JANET_NIL)) new_out = janet_getjfile(&maybe_stdout, 0);
if (!janet_checktype(maybe_stderr, JANET_NIL)) new_err = janet_getjfile(&maybe_stderr, 0);
if (janet_keyeq(maybe_stdin, "pipe")) {
new_in = make_pipes(&pipe_in, 1, &pipe_errflag);
} else if (!janet_checktype(maybe_stdin, JANET_NIL)) {
new_in = janet_getjfile(&maybe_stdin, 0);
}
if (janet_keyeq(maybe_stdout, "pipe")) {
new_out = make_pipes(&pipe_out, 0, &pipe_errflag);
} else if (!janet_checktype(maybe_stdout, JANET_NIL)) {
new_out = janet_getjfile(&maybe_stdout, 0);
}
if (janet_keyeq(maybe_stderr, "err")) {
new_err = make_pipes(&pipe_err, 0, &pipe_errflag);
} else if (!janet_checktype(maybe_stderr, JANET_NIL)) {
new_err = janet_getjfile(&maybe_stderr, 0);
}
}
/* Clean up if any of the pipes have any issues */
if (pipe_errflag) {
if (pipe_in != JANET_HANDLE_NONE) close_handle(pipe_in);
if (pipe_out != JANET_HANDLE_NONE) close_handle(pipe_out);
if (pipe_err != JANET_HANDLE_NONE) close_handle(pipe_err);
janet_panic("failed to create pipes");
}
/* Result */
@@ -488,12 +575,16 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) {
#ifdef JANET_WINDOWS
HANDLE pHandle, tHandle;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION processInfo;
STARTUPINFO startupInfo;
memset(&saAttr, 0, sizeof(saAttr));
memset(&processInfo, 0, sizeof(processInfo));
memset(&startupInfo, 0, sizeof(startupInfo));
startupInfo.cb = sizeof(startupInfo);
startupInfo.dwFlags |= STARTF_USESTDHANDLES;
saAttr.nLength = sizeof(saAttr);
saAttr.bInheritHandle = TRUE;
JanetBuffer *buf = os_exec_escape(exargs);
if (buf->count > 8191) {
@@ -502,33 +593,63 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) {
const char *path = (const char *) janet_unwrap_string(exargs.items[0]);
/* Do IO redirection */
startupInfo.hStdInput = (HANDLE) _get_osfhandle((new_in == NULL) ? 0 : _fileno(new_in->file));
startupInfo.hStdOutput = (HANDLE) _get_osfhandle((new_out == NULL) ? 1 : _fileno(new_out->file));
startupInfo.hStdError = (HANDLE) _get_osfhandle((new_err == NULL) ? 2 : _fileno(new_err->file));
if (pipe_in != JANET_HANDLE_NONE) {
startupInfo.hStdInput = pipe_in;
} else if (new_in != NULL) {
startupInfo.hStdInput = (HANDLE) _get_osfhandle(_fileno(new_in->file));
} else {
startupInfo.hStdInput = (HANDLE) _get_osfhandle(0);
}
if (pipe_out != JANET_HANDLE_NONE) {
startupInfo.hStdOutput = pipe_out;
} else if (new_out != NULL) {
startupInfo.hStdOutput = (HANDLE) _get_osfhandle(_fileno(new_out->file));
} else {
startupInfo.hStdOutput = (HANDLE) _get_osfhandle(1);
}
if (pipe_err != JANET_HANDLE_NONE) {
startupInfo.hStdError = pipe_err;
} else if (new_err != NULL) {
startupInfo.hStdError = (HANDLE) _get_osfhandle(_fileno(new_err->file));
} else {
startupInfo.hStdError = (HANDLE) _get_osfhandle(2);
}
/* Use _spawn family of functions. */
/* Windows docs say do this before any spawns. */
_flushall();
/* TODO - redirection, :p flag */
if (!CreateProcess(janet_flag_at(flags, 1) ? NULL : path, /* NULL? */
int cp_failed = 0;
if (!CreateProcess(janet_flag_at(flags, 1) ? NULL : path,
(char *) buf->data, /* Single CLI argument */
NULL, /* no proc inheritance */
NULL, /* no thread inheritance */
&saAttr, /* no proc inheritance */
&saAttr, /* no thread inheritance */
TRUE, /* handle inheritance */
0, /* flags */
envp, /* pass in environment */
use_environ ? NULL : envp, /* pass in environment */
NULL, /* use parents starting directory */
&startupInfo,
&processInfo)) {
&processInfo)) {
cp_failed = 1;
}
if (pipe_in != JANET_HANDLE_NONE) CloseHandle(pipe_in);
if (pipe_out != JANET_HANDLE_NONE) CloseHandle(pipe_out);
if (pipe_err != JANET_HANDLE_NONE) CloseHandle(pipe_err);
os_execute_cleanup(envp, NULL);
if (cp_failed) {
janet_panic("failed to create process");
}
pHandle = processInfo.hProcess;
tHandle = processInfo.hThread;
os_execute_cleanup(envp, NULL);
/* Wait and cleanup immedaitely */
if (!is_async) {
DWORD code;
@@ -550,8 +671,6 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) {
/* Use posix_spawn to spawn new process */
int use_environ = !janet_flag_at(flags, 0);
if (use_environ) {
janet_lock_environ();
}
@@ -559,13 +678,19 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) {
/* Posix spawn setup */
posix_spawn_file_actions_t actions;
posix_spawn_file_actions_init(&actions);
if (new_in != NULL) {
if (pipe_in != JANET_HANDLE_NONE) {
posix_spawn_file_actions_adddup2(&actions, pipe_in, 0);
} else if (new_in != NULL) {
posix_spawn_file_actions_adddup2(&actions, fileno(new_in->file), 0);
}
if (new_out != NULL) {
if (pipe_out != JANET_HANDLE_NONE) {
posix_spawn_file_actions_adddup2(&actions, pipe_out, 1);
} else if (new_out != NULL) {
posix_spawn_file_actions_adddup2(&actions, fileno(new_out->file), 1);
}
if (new_err != NULL) {
if (pipe_err != JANET_HANDLE_NONE) {
posix_spawn_file_actions_adddup2(&actions, pipe_err, 2);
} else if (new_err != NULL) {
posix_spawn_file_actions_adddup2(&actions, fileno(new_err->file), 2);
}
@@ -582,6 +707,10 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, int is_async) {
posix_spawn_file_actions_destroy(&actions);
if (pipe_in != JANET_HANDLE_NONE) close(pipe_in);
if (pipe_out != JANET_HANDLE_NONE) close(pipe_out);
if (pipe_err != JANET_HANDLE_NONE) close(pipe_err);
if (use_environ) {
janet_unlock_environ();
}
@@ -1554,6 +1683,11 @@ static const JanetReg os_cfuns[] = {
"env is a table or struct mapping environment variables to values. It can also "
"contain the keys :in, :out, and :err, which allow redirecting stdio in the subprocess. "
"These arguments should be core/file values. "
"One can also pass in the :pipe keyword "
"for these arguments to create files that will read (for :err and :out) or write (for :in) "
"to the file descriptor of the subprocess. This is only useful in os/spawn, which takes "
"the same parameters as os/execute, but will return an object that contains references to these "
"files via (return-value :in), (return-value :out), and (return-value :err). "
"Returns the exit status of the program.")
},
{

View File

@@ -87,6 +87,12 @@ static void pushcap(PegState *s, Janet capture, uint32_t tag) {
}
}
/* Convert a uint64_t to a int64_t by wrapping to a maximum number of bytes */
static int64_t peg_convert_u64_s64(uint64_t from, int width) {
int shift = 8 * (8 - width);
return ((int64_t)(from << shift)) >> shift;
}
/* Prevent stack overflow */
#define down1(s) do { \
if (0 == --((s)->depth)) janet_panic("peg/match recursed too deeply"); \
@@ -469,6 +475,47 @@ tail:
return next_text;
}
case RULE_READINT: {
uint32_t tag = rule[2];
uint32_t signedness = rule[1] & 0x10;
uint32_t endianess = rule[1] & 0x20;
int width = (int)(rule[1] & 0xF);
if (text + width > s->text_end) return NULL;
uint64_t accum = 0;
if (endianess) {
/* BE */
for (int i = 0; i < width; i++) accum = (accum << 8) | text[i];
} else {
/* LE */
for (int i = width - 1; i >= 0; i--) accum = (accum << 8) | text[i];
}
Janet capture_value;
/* We can only parse integeres of greater than 6 bytes reliable if int-types are enabled.
* Otherwise, we may lose precision, so 6 is the maximum size when int-types are disabled. */
#ifdef JANET_INT_TYPES
if (width > 6) {
if (signedness) {
capture_value = janet_wrap_s64(peg_convert_u64_s64(accum, width));
} else {
capture_value = janet_wrap_u64(accum);
}
} else
#endif
{
double double_value;
if (signedness) {
double_value = (double)(peg_convert_u64_s64(accum, width));
} else {
double_value = (double)accum;
}
capture_value = janet_wrap_number(double_value);
}
pushcap(s, capture_value, tag);
return text + width;
}
}
}
@@ -876,6 +923,36 @@ static void spec_matchtime(Builder *b, int32_t argc, const Janet *argv) {
emit_3(r, RULE_MATCHTIME, subrule, cindex, tag);
}
#ifdef JANET_INT_TYPES
#define JANET_MAX_READINT_WIDTH 8
#else
#define JANET_MAX_READINT_WIDTH 6
#endif
static void spec_readint(Builder *b, int32_t argc, const Janet *argv, uint32_t mask) {
peg_arity(b, argc, 1, 2);
Reserve r = reserve(b, 3);
uint32_t tag = (argc == 2) ? emit_tag(b, argv[3]) : 0;
int32_t width = peg_getnat(b, argv[0]);
if ((width < 0) || (width > JANET_MAX_READINT_WIDTH)) {
peg_panicf(b, "width must be between 0 and %d, got %d", JANET_MAX_READINT_WIDTH, width);
}
emit_2(r, RULE_READINT, mask | ((uint32_t) width), tag);
}
static void spec_uint_le(Builder *b, int32_t argc, const Janet *argv) {
spec_readint(b, argc, argv, 0x0u);
}
static void spec_int_le(Builder *b, int32_t argc, const Janet *argv) {
spec_readint(b, argc, argv, 0x10u);
}
static void spec_uint_be(Builder *b, int32_t argc, const Janet *argv) {
spec_readint(b, argc, argv, 0x20u);
}
static void spec_int_be(Builder *b, int32_t argc, const Janet *argv) {
spec_readint(b, argc, argv, 0x30u);
}
/* Special compiler form */
typedef void (*Special)(Builder *b, int32_t argc, const Janet *argv);
typedef struct {
@@ -912,6 +989,8 @@ static const SpecialPair peg_specials[] = {
{"group", spec_group},
{"if", spec_if},
{"if-not", spec_ifnot},
{"int", spec_int_le},
{"int-be", spec_int_be},
{"lenprefix", spec_lenprefix},
{"look", spec_look},
{"not", spec_not},
@@ -926,6 +1005,8 @@ static const SpecialPair peg_specials[] = {
{"some", spec_some},
{"thru", spec_thru},
{"to", spec_to},
{"uint", spec_uint_le},
{"uint-be", spec_uint_be},
};
/* Compile a janet value into a rule and return the rule index. */
@@ -1226,6 +1307,11 @@ static void *peg_unmarshal(JanetMarshalContext *ctx) {
op_flags[rule[1]] |= 0x01;
i += 2;
break;
case RULE_READINT:
/* [ width | (endianess << 5) | (signedness << 6), tag ] */
if (rule[1] > JANET_MAX_READINT_WIDTH) goto bad;
i += 3;
break;
default:
goto bad;
}

View File

@@ -101,4 +101,14 @@ void janet_threads_init(void);
void janet_threads_deinit(void);
#endif
#ifdef JANET_NET
void janet_net_init(void);
void janet_net_deinit(void);
#endif
#ifdef JANET_EV
void janet_ev_init(void);
void janet_ev_deinit(void);
#endif
#endif /* JANET_STATE_H_defined */

View File

@@ -724,15 +724,18 @@ static const JanetReg threadlib_cfuns[] = {
},
{
"thread/send", cfun_thread_send,
JDOC("(thread/send thread msg)\n\n"
"Send a message to the thread. This will never block and returns thread immediately. "
JDOC("(thread/send thread msgi &opt timeout)\n\n"
"Send a message to the thread. By default, the timeout is 1 second, but an optional timeout "
"in seconds can be provided. Use math/inf for no timeout. "
"Will throw an error if there is a problem sending the message.")
},
{
"thread/receive", cfun_thread_receive,
JDOC("(thread/receive &opt timeout)\n\n"
"Get a message sent to this thread. If timeout is provided, an error will be thrown after the timeout has elapsed but "
"no messages are received.")
"Get a message sent to this thread. If timeout (in seconds) is provided, an error "
"will be thrown after the timeout has elapsed but "
"no messages are received. The default timeout is 1 second, and math/inf cam be passed to "
"turn off the timeout.")
},
{
"thread/close", cfun_thread_close,

View File

@@ -140,8 +140,10 @@ void janet_lib_thread(JanetTable *env);
#endif
#ifdef JANET_NET
void janet_lib_net(JanetTable *env);
void janet_net_deinit(void);
void janet_net_markloop(void);
#endif
#ifdef JANET_EV
void janet_lib_ev(JanetTable *env);
void janet_ev_mark(void);
#endif
#endif

View File

@@ -1315,11 +1315,32 @@ static JanetSignal janet_check_can_resume(JanetFiber *fiber, Janet *out) {
return JANET_SIGNAL_OK;
}
void janet_try_init(JanetTryState *state) {
state->stackn = janet_vm_stackn++;
state->gc_handle = janet_vm_gc_suspend;
state->vm_fiber = janet_vm_fiber;
state->vm_jmp_buf = janet_vm_jmp_buf;
state->vm_return_reg = janet_vm_return_reg;
janet_vm_return_reg = &(state->payload);
janet_vm_jmp_buf = &(state->buf);
}
void janet_restore(JanetTryState *state) {
janet_vm_stackn = state->stackn;
janet_vm_gc_suspend = state->gc_handle;
janet_vm_fiber = state->vm_fiber;
janet_vm_jmp_buf = state->vm_jmp_buf;
janet_vm_return_reg = state->vm_return_reg;
}
static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *out) {
jmp_buf buf;
JanetFiberStatus old_status = janet_fiber_status(fiber);
#ifdef JANET_EV
janet_fiber_did_resume(fiber);
#endif
/* Continue child fiber if it exists */
if (fiber->child) {
if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber;
@@ -1349,45 +1370,23 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
}
/* Save global state */
int32_t oldn = janet_vm_stackn++;
int handle = janet_vm_gc_suspend;
JanetFiber *old_vm_fiber = janet_vm_fiber;
jmp_buf *old_vm_jmp_buf = janet_vm_jmp_buf;
Janet *old_vm_return_reg = janet_vm_return_reg;
/* Setup fiber */
if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber;
janet_vm_fiber = fiber;
janet_gcroot(janet_wrap_fiber(fiber));
janet_fiber_set_status(fiber, JANET_STATUS_ALIVE);
janet_vm_return_reg = out;
janet_vm_jmp_buf = &buf;
/* Run loop */
JanetSignal signal;
int jmpsig;
#if defined(JANET_BSD) || defined(JANET_APPLE)
jmpsig = _setjmp(buf);
#else
jmpsig = setjmp(buf);
#endif
if (jmpsig) {
signal = (JanetSignal) jmpsig;
} else {
JanetTryState tstate;
JanetSignal signal = janet_try(&tstate);
if (!signal) {
/* Normal setup */
if (janet_vm_root_fiber == NULL) janet_vm_root_fiber = fiber;
janet_vm_fiber = fiber;
janet_gcroot(janet_wrap_fiber(fiber));
janet_fiber_set_status(fiber, JANET_STATUS_ALIVE);
signal = run_vm(fiber, in);
}
/* Tear down fiber */
/* Restore */
if (janet_vm_root_fiber == fiber) janet_vm_root_fiber = NULL;
janet_fiber_set_status(fiber, signal);
janet_gcunroot(janet_wrap_fiber(fiber));
/* Restore global state */
if (janet_vm_root_fiber == fiber) janet_vm_root_fiber = NULL;
janet_vm_gc_suspend = handle;
janet_vm_fiber = old_vm_fiber;
janet_vm_stackn = oldn;
janet_vm_return_reg = old_vm_return_reg;
janet_vm_jmp_buf = old_vm_jmp_buf;
janet_restore(&tstate);
*out = tstate.payload;
return signal;
}
@@ -1484,6 +1483,12 @@ int janet_init(void) {
/* Threads */
#ifdef JANET_THREADS
janet_threads_init();
#endif
#ifdef JANET_EV
janet_ev_init();
#endif
#ifdef JANET_NET
janet_net_init();
#endif
return 0;
}
@@ -1506,6 +1511,9 @@ void janet_deinit(void) {
#ifdef JANET_THREADS
janet_threads_deinit();
#endif
#ifdef JANET_EV
janet_ev_deinit();
#endif
#ifdef JANET_NET
janet_net_deinit();
#endif

View File

@@ -177,8 +177,13 @@ extern "C" {
#define JANET_TYPED_ARRAY
#endif
/* Enable or disable event loop */
#if !defined(JANET_NO_EV) && !defined(__EMSCRIPTEN__)
#define JANET_EV
#endif
/* Enable or disable networking */
#if !defined(JANET_NO_NET) && !defined(__EMSCRIPTEN__)
#if defined(JANET_EV) && !defined(JANET_NO_NET) && !defined(__EMSCRIPTEN__)
#define JANET_NET
#endif
@@ -310,6 +315,15 @@ JANET_API extern const char *const janet_type_names[16];
JANET_API extern const char *const janet_signal_names[14];
JANET_API extern const char *const janet_status_names[16];
/* For various IO routines, we want to use an int on posix and HANDLE on windows */
#ifdef JANET_WINDOWS
typedef void *JanetHandle;
#define JANET_HANDLE_NONE NULL
#else
typedef int JanetHandle;
#define JANET_HANDLE_NONE (-1)
#endif
/* Fiber signals */
typedef enum {
JANET_SIGNAL_OK,
@@ -483,6 +497,62 @@ typedef void *JanetAbstract;
#define JANET_TFLAG_CALLABLE (JANET_TFLAG_FUNCTION | JANET_TFLAG_CFUNCTION | \
JANET_TFLAG_LENGTHABLE | JANET_TFLAG_ABSTRACT)
/* Event Loop Types */
#ifdef JANET_EV
#define JANET_POLL_FLAG_CLOSED 0x1
#define JANET_POLL_FLAG_SOCKET 0x2
typedef enum {
JANET_ASYNC_EVENT_INIT,
JANET_ASYNC_EVENT_MARK,
JANET_ASYNC_EVENT_DEINIT,
JANET_ASYNC_EVENT_CLOSE,
JANET_ASYNC_EVENT_READ,
JANET_ASYNC_EVENT_WRITE,
JANET_ASYNC_EVENT_TIMEOUT,
JANET_ASYNC_EVENT_COMPLETE /* Used on windows for IOCP */
} JanetAsyncEvent;
#define JANET_ASYNC_LISTEN_READ (1 << JANET_ASYNC_EVENT_READ)
#define JANET_ASYNC_LISTEN_WRITE (1 << JANET_ASYNC_EVENT_WRITE)
#define JANET_ASYNC_LISTEN_SPAWNER 0x1000
typedef enum {
JANET_ASYNC_STATUS_NOT_DONE,
JANET_ASYNC_STATUS_DONE
} JanetAsyncStatus;
/* Typedefs */
typedef struct JanetListenerState JanetListenerState;
typedef struct JanetPollable JanetPollable;
typedef JanetAsyncStatus(*JanetListener)(JanetListenerState *state, JanetAsyncEvent event);
/* Wrapper around file descriptors and HANDLEs that can be polled. */
struct JanetPollable {
JanetHandle handle;
uint32_t flags;
/* Linked list of all in-flight IO routines for this pollable */
JanetListenerState *state;
/* internal - used to disallow multiple concurrent reads / writes on the same pollable.
* this constraint may be lifted later but allowing such would require more internal book keeping
* for some implementations. You can read and write at the same time on the same pollable, though. */
int _mask;
};
/* Interface for state machine based event loop */
struct JanetListenerState {
JanetListener machine;
JanetFiber *fiber;
JanetPollable *pollable;
void *event; /* Used to pass data from asynchronous IO event. Contents depend on both
implementation of the event loop and the particular event. */
/* internal */
int _index; /* not used in all implementations */
int _mask;
JanetListenerState *_next;
};
#endif
/* We provide three possible implementations of Janets. The preferred
* nanboxing approach, for 32 or 64 bits, and the standard C version. Code in the rest of the
* application must interact through exposed interface. */
@@ -750,11 +820,16 @@ struct JanetFiber {
int32_t frame; /* Index of the stack frame */
int32_t stackstart; /* Beginning of next args */
int32_t stacktop; /* Top of stack. Where values are pushed and popped from. */
int32_t capacity;
int32_t capacity; /* How big is the stack memory */
int32_t maxstack; /* Arbitrary defined limit for stack overflow */
JanetTable *env; /* Dynamic bindings table (usually current environment). */
Janet *data;
Janet *data; /* Dynamically resized stack memory */
JanetFiber *child; /* Keep linked list of fibers for restarting pending fibers */
#ifdef JANET_EV
JanetListenerState *waiting;
int32_t timeout_index;
uint32_t sched_id; /* Increment everytime fiber is scheduled by event loop */
#endif
};
/* Mark if a stack frame is a tail call for debugging */
@@ -1021,6 +1096,19 @@ struct JanetFile {
int32_t flags;
};
/* For janet_try and janet_restore */
typedef struct {
/* old state */
int32_t stackn;
int gc_handle;
JanetFiber *vm_fiber;
jmp_buf *vm_jmp_buf;
Janet *vm_return_reg;
/* new state */
jmp_buf buf;
Janet payload;
} JanetTryState;
/* Thread types */
#ifdef JANET_THREADS
typedef struct JanetThread JanetThread;
@@ -1153,9 +1241,31 @@ extern enum JanetInstructionType janet_instructions[JOP_INSTRUCTION_COUNT];
/***** START SECTION MAIN *****/
/* Event Loop */
#ifdef JANET_NET
#ifdef JANET_EV
/* Run the event loop */
JANET_API void janet_loop(void);
/* Wrapper around pollables */
JANET_API void janet_pollable_init(JanetPollable *pollable, JanetHandle handle);
JANET_API void janet_pollable_mark(JanetPollable *pollable);
JANET_API void janet_pollable_deinit(JanetPollable *pollable);
/* Queue a fiber to run on the event loop */
JANET_API void janet_schedule(JanetFiber *fiber, Janet value);
JANET_API void janet_cancel(JanetFiber *fiber, Janet value);
JANET_API void janet_schedule_signal(JanetFiber *fiber, Janet value, JanetSignal sig);
/* Start a state machine listening for events from a pollable */
JANET_API JanetListenerState *janet_listen(JanetPollable *pollable, JanetListener behavior, int mask, size_t size, void *user);
/* Shorthand for yielding to event loop in C */
JANET_NO_RETURN JANET_API void janet_await(void);
/* For use inside listeners - adds a timeout to the current fiber, such that
* it will be resumed after sec seconds if no other event schedules the current fiber. */
void janet_addtimeout(double sec);
#endif
/* Parsing */
@@ -1402,6 +1512,13 @@ JANET_API JanetBuffer *janet_pretty(JanetBuffer *buffer, int depth, int flags, J
#define JANET_HASH_KEY_SIZE 16
JANET_API void janet_init_hash_key(uint8_t key[JANET_HASH_KEY_SIZE]);
#endif
JANET_API void janet_try_init(JanetTryState *state);
#if defined(JANET_BSD) || defined(JANET_APPLE)
#define janet_try(state) (janet_try_init(state), (JanetSignal) _setjmp((state)->buf))
#else
#define janet_try(state) (janet_try_init(state), (JanetSignal) setjmp((state)->buf))
#endif
JANET_API void janet_restore(JanetTryState *state);
JANET_API int janet_equals(Janet x, Janet y);
JANET_API int32_t janet_hash(Janet x);
JANET_API int janet_compare(Janet x, Janet y);
@@ -1560,6 +1677,7 @@ extern JANET_API const JanetAbstractType janet_file_type;
#define JANET_FILE_NONIL 512
JANET_API Janet janet_makefile(FILE *f, int32_t flags);
JANET_API JanetFile *janet_makejfile(FILE *f, int32_t flags);
JANET_API FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags);
JANET_API FILE *janet_dynfile(const char *name, FILE *def);
JANET_API JanetFile *janet_getjfile(const Janet *argv, int32_t n);
@@ -1622,6 +1740,7 @@ typedef enum {
RULE_TO, /* [rule] */
RULE_THRU, /* [rule] */
RULE_LENPREFIX, /* [rule_a, rule_b (repeat rule_b rule_a times)] */
RULE_READINT, /* [(signedness << 4) | (endianess << 5) | bytewidth, tag] */
} JanetPegOpcode;
typedef struct {

View File

@@ -1064,7 +1064,7 @@ int main(int argc, char **argv) {
janet_stacktrace(fiber, out);
}
#ifdef JANET_NET
#ifdef JANET_EV
status = JANET_SIGNAL_OK;
janet_loop();
#endif

View File

@@ -3,7 +3,6 @@
(var num-tests-passed 0)
(var num-tests-run 0)
(var suite-num 0)
(var numchecks 0)
(var start-time 0)
(defn assert
@@ -12,17 +11,12 @@
(default e "assert error")
(++ num-tests-run)
(when x (++ num-tests-passed))
(def str (string e))
(def truncated
(if (> (length e) 40) (string (string/slice e 0 35) "...") (string e)))
(if x
(do
(when (= numchecks 25)
(set numchecks 0)
(print))
(++ numchecks)
(file/write stdout "\e[32m✔\e[0m"))
(do
(file/write stdout "\n\e[31m✘\e[0m ")
(set numchecks 0)
(print e)))
(xprintf stdout "\e[32m✔\e[0m %s: %v" truncated x)
(xprintf stdout "\n\e[31m✘\e[0m %s: %v" truncated x))
x)
(defmacro assert-error

View File

@@ -443,4 +443,26 @@
(check-match redef-b "aabeef" false)
(check-match redef-b "aaaaaa" false)
# Integer parsing
(check-deep '(int 1) "a" @[(chr "a")])
(check-deep '(uint 1) "a" @[(chr "a")])
(check-deep '(int-be 1) "a" @[(chr "a")])
(check-deep '(uint-be 1) "a" @[(chr "a")])
(check-deep '(int 1) "\xFF" @[-1])
(check-deep '(uint 1) "\xFF" @[255])
(check-deep '(int-be 1) "\xFF" @[-1])
(check-deep '(uint-be 1) "\xFF" @[255])
(check-deep '(int 2) "\xFF\x7f" @[0x7fff])
(check-deep '(int-be 2) "\x7f\xff" @[0x7fff])
(check-deep '(uint 2) "\xff\x7f" @[0x7fff])
(check-deep '(uint-be 2) "\x7f\xff" @[0x7fff])
(check-deep '(uint-be 2) "\x7f\xff" @[0x7fff])
(check-deep '(uint 8) "\xff\x7f\x00\x00\x00\x00\x00\x00" @[(int/u64 0x7fff)])
(check-deep '(int 8) "\xff\x7f\x00\x00\x00\x00\x00\x00" @[(int/s64 0x7fff)])
(check-deep '(uint 7) "\xff\x7f\x00\x00\x00\x00\x00" @[(int/u64 0x7fff)])
(check-deep '(int 7) "\xff\x7f\x00\x00\x00\x00\x00" @[(int/s64 0x7fff)])
(check-deep '(* (int 2) -1) "123" nil)
(end-suite)

View File

@@ -224,7 +224,7 @@ neldb\0\0\0\xD8\x05printG\x01\0\xDE\xDE\xDE'\x03\0marshal_tes/\x02
# No segfault, valgrind clean.
(def x @"\xCC\xCD.nd\x80\0\r\x1C\xCDg!\0\x07\xCC\xCD\r\x1Ce\x10\0\r;\xCDb\x04\xFF9\xFF\x80\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04uu\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\0\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04}\x04\x04\x04\x04\x04\x04\x04\x04#\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\0\x01\0\0\x03\x04\x04\x04\xE2\x03\x04\x04\x04\x04\x04\x04\x04\x04\x04\x14\x1A\x04\x04\x04\x04\x04\x18\x04\x04!\x04\xE2\x03\x04\x04\x04\x04\x04\x04$\x04\x04\x04\x04\x04\x04\x04\x04\x04\x80\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04A\0\0\0\x03\0\0!\xBF\xFF")
(unmarshal x load-image-dict)
(assert-error "bad fiber status" (unmarshal x load-image-dict))
(gccollect)
(marshal x make-image-dict)

View File

@@ -61,4 +61,8 @@
(assert-no-error "import macro 1" (macex '(import a :as b :fresh maybe)))
(assert (deep= ~(,import* "a" :as "b" :fresh maybe) (macex '(import a :as b :fresh maybe))) "import macro 2")
# #477 walk preserving bracket type
(assert (= :brackets (tuple/type (postwalk identity '[]))) "walk square brackets 1")
(assert (= :brackets (tuple/type (walk identity '[]))) "walk square brackets 2")
(end-suite)