mirror of
https://github.com/janet-lang/janet
synced 2026-05-04 12:41:28 +00:00
Compare commits
45 Commits
v1.41.0
...
connect-ex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d388f267a | ||
|
|
37a2677ecb | ||
|
|
143ba6ba0f | ||
|
|
0e8c3e3e0a | ||
|
|
4d13437c31 | ||
|
|
b134f5359d | ||
|
|
ce56342168 | ||
|
|
ab86ef09ef | ||
|
|
b8db108702 | ||
|
|
479d846f7a | ||
|
|
39f8cf207c | ||
|
|
95f2e233c5 | ||
|
|
e8f9c12935 | ||
|
|
32d75c9e49 | ||
|
|
5fec2aa9df | ||
|
|
54fbd7607f | ||
|
|
019829fdf9 | ||
|
|
2602bec017 | ||
|
|
403b2c704a | ||
|
|
ca9ffaa5bb | ||
|
|
e61194a8d9 | ||
|
|
08e4030487 | ||
|
|
56b5998553 | ||
|
|
ea997d585b | ||
|
|
fc725e2511 | ||
|
|
d636502c32 | ||
|
|
0fea20c821 | ||
|
|
91cc499e77 | ||
|
|
68850a0a05 | ||
|
|
d3d7c675a8 | ||
|
|
b2c9fc123c | ||
|
|
fa0c039cd3 | ||
|
|
78ef9d1733 | ||
|
|
b6676f350c | ||
|
|
0299620a2d | ||
|
|
739d9d9fe3 | ||
|
|
1557f9da78 | ||
|
|
529d8c9e4a | ||
|
|
2df16e5a48 | ||
|
|
b0db2b22d6 | ||
|
|
8b6d56edae | ||
|
|
a2a7e9f01e | ||
|
|
4b078e7a45 | ||
|
|
5c0bb4b385 | ||
|
|
2aaa7dfa10 |
@@ -10,3 +10,9 @@ tasks:
|
|||||||
gmake test
|
gmake test
|
||||||
sudo gmake install
|
sudo gmake install
|
||||||
sudo gmake uninstall
|
sudo gmake uninstall
|
||||||
|
- build-sanitizers: |
|
||||||
|
cd janet
|
||||||
|
CFLAGS="-g -O2 -fsanitize=address,undefined" gmake
|
||||||
|
gmake test
|
||||||
|
sudo gmake install
|
||||||
|
sudo gmake uninstall
|
||||||
|
|||||||
8
.github/cosmo/setup
vendored
8
.github/cosmo/setup
vendored
@@ -1,19 +1,19 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
sudo apt update
|
sudo apt-get update
|
||||||
sudo apt-get install -y ca-certificates libssl-dev\
|
sudo apt-get install -y ca-certificates libssl-dev\
|
||||||
qemu qemu-utils qemu-user-static\
|
qemu-utils qemu-user-static\
|
||||||
texinfo groff\
|
texinfo groff\
|
||||||
cmake ninja-build bison zip\
|
cmake ninja-build bison zip\
|
||||||
pkg-config build-essential autoconf re2c
|
pkg-config build-essential autoconf re2c
|
||||||
|
|
||||||
# download cosmocc
|
# download cosmocc
|
||||||
cd /sc
|
cd /sc
|
||||||
wget https://github.com/jart/cosmopolitan/releases/download/3.3.3/cosmocc-3.3.3.zip
|
wget https://github.com/jart/cosmopolitan/releases/download/4.0.2/cosmocc-4.0.2.zip
|
||||||
mkdir -p cosmocc
|
mkdir -p cosmocc
|
||||||
cd cosmocc
|
cd cosmocc
|
||||||
unzip ../cosmocc-3.3.3.zip
|
unzip ../cosmocc-4.0.2.zip
|
||||||
|
|
||||||
# register
|
# register
|
||||||
cd /sc/cosmocc
|
cd /sc/cosmocc
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-latest, macos-13 ]
|
os: [ ubuntu-latest ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
@@ -46,7 +46,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ macos-latest ]
|
os: [ macos-latest, macos-15-intel ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
|
|||||||
56
.github/workflows/test.yml
vendored
56
.github/workflows/test.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-latest, macos-latest, macos-14 ]
|
os: [ ubuntu-latest, macos-latest, macos-14, macos-15-intel ]
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout the repository
|
- name: Checkout the repository
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
@@ -21,6 +21,20 @@ jobs:
|
|||||||
- name: Test the project
|
- name: Test the project
|
||||||
run: make test
|
run: make test
|
||||||
|
|
||||||
|
test-posix-sanitizers:
|
||||||
|
name: Build and test on POSIX systems with sanitizers turned on
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ ubuntu-latest ]
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@master
|
||||||
|
- name: Compile the project
|
||||||
|
run: make clean && CFLAGS="-g -O2 -fsanitize=address,undefined" make
|
||||||
|
- name: Test the project
|
||||||
|
run: make test
|
||||||
|
|
||||||
test-windows:
|
test-windows:
|
||||||
name: Build and test on Windows
|
name: Build and test on Windows
|
||||||
strategy:
|
strategy:
|
||||||
@@ -42,6 +56,27 @@ jobs:
|
|||||||
shell: cmd
|
shell: cmd
|
||||||
run: build_win dist
|
run: build_win dist
|
||||||
|
|
||||||
|
test-windows-sanitizers:
|
||||||
|
name: Build and test on Windows with sanitizers
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ windows-latest ]
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@master
|
||||||
|
- name: Setup MSVC
|
||||||
|
uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
- name: Build the project
|
||||||
|
shell: cmd
|
||||||
|
run: set SANITIZE=1 & build_win
|
||||||
|
- name: Test the project
|
||||||
|
shell: cmd
|
||||||
|
run: set VERBOSE=1 & build_win test
|
||||||
|
- name: Test installer build
|
||||||
|
shell: cmd
|
||||||
|
run: build_win dist
|
||||||
|
|
||||||
test-windows-min:
|
test-windows-min:
|
||||||
name: Build and test on Windows Minimal build
|
name: Build and test on Windows Minimal build
|
||||||
strategy:
|
strategy:
|
||||||
@@ -136,3 +171,22 @@ jobs:
|
|||||||
run: docker run --privileged --rm tonistiigi/binfmt --install s390x
|
run: docker run --privileged --rm tonistiigi/binfmt --install s390x
|
||||||
- name: Build and run on emulated architecture
|
- name: Build and run on emulated architecture
|
||||||
run: docker run --rm -v .:/janet --platform linux/s390x alpine sh -c "apk update && apk add --no-interactive git build-base && cd /janet && make -j3 && make test"
|
run: docker run --rm -v .:/janet --platform linux/s390x alpine sh -c "apk update && apk add --no-interactive git build-base && cd /janet && make -j3 && make test"
|
||||||
|
|
||||||
|
test-cosmo:
|
||||||
|
name: Test build for Cosmo
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@master
|
||||||
|
- name: create build folder
|
||||||
|
run: |
|
||||||
|
sudo mkdir -p /sc
|
||||||
|
sudo chmod -R 0777 /sc
|
||||||
|
- name: setup Cosmopolitan Libc
|
||||||
|
run: bash ./.github/cosmo/setup
|
||||||
|
- name: Set the version
|
||||||
|
run: echo "version=${GITHUB_REF/refs\/tags\//}" >> $GITHUB_ENV
|
||||||
|
- name: Set the platform
|
||||||
|
run: echo "platform=cosmo" >> $GITHUB_ENV
|
||||||
|
- name: build Janet APE binary
|
||||||
|
run: bash ./.github/cosmo/build
|
||||||
|
|||||||
12
CHANGELOG.md
12
CHANGELOG.md
@@ -1,7 +1,17 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## 1.41.0 - 2026-02-15
|
## Unreleased - ???
|
||||||
|
- Documentation fixes
|
||||||
|
|
||||||
|
## 1.41.2 - 2026-02-18
|
||||||
|
- Fix regressions in `put` for arrays and buffers.
|
||||||
|
- Add `module/add-file-extension`
|
||||||
|
- Add `module/add-syspath`
|
||||||
|
- Fix issue with possible stack corrpution with abstract types that modify the current fiber.
|
||||||
|
- Allow use of the interpreter and garbage collection inside module entry for native modules.
|
||||||
|
|
||||||
|
## 1.41.1 - 2026-02-15
|
||||||
- Revert to blocking behaior of `net/connect` on windows to fix spurious errors.
|
- Revert to blocking behaior of `net/connect` on windows to fix spurious errors.
|
||||||
- Allow overriding the loader when doing imports with the `:loader` argument.
|
- Allow overriding the loader when doing imports with the `:loader` argument.
|
||||||
- Allow importing modules with a path extension to do what one would expect.
|
- Allow importing modules with a path extension to do what one would expect.
|
||||||
|
|||||||
@@ -23,7 +23,17 @@
|
|||||||
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
|
@rem set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD /fsanitize=address /Zi
|
||||||
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
|
@rem set JANET_LINK=link /nologo clang_rt.asan_dynamic-x86_64.lib clang_rt.asan_dynamic_runtime_thunk-x86_64.lib
|
||||||
|
|
||||||
@set JANET_COMPILE=cl /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD
|
if DEFINED CLANG (
|
||||||
|
@set COMPILER=clang-cl.exe
|
||||||
|
) else (
|
||||||
|
@set COMPILER=cl.exe
|
||||||
|
)
|
||||||
|
if DEFINED SANITIZE (
|
||||||
|
@set "SANITIZERS=/fsanitize=address"
|
||||||
|
) else (
|
||||||
|
@set "SANITIZERS="
|
||||||
|
)
|
||||||
|
@set JANET_COMPILE=%COMPILER% /nologo /Isrc\include /Isrc\conf /c /O2 /W3 /D_CRT_SECURE_NO_WARNINGS /MD %SANITIZERS%
|
||||||
@set JANET_LINK=link /nologo
|
@set JANET_LINK=link /nologo
|
||||||
|
|
||||||
@set JANET_LINK_STATIC=lib /nologo
|
@set JANET_LINK_STATIC=lib /nologo
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
|
|
||||||
project('janet', 'c',
|
project('janet', 'c',
|
||||||
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
|
default_options : ['c_std=c99', 'build.c_std=c99', 'b_lundef=false', 'default_library=both'],
|
||||||
version : '1.41.0')
|
version : '1.41.3')
|
||||||
|
|
||||||
# Global settings
|
# Global settings
|
||||||
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
|
janet_path = join_paths(get_option('prefix'), get_option('libdir'), 'janet')
|
||||||
|
|||||||
@@ -2925,11 +2925,23 @@
|
|||||||
(array/insert mp sys-index [(string ":sys:/:all:" ext) loader check-is-dep])
|
(array/insert mp sys-index [(string ":sys:/:all:" ext) loader check-is-dep])
|
||||||
(def curall-index (find-prefix ":cur:/:all:"))
|
(def curall-index (find-prefix ":cur:/:all:"))
|
||||||
(array/insert mp curall-index [(string ":cur:/:all:" ext) loader check-relative])
|
(array/insert mp curall-index [(string ":cur:/:all:" ext) loader check-relative])
|
||||||
|
mp)
|
||||||
|
|
||||||
|
(defn module/add-file-extension
|
||||||
|
```
|
||||||
|
Add paths to `module/paths` for a given file extension such that
|
||||||
|
the programmer can import a module by relative or absolute path from
|
||||||
|
the current working directory.
|
||||||
|
Returns the modified `module/paths`.
|
||||||
|
```
|
||||||
|
[ext loader]
|
||||||
|
(assert (string/has-prefix? "." ext) "file extension must have . prefix")
|
||||||
|
(def mp (dyn *module-paths* module/paths))
|
||||||
(array/insert mp 0 [":all:" loader (fn :check-ext [x] (string/has-suffix? ext x))])
|
(array/insert mp 0 [":all:" loader (fn :check-ext [x] (string/has-suffix? ext x))])
|
||||||
mp)
|
mp)
|
||||||
|
|
||||||
# Don't expose this externally yet - could break if custom module/paths is setup.
|
# Don't expose this externally yet - could break if custom module/paths is setup.
|
||||||
(defn- module/add-syspath
|
(defn module/add-syspath
|
||||||
```
|
```
|
||||||
Add a custom syspath to `module/paths` by duplicating all entries that being with `:sys:` and
|
Add a custom syspath to `module/paths` by duplicating all entries that being with `:sys:` and
|
||||||
adding duplicates with a specific path prefix instead.
|
adding duplicates with a specific path prefix instead.
|
||||||
@@ -2950,6 +2962,12 @@
|
|||||||
(module/add-paths "/init.janet" :source)
|
(module/add-paths "/init.janet" :source)
|
||||||
(module/add-paths ".janet" :source)
|
(module/add-paths ".janet" :source)
|
||||||
(module/add-paths ".jimage" :image)
|
(module/add-paths ".jimage" :image)
|
||||||
|
(module/add-file-extension ".janet" :source)
|
||||||
|
(module/add-file-extension ".jimage" :source)
|
||||||
|
# These obviously won't work on all platforms, but if a user explicitly
|
||||||
|
# tries to import them, we may as well try.
|
||||||
|
(module/add-file-extension ".so" :native)
|
||||||
|
(module/add-file-extension ".dll" :native)
|
||||||
(array/insert module/paths 0
|
(array/insert module/paths 0
|
||||||
[(fn is-cached [path] (if (in (dyn *module-cache* module/cache) path) path))
|
[(fn is-cached [path] (if (in (dyn *module-cache* module/cache) path) path))
|
||||||
:preload
|
:preload
|
||||||
@@ -3284,7 +3302,6 @@
|
|||||||
[&opt env local]
|
[&opt env local]
|
||||||
(env-walk keyword? env local))
|
(env-walk keyword? env local))
|
||||||
|
|
||||||
|
|
||||||
(defdyn *doc-width*
|
(defdyn *doc-width*
|
||||||
"Width in columns to print documentation printed with `doc-format`.")
|
"Width in columns to print documentation printed with `doc-format`.")
|
||||||
|
|
||||||
@@ -3997,7 +4014,7 @@
|
|||||||
"handler not supported for :datagram servers")
|
"handler not supported for :datagram servers")
|
||||||
(def s (net/listen host port type no-reuse))
|
(def s (net/listen host port type no-reuse))
|
||||||
(if handler
|
(if handler
|
||||||
(ev/go (fn [] (net/accept-loop s handler))))
|
(ev/go (fn :net/server-handler [] (net/accept-loop s handler))))
|
||||||
s))
|
s))
|
||||||
|
|
||||||
###
|
###
|
||||||
@@ -4653,8 +4670,7 @@
|
|||||||
|
|
||||||
(defn- run-main
|
(defn- run-main
|
||||||
[env subargs arg]
|
[env subargs arg]
|
||||||
(when-let [entry (in env 'main)
|
(when-let [main (module/value env 'main true)]
|
||||||
main (or (get entry :value) (in (get entry :ref) 0))]
|
|
||||||
(def guard (if (get env :debug) :ydt :y))
|
(def guard (if (get env :debug) :ydt :y))
|
||||||
(defn wrap-main [&]
|
(defn wrap-main [&]
|
||||||
(main ;subargs))
|
(main ;subargs))
|
||||||
@@ -4743,7 +4759,8 @@
|
|||||||
(apply-color
|
(apply-color
|
||||||
(and
|
(and
|
||||||
(not (getenv-alias "NO_COLOR"))
|
(not (getenv-alias "NO_COLOR"))
|
||||||
(os/isatty stdout)))
|
(os/isatty stdout)
|
||||||
|
(os/isatty stderr)))
|
||||||
|
|
||||||
(defn- get-lint-level
|
(defn- get-lint-level
|
||||||
[i]
|
[i]
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
|
|
||||||
#define JANET_VERSION_MAJOR 1
|
#define JANET_VERSION_MAJOR 1
|
||||||
#define JANET_VERSION_MINOR 41
|
#define JANET_VERSION_MINOR 41
|
||||||
#define JANET_VERSION_PATCH 0
|
#define JANET_VERSION_PATCH 3
|
||||||
#define JANET_VERSION_EXTRA "-dev"
|
#define JANET_VERSION_EXTRA "-dev"
|
||||||
#define JANET_VERSION "1.41.0-dev"
|
#define JANET_VERSION "1.41.3-dev"
|
||||||
|
|
||||||
/* #define JANET_BUILD "local" */
|
/* #define JANET_BUILD "local" */
|
||||||
|
|
||||||
|
|||||||
@@ -201,14 +201,29 @@ static JanetSlot do_cmp(JanetFopts opts, JanetSlot *args) {
|
|||||||
return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil(), janet_wrap_nil());
|
return opreduce(opts, args, JOP_COMPARE, 0, janet_wrap_nil(), janet_wrap_nil());
|
||||||
}
|
}
|
||||||
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
|
static JanetSlot do_put(JanetFopts opts, JanetSlot *args) {
|
||||||
if (opts.flags & JANET_FOPTS_DROP) {
|
int8_t inline_index = 0;
|
||||||
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
|
if (can_slot_be_imm(args[1], &inline_index)) {
|
||||||
return janetc_cslot(janet_wrap_nil());
|
/* Use JOP_PUT_INDEX */
|
||||||
|
if (opts.flags & JANET_FOPTS_DROP) {
|
||||||
|
janetc_emit_ssi(opts.compiler, JOP_PUT_INDEX, args[0], args[2], inline_index, 0);
|
||||||
|
return janetc_cslot(janet_wrap_nil());
|
||||||
|
} else {
|
||||||
|
JanetSlot t = janetc_gettarget(opts);
|
||||||
|
janetc_copy(opts.compiler, t, args[0]);
|
||||||
|
janetc_emit_ssi(opts.compiler, JOP_PUT_INDEX, t, args[2], inline_index, 0);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
JanetSlot t = janetc_gettarget(opts);
|
/* Use JOP_PUT */
|
||||||
janetc_copy(opts.compiler, t, args[0]);
|
if (opts.flags & JANET_FOPTS_DROP) {
|
||||||
janetc_emit_sss(opts.compiler, JOP_PUT, t, args[1], args[2], 0);
|
janetc_emit_sss(opts.compiler, JOP_PUT, args[0], args[1], args[2], 0);
|
||||||
return t;
|
return janetc_cslot(janet_wrap_nil());
|
||||||
|
} else {
|
||||||
|
JanetSlot t = janetc_gettarget(opts);
|
||||||
|
janetc_copy(opts.compiler, t, args[0]);
|
||||||
|
janetc_emit_sss(opts.compiler, JOP_PUT, t, args[1], args[2], 0);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static JanetSlot do_length(JanetFopts opts, JanetSlot *args) {
|
static JanetSlot do_length(JanetFopts opts, JanetSlot *args) {
|
||||||
|
|||||||
@@ -459,11 +459,22 @@ JanetSlot *janetc_toslotskv(JanetCompiler *c, Janet ds) {
|
|||||||
const JanetKV *kvs = NULL;
|
const JanetKV *kvs = NULL;
|
||||||
int32_t cap = 0, len = 0;
|
int32_t cap = 0, len = 0;
|
||||||
janet_dictionary_view(ds, &kvs, &len, &cap);
|
janet_dictionary_view(ds, &kvs, &len, &cap);
|
||||||
for (int32_t i = 0; i < cap; i++) {
|
/* Sort keys for stability of order? */
|
||||||
if (janet_checktype(kvs[i].key, JANET_NIL)) continue;
|
int32_t *index_buf;
|
||||||
janet_v_push(ret, janetc_value(subopts, kvs[i].key));
|
int32_t index_buf_stack[32];
|
||||||
janet_v_push(ret, janetc_value(subopts, kvs[i].value));
|
int32_t *index_buf_heap = NULL;
|
||||||
|
if (len < 32) {
|
||||||
|
index_buf = index_buf_stack;
|
||||||
|
} else {
|
||||||
|
index_buf_heap = janet_smalloc(sizeof(int32_t) * len);
|
||||||
|
index_buf = index_buf_heap;
|
||||||
}
|
}
|
||||||
|
if (len) janet_sorted_keys(kvs, cap, index_buf);
|
||||||
|
for (int32_t i = 0; i < len; i++) {
|
||||||
|
janet_v_push(ret, janetc_value(subopts, kvs[index_buf[i]].key));
|
||||||
|
janet_v_push(ret, janetc_value(subopts, kvs[index_buf[i]].value));
|
||||||
|
}
|
||||||
|
if (index_buf_heap) janet_sfree(index_buf_heap);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "compile.h"
|
#include "compile.h"
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "fiber.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Generated bytes */
|
/* Generated bytes */
|
||||||
@@ -294,6 +295,7 @@ JANET_CORE_FN(janet_core_native,
|
|||||||
"from the native module.") {
|
"from the native module.") {
|
||||||
JanetModule init;
|
JanetModule init;
|
||||||
janet_arity(argc, 1, 2);
|
janet_arity(argc, 1, 2);
|
||||||
|
Janet argv0 = argv[0];
|
||||||
const uint8_t *path = janet_getstring(argv, 0);
|
const uint8_t *path = janet_getstring(argv, 0);
|
||||||
const uint8_t *error = NULL;
|
const uint8_t *error = NULL;
|
||||||
JanetTable *env;
|
JanetTable *env;
|
||||||
@@ -306,8 +308,10 @@ JANET_CORE_FN(janet_core_native,
|
|||||||
if (!init) {
|
if (!init) {
|
||||||
janet_panicf("could not load native %S: %S", path, error);
|
janet_panicf("could not load native %S: %S", path, error);
|
||||||
}
|
}
|
||||||
|
/* GC root incase garbage collection called inside module entry */
|
||||||
|
janet_fiber_push(janet_vm.fiber, janet_wrap_table(env));
|
||||||
init(env);
|
init(env);
|
||||||
janet_table_put(env, janet_ckeywordv("native"), argv[0]);
|
janet_table_put(env, janet_ckeywordv("native"), argv0);
|
||||||
return janet_wrap_table(env);
|
return janet_wrap_table(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -524,9 +524,9 @@ static void janet_schedule_general(JanetFiber *fiber, Janet value, JanetSignal s
|
|||||||
fiber->gc.flags |= JANET_FIBER_FLAG_ROOT;
|
fiber->gc.flags |= JANET_FIBER_FLAG_ROOT;
|
||||||
if (sig == JANET_SIGNAL_ERROR) fiber->gc.flags |= JANET_FIBER_EV_FLAG_CANCELED;
|
if (sig == JANET_SIGNAL_ERROR) fiber->gc.flags |= JANET_FIBER_EV_FLAG_CANCELED;
|
||||||
if (soon) {
|
if (soon) {
|
||||||
janet_q_push_head(&janet_vm.spawn, &t, sizeof(t));
|
janet_assert(!janet_q_push_head(&janet_vm.spawn, &t, sizeof(t)), "schedule queue overflow");
|
||||||
} else {
|
} else {
|
||||||
janet_q_push(&janet_vm.spawn, &t, sizeof(t));
|
janet_assert(!janet_q_push(&janet_vm.spawn, &t, sizeof(t)), "schedule queue overflow");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -959,11 +959,12 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
|
|||||||
janet_schedule(fiber, janet_wrap_nil());
|
janet_schedule(fiber, janet_wrap_nil());
|
||||||
}
|
}
|
||||||
} else if (mode != JANET_CP_MODE_CLOSE) {
|
} else if (mode != JANET_CP_MODE_CLOSE) {
|
||||||
/* Fiber has already been cancelled or resumed. */
|
/* Fiber has already been canceled or resumed. */
|
||||||
/* Resend event to another waiting thread, depending on mode */
|
/* Resend event to another waiting thread, depending on mode */
|
||||||
int is_read = (mode == JANET_CP_MODE_CHOICE_READ) || (mode == JANET_CP_MODE_READ);
|
int is_read = (mode == JANET_CP_MODE_CHOICE_READ) || (mode == JANET_CP_MODE_READ);
|
||||||
if (is_read) {
|
if (is_read) {
|
||||||
JanetChannelPending reader;
|
JanetChannelPending reader;
|
||||||
|
int sent = 0;
|
||||||
while (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) {
|
while (!janet_q_pop(&channel->read_pending, &reader, sizeof(reader))) {
|
||||||
JanetVM *vm = reader.thread;
|
JanetVM *vm = reader.thread;
|
||||||
if (!vm) continue;
|
if (!vm) continue;
|
||||||
@@ -974,8 +975,12 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
|
|||||||
msg.argp = channel;
|
msg.argp = channel;
|
||||||
msg.argj = x;
|
msg.argj = x;
|
||||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||||
|
sent = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!sent) {
|
||||||
|
janet_chan_unpack(channel, &x, 1);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
JanetChannelPending writer;
|
JanetChannelPending writer;
|
||||||
while (!janet_q_pop(&channel->write_pending, &writer, sizeof(writer))) {
|
while (!janet_q_pop(&channel->write_pending, &writer, sizeof(writer))) {
|
||||||
@@ -1001,14 +1006,14 @@ static void janet_thread_chan_cb(JanetEVGenericMessage msg) {
|
|||||||
static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode) {
|
static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode) {
|
||||||
JanetChannelPending reader;
|
JanetChannelPending reader;
|
||||||
int is_empty;
|
int is_empty;
|
||||||
if (janet_chan_pack(channel, &x)) {
|
|
||||||
janet_chan_unlock(channel);
|
|
||||||
janet_panicf("failed to pack value for channel: %v", x);
|
|
||||||
}
|
|
||||||
if (channel->closed) {
|
if (channel->closed) {
|
||||||
janet_chan_unlock(channel);
|
janet_chan_unlock(channel);
|
||||||
janet_panic("cannot write to closed channel");
|
janet_panic("cannot write to closed channel");
|
||||||
}
|
}
|
||||||
|
if (janet_chan_pack(channel, &x)) {
|
||||||
|
janet_chan_unlock(channel);
|
||||||
|
janet_panicf("failed to pack value for channel: %v", x);
|
||||||
|
}
|
||||||
int is_threaded = janet_chan_is_threaded(channel);
|
int is_threaded = janet_chan_is_threaded(channel);
|
||||||
if (is_threaded) {
|
if (is_threaded) {
|
||||||
/* don't dereference fiber from another thread */
|
/* don't dereference fiber from another thread */
|
||||||
@@ -1021,6 +1026,7 @@ static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode
|
|||||||
if (is_empty) {
|
if (is_empty) {
|
||||||
/* No pending reader */
|
/* No pending reader */
|
||||||
if (janet_q_push(&channel->items, &x, sizeof(Janet))) {
|
if (janet_q_push(&channel->items, &x, sizeof(Janet))) {
|
||||||
|
janet_chan_unpack(channel, &x, 1);
|
||||||
janet_chan_unlock(channel);
|
janet_chan_unlock(channel);
|
||||||
janet_panicf("channel overflow: %v", x);
|
janet_panicf("channel overflow: %v", x);
|
||||||
} else if (janet_q_count(&channel->items) > channel->limit) {
|
} else if (janet_q_count(&channel->items) > channel->limit) {
|
||||||
@@ -1054,6 +1060,9 @@ static int janet_channel_push_with_lock(JanetChannel *channel, Janet x, int mode
|
|||||||
msg.argj = x;
|
msg.argj = x;
|
||||||
if (vm) {
|
if (vm) {
|
||||||
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
janet_ev_post_event(vm, janet_thread_chan_cb, msg);
|
||||||
|
} else {
|
||||||
|
/* If no vm to send to, we must clean up (unpack) the packed payload to avoid leak */
|
||||||
|
janet_chan_unpack(channel, &x, 1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
|
if (reader.mode == JANET_CP_MODE_CHOICE_READ) {
|
||||||
@@ -1199,20 +1208,6 @@ JANET_CORE_FN(cfun_channel_pop,
|
|||||||
janet_await();
|
janet_await();
|
||||||
}
|
}
|
||||||
|
|
||||||
static void chan_unlock_args(const Janet *argv, int32_t n) {
|
|
||||||
for (int32_t i = 0; i < n; i++) {
|
|
||||||
int32_t len;
|
|
||||||
const Janet *data;
|
|
||||||
JanetChannel *chan;
|
|
||||||
if (janet_indexed_view(argv[i], &data, &len) && len == 2) {
|
|
||||||
chan = janet_getchannel(data, 0);
|
|
||||||
} else {
|
|
||||||
chan = janet_getchannel(argv, i);
|
|
||||||
}
|
|
||||||
janet_chan_unlock(chan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JANET_CORE_FN(cfun_channel_choice,
|
JANET_CORE_FN(cfun_channel_choice,
|
||||||
"(ev/select & clauses)",
|
"(ev/select & clauses)",
|
||||||
"Block until the first of several channel operations occur. Returns a "
|
"Block until the first of several channel operations occur. Returns a "
|
||||||
@@ -1241,29 +1236,27 @@ JANET_CORE_FN(cfun_channel_choice,
|
|||||||
janet_chan_lock(chan);
|
janet_chan_lock(chan);
|
||||||
if (chan->closed) {
|
if (chan->closed) {
|
||||||
janet_chan_unlock(chan);
|
janet_chan_unlock(chan);
|
||||||
chan_unlock_args(argv, i);
|
|
||||||
return make_close_result(chan);
|
return make_close_result(chan);
|
||||||
}
|
}
|
||||||
if (janet_q_count(&chan->items) < chan->limit) {
|
if (janet_q_count(&chan->items) < chan->limit) {
|
||||||
janet_channel_push_with_lock(chan, data[1], 1);
|
janet_channel_push_with_lock(chan, data[1], 1);
|
||||||
chan_unlock_args(argv, i);
|
|
||||||
return make_write_result(chan);
|
return make_write_result(chan);
|
||||||
}
|
}
|
||||||
|
janet_chan_unlock(chan);
|
||||||
} else {
|
} else {
|
||||||
/* Read */
|
/* Read */
|
||||||
JanetChannel *chan = janet_getchannel(argv, i);
|
JanetChannel *chan = janet_getchannel(argv, i);
|
||||||
janet_chan_lock(chan);
|
janet_chan_lock(chan);
|
||||||
if (chan->closed) {
|
if (chan->closed) {
|
||||||
janet_chan_unlock(chan);
|
janet_chan_unlock(chan);
|
||||||
chan_unlock_args(argv, i);
|
|
||||||
return make_close_result(chan);
|
return make_close_result(chan);
|
||||||
}
|
}
|
||||||
if (chan->items.head != chan->items.tail) {
|
if (chan->items.head != chan->items.tail) {
|
||||||
Janet item;
|
Janet item;
|
||||||
janet_channel_pop_with_lock(chan, &item, 1);
|
janet_channel_pop_with_lock(chan, &item, 1);
|
||||||
chan_unlock_args(argv, i);
|
|
||||||
return make_read_result(chan, item);
|
return make_read_result(chan, item);
|
||||||
}
|
}
|
||||||
|
janet_chan_unlock(chan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1474,11 +1467,12 @@ static void *janet_chanat_unmarshal(JanetMarshalContext *ctx) {
|
|||||||
int32_t limit = janet_unmarshal_int(ctx);
|
int32_t limit = janet_unmarshal_int(ctx);
|
||||||
int32_t count = janet_unmarshal_int(ctx);
|
int32_t count = janet_unmarshal_int(ctx);
|
||||||
if (count < 0) janet_panic("invalid negative channel count");
|
if (count < 0) janet_panic("invalid negative channel count");
|
||||||
|
if (count > limit) janet_panic("invalid channel count");
|
||||||
janet_chan_init(abst, limit, 0);
|
janet_chan_init(abst, limit, 0);
|
||||||
abst->closed = !!is_closed;
|
abst->closed = !!is_closed;
|
||||||
for (int32_t i = 0; i < count; i++) {
|
for (int32_t i = 0; i < count; i++) {
|
||||||
Janet item = janet_unmarshal_janet(ctx);
|
Janet item = janet_unmarshal_janet(ctx);
|
||||||
janet_q_push(&abst->items, &item, sizeof(item));
|
janet_assert(!janet_q_push(&abst->items, &item, sizeof(item)), "bad unmarshal channel");
|
||||||
}
|
}
|
||||||
return abst;
|
return abst;
|
||||||
}
|
}
|
||||||
@@ -1718,20 +1712,20 @@ void janet_loop1_impl(int has_timeout, JanetTimestamp to) {
|
|||||||
janet_free(response);
|
janet_free(response);
|
||||||
} else {
|
} else {
|
||||||
/* Normal event */
|
/* Normal event */
|
||||||
|
JanetOverlapped *jo = (JanetOverlapped *) overlapped;
|
||||||
JanetStream *stream = (JanetStream *) completionKey;
|
JanetStream *stream = (JanetStream *) completionKey;
|
||||||
JanetFiber *fiber = NULL;
|
JanetFiber *fiber = NULL;
|
||||||
if (stream->read_fiber && stream->read_fiber->ev_state == overlapped) {
|
if (stream->read_fiber && stream->read_fiber->ev_state == jo) {
|
||||||
fiber = stream->read_fiber;
|
fiber = stream->read_fiber;
|
||||||
} else if (stream->write_fiber && stream->write_fiber->ev_state == overlapped) {
|
} else if (stream->write_fiber && stream->write_fiber->ev_state == jo) {
|
||||||
fiber = stream->write_fiber;
|
fiber = stream->write_fiber;
|
||||||
}
|
}
|
||||||
if (fiber != NULL) {
|
if (fiber != NULL) {
|
||||||
fiber->flags &= ~JANET_FIBER_EV_FLAG_IN_FLIGHT;
|
fiber->flags &= ~JANET_FIBER_EV_FLAG_IN_FLIGHT;
|
||||||
/* System is done with this, we can reused this data */
|
jo->bytes_transfered = (ULONG_PTR) num_bytes_transferred;
|
||||||
overlapped->InternalHigh = (ULONG_PTR) num_bytes_transferred;
|
|
||||||
fiber->ev_callback(fiber, result ? JANET_ASYNC_EVENT_COMPLETE : JANET_ASYNC_EVENT_FAILED);
|
fiber->ev_callback(fiber, result ? JANET_ASYNC_EVENT_COMPLETE : JANET_ASYNC_EVENT_FAILED);
|
||||||
} else {
|
} else {
|
||||||
janet_free((void *) overlapped);
|
janet_free((void *) jo);
|
||||||
janet_ev_dec_refcount();
|
janet_ev_dec_refcount();
|
||||||
}
|
}
|
||||||
janet_stream_checktoclose(stream);
|
janet_stream_checktoclose(stream);
|
||||||
@@ -2443,7 +2437,7 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
OVERLAPPED overlapped;
|
JanetOverlapped overlapped;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
#ifdef JANET_NET
|
#ifdef JANET_NET
|
||||||
WSABUF wbuf;
|
WSABUF wbuf;
|
||||||
@@ -2478,7 +2472,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
case JANET_ASYNC_EVENT_FAILED:
|
case JANET_ASYNC_EVENT_FAILED:
|
||||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||||
/* Called when read finished */
|
/* Called when read finished */
|
||||||
uint32_t ev_bytes = (uint32_t) state->overlapped.InternalHigh;
|
uint32_t ev_bytes = (uint32_t) state->overlapped.bytes_transfered;
|
||||||
state->bytes_read += ev_bytes;
|
state->bytes_read += ev_bytes;
|
||||||
if (state->bytes_read == 0 && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) {
|
if (state->bytes_read == 0 && (state->mode != JANET_ASYNC_READMODE_RECVFROM)) {
|
||||||
janet_schedule(fiber, janet_wrap_nil());
|
janet_schedule(fiber, janet_wrap_nil());
|
||||||
@@ -2510,7 +2504,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
/* fallthrough */
|
/* fallthrough */
|
||||||
case JANET_ASYNC_EVENT_INIT: {
|
case JANET_ASYNC_EVENT_INIT: {
|
||||||
int32_t chunk_size = state->bytes_left > JANET_EV_CHUNKSIZE ? JANET_EV_CHUNKSIZE : state->bytes_left;
|
int32_t chunk_size = state->bytes_left > JANET_EV_CHUNKSIZE ? JANET_EV_CHUNKSIZE : state->bytes_left;
|
||||||
memset(&(state->overlapped), 0, sizeof(OVERLAPPED));
|
memset(&(state->overlapped), 0, sizeof(JanetOverlapped));
|
||||||
int status;
|
int status;
|
||||||
#ifdef JANET_NET
|
#ifdef JANET_NET
|
||||||
if (state->mode == JANET_ASYNC_READMODE_RECVFROM) {
|
if (state->mode == JANET_ASYNC_READMODE_RECVFROM) {
|
||||||
@@ -2518,7 +2512,7 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
state->wbuf.buf = (char *) state->chunk_buf;
|
state->wbuf.buf = (char *) state->chunk_buf;
|
||||||
state->fromlen = sizeof(state->from);
|
state->fromlen = sizeof(state->from);
|
||||||
status = WSARecvFrom((SOCKET) stream->handle, &state->wbuf, 1,
|
status = WSARecvFrom((SOCKET) stream->handle, &state->wbuf, 1,
|
||||||
NULL, &state->flags, &state->from, &state->fromlen, &state->overlapped, NULL);
|
NULL, &state->flags, &state->from, &state->fromlen, &state->overlapped.as.wsaoverlapped, NULL);
|
||||||
if (status && (WSA_IO_PENDING != WSAGetLastError())) {
|
if (status && (WSA_IO_PENDING != WSAGetLastError())) {
|
||||||
janet_cancel(fiber, janet_ev_lasterr());
|
janet_cancel(fiber, janet_ev_lasterr());
|
||||||
janet_async_end(fiber);
|
janet_async_end(fiber);
|
||||||
@@ -2529,9 +2523,9 @@ void ev_callback_read(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
{
|
{
|
||||||
/* Some handles (not all) read from the offset in lpOverlapped
|
/* Some handles (not all) read from the offset in lpOverlapped
|
||||||
* if its not set before calling `ReadFile` these streams will always read from offset 0 */
|
* if its not set before calling `ReadFile` these streams will always read from offset 0 */
|
||||||
state->overlapped.Offset = (DWORD) state->bytes_read;
|
state->overlapped.as.overlapped.Offset = (DWORD) state->bytes_read;
|
||||||
|
|
||||||
status = ReadFile(stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped);
|
status = ReadFile(stream->handle, state->chunk_buf, chunk_size, NULL, &state->overlapped.as.overlapped);
|
||||||
if (!status && (ERROR_IO_PENDING != GetLastError())) {
|
if (!status && (ERROR_IO_PENDING != GetLastError())) {
|
||||||
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
if (GetLastError() == ERROR_BROKEN_PIPE) {
|
||||||
if (state->bytes_read) {
|
if (state->bytes_read) {
|
||||||
@@ -2687,7 +2681,7 @@ typedef enum {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
OVERLAPPED overlapped;
|
JanetOverlapped overlapped;
|
||||||
DWORD flags;
|
DWORD flags;
|
||||||
#ifdef JANET_NET
|
#ifdef JANET_NET
|
||||||
WSABUF wbuf;
|
WSABUF wbuf;
|
||||||
@@ -2728,7 +2722,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
case JANET_ASYNC_EVENT_FAILED:
|
case JANET_ASYNC_EVENT_FAILED:
|
||||||
case JANET_ASYNC_EVENT_COMPLETE: {
|
case JANET_ASYNC_EVENT_COMPLETE: {
|
||||||
/* Called when write finished */
|
/* Called when write finished */
|
||||||
uint32_t ev_bytes = (uint32_t) state->overlapped.InternalHigh;
|
uint32_t ev_bytes = (uint32_t) state->overlapped.bytes_transfered;
|
||||||
if (ev_bytes == 0 && (state->mode != JANET_ASYNC_WRITEMODE_SENDTO)) {
|
if (ev_bytes == 0 && (state->mode != JANET_ASYNC_WRITEMODE_SENDTO)) {
|
||||||
janet_cancel(fiber, janet_cstringv("disconnect"));
|
janet_cancel(fiber, janet_cstringv("disconnect"));
|
||||||
janet_async_end(fiber);
|
janet_async_end(fiber);
|
||||||
@@ -2757,7 +2751,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
bytes = state->src.str;
|
bytes = state->src.str;
|
||||||
len = janet_string_length(bytes);
|
len = janet_string_length(bytes);
|
||||||
}
|
}
|
||||||
memset(&(state->overlapped), 0, sizeof(WSAOVERLAPPED));
|
memset(&(state->overlapped), 0, sizeof(JanetOverlapped));
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
#ifdef JANET_NET
|
#ifdef JANET_NET
|
||||||
@@ -2767,7 +2761,7 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
state->wbuf.len = len;
|
state->wbuf.len = len;
|
||||||
const struct sockaddr *to = state->dest_abst;
|
const struct sockaddr *to = state->dest_abst;
|
||||||
int tolen = (int) janet_abstract_size((void *) to);
|
int tolen = (int) janet_abstract_size((void *) to);
|
||||||
status = WSASendTo(sock, &state->wbuf, 1, NULL, state->flags, to, tolen, &state->overlapped, NULL);
|
status = WSASendTo(sock, &state->wbuf, 1, NULL, state->flags, to, tolen, &state->overlapped.as.wsaoverlapped, NULL);
|
||||||
if (status) {
|
if (status) {
|
||||||
if (WSA_IO_PENDING == WSAGetLastError()) {
|
if (WSA_IO_PENDING == WSAGetLastError()) {
|
||||||
janet_async_in_flight(fiber);
|
janet_async_in_flight(fiber);
|
||||||
@@ -2790,9 +2784,9 @@ void ev_callback_write(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
* for more details see the lpOverlapped parameter in
|
* for more details see the lpOverlapped parameter in
|
||||||
* https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
|
* https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefile
|
||||||
*/
|
*/
|
||||||
state->overlapped.Offset = (DWORD) 0xFFFFFFFF;
|
state->overlapped.as.overlapped.Offset = (DWORD) 0xFFFFFFFF;
|
||||||
state->overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF;
|
state->overlapped.as.overlapped.OffsetHigh = (DWORD) 0xFFFFFFFF;
|
||||||
status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped);
|
status = WriteFile(stream->handle, bytes, len, NULL, &state->overlapped.as.overlapped);
|
||||||
if (!status) {
|
if (!status) {
|
||||||
if (ERROR_IO_PENDING == GetLastError()) {
|
if (ERROR_IO_PENDING == GetLastError()) {
|
||||||
janet_async_in_flight(fiber);
|
janet_async_in_flight(fiber);
|
||||||
@@ -2948,10 +2942,11 @@ int janet_make_pipe(JanetHandle handles[2], int mode) {
|
|||||||
if (!CreatePipe(handles, handles + 1, &saAttr, 0)) return -1;
|
if (!CreatePipe(handles, handles + 1, &saAttr, 0)) return -1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
sprintf(PipeNameBuffer,
|
snprintf(PipeNameBuffer,
|
||||||
"\\\\.\\Pipe\\JanetPipeFile.%08x.%08x",
|
sizeof(PipeNameBuffer),
|
||||||
(unsigned int) GetCurrentProcessId(),
|
"\\\\.\\Pipe\\JanetPipeFile.%08x.%08x",
|
||||||
(unsigned int) InterlockedIncrement(&PipeSerialNumber));
|
(unsigned int) GetCurrentProcessId(),
|
||||||
|
(unsigned int) InterlockedIncrement(&PipeSerialNumber));
|
||||||
|
|
||||||
/* server handle goes to subprocess */
|
/* server handle goes to subprocess */
|
||||||
shandle = CreateNamedPipeA(
|
shandle = CreateNamedPipeA(
|
||||||
|
|||||||
@@ -592,8 +592,8 @@ JANET_CORE_FN(cfun_fiber_status,
|
|||||||
"* :user(0-7) - the fiber is suspended by a user signal\n"
|
"* :user(0-7) - the fiber is suspended by a user signal\n"
|
||||||
"* :interrupted - the fiber was interrupted\n"
|
"* :interrupted - the fiber was interrupted\n"
|
||||||
"* :suspended - the fiber is waiting to be resumed by the scheduler\n"
|
"* :suspended - the fiber is waiting to be resumed by the scheduler\n"
|
||||||
"* :alive - the fiber is currently running and cannot be resumed\n"
|
"* :new - the fiber has just been created and not yet run\n"
|
||||||
"* :new - the fiber has just been created and not yet run") {
|
"* :alive - the fiber is currently running and cannot be resumed") {
|
||||||
janet_fixarity(argc, 1);
|
janet_fixarity(argc, 1);
|
||||||
JanetFiber *fiber = janet_getfiber(argv, 0);
|
JanetFiber *fiber = janet_getfiber(argv, 0);
|
||||||
uint32_t s = janet_fiber_status(fiber);
|
uint32_t s = janet_fiber_status(fiber);
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ static void janet_watcher_init(JanetWatcher *watcher, JanetChannel *channel, uin
|
|||||||
#define FILE_INFO_PADDING (4096 * 4)
|
#define FILE_INFO_PADDING (4096 * 4)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
OVERLAPPED overlapped;
|
JanetOverlapped overlapped;
|
||||||
JanetStream *stream;
|
JanetStream *stream;
|
||||||
JanetWatcher *watcher;
|
JanetWatcher *watcher;
|
||||||
JanetFiber *fiber;
|
JanetFiber *fiber;
|
||||||
@@ -456,7 +456,7 @@ static void janet_watcher_add(JanetWatcher *watcher, const char *path, uint32_t
|
|||||||
Janet pathv = janet_wrap_string(ow->dir_path);
|
Janet pathv = janet_wrap_string(ow->dir_path);
|
||||||
ow->flags = flags | watcher->default_flags;
|
ow->flags = flags | watcher->default_flags;
|
||||||
ow->watcher = watcher;
|
ow->watcher = watcher;
|
||||||
ow->overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL); /* Do we need this */
|
ow->overlapped.as.overlapped.hEvent = CreateEvent(NULL, FALSE, 0, NULL); /* Do we need this */
|
||||||
Janet streamv = janet_wrap_pointer(ow);
|
Janet streamv = janet_wrap_pointer(ow);
|
||||||
janet_table_put(watcher->watch_descriptors, pathv, streamv);
|
janet_table_put(watcher->watch_descriptors, pathv, streamv);
|
||||||
if (watcher->is_watching) {
|
if (watcher->is_watching) {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ static void *io_file_unmarshal(JanetMarshalContext *ctx);
|
|||||||
static Janet io_file_next(void *p, Janet key);
|
static Janet io_file_next(void *p, Janet key);
|
||||||
|
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
|
#include <io.h>
|
||||||
#define ftell _ftelli64
|
#define ftell _ftelli64
|
||||||
#define fseek _fseeki64
|
#define fseek _fseeki64
|
||||||
#endif
|
#endif
|
||||||
@@ -109,10 +110,11 @@ static int32_t checkflags(const uint8_t *str) {
|
|||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *makef(FILE *f, int32_t flags) {
|
static void *makef(FILE *f, int32_t flags, size_t bufsize) {
|
||||||
JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
|
JanetFile *iof = (JanetFile *) janet_abstract(&janet_file_type, sizeof(JanetFile));
|
||||||
iof->file = f;
|
iof->file = f;
|
||||||
iof->flags = flags;
|
iof->flags = flags;
|
||||||
|
iof->vbufsize = bufsize;
|
||||||
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
|
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
|
||||||
/* While we would like fopen to set cloexec by default (like O_CLOEXEC) with the e flag, that is
|
/* While we would like fopen to set cloexec by default (like O_CLOEXEC) with the e flag, that is
|
||||||
* not standard. */
|
* not standard. */
|
||||||
@@ -164,6 +166,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
|||||||
flags = JANET_FILE_READ;
|
flags = JANET_FILE_READ;
|
||||||
}
|
}
|
||||||
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
FILE *f = fopen((const char *)fname, (const char *)fmode);
|
||||||
|
size_t bufsize = BUFSIZ;
|
||||||
if (f != NULL) {
|
if (f != NULL) {
|
||||||
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
|
#if !(defined(JANET_WINDOWS) || defined(JANET_PLAN9))
|
||||||
struct stat st;
|
struct stat st;
|
||||||
@@ -173,7 +176,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
|||||||
janet_panicf("cannot open directory: %s", fname);
|
janet_panicf("cannot open directory: %s", fname);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
size_t bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
bufsize = janet_optsize(argv, argc, 2, BUFSIZ);
|
||||||
if (bufsize != BUFSIZ) {
|
if (bufsize != BUFSIZ) {
|
||||||
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
|
int result = setvbuf(f, NULL, bufsize ? _IOFBF : _IONBF, bufsize);
|
||||||
if (result) {
|
if (result) {
|
||||||
@@ -181,7 +184,7 @@ JANET_CORE_FN(cfun_io_fopen,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return f ? janet_makefile(f, flags)
|
return f ? janet_wrap_abstract(makef(f, flags, bufsize))
|
||||||
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
|
: (flags & JANET_FILE_NONIL) ? (janet_panicf("failed to open file %s: %s", fname, janet_strerror(errno)), janet_wrap_nil())
|
||||||
: janet_wrap_nil();
|
: janet_wrap_nil();
|
||||||
}
|
}
|
||||||
@@ -410,12 +413,23 @@ static void io_file_marshal(void *p, JanetMarshalContext *ctx) {
|
|||||||
JanetFile *iof = (JanetFile *)p;
|
JanetFile *iof = (JanetFile *)p;
|
||||||
if (ctx->flags & JANET_MARSHAL_UNSAFE) {
|
if (ctx->flags & JANET_MARSHAL_UNSAFE) {
|
||||||
janet_marshal_abstract(ctx, p);
|
janet_marshal_abstract(ctx, p);
|
||||||
|
int fno = -1;
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
janet_marshal_int(ctx, _fileno(iof->file));
|
if (iof->flags & JANET_FILE_NOT_CLOSEABLE) {
|
||||||
|
fno = _fileno(iof->file);
|
||||||
|
} else {
|
||||||
|
fno = _dup(_fileno(iof->file));
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
janet_marshal_int(ctx, fileno(iof->file));
|
if (iof->flags & JANET_FILE_NOT_CLOSEABLE) {
|
||||||
|
fno = fileno(iof->file);
|
||||||
|
} else {
|
||||||
|
fno = dup(fileno(iof->file));
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
janet_marshal_int(ctx, fno);
|
||||||
janet_marshal_int(ctx, iof->flags);
|
janet_marshal_int(ctx, iof->flags);
|
||||||
|
janet_marshal_size(ctx, iof->vbufsize);
|
||||||
} else {
|
} else {
|
||||||
janet_panic("cannot marshal file in safe mode");
|
janet_panic("cannot marshal file in safe mode");
|
||||||
}
|
}
|
||||||
@@ -444,6 +458,11 @@ static void *io_file_unmarshal(JanetMarshalContext *ctx) {
|
|||||||
} else {
|
} else {
|
||||||
iof->flags = flags;
|
iof->flags = flags;
|
||||||
}
|
}
|
||||||
|
iof->vbufsize = janet_unmarshal_size(ctx);
|
||||||
|
if (iof->vbufsize != BUFSIZ) {
|
||||||
|
int result = setvbuf(iof->file, NULL, iof->vbufsize ? _IOFBF : _IONBF, iof->vbufsize);
|
||||||
|
janet_assert(!result, "unmarshal setvbuf");
|
||||||
|
}
|
||||||
return iof;
|
return iof;
|
||||||
} else {
|
} else {
|
||||||
janet_panic("cannot unmarshal file in safe mode");
|
janet_panic("cannot unmarshal file in safe mode");
|
||||||
@@ -785,11 +804,11 @@ FILE *janet_getfile(const Janet *argv, int32_t n, int32_t *flags) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
JanetFile *janet_makejfile(FILE *f, int32_t flags) {
|
JanetFile *janet_makejfile(FILE *f, int32_t flags) {
|
||||||
return makef(f, flags);
|
return makef(f, flags, BUFSIZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
Janet janet_makefile(FILE *f, int32_t flags) {
|
Janet janet_makefile(FILE *f, int32_t flags) {
|
||||||
return janet_wrap_abstract(makef(f, flags));
|
return janet_wrap_abstract(makef(f, flags, BUFSIZ));
|
||||||
}
|
}
|
||||||
|
|
||||||
JanetAbstract janet_checkfile(Janet j) {
|
JanetAbstract janet_checkfile(Janet j) {
|
||||||
|
|||||||
@@ -140,6 +140,35 @@ static int net_get_address_family(Janet x) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* State machine for async connect */
|
/* State machine for async connect */
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
|
||||||
|
typedef struct NetStateConnect {
|
||||||
|
/* Only used for ConnectEx */
|
||||||
|
JanetOverlapped overlapped;
|
||||||
|
} NetStateConnect;
|
||||||
|
|
||||||
|
static LPFN_CONNECTEX lazy_get_connectex(JSock sock) {
|
||||||
|
/* Get ConnectEx */
|
||||||
|
if (janet_vm.connect_ex_loaded) {
|
||||||
|
return janet_vm.connect_ex;
|
||||||
|
}
|
||||||
|
GUID guid = WSAID_CONNECTEX;
|
||||||
|
LPFN_CONNECTEX connect_ex_ptr = NULL;
|
||||||
|
DWORD byte_len = 0;
|
||||||
|
int success = WSAIoctl(sock, SIO_GET_EXTENSION_FUNCTION_POINTER,
|
||||||
|
(void*)&guid, sizeof(guid),
|
||||||
|
(void*)&connect_ex_ptr, sizeof(connect_ex_ptr),
|
||||||
|
&byte_len, NULL, NULL);
|
||||||
|
if (success) {
|
||||||
|
janet_vm.connect_ex = connect_ex_ptr;
|
||||||
|
} else {
|
||||||
|
janet_vm.connect_ex = NULL;
|
||||||
|
}
|
||||||
|
janet_vm.connect_ex_loaded = 1;
|
||||||
|
return janet_vm.connect_ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
||||||
JanetStream *stream = fiber->ev_stream;
|
JanetStream *stream = fiber->ev_stream;
|
||||||
@@ -159,15 +188,21 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
|
/* We should be using ConnectEx here */
|
||||||
int res = 0;
|
int res = 0;
|
||||||
int size = sizeof(res);
|
int size = sizeof(res);
|
||||||
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_ERROR, (char *)&res, &size);
|
int r = getsockopt((SOCKET)stream->handle, SOL_SOCKET, SO_CONNECT_TIME, (char *)&res, &size);
|
||||||
|
if (r == NO_ERROR && res == 0xFFFFFFFF) {
|
||||||
|
return; /* This apparently indicates we haven't yet gotten a connection */
|
||||||
|
}
|
||||||
|
const int no_error = NO_ERROR;
|
||||||
#else
|
#else
|
||||||
int res = 0;
|
int res = 0;
|
||||||
socklen_t size = sizeof res;
|
socklen_t size = sizeof(res);
|
||||||
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
|
int r = getsockopt(stream->handle, SOL_SOCKET, SO_ERROR, &res, &size);
|
||||||
|
const int no_error = 0;
|
||||||
#endif
|
#endif
|
||||||
if (r == 0) {
|
if (r == no_error) {
|
||||||
if (res == 0) {
|
if (res == 0) {
|
||||||
janet_schedule(fiber, janet_wrap_abstract(stream));
|
janet_schedule(fiber, janet_wrap_abstract(stream));
|
||||||
} else {
|
} else {
|
||||||
@@ -181,8 +216,8 @@ void net_callback_connect(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
janet_async_end(fiber);
|
janet_async_end(fiber);
|
||||||
}
|
}
|
||||||
|
|
||||||
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
|
static JANET_NO_RETURN void net_sched_connect(JanetStream *stream, void *state) {
|
||||||
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, NULL);
|
janet_async_start(stream, JANET_ASYNC_LISTEN_WRITE, net_callback_connect, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* State machine for accepting connections. */
|
/* State machine for accepting connections. */
|
||||||
@@ -190,7 +225,7 @@ static JANET_NO_RETURN void net_sched_connect(JanetStream *stream) {
|
|||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
WSAOVERLAPPED overlapped;
|
JanetOverlapped overlapped;
|
||||||
JanetFunction *function;
|
JanetFunction *function;
|
||||||
JanetStream *lstream;
|
JanetStream *lstream;
|
||||||
JanetStream *astream;
|
JanetStream *astream;
|
||||||
@@ -253,7 +288,7 @@ void net_callback_accept(JanetFiber *fiber, JanetAsyncEvent event) {
|
|||||||
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
|
JANET_NO_RETURN static void janet_sched_accept(JanetStream *stream, JanetFunction *fun) {
|
||||||
Janet err;
|
Janet err;
|
||||||
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
NetStateAccept *state = janet_malloc(sizeof(NetStateAccept));
|
||||||
memset(&state->overlapped, 0, sizeof(WSAOVERLAPPED));
|
memset(&state->overlapped, 0, sizeof(JanetOverlapped));
|
||||||
memset(&state->buf, 0, 1024);
|
memset(&state->buf, 0, 1024);
|
||||||
state->function = fun;
|
state->function = fun;
|
||||||
state->lstream = stream;
|
state->lstream = stream;
|
||||||
@@ -274,7 +309,7 @@ static int net_sched_accept_impl(NetStateAccept *state, JanetFiber *fiber, Janet
|
|||||||
JanetStream *astream = make_stream(asock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
JanetStream *astream = make_stream(asock, JANET_STREAM_READABLE | JANET_STREAM_WRITABLE);
|
||||||
state->astream = astream;
|
state->astream = astream;
|
||||||
int socksize = sizeof(SOCKADDR_STORAGE) + 16;
|
int socksize = sizeof(SOCKADDR_STORAGE) + 16;
|
||||||
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped)) {
|
if (FALSE == AcceptEx(lsock, asock, state->buf, 0, socksize, socksize, NULL, &state->overlapped.as.wsaoverlapped)) {
|
||||||
int code = WSAGetLastError();
|
int code = WSAGetLastError();
|
||||||
if (code == WSA_IO_PENDING) {
|
if (code == WSA_IO_PENDING) {
|
||||||
/* indicates io is happening async */
|
/* indicates io is happening async */
|
||||||
@@ -572,11 +607,39 @@ JANET_CORE_FN(cfun_net_connect,
|
|||||||
|
|
||||||
/* Connect to socket */
|
/* Connect to socket */
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
int status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
|
int status = 0;
|
||||||
int err = WSAGetLastError();
|
int err = 0;
|
||||||
freeaddrinfo(ai);
|
LPFN_CONNECTEX connect_ex = NULL;
|
||||||
/* Set up the socket for non-blocking IO after connecting on windows by default */
|
if (socktype == SOCK_STREAM && ((connect_ex = lazy_get_connectex(sock)))) {
|
||||||
janet_net_socknoblock(sock);
|
/* Prefer ConnecEx as it works well with overlapped IO. */
|
||||||
|
janet_net_socknoblock(sock);
|
||||||
|
NetStateConnect *state = janet_malloc(sizeof(NetStateConnect));
|
||||||
|
memset(state, 0, sizeof(NetStateConnect));
|
||||||
|
BOOL success = connect_ex(sock, addr, addrlen, NULL, 0, NULL, &state->overlapped.as.overlapped);
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
if (success) {
|
||||||
|
/* Did not fail */
|
||||||
|
} else {
|
||||||
|
int err = WSAGetLastError();
|
||||||
|
if (err == ERROR_IO_PENDING) {
|
||||||
|
/* Did not actually fail yet */
|
||||||
|
} else {
|
||||||
|
janet_free(state);
|
||||||
|
Janet lasterr = janet_ev_lasterr();
|
||||||
|
janet_panicf("could not connect socket (ConnectEx): %V", lasterr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
net_sched_connect(stream, state);
|
||||||
|
} else {
|
||||||
|
/* Default to blocking connect if ConnectEx not available */
|
||||||
|
status = WSAConnect(sock, addr, addrlen, NULL, NULL, NULL, NULL);
|
||||||
|
err = WSAGetLastError();
|
||||||
|
freeaddrinfo(ai);
|
||||||
|
/* Set up the socket for non-blocking IO after connecting on windows by default */
|
||||||
|
janet_net_socknoblock(sock);
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
/* Set up the socket for non-blocking IO before connecting */
|
/* Set up the socket for non-blocking IO before connecting */
|
||||||
janet_net_socknoblock(sock);
|
janet_net_socknoblock(sock);
|
||||||
@@ -613,7 +676,7 @@ JANET_CORE_FN(cfun_net_connect,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
net_sched_connect(stream);
|
net_sched_connect(stream, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
JANET_CORE_FN(cfun_net_socket,
|
JANET_CORE_FN(cfun_net_socket,
|
||||||
@@ -1122,7 +1185,7 @@ JANET_CORE_FN(cfun_net_setsockopt,
|
|||||||
val.v_int = janet_getboolean(argv, 2);
|
val.v_int = janet_getboolean(argv, 2);
|
||||||
optlen = sizeof(val.v_int);
|
optlen = sizeof(val.v_int);
|
||||||
} else if (st->type == JANET_NUMBER) {
|
} else if (st->type == JANET_NUMBER) {
|
||||||
#ifdef JANET_BSD
|
#if defined(JANET_BSD) || defined(JANET_ILLUMOS)
|
||||||
int v_int = janet_getinteger(argv, 2);
|
int v_int = janet_getinteger(argv, 2);
|
||||||
if (st->optname == IP_MULTICAST_TTL) {
|
if (st->optname == IP_MULTICAST_TTL) {
|
||||||
val.v_uchar = v_int;
|
val.v_uchar = v_int;
|
||||||
@@ -1213,6 +1276,8 @@ void janet_net_init(void) {
|
|||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
WSADATA wsaData;
|
WSADATA wsaData;
|
||||||
janet_assert(!WSAStartup(MAKEWORD(2, 2), &wsaData), "could not start winsock");
|
janet_assert(!WSAStartup(MAKEWORD(2, 2), &wsaData), "could not start winsock");
|
||||||
|
janet_vm.connect_ex_loaded = 0;
|
||||||
|
janet_vm.connect_ex = NULL;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
#ifdef JANET_BSD
|
#ifdef JANET_BSD
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
@@ -1331,7 +1332,7 @@ static Janet os_execute_impl(int32_t argc, Janet *argv, JanetExecuteMode mode) {
|
|||||||
msgbuf,
|
msgbuf,
|
||||||
sizeof(msgbuf),
|
sizeof(msgbuf),
|
||||||
NULL);
|
NULL);
|
||||||
if (!*msgbuf) snprintf(msgbuf, sizeof(msgbuf), "%d", cp_error_code);
|
if (!*msgbuf) snprintf(msgbuf, sizeof(msgbuf), "%" PRIu32, (uint32_t) cp_error_code);
|
||||||
char *c = msgbuf;
|
char *c = msgbuf;
|
||||||
while (*c) {
|
while (*c) {
|
||||||
if (*c == '\n' || *c == '\r') {
|
if (*c == '\n' || *c == '\r') {
|
||||||
@@ -2578,7 +2579,7 @@ JANET_CORE_FN(os_dir,
|
|||||||
char pattern[MAX_PATH + 1];
|
char pattern[MAX_PATH + 1];
|
||||||
if (strlen(dir) > (sizeof(pattern) - 3))
|
if (strlen(dir) > (sizeof(pattern) - 3))
|
||||||
janet_panicf("path too long: %s", dir);
|
janet_panicf("path too long: %s", dir);
|
||||||
sprintf(pattern, "%s/*", dir);
|
snprintf(pattern, sizeof(pattern), "%s/*", dir);
|
||||||
intptr_t res = _findfirst(pattern, &afile);
|
intptr_t res = _findfirst(pattern, &afile);
|
||||||
if (-1 == res) janet_panicv(janet_cstringv(janet_strerror(errno)));
|
if (-1 == res) janet_panicv(janet_cstringv(janet_strerror(errno)));
|
||||||
do {
|
do {
|
||||||
|
|||||||
@@ -909,7 +909,7 @@ static JanetSlot janetc_while(JanetFopts opts, int32_t argn, const Janet *argv)
|
|||||||
janetc_regalloc_freetemp(&c->scope->ra, tempself, JANETC_REGTEMP_0);
|
janetc_regalloc_freetemp(&c->scope->ra, tempself, JANETC_REGTEMP_0);
|
||||||
/* Compile function */
|
/* Compile function */
|
||||||
JanetFuncDef *def = janetc_pop_funcdef(c);
|
JanetFuncDef *def = janetc_pop_funcdef(c);
|
||||||
def->name = janet_cstring("_while");
|
def->name = janet_cstring("while");
|
||||||
janet_def_addflags(def);
|
janet_def_addflags(def);
|
||||||
int32_t defindex = janetc_addfuncdef(c, def);
|
int32_t defindex = janetc_addfuncdef(c, def);
|
||||||
/* And then load the closure and call it. */
|
/* And then load the closure and call it. */
|
||||||
|
|||||||
@@ -182,6 +182,8 @@ struct JanetVM {
|
|||||||
JanetTable signal_handlers;
|
JanetTable signal_handlers;
|
||||||
#ifdef JANET_WINDOWS
|
#ifdef JANET_WINDOWS
|
||||||
void **iocp;
|
void **iocp;
|
||||||
|
void *connect_ex; /* MSWsock extension if available */
|
||||||
|
int connect_ex_loaded;
|
||||||
#elif defined(JANET_EV_EPOLL)
|
#elif defined(JANET_EV_EPOLL)
|
||||||
pthread_attr_t new_thread_attr;
|
pthread_attr_t new_thread_attr;
|
||||||
JanetHandle selfpipe[2];
|
JanetHandle selfpipe[2];
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ int32_t janet_kv_calchash(const JanetKV *kvs, int32_t len) {
|
|||||||
return (int32_t) hash;
|
return (int32_t) hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Calculate next power of 2. May overflow. If n is 0,
|
/* Calculate next power of 2. May overflow. If n < 0,
|
||||||
* will return 0. */
|
* will return 0. */
|
||||||
int32_t janet_tablen(int32_t n) {
|
int32_t janet_tablen(int32_t n) {
|
||||||
if (n < 0) return 0;
|
if (n < 0) return 0;
|
||||||
|
|||||||
@@ -203,6 +203,21 @@ char *get_processed_name(const char *name);
|
|||||||
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
|
#define RETRY_EINTR(RC, CALL) do { (RC) = CALL; } while((RC) < 0 && errno == EINTR)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef JANET_EV
|
||||||
|
#ifdef JANET_WINDOWS
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#include <io.h>
|
||||||
|
typedef struct {
|
||||||
|
union {
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
WSAOVERLAPPED wsaoverlapped;
|
||||||
|
} as;
|
||||||
|
uint32_t bytes_transfered;
|
||||||
|
} JanetOverlapped;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Initialize builtin libraries */
|
/* Initialize builtin libraries */
|
||||||
void janet_lib_io(JanetTable *env);
|
void janet_lib_io(JanetTable *env);
|
||||||
void janet_lib_math(JanetTable *env);
|
void janet_lib_math(JanetTable *env);
|
||||||
|
|||||||
@@ -495,7 +495,7 @@ Janet janet_in(Janet ds, Janet key) {
|
|||||||
if (!(type->get)(janet_unwrap_abstract(ds), key, &value))
|
if (!(type->get)(janet_unwrap_abstract(ds), key, &value))
|
||||||
janet_panicf("key %v not found in %v ", key, ds);
|
janet_panicf("key %v not found in %v ", key, ds);
|
||||||
} else {
|
} else {
|
||||||
janet_panicf("no getter for %v ", ds);
|
janet_panicf("no getter for %v", ds);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -622,7 +622,7 @@ Janet janet_getindex(Janet ds, int32_t index) {
|
|||||||
if (!(type->get)(janet_unwrap_abstract(ds), janet_wrap_integer(index), &value))
|
if (!(type->get)(janet_unwrap_abstract(ds), janet_wrap_integer(index), &value))
|
||||||
value = janet_wrap_nil();
|
value = janet_wrap_nil();
|
||||||
} else {
|
} else {
|
||||||
janet_panicf("no getter for %v ", ds);
|
janet_panicf("no getter for %v", ds);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -724,6 +724,9 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
|
|||||||
JanetArray *array = janet_unwrap_array(ds);
|
JanetArray *array = janet_unwrap_array(ds);
|
||||||
if (index >= array->count) {
|
if (index >= array->count) {
|
||||||
janet_array_ensure(array, index + 1, 2);
|
janet_array_ensure(array, index + 1, 2);
|
||||||
|
for (int32_t i = array->count; i < index + 1; i++) {
|
||||||
|
array->data[i] = janet_wrap_nil();
|
||||||
|
}
|
||||||
array->count = index + 1;
|
array->count = index + 1;
|
||||||
}
|
}
|
||||||
array->data[index] = value;
|
array->data[index] = value;
|
||||||
@@ -735,6 +738,7 @@ void janet_putindex(Janet ds, int32_t index, Janet value) {
|
|||||||
janet_panicf("can only put integers in buffers, got %v", value);
|
janet_panicf("can only put integers in buffers, got %v", value);
|
||||||
if (index >= buffer->count) {
|
if (index >= buffer->count) {
|
||||||
janet_buffer_ensure(buffer, index + 1, 2);
|
janet_buffer_ensure(buffer, index + 1, 2);
|
||||||
|
memset(buffer->data + buffer->count, 0, index + 1 - buffer->count);
|
||||||
buffer->count = index + 1;
|
buffer->count = index + 1;
|
||||||
}
|
}
|
||||||
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
|
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
|
||||||
@@ -768,6 +772,9 @@ void janet_put(Janet ds, Janet key, Janet value) {
|
|||||||
int32_t index = getter_checkint(type, key, INT32_MAX - 1);
|
int32_t index = getter_checkint(type, key, INT32_MAX - 1);
|
||||||
if (index >= array->count) {
|
if (index >= array->count) {
|
||||||
janet_array_ensure(array, index + 1, 2);
|
janet_array_ensure(array, index + 1, 2);
|
||||||
|
for (int32_t i = array->count; i < index + 1; i++) {
|
||||||
|
array->data[i] = janet_wrap_nil();
|
||||||
|
}
|
||||||
array->count = index + 1;
|
array->count = index + 1;
|
||||||
}
|
}
|
||||||
array->data[index] = value;
|
array->data[index] = value;
|
||||||
@@ -780,6 +787,7 @@ void janet_put(Janet ds, Janet key, Janet value) {
|
|||||||
janet_panicf("can only put integers in buffers, got %v", value);
|
janet_panicf("can only put integers in buffers, got %v", value);
|
||||||
if (index >= buffer->count) {
|
if (index >= buffer->count) {
|
||||||
janet_buffer_ensure(buffer, index + 1, 2);
|
janet_buffer_ensure(buffer, index + 1, 2);
|
||||||
|
memset(buffer->data + buffer->count, 0, index + 1 - buffer->count);
|
||||||
buffer->count = index + 1;
|
buffer->count = index + 1;
|
||||||
}
|
}
|
||||||
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
|
buffer->data[index] = (uint8_t)(janet_unwrap_integer(value) & 0xFF);
|
||||||
|
|||||||
@@ -129,7 +129,9 @@
|
|||||||
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
||||||
vm_commit();\
|
vm_commit();\
|
||||||
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
|
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
|
||||||
stack[A] = janet_mcall(#op, 2, _argv);\
|
Janet a = janet_mcall(#op, 2, _argv);\
|
||||||
|
stack = fiber->data + fiber->frame;\
|
||||||
|
stack[A] = a;\
|
||||||
vm_checkgc_pcnext();\
|
vm_checkgc_pcnext();\
|
||||||
} else {\
|
} else {\
|
||||||
double x1 = janet_unwrap_number(op1);\
|
double x1 = janet_unwrap_number(op1);\
|
||||||
@@ -143,7 +145,9 @@
|
|||||||
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
if (!janet_checktype(op1, JANET_NUMBER)) {\
|
||||||
vm_commit();\
|
vm_commit();\
|
||||||
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
|
Janet _argv[2] = { op1, janet_wrap_number(CS) };\
|
||||||
stack[A] = janet_mcall(#op, 2, _argv);\
|
Janet a = janet_mcall(#op, 2, _argv);\
|
||||||
|
stack = fiber->data + fiber->frame;\
|
||||||
|
stack[A] = a;\
|
||||||
vm_checkgc_pcnext();\
|
vm_checkgc_pcnext();\
|
||||||
} else {\
|
} else {\
|
||||||
double y1 = janet_unwrap_number(op1);\
|
double y1 = janet_unwrap_number(op1);\
|
||||||
@@ -166,7 +170,9 @@
|
|||||||
vm_pcnext();\
|
vm_pcnext();\
|
||||||
} else {\
|
} else {\
|
||||||
vm_commit();\
|
vm_commit();\
|
||||||
stack[A] = janet_binop_call(#op, "r" #op, op1, op2);\
|
Janet a = janet_binop_call(#op, "r" #op, op1, op2);\
|
||||||
|
stack = fiber->data + fiber->frame;\
|
||||||
|
stack[A] = a;\
|
||||||
vm_checkgc_pcnext();\
|
vm_checkgc_pcnext();\
|
||||||
}\
|
}\
|
||||||
}
|
}
|
||||||
@@ -186,7 +192,9 @@
|
|||||||
vm_pcnext();\
|
vm_pcnext();\
|
||||||
} else {\
|
} else {\
|
||||||
vm_commit();\
|
vm_commit();\
|
||||||
stack[A] = janet_binop_call(#op, "r" #op, op1, op2);\
|
Janet a = janet_binop_call(#op, "r" #op, op1, op2);\
|
||||||
|
stack = fiber->data + fiber->frame;\
|
||||||
|
stack[A] = a;\
|
||||||
vm_checkgc_pcnext();\
|
vm_checkgc_pcnext();\
|
||||||
}\
|
}\
|
||||||
}
|
}
|
||||||
@@ -203,7 +211,9 @@
|
|||||||
vm_pcnext();\
|
vm_pcnext();\
|
||||||
} else {\
|
} else {\
|
||||||
vm_commit();\
|
vm_commit();\
|
||||||
stack[A] = janet_wrap_boolean(janet_compare(op1, op2) op 0);\
|
Janet a = janet_wrap_boolean(janet_compare(op1, op2) op 0);\
|
||||||
|
stack = fiber->data + fiber->frame;\
|
||||||
|
stack[A] = a;\
|
||||||
vm_checkgc_pcnext();\
|
vm_checkgc_pcnext();\
|
||||||
}\
|
}\
|
||||||
}
|
}
|
||||||
@@ -217,7 +227,9 @@
|
|||||||
vm_pcnext();\
|
vm_pcnext();\
|
||||||
} else {\
|
} else {\
|
||||||
vm_commit();\
|
vm_commit();\
|
||||||
stack[A] = janet_wrap_boolean(janet_compare(op1, janet_wrap_integer(CS)) op 0);\
|
Janet a = janet_wrap_boolean(janet_compare(op1, janet_wrap_integer(CS)) op 0);\
|
||||||
|
stack = fiber->data + fiber->frame;\
|
||||||
|
stack[A] = a;\
|
||||||
vm_checkgc_pcnext();\
|
vm_checkgc_pcnext();\
|
||||||
}\
|
}\
|
||||||
}
|
}
|
||||||
@@ -710,7 +722,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
} else {
|
} else {
|
||||||
vm_commit();
|
vm_commit();
|
||||||
stack[A] = janet_binop_call("div", "rdiv", op1, op2);
|
Janet a = janet_binop_call("div", "rdiv", op1, op2);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
vm_checkgc_pcnext();
|
vm_checkgc_pcnext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -730,7 +744,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
} else {
|
} else {
|
||||||
vm_commit();
|
vm_commit();
|
||||||
stack[A] = janet_binop_call("mod", "rmod", op1, op2);
|
Janet a = janet_binop_call("mod", "rmod", op1, op2);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
vm_checkgc_pcnext();
|
vm_checkgc_pcnext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -745,7 +761,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
} else {
|
} else {
|
||||||
vm_commit();
|
vm_commit();
|
||||||
stack[A] = janet_binop_call("%", "r%", op1, op2);
|
Janet a = janet_binop_call("%", "r%", op1, op2);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
vm_checkgc_pcnext();
|
vm_checkgc_pcnext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -766,7 +784,9 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
} else {
|
} else {
|
||||||
vm_commit();
|
vm_commit();
|
||||||
stack[A] = janet_unary_call("~", op);
|
Janet a = janet_unary_call("~", op);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
vm_checkgc_pcnext();
|
vm_checkgc_pcnext();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -872,8 +892,11 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||||||
stack[A] = janet_wrap_boolean(!janet_checktype(stack[B], JANET_NUMBER) || (janet_unwrap_number(stack[B]) != (double) CS));
|
stack[A] = janet_wrap_boolean(!janet_checktype(stack[B], JANET_NUMBER) || (janet_unwrap_number(stack[B]) != (double) CS));
|
||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
|
|
||||||
VM_OP(JOP_COMPARE)
|
VM_OP(JOP_COMPARE) {
|
||||||
stack[A] = janet_wrap_integer(janet_compare(stack[B], stack[C]));
|
Janet a = janet_wrap_integer(janet_compare(stack[B], stack[C]));
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
|
}
|
||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
|
|
||||||
VM_OP(JOP_NEXT)
|
VM_OP(JOP_NEXT)
|
||||||
@@ -1104,11 +1127,11 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||||||
}
|
}
|
||||||
fiber->child = child;
|
fiber->child = child;
|
||||||
JanetSignal sig = janet_continue_no_check(child, stack[C], &retreg);
|
JanetSignal sig = janet_continue_no_check(child, stack[C], &retreg);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
|
if (sig != JANET_SIGNAL_OK && !(child->flags & (1 << sig))) {
|
||||||
vm_return(sig, retreg);
|
vm_return(sig, retreg);
|
||||||
}
|
}
|
||||||
fiber->child = NULL;
|
fiber->child = NULL;
|
||||||
stack = fiber->data + fiber->frame;
|
|
||||||
stack[A] = retreg;
|
stack[A] = retreg;
|
||||||
vm_checkgc_pcnext();
|
vm_checkgc_pcnext();
|
||||||
}
|
}
|
||||||
@@ -1157,6 +1180,7 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||||||
vm_commit();
|
vm_commit();
|
||||||
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
|
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
|
||||||
janet_put(stack[A], stack[B], stack[C]);
|
janet_put(stack[A], stack[B], stack[C]);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
|
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
|
||||||
vm_checkgc_pcnext();
|
vm_checkgc_pcnext();
|
||||||
|
|
||||||
@@ -1164,27 +1188,44 @@ static JanetSignal run_vm(JanetFiber *fiber, Janet in) {
|
|||||||
vm_commit();
|
vm_commit();
|
||||||
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
|
fiber->flags |= JANET_FIBER_RESUME_NO_USEVAL;
|
||||||
janet_putindex(stack[A], C, stack[B]);
|
janet_putindex(stack[A], C, stack[B]);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
|
fiber->flags &= ~JANET_FIBER_RESUME_NO_USEVAL;
|
||||||
vm_checkgc_pcnext();
|
vm_checkgc_pcnext();
|
||||||
|
|
||||||
VM_OP(JOP_IN)
|
VM_OP(JOP_IN)
|
||||||
vm_commit();
|
vm_commit();
|
||||||
stack[A] = janet_in(stack[B], stack[C]);
|
{
|
||||||
|
Janet a = janet_in(stack[B], stack[C]);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
|
}
|
||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
|
|
||||||
VM_OP(JOP_GET)
|
VM_OP(JOP_GET)
|
||||||
vm_commit();
|
vm_commit();
|
||||||
stack[A] = janet_get(stack[B], stack[C]);
|
{
|
||||||
|
Janet a = janet_get(stack[B], stack[C]);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
|
}
|
||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
|
|
||||||
VM_OP(JOP_GET_INDEX)
|
VM_OP(JOP_GET_INDEX)
|
||||||
vm_commit();
|
vm_commit();
|
||||||
stack[A] = janet_getindex(stack[B], C);
|
{
|
||||||
|
Janet a = janet_getindex(stack[B], C);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
|
}
|
||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
|
|
||||||
VM_OP(JOP_LENGTH)
|
VM_OP(JOP_LENGTH)
|
||||||
vm_commit();
|
vm_commit();
|
||||||
stack[A] = janet_lengthv(stack[E]);
|
{
|
||||||
|
Janet a = janet_lengthv(stack[E]);
|
||||||
|
stack = fiber->data + fiber->frame;
|
||||||
|
stack[A] = a;
|
||||||
|
}
|
||||||
vm_pcnext();
|
vm_pcnext();
|
||||||
|
|
||||||
VM_OP(JOP_MAKE_ARRAY) {
|
VM_OP(JOP_MAKE_ARRAY) {
|
||||||
@@ -1518,6 +1559,15 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If this is a nested continue (root_fiber already set), root the fiber
|
||||||
|
* so it survives GC. janet_collect only marks root_fiber, so without
|
||||||
|
* this a nested fiber (e.g., from janet_pcall in a C function) would be
|
||||||
|
* invisible to GC and could be collected while actively running. */
|
||||||
|
int fiber_rooted = (janet_vm.root_fiber != NULL);
|
||||||
|
if (fiber_rooted) {
|
||||||
|
janet_gcroot(janet_wrap_fiber(fiber));
|
||||||
|
}
|
||||||
|
|
||||||
/* Save global state */
|
/* Save global state */
|
||||||
JanetTryState tstate;
|
JanetTryState tstate;
|
||||||
JanetSignal sig = janet_try(&tstate);
|
JanetSignal sig = janet_try(&tstate);
|
||||||
@@ -1533,6 +1583,9 @@ static JanetSignal janet_continue_no_check(JanetFiber *fiber, Janet in, Janet *o
|
|||||||
if (janet_vm.root_fiber == fiber) janet_vm.root_fiber = NULL;
|
if (janet_vm.root_fiber == fiber) janet_vm.root_fiber = NULL;
|
||||||
janet_fiber_set_status(fiber, sig);
|
janet_fiber_set_status(fiber, sig);
|
||||||
janet_restore(&tstate);
|
janet_restore(&tstate);
|
||||||
|
if (fiber_rooted) {
|
||||||
|
janet_gcunroot(janet_wrap_fiber(fiber));
|
||||||
|
}
|
||||||
fiber->last_value = tstate.payload;
|
fiber->last_value = tstate.payload;
|
||||||
*out = tstate.payload;
|
*out = tstate.payload;
|
||||||
|
|
||||||
|
|||||||
@@ -1281,6 +1281,7 @@ typedef struct JanetFile JanetFile;
|
|||||||
struct JanetFile {
|
struct JanetFile {
|
||||||
FILE *file;
|
FILE *file;
|
||||||
int32_t flags;
|
int32_t flags;
|
||||||
|
size_t vbufsize;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* For janet_try and janet_restore */
|
/* For janet_try and janet_restore */
|
||||||
|
|||||||
@@ -1258,7 +1258,10 @@ int main(int argc, char **argv) {
|
|||||||
status = janet_loop_fiber(fiber);
|
status = janet_loop_fiber(fiber);
|
||||||
|
|
||||||
/* Deinitialize vm */
|
/* Deinitialize vm */
|
||||||
|
|
||||||
|
#if !defined(JANET_SIMPLE_GETLINE)
|
||||||
savehistory();
|
savehistory();
|
||||||
|
#endif
|
||||||
janet_deinit();
|
janet_deinit();
|
||||||
janet_line_deinit();
|
janet_line_deinit();
|
||||||
|
|
||||||
|
|||||||
143
test/c/test-gc-pcall.c
Normal file
143
test/c/test-gc-pcall.c
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* Test that GC does not collect fibers during janet_pcall.
|
||||||
|
*
|
||||||
|
* Bug: janet_collect() marks janet_vm.root_fiber but not janet_vm.fiber.
|
||||||
|
* When janet_pcall is called from a C function, the inner fiber becomes
|
||||||
|
* janet_vm.fiber while root_fiber still points to the outer fiber. If GC
|
||||||
|
* triggers inside the inner fiber's execution, the inner fiber is not in
|
||||||
|
* any GC root set and can be collected — including its stack memory —
|
||||||
|
* while it is actively running.
|
||||||
|
*
|
||||||
|
* Two tests:
|
||||||
|
* 1. Single nesting: F1 -> C func -> janet_pcall -> F2
|
||||||
|
* F2 is not marked (it's janet_vm.fiber but not root_fiber)
|
||||||
|
* 2. Deep nesting: F1 -> C func -> janet_pcall -> F2 -> C func -> janet_pcall -> F3
|
||||||
|
* F2 is not marked (saved only in a C stack local tstate.vm_fiber)
|
||||||
|
*
|
||||||
|
* Build (after building janet):
|
||||||
|
* cc -o build/test-gc-pcall test/test-gc-pcall.c \
|
||||||
|
* -Isrc/include -Isrc/conf build/libjanet.a -lm -lpthread -ldl
|
||||||
|
*
|
||||||
|
* Run:
|
||||||
|
* ./build/test-gc-pcall
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "janet.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
/* C function that calls a Janet callback via janet_pcall. */
|
||||||
|
static Janet cfun_call_via_pcall(int32_t argc, Janet *argv) {
|
||||||
|
janet_fixarity(argc, 1);
|
||||||
|
JanetFunction *fn = janet_getfunction(argv, 0);
|
||||||
|
|
||||||
|
Janet result;
|
||||||
|
JanetFiber *fiber = NULL;
|
||||||
|
JanetSignal sig = janet_pcall(fn, 0, NULL, &result, &fiber);
|
||||||
|
|
||||||
|
if (sig != JANET_SIGNAL_OK) {
|
||||||
|
janet_panicv(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int run_test(JanetTable *env, const char *name, const char *source) {
|
||||||
|
printf(" %s... ", name);
|
||||||
|
fflush(stdout);
|
||||||
|
Janet result;
|
||||||
|
int status = janet_dostring(env, source, name, &result);
|
||||||
|
if (status != 0) {
|
||||||
|
printf("FAIL (crashed or errored)\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("PASS\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Test 1: Single nesting.
|
||||||
|
* F1 -> cfun_call_via_pcall -> janet_pcall -> F2
|
||||||
|
* F2 is janet_vm.fiber but not root_fiber, so GC can collect it.
|
||||||
|
*
|
||||||
|
* All allocations are done in Janet code so GC checks trigger in the
|
||||||
|
* VM loop (janet_gcalloc does NOT call janet_collect — only the VM's
|
||||||
|
* vm_checkgc_next does). */
|
||||||
|
static const char test_single[] =
|
||||||
|
"(gcsetinterval 1024)\n"
|
||||||
|
"(def cb\n"
|
||||||
|
" (do\n"
|
||||||
|
" (def captured @{:key \"value\" :nested @[1 2 3 4 5]})\n"
|
||||||
|
" (fn []\n"
|
||||||
|
" (var result nil)\n"
|
||||||
|
" (for i 0 500\n"
|
||||||
|
" (def t @{:i i :s (string \"iter-\" i) :arr @[i (+ i 1) (+ i 2)]})\n"
|
||||||
|
" (set result (get captured :key)))\n"
|
||||||
|
" result)))\n"
|
||||||
|
"(for round 0 200\n"
|
||||||
|
" (def result (call-via-pcall cb))\n"
|
||||||
|
" (assert (= result \"value\")\n"
|
||||||
|
" (string \"round \" round \": expected 'value', got \" (describe result))))\n";
|
||||||
|
|
||||||
|
/* Test 2: Deep nesting.
|
||||||
|
* F1 -> cfun_call_via_pcall -> janet_pcall -> F2 -> cfun_call_via_pcall -> janet_pcall -> F3
|
||||||
|
* F2 is saved only in C stack local tstate.vm_fiber, invisible to GC.
|
||||||
|
* F2's stack data can be freed if F2 is collected during F3's execution.
|
||||||
|
*
|
||||||
|
* The inner callback allocates in Janet code (not C) to ensure the
|
||||||
|
* VM loop triggers GC checks during F3's execution. */
|
||||||
|
static const char test_deep[] =
|
||||||
|
"(gcsetinterval 1024)\n"
|
||||||
|
"(def inner-cb\n"
|
||||||
|
" (do\n"
|
||||||
|
" (def captured @{:key \"deep\" :nested @[10 20 30]})\n"
|
||||||
|
" (fn []\n"
|
||||||
|
" (var result nil)\n"
|
||||||
|
" (for i 0 500\n"
|
||||||
|
" (def t @{:i i :s (string \"iter-\" i) :arr @[i (+ i 1) (+ i 2)]})\n"
|
||||||
|
" (set result (get captured :key)))\n"
|
||||||
|
" result)))\n"
|
||||||
|
"\n"
|
||||||
|
"(def outer-cb\n"
|
||||||
|
" (do\n"
|
||||||
|
" (def state @{:count 0 :data @[\"a\" \"b\" \"c\" \"d\" \"e\"]})\n"
|
||||||
|
" (fn []\n"
|
||||||
|
" # This runs on F2. Calling call-via-pcall here creates F3.\n"
|
||||||
|
" # F2 becomes unreachable: it's not root_fiber (that's F1)\n"
|
||||||
|
" # and it's no longer janet_vm.fiber (that's now F3).\n"
|
||||||
|
" (def inner-result (call-via-pcall inner-cb))\n"
|
||||||
|
" # If F2 was collected during F3's execution, accessing\n"
|
||||||
|
" # state here reads freed memory.\n"
|
||||||
|
" (put state :count (+ (state :count) 1))\n"
|
||||||
|
" (string inner-result \"-\" (state :count)))))\n"
|
||||||
|
"\n"
|
||||||
|
"(for round 0 200\n"
|
||||||
|
" (def result (call-via-pcall outer-cb))\n"
|
||||||
|
" (def expected (string \"deep-\" (+ round 1)))\n"
|
||||||
|
" (assert (= result expected)\n"
|
||||||
|
" (string \"round \" round \": expected '\" expected \"', got '\" (describe result) \"'\")))\n";
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
(void)argc;
|
||||||
|
(void)argv;
|
||||||
|
int failures = 0;
|
||||||
|
|
||||||
|
janet_init();
|
||||||
|
|
||||||
|
JanetTable *env = janet_core_env(NULL);
|
||||||
|
|
||||||
|
janet_def(env, "call-via-pcall",
|
||||||
|
janet_wrap_cfunction(cfun_call_via_pcall),
|
||||||
|
"Call a function via janet_pcall from C.");
|
||||||
|
|
||||||
|
printf("Testing janet_pcall GC safety:\n");
|
||||||
|
failures += run_test(env, "single-nesting", test_single);
|
||||||
|
failures += run_test(env, "deep-nesting", test_deep);
|
||||||
|
|
||||||
|
janet_deinit();
|
||||||
|
|
||||||
|
if (failures > 0) {
|
||||||
|
printf("\n%d test(s) FAILED\n", failures);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
printf("\nAll tests passed.\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -86,5 +86,10 @@
|
|||||||
(assert-error "array/join error 4" (array/join @[] "abc123"))
|
(assert-error "array/join error 4" (array/join @[] "abc123"))
|
||||||
(assert-error "array/join error 5" (array/join @[] "abc123"))
|
(assert-error "array/join error 5" (array/join @[] "abc123"))
|
||||||
|
|
||||||
|
# Regression 1714
|
||||||
|
(repeat 10
|
||||||
|
(assert (deep= (put @[] 100 10) (put (seq [_ :range [0 101]] nil) 100 10)) "regression 1714")
|
||||||
|
(assert (deep= (put @[] 200 10) (put (seq [_ :range [0 201]] nil) 200 10)) "regression 1714"))
|
||||||
|
|
||||||
(end-suite)
|
(end-suite)
|
||||||
|
|
||||||
|
|||||||
@@ -179,5 +179,10 @@
|
|||||||
(assert (= (string buf) "xxxxxx") "buffer/format-at negative index")
|
(assert (= (string buf) "xxxxxx") "buffer/format-at negative index")
|
||||||
(assert-error "expected index at to be in range [0, 0), got 1" (buffer/format-at @"" 1 "abc"))
|
(assert-error "expected index at to be in range [0, 0), got 1" (buffer/format-at @"" 1 "abc"))
|
||||||
|
|
||||||
|
# Regression 1714
|
||||||
|
(repeat 10
|
||||||
|
(assert (deep= (put @"" 100 10) (put (buffer (string/repeat "\0" 101)) 100 10)) "regression 1714")
|
||||||
|
(assert (deep= (put @"" 200 10) (put (buffer (string/repeat "\0" 201)) 200 10)) "regression 1714"))
|
||||||
|
|
||||||
(end-suite)
|
(end-suite)
|
||||||
|
|
||||||
|
|||||||
@@ -84,4 +84,21 @@
|
|||||||
(assert-error "cannot schedule non-new fiber"
|
(assert-error "cannot schedule non-new fiber"
|
||||||
(ev/go f))
|
(ev/go f))
|
||||||
|
|
||||||
|
# IO file copying
|
||||||
|
(os/mkdir "tmp")
|
||||||
|
(def f-original (file/open "tmp/out.txt" :wb))
|
||||||
|
(xprin f-original "hello\n")
|
||||||
|
(file/flush f-original)
|
||||||
|
(ev/do-thread
|
||||||
|
# Closes a COPY of the original file, otherwise we get a user-after-close file descriptor
|
||||||
|
(:close f-original))
|
||||||
|
(def g-original (file/open "tmp/out2.txt" :wb))
|
||||||
|
(xprin g-original "world1\n")
|
||||||
|
(xprin f-original "world2\n")
|
||||||
|
(:close f-original)
|
||||||
|
(xprin g-original "abc\n")
|
||||||
|
(:close g-original)
|
||||||
|
(assert (deep= @"hello\nworld2\n" (slurp "tmp/out.txt")) "file threading 1")
|
||||||
|
(assert (deep= @"world1\nabc\n" (slurp "tmp/out2.txt")) "file threading 2")
|
||||||
|
|
||||||
(end-suite)
|
(end-suite)
|
||||||
|
|||||||
@@ -148,11 +148,10 @@
|
|||||||
|
|
||||||
# os/execute with empty environment
|
# os/execute with empty environment
|
||||||
# pr #1686
|
# pr #1686
|
||||||
# native MinGW can't find system DLLs without PATH and so fails
|
# native MinGW can't find system DLLs without PATH, SystemRoot, etc. and so fails
|
||||||
(assert (= (if (and (= :mingw (os/which))
|
# Also fails for address sanitizer builds on windows.
|
||||||
(nil? (os/stat "C:\\windows\\system32\\wineboot.exe")))
|
(def result (os/execute [;run janet "-e" "(+ 1 2 3)"] :pe {}))
|
||||||
-1073741515 0)
|
(assert (or (= result -1073741515) (= result 0))
|
||||||
(os/execute [;run janet "-e" "(+ 1 2 3)"] :pe {}))
|
|
||||||
"os/execute with minimal env")
|
"os/execute with minimal env")
|
||||||
|
|
||||||
# os/execute regressions
|
# os/execute regressions
|
||||||
|
|||||||
Reference in New Issue
Block a user