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