@mikrojs/native 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CMakeLists.txt +3 -1
- package/include/mikrojs/mikrojs.h +21 -0
- package/include/mikrojs/ota_policy.h +17 -0
- package/include/mikrojs/platform.h +3 -0
- package/include/mikrojs/private.h +40 -0
- package/include/mikrojs/utils.h +5 -0
- package/package.json +5 -4
- package/prebuilds/darwin-arm64/mikrojs.napi.node +0 -0
- package/prebuilds/linux-arm64/mikrojs.napi.node +0 -0
- package/prebuilds/linux-x64/mikrojs.napi.node +0 -0
- package/runtime/internal.d.ts +5 -5
- package/runtime/ota/ota.ts +2 -10
- package/runtime/ota/types.ts +58 -89
- package/runtime/test/test.ts +68 -31
- package/runtime/watchdog/types.ts +9 -0
- package/runtime/watchdog/watchdog.ts +7 -0
- package/src/eval_bytecode.cpp +1 -0
- package/src/mik_app_config.cpp +38 -0
- package/src/mik_console.cpp +5 -0
- package/src/mik_ota_policy.cpp +34 -0
- package/src/mik_repl.cpp +44 -24
- package/src/mik_watchdog.cpp +170 -0
- package/src/mikrojs.cpp +36 -1
- package/src/utils.cpp +21 -0
package/src/mikrojs.cpp
CHANGED
|
@@ -262,6 +262,9 @@ MIKRuntime* MIK_NewRuntimeInternal(MIKRunOptions* options) {
|
|
|
262
262
|
|
|
263
263
|
memcpy(&mik_rt->options, options, sizeof(*options));
|
|
264
264
|
MIK_DefaultConfig(&mik_rt->config);
|
|
265
|
+
/* Runtimes that never see MIK_SetConfig (host tests, the Node addon)
|
|
266
|
+
* still get the default blocking budget. */
|
|
267
|
+
mik__watchdog_arm(mik_rt);
|
|
265
268
|
|
|
266
269
|
/* Switch the QuickJS heap to PSRAM before constructing the runtime,
|
|
267
270
|
* so JS_NewRuntime2 and every subsequent QuickJS allocation lands
|
|
@@ -346,6 +349,9 @@ MIKRuntime* MIK_NewRuntimeInternal(MIKRunOptions* options) {
|
|
|
346
349
|
/* unhandled promise rejection tracker */
|
|
347
350
|
JS_SetHostPromiseRejectionTracker(rt, mik__promise_rejection_tracker, NULL);
|
|
348
351
|
|
|
352
|
+
/* Blocking watchdog: polled every 10k branches/calls. */
|
|
353
|
+
JS_SetInterruptHandler(rt, mik__watchdog_interrupt, mik_rt);
|
|
354
|
+
|
|
349
355
|
/* Register internal C modules */
|
|
350
356
|
JSModuleDef* sys_mod = JS_NewCModule(ctx, "native:mikro/sys", mik__sys_module_init);
|
|
351
357
|
CHECK_NOT_NULL(sys_mod);
|
|
@@ -360,6 +366,7 @@ MIKRuntime* MIK_NewRuntimeInternal(MIKRunOptions* options) {
|
|
|
360
366
|
mik__result_init(ctx);
|
|
361
367
|
mik__cbor_init(ctx);
|
|
362
368
|
mik__udp_init(ctx);
|
|
369
|
+
mik__watchdog_init(ctx);
|
|
363
370
|
mik__observable_init(ctx);
|
|
364
371
|
|
|
365
372
|
/* Native mikrojs modules (replace bytecode builtins). The http client
|
|
@@ -666,13 +673,21 @@ void mik__execute_jobs(JSContext* ctx) {
|
|
|
666
673
|
|
|
667
674
|
/* main loop which calls the user JS callbacks */
|
|
668
675
|
int MIK_Loop(MIKRuntime* mik_rt) {
|
|
676
|
+
const MIKPlatform* platform = MIK_GetPlatform();
|
|
677
|
+
/* Feed the hardware watchdog before the grace-window return below, so
|
|
678
|
+
* a long onPanic.delay never counts against it. */
|
|
679
|
+
if (platform->feed_watchdog) {
|
|
680
|
+
platform->feed_watchdog();
|
|
681
|
+
}
|
|
682
|
+
/* One blocking budget per pass, not per job: a fresh budget per job
|
|
683
|
+
* would let `while (true) await 0` spin forever unnoticed. */
|
|
684
|
+
mik__blocking_begin(mik_rt);
|
|
669
685
|
/* Deferred restart (see MIK_Stop): once the grace window elapses, reboot
|
|
670
686
|
* the device. While we're still in the window, return 0 without pumping
|
|
671
687
|
* timers/consumers so the protocol serve loop keeps reading host
|
|
672
688
|
* commands without firing any more user JS on the dead runtime. The
|
|
673
689
|
* serve loop also skips its microtask drain on this condition. */
|
|
674
690
|
if (mik_rt->restart_at_us > 0) {
|
|
675
|
-
const MIKPlatform* platform = MIK_GetPlatform();
|
|
676
691
|
if (platform->get_boot_us() >= mik_rt->restart_at_us) {
|
|
677
692
|
/* The grace window has elapsed; take the configured panic action.
|
|
678
693
|
* In deep-sleep mode the timer wake reboots the chip, so the wake
|
|
@@ -680,12 +695,18 @@ int MIK_Loop(MIKRuntime* mik_rt) {
|
|
|
680
695
|
* design (the CPU is suspended). If the platform has no deep-sleep
|
|
681
696
|
* hook (hosts), fall through to a plain restart. */
|
|
682
697
|
if (mik_rt->config.panic_mode == MIK_PANIC_DEEP_SLEEP && platform->deep_sleep_us) {
|
|
698
|
+
mik__print_error_line("[panic] deep-sleeping for %d ms",
|
|
699
|
+
mik_rt->config.panic_sleep_duration_ms);
|
|
683
700
|
platform->deep_sleep_us((uint64_t)mik_rt->config.panic_sleep_duration_ms * 1000);
|
|
684
701
|
}
|
|
702
|
+
mik__print_error_line("[panic] restarting");
|
|
685
703
|
platform->restart();
|
|
686
704
|
}
|
|
687
705
|
return 0;
|
|
688
706
|
}
|
|
707
|
+
/* Feed/awake deadlines: a feed miss lands in the exception branch below,
|
|
708
|
+
* an awake overrun in the stop_requested one. */
|
|
709
|
+
mik__watchdog_check(mik_rt);
|
|
689
710
|
if (mik_rt->stop_requested) {
|
|
690
711
|
return 1;
|
|
691
712
|
}
|
|
@@ -730,6 +751,7 @@ void MIK_SetConfig(MIKRuntime* mik_rt, const MIKConfig* config) {
|
|
|
730
751
|
if (config->fs_read_max > 0) {
|
|
731
752
|
mik_rt->fs_read_max = config->fs_read_max;
|
|
732
753
|
}
|
|
754
|
+
mik__watchdog_arm(mik_rt);
|
|
733
755
|
}
|
|
734
756
|
|
|
735
757
|
/* Record a profile entry for the entry module loaded via MIK_EvalModule.
|
|
@@ -850,6 +872,15 @@ void MIK_Stop(MIKRuntime* mik_rt) {
|
|
|
850
872
|
const MIKPlatform* platform = MIK_GetPlatform();
|
|
851
873
|
mik_rt->restart_at_us =
|
|
852
874
|
platform->get_boot_us() + (int64_t)mik_rt->config.panic_restart_delay_ms * 1000;
|
|
875
|
+
/* Say what comes next: with deep sleep the device goes silent. */
|
|
876
|
+
if (mik_rt->config.panic_mode == MIK_PANIC_DEEP_SLEEP && platform->deep_sleep_us) {
|
|
877
|
+
mik__print_error_line("[panic] deep-sleeping for %d ms in %d ms",
|
|
878
|
+
mik_rt->config.panic_sleep_duration_ms,
|
|
879
|
+
mik_rt->config.panic_restart_delay_ms);
|
|
880
|
+
} else {
|
|
881
|
+
mik__print_error_line("[panic] restarting in %d ms",
|
|
882
|
+
mik_rt->config.panic_restart_delay_ms);
|
|
883
|
+
}
|
|
853
884
|
}
|
|
854
885
|
}
|
|
855
886
|
|
|
@@ -974,6 +1005,7 @@ JSValue MIK_EvalModuleContent(JSContext* ctx, const char* filename, const char*
|
|
|
974
1005
|
const char* eval_src = pp_source ? pp_source : content;
|
|
975
1006
|
size_t eval_len = pp_source ? pp_len : len;
|
|
976
1007
|
|
|
1008
|
+
mik__blocking_begin(mik_rt);
|
|
977
1009
|
/* Compile then run to be able to set import.meta */
|
|
978
1010
|
JSValue ret =
|
|
979
1011
|
JS_Eval(ctx, eval_src, eval_len, filename, JS_EVAL_TYPE_MODULE | JS_EVAL_FLAG_COMPILE_ONLY);
|
|
@@ -987,6 +1019,7 @@ JSValue MIK_EvalModuleContent(JSContext* ctx, const char* filename, const char*
|
|
|
987
1019
|
}
|
|
988
1020
|
|
|
989
1021
|
JSValue MIK_EvalScriptContent(JSContext* ctx, const char* content, size_t len) {
|
|
1022
|
+
mik__blocking_begin(MIK_GetRuntime(ctx));
|
|
990
1023
|
return JS_Eval(ctx, content, len, "<eval>", JS_EVAL_TYPE_GLOBAL);
|
|
991
1024
|
}
|
|
992
1025
|
|
|
@@ -1009,6 +1042,7 @@ JSValue MIK_EvalScript(JSContext* ctx, const char* filename) {
|
|
|
1009
1042
|
/* Add null termination, required by JS_Eval. */
|
|
1010
1043
|
dbuf_putc(&dbuf, '\0');
|
|
1011
1044
|
|
|
1045
|
+
mik__blocking_begin(MIK_GetRuntime(ctx));
|
|
1012
1046
|
ret = JS_Eval(ctx, (char*)dbuf.buf, dbuf_size - 1, filename, JS_EVAL_TYPE_GLOBAL);
|
|
1013
1047
|
|
|
1014
1048
|
dbuf_free(&dbuf);
|
|
@@ -1054,6 +1088,7 @@ JSValue MIK_EvalModule(JSContext* ctx, const char* filename, bool is_main) {
|
|
|
1054
1088
|
dbuf_size = dbuf.size;
|
|
1055
1089
|
|
|
1056
1090
|
MIKRuntime* mik_rt = MIK_GetRuntime(ctx);
|
|
1091
|
+
mik__blocking_begin(mik_rt);
|
|
1057
1092
|
bool profile = mik_rt != nullptr && mik_rt->profile_enabled;
|
|
1058
1093
|
int64_t malloc_before = 0;
|
|
1059
1094
|
size_t entries_before = 0;
|
package/src/utils.cpp
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#include <string.h>
|
|
8
8
|
|
|
9
9
|
#include "mikrojs/mikrojs.h"
|
|
10
|
+
#include "mikrojs/platform.h"
|
|
10
11
|
#include "mikrojs/private.h"
|
|
11
12
|
|
|
12
13
|
JSValue mik_new_error(JSContext* ctx, int err) {
|
|
@@ -38,7 +39,27 @@ static void mik_dump_obj(JSContext* ctx, FILE* f, JSValue val) {
|
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
void mik__print_error_line(const char* fmt, ...) {
|
|
43
|
+
char buf[256];
|
|
44
|
+
va_list args;
|
|
45
|
+
va_start(args, fmt);
|
|
46
|
+
int n = vsnprintf(buf, sizeof(buf) - 2, fmt, args);
|
|
47
|
+
va_end(args);
|
|
48
|
+
if (n < 0) return;
|
|
49
|
+
size_t len = (size_t)n;
|
|
50
|
+
/* vsnprintf wrote at most sizeof(buf) - 3 chars plus a NUL. */
|
|
51
|
+
if (len > sizeof(buf) - 3) len = sizeof(buf) - 3;
|
|
52
|
+
if (mik__repl_is_protocol_mode()) {
|
|
53
|
+
mik__repl_proto_send_output(MIK_MSG_ERROR, buf, len);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
buf[len++] = '\r';
|
|
57
|
+
buf[len++] = '\n';
|
|
58
|
+
MIK_GetPlatform()->stderr_write(buf, len);
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
void mik_dump_error(JSContext* ctx) {
|
|
62
|
+
mik__watchdog_report_blocking(MIK_GetRuntime(ctx));
|
|
42
63
|
JSValue exception_val = JS_GetException(ctx);
|
|
43
64
|
mik_dump_error1(ctx, exception_val);
|
|
44
65
|
JS_FreeValue(ctx, exception_val);
|