mini_racer 0.21.3 → 0.22.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.
- checksums.yaml +4 -4
- data/CHANGELOG +11 -0
- data/README.md +34 -0
- data/ext/mini_racer_extension/mini_racer_extension.c +232 -59
- data/ext/mini_racer_extension/mini_racer_v8.cc +188 -17
- data/ext/mini_racer_extension/mini_racer_v8.h +6 -1
- data/lib/mini_racer/shared.rb +151 -111
- data/lib/mini_racer/truffleruby.rb +89 -66
- data/lib/mini_racer/version.rb +1 -1
- data/lib/mini_racer.rb +17 -9
- metadata +33 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7a20d79f246e2bd3f9aa86cdf19c83413d2b782b7738f0bfc13017227e2c20f7
|
|
4
|
+
data.tar.gz: b1c6d7b75c8bbc7520f0722ed255d042e70e4e606bb627a59926523135640921
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9c828f6078e1d6f48503bf9870cab6ced783e853decf679c0534735d7c3a9a8d6efb3c06ed7528f88f1ea8f90ddf5e2c8d50c73e89d8e7672d5b03ad7b85e3ef
|
|
7
|
+
data.tar.gz: 62eb803f2a6aea12b7a80cc6385858631a60dadb61c2eb409e1f5ecf79cfa969f3d9350519377c0a169bf0ffd15987c06635da2d3aaa84f3017183d51b5216bb
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
- 0.22.0 - 12-08-2026
|
|
2
|
+
- Add `Context#call_await` and `Context#eval_await`: like `call`/`eval` but block until a returned Promise settles and return the settled value; rejections raise `MiniRacer::RuntimeError`
|
|
3
|
+
- Fix a `call` or `eval` made from a Ruby callback taking the timeout or `stop` meant for the evaluation around it, which then kept running
|
|
4
|
+
- Fix a timeout that fires just after its own evaluation ends, where it would stop the next evaluation instead
|
|
5
|
+
- Fix a race introduced in 0.21.4 where a request sent right after a nested dispatch (e.g. `perform_microtask_checkpoint` or a nested `call` from an attached callback) could be dropped, deadlocking the context
|
|
6
|
+
|
|
7
|
+
- 0.21.4 - 24-06-2026
|
|
8
|
+
- Fix stale V8 termination state after interrupts/timeouts so contexts remain usable after cancelled evaluations
|
|
9
|
+
- Let Ruby interrupts wake MiniRacer calls without immediately terminating V8, allowing signal traps and nested callbacks to unwind safely
|
|
10
|
+
- Add benchmark suite covering eval, serialization/deserialization, and transpilation workloads
|
|
11
|
+
|
|
1
12
|
- 0.21.3 - 18-06-2026
|
|
2
13
|
- Fix `:single_threaded` contexts inherited across `fork` by recovering idle reusable native runners in the child process without falling back to per-dispatch native thread spawning
|
|
3
14
|
- Avoid intermittent heap corruption during `:single_threaded` context finalization, especially when forked children exit normally after touching inherited contexts
|
data/README.md
CHANGED
|
@@ -348,6 +348,40 @@ Performance is slightly better than running `context.eval("hello('George')")` si
|
|
|
348
348
|
* compilation of eval'd string is avoided
|
|
349
349
|
* function arguments don't need to be converted to JSON
|
|
350
350
|
|
|
351
|
+
### Promises: call_await and eval_await
|
|
352
|
+
|
|
353
|
+
`call_await` and `eval_await` work like `call` and `eval`, but when the result is a
|
|
354
|
+
Promise they block until it settles and return the settled value. A rejected
|
|
355
|
+
promise raises `MiniRacer::RuntimeError`, just like a synchronous `throw`:
|
|
356
|
+
|
|
357
|
+
```ruby
|
|
358
|
+
context = MiniRacer::Context.new
|
|
359
|
+
context.eval("async function f(x) { await Promise.resolve(); return x * 2 }")
|
|
360
|
+
context.call_await("f", 21)
|
|
361
|
+
# => 42
|
|
362
|
+
|
|
363
|
+
context.eval_await("(async () => 6 * 7)()")
|
|
364
|
+
# => 42
|
|
365
|
+
|
|
366
|
+
context.eval("async function boom() { throw new Error('kaboom') }")
|
|
367
|
+
context.call_await("boom")
|
|
368
|
+
# => raises MiniRacer::RuntimeError (Error: kaboom)
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Non-Promise results pass through unchanged, so `call_await` is a drop-in
|
|
372
|
+
superset of `call` (same for `eval_await`/`eval`).
|
|
373
|
+
|
|
374
|
+
A promise that never settles blocks forever, just like an infinite loop. The
|
|
375
|
+
`timeout:` option and `Context#stop` both interrupt it, raising
|
|
376
|
+
`MiniRacer::ScriptTerminatedError`.
|
|
377
|
+
|
|
378
|
+
Calling `call_await` or `eval_await` recursively on the same context from an
|
|
379
|
+
attached Ruby callback is not supported and raises `MiniRacer::RuntimeError`.
|
|
380
|
+
V8 cannot run the nested microtask checkpoint needed to settle such a call.
|
|
381
|
+
Synchronous nested `call` and `eval` remain supported.
|
|
382
|
+
|
|
383
|
+
`call_await` and `eval_await` are not currently supported on TruffleRuby.
|
|
384
|
+
|
|
351
385
|
### Microtask checkpoints
|
|
352
386
|
|
|
353
387
|
V8 drains its microtask queue (e.g. callbacks queued via `Promise.resolve().then(...)`) automatically when script execution returns to the embedder, so most code "just works":
|
|
@@ -137,6 +137,8 @@ typedef struct Context
|
|
|
137
137
|
VALUE procs; // array of js -> ruby callbacks
|
|
138
138
|
VALUE exception; // pending exception or Qnil
|
|
139
139
|
Buf req, res; // ruby->v8 request/response, mediated by |mtx| and |cv|
|
|
140
|
+
Buf v8_req; // stable v8-side copy of a request returned by v8_roundtrip
|
|
141
|
+
int res_ready; // protected by |mtx|; response may be filled before ready
|
|
140
142
|
Buf snapshot;
|
|
141
143
|
pthread_t single_threaded_thr;
|
|
142
144
|
pid_t single_threaded_pid;
|
|
@@ -150,7 +152,8 @@ typedef struct Context
|
|
|
150
152
|
struct {
|
|
151
153
|
pthread_mutex_t mtx;
|
|
152
154
|
pthread_cond_t cv;
|
|
153
|
-
int
|
|
155
|
+
int active; // dispatch thread only
|
|
156
|
+
int cancel; // protected by |mtx|
|
|
154
157
|
} wd; // watchdog
|
|
155
158
|
Barrier early_init, late_init;
|
|
156
159
|
} Context;
|
|
@@ -230,6 +233,8 @@ struct rendezvous_nogvl
|
|
|
230
233
|
Context *context;
|
|
231
234
|
Buf *req, *res;
|
|
232
235
|
atomic_int active;
|
|
236
|
+
atomic_int interrupted;
|
|
237
|
+
int started, finished, has_rr_mtx;
|
|
233
238
|
};
|
|
234
239
|
|
|
235
240
|
struct rendezvous_des
|
|
@@ -768,7 +773,7 @@ static void *v8_watchdog(void *arg)
|
|
|
768
773
|
if (c->wd.cancel)
|
|
769
774
|
break;
|
|
770
775
|
if (deadline_exceeded(deadline)) {
|
|
771
|
-
|
|
776
|
+
v8_terminate_watchdog(c->pst);
|
|
772
777
|
break;
|
|
773
778
|
}
|
|
774
779
|
}
|
|
@@ -782,8 +787,14 @@ static void v8_timedwait(Context *c, const uint8_t *p, size_t n,
|
|
|
782
787
|
pthread_t thr;
|
|
783
788
|
int r;
|
|
784
789
|
|
|
785
|
-
|
|
786
|
-
|
|
790
|
+
if (c->timeout <= 0 || c->wd.active) {
|
|
791
|
+
func(c->pst, p, n);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
c->wd.active = 1;
|
|
795
|
+
r = pthread_create(&thr, NULL, v8_watchdog, c);
|
|
796
|
+
if (r) {
|
|
797
|
+
c->wd.active = 0;
|
|
787
798
|
fprintf(stderr, "mini_racer: watchdog: pthread_create: %s\n", strerror(r));
|
|
788
799
|
fflush(stderr);
|
|
789
800
|
}
|
|
@@ -795,7 +806,9 @@ static void v8_timedwait(Context *c, const uint8_t *p, size_t n,
|
|
|
795
806
|
pthread_cond_signal(&c->wd.cv);
|
|
796
807
|
pthread_mutex_unlock(&c->wd.mtx);
|
|
797
808
|
pthread_join(thr, NULL);
|
|
809
|
+
v8_cancel_watchdog_termination(c->pst);
|
|
798
810
|
c->wd.cancel = 0;
|
|
811
|
+
c->wd.active = 0;
|
|
799
812
|
}
|
|
800
813
|
|
|
801
814
|
static void dispatch1(Context *c, const uint8_t *p, size_t n)
|
|
@@ -806,7 +819,9 @@ static void dispatch1(Context *c, const uint8_t *p, size_t n)
|
|
|
806
819
|
switch (*p) {
|
|
807
820
|
case 'A': return v8_attach(c->pst, p+1, n-1);
|
|
808
821
|
case 'C': return v8_timedwait(c, p+1, n-1, v8_call);
|
|
822
|
+
case 'D': return v8_timedwait(c, p+1, n-1, v8_call_await);
|
|
809
823
|
case 'E': return v8_timedwait(c, p+1, n-1, v8_eval);
|
|
824
|
+
case 'F': return v8_timedwait(c, p+1, n-1, v8_eval_await);
|
|
810
825
|
case 'H': return v8_heap_snapshot(c->pst);
|
|
811
826
|
case 'M': return v8_perform_microtask_checkpoint(c->pst);
|
|
812
827
|
case 'P': return v8_pump_message_loop(c->pst);
|
|
@@ -822,11 +837,28 @@ static void dispatch1(Context *c, const uint8_t *p, size_t n)
|
|
|
822
837
|
fflush(stderr);
|
|
823
838
|
}
|
|
824
839
|
|
|
825
|
-
static void
|
|
840
|
+
static void dispatch_buf(Context *c, Buf *req)
|
|
826
841
|
{
|
|
842
|
+
Buf local_req;
|
|
843
|
+
|
|
844
|
+
// Called with Context.mtx held. Release it while V8 runs so an rb_nogvl
|
|
845
|
+
// UBF can wake the Ruby thread to process interrupts with
|
|
846
|
+
// rb_thread_check_ints(), without forcing termination first. Move the
|
|
847
|
+
// request out first so cancellation cannot free a buffer V8 is reading.
|
|
848
|
+
buf_move(req, &local_req);
|
|
827
849
|
buf_reset(&c->res);
|
|
828
|
-
|
|
829
|
-
|
|
850
|
+
c->res_ready = 0;
|
|
851
|
+
pthread_mutex_unlock(&c->mtx);
|
|
852
|
+
dispatch1(c, local_req.buf, local_req.len);
|
|
853
|
+
pthread_mutex_lock(&c->mtx);
|
|
854
|
+
buf_reset(&local_req);
|
|
855
|
+
c->res_ready = 1;
|
|
856
|
+
pthread_cond_signal(&c->cv);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
static void dispatch(Context *c)
|
|
860
|
+
{
|
|
861
|
+
dispatch_buf(c, &c->req);
|
|
830
862
|
}
|
|
831
863
|
|
|
832
864
|
// called by v8_isolate_and_context
|
|
@@ -859,19 +891,22 @@ void v8_thread_main(Context *c, struct State *pst)
|
|
|
859
891
|
}
|
|
860
892
|
}
|
|
861
893
|
|
|
862
|
-
// called by v8_thread_main and from mini_racer_v8.cc
|
|
863
|
-
// in all cases with Context.mtx held
|
|
894
|
+
// called by v8_thread_main and from mini_racer_v8.cc
|
|
864
895
|
void v8_dispatch(Context *c)
|
|
865
896
|
{
|
|
866
|
-
|
|
867
|
-
|
|
897
|
+
pthread_mutex_lock(&c->mtx);
|
|
898
|
+
dispatch_buf(c, &c->v8_req);
|
|
899
|
+
pthread_mutex_unlock(&c->mtx);
|
|
868
900
|
}
|
|
869
901
|
|
|
870
|
-
// called
|
|
871
|
-
//
|
|
902
|
+
// only called when inside v8_call, v8_eval (and their await variants),
|
|
903
|
+
// or v8_pump_message_loop
|
|
872
904
|
void v8_roundtrip(Context *c, const uint8_t **p, size_t *n)
|
|
873
905
|
{
|
|
874
|
-
|
|
906
|
+
pthread_mutex_lock(&c->mtx);
|
|
907
|
+
buf_reset(&c->v8_req);
|
|
908
|
+
if (c->res.len)
|
|
909
|
+
c->res_ready = 1;
|
|
875
910
|
pthread_cond_signal(&c->cv);
|
|
876
911
|
while (!c->req.len && !atomic_load(&c->quit))
|
|
877
912
|
pthread_cond_wait(&c->cv, &c->mtx);
|
|
@@ -879,17 +914,22 @@ void v8_roundtrip(Context *c, const uint8_t **p, size_t *n)
|
|
|
879
914
|
static const uint8_t disposed[] = "edisposed context";
|
|
880
915
|
*p = disposed;
|
|
881
916
|
*n = sizeof(disposed) - 1;
|
|
917
|
+
pthread_mutex_unlock(&c->mtx);
|
|
882
918
|
return;
|
|
883
919
|
}
|
|
884
920
|
buf_reset(&c->res);
|
|
885
|
-
|
|
886
|
-
|
|
921
|
+
c->res_ready = 0;
|
|
922
|
+
buf_move(&c->req, &c->v8_req);
|
|
923
|
+
*p = c->v8_req.buf;
|
|
924
|
+
*n = c->v8_req.len;
|
|
925
|
+
pthread_mutex_unlock(&c->mtx);
|
|
887
926
|
}
|
|
888
927
|
|
|
889
|
-
// called from mini_racer_v8.cc with Context.mtx held
|
|
890
928
|
void v8_reply(Context *c, const uint8_t *p, size_t n)
|
|
891
929
|
{
|
|
930
|
+
pthread_mutex_lock(&c->mtx);
|
|
892
931
|
buf_put(&c->res, p, n);
|
|
932
|
+
pthread_mutex_unlock(&c->mtx);
|
|
893
933
|
}
|
|
894
934
|
|
|
895
935
|
static void v8_once_init(void)
|
|
@@ -1059,6 +1099,19 @@ static int single_threaded_runner_start(Context *c)
|
|
|
1059
1099
|
return r;
|
|
1060
1100
|
}
|
|
1061
1101
|
|
|
1102
|
+
static void rendezvous_release(struct rendezvous_nogvl *a)
|
|
1103
|
+
{
|
|
1104
|
+
Context *c;
|
|
1105
|
+
|
|
1106
|
+
atomic_store(&a->active, 0);
|
|
1107
|
+
if (!a->has_rr_mtx)
|
|
1108
|
+
return;
|
|
1109
|
+
c = a->context;
|
|
1110
|
+
c->depth--;
|
|
1111
|
+
a->has_rr_mtx = 0;
|
|
1112
|
+
pthread_mutex_unlock(&c->rr_mtx);
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1062
1115
|
static inline void *rendezvous_nogvl(void *arg)
|
|
1063
1116
|
{
|
|
1064
1117
|
struct rendezvous_nogvl *a;
|
|
@@ -1067,62 +1120,77 @@ static inline void *rendezvous_nogvl(void *arg)
|
|
|
1067
1120
|
|
|
1068
1121
|
a = arg;
|
|
1069
1122
|
c = a->context;
|
|
1070
|
-
if (
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1123
|
+
if (!a->started) {
|
|
1124
|
+
if (single_threaded && (r = single_threaded_recover_after_fork(c)))
|
|
1125
|
+
return (void *)(intptr_t)r;
|
|
1126
|
+
pthread_mutex_lock(&c->rr_mtx);
|
|
1127
|
+
a->has_rr_mtx = 1;
|
|
1128
|
+
if (c->depth > 0 && c->depth%50 == 0) { // TODO stop steep recursion
|
|
1129
|
+
fprintf(stderr, "mini_racer: deep js->ruby->js recursion, depth=%d\n", c->depth);
|
|
1130
|
+
fflush(stderr);
|
|
1131
|
+
}
|
|
1132
|
+
c->depth++;
|
|
1133
|
+
a->started = 1;
|
|
1077
1134
|
}
|
|
1078
|
-
|
|
1135
|
+
|
|
1079
1136
|
next:
|
|
1137
|
+
atomic_store(&a->active, 1);
|
|
1080
1138
|
pthread_mutex_lock(&c->mtx);
|
|
1081
1139
|
if (atomic_load(&c->quit)) {
|
|
1082
1140
|
buf_reset(a->req);
|
|
1083
1141
|
pthread_mutex_unlock(&c->mtx);
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
pthread_mutex_unlock(&c->rr_mtx);
|
|
1142
|
+
a->finished = 1;
|
|
1143
|
+
rendezvous_release(a);
|
|
1087
1144
|
return (void *)(intptr_t)ECANCELED;
|
|
1088
1145
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1146
|
+
if (a->req->len) {
|
|
1147
|
+
assert(c->req.len == 0);
|
|
1148
|
+
assert(!c->res_ready);
|
|
1149
|
+
buf_move(a->req, &c->req); // v8 thread takes ownership of req
|
|
1150
|
+
if (single_threaded) {
|
|
1151
|
+
r = single_threaded_runner_start(c);
|
|
1152
|
+
if (r) {
|
|
1153
|
+
buf_move(&c->req, a->req);
|
|
1154
|
+
pthread_mutex_unlock(&c->mtx);
|
|
1155
|
+
a->finished = 1;
|
|
1156
|
+
rendezvous_release(a);
|
|
1157
|
+
return (void *)(intptr_t)r;
|
|
1158
|
+
}
|
|
1101
1159
|
}
|
|
1102
1160
|
pthread_cond_signal(&c->cv);
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1161
|
+
}
|
|
1162
|
+
while (!c->res_ready && !atomic_load(&a->interrupted) && !atomic_load(&c->quit))
|
|
1163
|
+
pthread_cond_wait(&c->cv, &c->mtx);
|
|
1164
|
+
if (!c->res_ready && atomic_load(&a->interrupted)) {
|
|
1165
|
+
atomic_store(&a->active, 0);
|
|
1166
|
+
pthread_mutex_unlock(&c->mtx);
|
|
1167
|
+
return (void *)(intptr_t)EINTR;
|
|
1168
|
+
}
|
|
1169
|
+
if (!c->res_ready && atomic_load(&c->quit)) {
|
|
1170
|
+
buf_reset(a->req);
|
|
1171
|
+
pthread_mutex_unlock(&c->mtx);
|
|
1172
|
+
a->finished = 1;
|
|
1173
|
+
rendezvous_release(a);
|
|
1174
|
+
return (void *)(intptr_t)ECANCELED;
|
|
1107
1175
|
}
|
|
1108
1176
|
buf_move(&c->res, a->res);
|
|
1177
|
+
c->res_ready = 0;
|
|
1109
1178
|
pthread_cond_broadcast(&c->cv);
|
|
1110
1179
|
pthread_mutex_unlock(&c->mtx);
|
|
1180
|
+
atomic_store(&a->active, 0);
|
|
1111
1181
|
if (*a->res->buf == 'c') { // js -> ruby callback?
|
|
1112
1182
|
rb_thread_call_with_gvl(rendezvous_callback, a);
|
|
1113
1183
|
buf_reset(a->res);
|
|
1114
1184
|
if (atomic_load(&c->quit)) {
|
|
1115
1185
|
buf_reset(a->req);
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
pthread_mutex_unlock(&c->rr_mtx);
|
|
1186
|
+
a->finished = 1;
|
|
1187
|
+
rendezvous_release(a);
|
|
1119
1188
|
return (void *)(intptr_t)ECANCELED;
|
|
1120
1189
|
}
|
|
1121
1190
|
goto next;
|
|
1122
1191
|
}
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
pthread_mutex_unlock(&c->rr_mtx);
|
|
1192
|
+
a->finished = 1;
|
|
1193
|
+
rendezvous_release(a);
|
|
1126
1194
|
return NULL;
|
|
1127
1195
|
}
|
|
1128
1196
|
|
|
@@ -1134,9 +1202,8 @@ static void rendezvous_ubf(void *arg)
|
|
|
1134
1202
|
a = arg;
|
|
1135
1203
|
if (!atomic_load(&a->active))
|
|
1136
1204
|
return;
|
|
1205
|
+
atomic_store(&a->interrupted, 1);
|
|
1137
1206
|
c = a->context;
|
|
1138
|
-
if (c->pst)
|
|
1139
|
-
v8_terminate_execution(c->pst);
|
|
1140
1207
|
pthread_cond_broadcast(&c->cv);
|
|
1141
1208
|
}
|
|
1142
1209
|
|
|
@@ -1150,9 +1217,82 @@ static void terminate_ubf(void *arg)
|
|
|
1150
1217
|
pthread_cond_broadcast(&c->cv);
|
|
1151
1218
|
}
|
|
1152
1219
|
|
|
1220
|
+
static void *rendezvous_cancel_nogvl(void *arg)
|
|
1221
|
+
{
|
|
1222
|
+
// Reply to any pending JS->Ruby callback with an 'e' marker plus message
|
|
1223
|
+
// so V8 throws, unwinds, and reaches its normal termination cleanup.
|
|
1224
|
+
static const uint8_t terminated[] = "eterminated";
|
|
1225
|
+
struct rendezvous_nogvl *a;
|
|
1226
|
+
Context *c;
|
|
1227
|
+
|
|
1228
|
+
a = arg;
|
|
1229
|
+
c = a->context;
|
|
1230
|
+
atomic_store(&a->active, 0);
|
|
1231
|
+
if (c->pst)
|
|
1232
|
+
v8_terminate_execution(c->pst);
|
|
1233
|
+
pthread_mutex_lock(&c->mtx);
|
|
1234
|
+
pthread_cond_broadcast(&c->cv);
|
|
1235
|
+
while (!atomic_load(&c->quit)) {
|
|
1236
|
+
while (!c->res_ready && !atomic_load(&c->quit))
|
|
1237
|
+
pthread_cond_wait(&c->cv, &c->mtx);
|
|
1238
|
+
if (!c->res_ready)
|
|
1239
|
+
break;
|
|
1240
|
+
if (c->res.len && *c->res.buf != 'c')
|
|
1241
|
+
break;
|
|
1242
|
+
buf_reset(&c->res);
|
|
1243
|
+
c->res_ready = 0;
|
|
1244
|
+
buf_reset(&c->req);
|
|
1245
|
+
buf_put(&c->req, terminated, sizeof(terminated) - 1);
|
|
1246
|
+
pthread_cond_signal(&c->cv);
|
|
1247
|
+
}
|
|
1248
|
+
buf_reset(&c->req);
|
|
1249
|
+
buf_reset(&c->res);
|
|
1250
|
+
buf_reset(&c->v8_req);
|
|
1251
|
+
c->res_ready = 0;
|
|
1252
|
+
pthread_cond_broadcast(&c->cv);
|
|
1253
|
+
pthread_mutex_unlock(&c->mtx);
|
|
1254
|
+
if (c->pst)
|
|
1255
|
+
v8_cancel_terminate_execution(c->pst);
|
|
1256
|
+
a->finished = 1;
|
|
1257
|
+
rendezvous_release(a);
|
|
1258
|
+
return NULL;
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
static VALUE rendezvous_no_des_body(VALUE arg)
|
|
1262
|
+
{
|
|
1263
|
+
struct rendezvous_nogvl *a;
|
|
1264
|
+
void *r;
|
|
1265
|
+
|
|
1266
|
+
a = (void *)arg;
|
|
1267
|
+
for (;;) {
|
|
1268
|
+
// Let the UBF wake this wait without deciding why Ruby interrupted it.
|
|
1269
|
+
// Back under the GVL, rb_thread_check_ints() runs signal traps and
|
|
1270
|
+
// raises real asynchronous exceptions (Timeout, Thread#raise, kill).
|
|
1271
|
+
atomic_store(&a->interrupted, 0);
|
|
1272
|
+
atomic_store(&a->active, 1);
|
|
1273
|
+
r = rb_nogvl(rendezvous_nogvl, a, rendezvous_ubf, a, RB_NOGVL_INTR_FAIL);
|
|
1274
|
+
atomic_store(&a->active, 0);
|
|
1275
|
+
if (a->finished || (r && (int)(intptr_t)r != EINTR))
|
|
1276
|
+
return LONG2NUM((long)(intptr_t)r);
|
|
1277
|
+
rb_thread_check_ints();
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
static VALUE rendezvous_no_des_ensure(VALUE arg)
|
|
1282
|
+
{
|
|
1283
|
+
struct rendezvous_nogvl *a;
|
|
1284
|
+
|
|
1285
|
+
a = (void *)arg;
|
|
1286
|
+
if (a->started && !a->finished)
|
|
1287
|
+
rb_nogvl(rendezvous_cancel_nogvl, a, NULL, NULL, 0);
|
|
1288
|
+
buf_reset(a->req);
|
|
1289
|
+
return Qnil;
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1153
1292
|
static void rendezvous_no_des(Context *c, Buf *req, Buf *res)
|
|
1154
1293
|
{
|
|
1155
1294
|
void *r;
|
|
1295
|
+
VALUE rv;
|
|
1156
1296
|
struct rendezvous_nogvl a;
|
|
1157
1297
|
|
|
1158
1298
|
if (atomic_load(&c->quit)) {
|
|
@@ -1163,7 +1303,15 @@ static void rendezvous_no_des(Context *c, Buf *req, Buf *res)
|
|
|
1163
1303
|
a.req = req;
|
|
1164
1304
|
a.res = res;
|
|
1165
1305
|
atomic_init(&a.active, 0);
|
|
1166
|
-
|
|
1306
|
+
atomic_init(&a.interrupted, 0);
|
|
1307
|
+
a.started = 0;
|
|
1308
|
+
a.finished = 0;
|
|
1309
|
+
a.has_rr_mtx = 0;
|
|
1310
|
+
rv = rb_ensure(rendezvous_no_des_body, (VALUE)&a,
|
|
1311
|
+
rendezvous_no_des_ensure, (VALUE)&a);
|
|
1312
|
+
r = (void *)(intptr_t)NUM2LONG(rv);
|
|
1313
|
+
if ((int)(intptr_t)r == ECANCELED)
|
|
1314
|
+
rb_raise(context_disposed_error, "disposed context");
|
|
1167
1315
|
if (r)
|
|
1168
1316
|
rb_raise(runtime_error, "single-threaded runner: %s", strerror((int)(intptr_t)r));
|
|
1169
1317
|
}
|
|
@@ -1283,6 +1431,7 @@ static VALUE context_alloc(VALUE klass)
|
|
|
1283
1431
|
buf_init(&c->snapshot);
|
|
1284
1432
|
buf_init(&c->req);
|
|
1285
1433
|
buf_init(&c->res);
|
|
1434
|
+
buf_init(&c->v8_req);
|
|
1286
1435
|
cause = "pthread_condattr_init";
|
|
1287
1436
|
if ((r = pthread_condattr_init(&cattr)))
|
|
1288
1437
|
goto fail0;
|
|
@@ -1387,6 +1536,7 @@ static void context_abandon(Context *c)
|
|
|
1387
1536
|
buf_reset(&c->snapshot);
|
|
1388
1537
|
buf_reset(&c->req);
|
|
1389
1538
|
buf_reset(&c->res);
|
|
1539
|
+
buf_reset(&c->v8_req);
|
|
1390
1540
|
ruby_xfree(c);
|
|
1391
1541
|
}
|
|
1392
1542
|
|
|
@@ -1402,6 +1552,7 @@ static void context_destroy(Context *c)
|
|
|
1402
1552
|
buf_reset(&c->snapshot);
|
|
1403
1553
|
buf_reset(&c->req);
|
|
1404
1554
|
buf_reset(&c->res);
|
|
1555
|
+
buf_reset(&c->v8_req);
|
|
1405
1556
|
ruby_xfree(c);
|
|
1406
1557
|
}
|
|
1407
1558
|
|
|
@@ -1515,7 +1666,7 @@ static VALUE context_stop(VALUE self)
|
|
|
1515
1666
|
return Qnil;
|
|
1516
1667
|
}
|
|
1517
1668
|
|
|
1518
|
-
static VALUE
|
|
1669
|
+
static VALUE context_call_common(int argc, VALUE *argv, VALUE self, char op)
|
|
1519
1670
|
{
|
|
1520
1671
|
VALUE name, args;
|
|
1521
1672
|
VALUE a, e;
|
|
@@ -1526,8 +1677,8 @@ static VALUE context_call(int argc, VALUE *argv, VALUE self)
|
|
|
1526
1677
|
rb_scan_args(argc, argv, "1*", &name, &args);
|
|
1527
1678
|
Check_Type(name, T_STRING);
|
|
1528
1679
|
rb_ary_unshift(args, name);
|
|
1529
|
-
// request is (C)all, [name, args...] array
|
|
1530
|
-
ser_init1(&s,
|
|
1680
|
+
// request is (C)all or (D) call_await, [name, args...] array
|
|
1681
|
+
ser_init1(&s, op);
|
|
1531
1682
|
if (serialize(&s, args)) {
|
|
1532
1683
|
ser_reset(&s);
|
|
1533
1684
|
rb_raise(runtime_error, "Context.call: %s", s.err);
|
|
@@ -1539,7 +1690,17 @@ static VALUE context_call(int argc, VALUE *argv, VALUE self)
|
|
|
1539
1690
|
return rb_ary_pop(a);
|
|
1540
1691
|
}
|
|
1541
1692
|
|
|
1542
|
-
static VALUE
|
|
1693
|
+
static VALUE context_call(int argc, VALUE *argv, VALUE self)
|
|
1694
|
+
{
|
|
1695
|
+
return context_call_common(argc, argv, self, 'C');
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
static VALUE context_call_await(int argc, VALUE *argv, VALUE self)
|
|
1699
|
+
{
|
|
1700
|
+
return context_call_common(argc, argv, self, 'D');
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
static VALUE context_eval_common(int argc, VALUE *argv, VALUE self, char op)
|
|
1543
1704
|
{
|
|
1544
1705
|
VALUE a, e, source, filename, kwargs;
|
|
1545
1706
|
Context *c;
|
|
@@ -1554,8 +1715,8 @@ static VALUE context_eval(int argc, VALUE *argv, VALUE self)
|
|
|
1554
1715
|
if (NIL_P(filename))
|
|
1555
1716
|
filename = rb_str_new_cstr("<eval>");
|
|
1556
1717
|
Check_Type(filename, T_STRING);
|
|
1557
|
-
// request is (E)val, [filename, source] array
|
|
1558
|
-
ser_init1(&s,
|
|
1718
|
+
// request is (E)val or (F) eval_await, [filename, source] array
|
|
1719
|
+
ser_init1(&s, op);
|
|
1559
1720
|
ser_array_begin(&s, 2);
|
|
1560
1721
|
add_string(&s, filename);
|
|
1561
1722
|
add_string(&s, source);
|
|
@@ -1567,6 +1728,16 @@ static VALUE context_eval(int argc, VALUE *argv, VALUE self)
|
|
|
1567
1728
|
return rb_ary_pop(a);
|
|
1568
1729
|
}
|
|
1569
1730
|
|
|
1731
|
+
static VALUE context_eval(int argc, VALUE *argv, VALUE self)
|
|
1732
|
+
{
|
|
1733
|
+
return context_eval_common(argc, argv, self, 'E');
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
static VALUE context_eval_await(int argc, VALUE *argv, VALUE self)
|
|
1737
|
+
{
|
|
1738
|
+
return context_eval_common(argc, argv, self, 'F');
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1570
1741
|
static VALUE context_heap_stats(VALUE self)
|
|
1571
1742
|
{
|
|
1572
1743
|
VALUE a, h, k, v;
|
|
@@ -2007,7 +2178,9 @@ void Init_mini_racer_extension(void)
|
|
|
2007
2178
|
rb_define_method(c, "dispose", context_dispose, 0);
|
|
2008
2179
|
rb_define_method(c, "stop", context_stop, 0);
|
|
2009
2180
|
rb_define_method(c, "call", context_call, -1);
|
|
2181
|
+
rb_define_method(c, "call_await", context_call_await, -1);
|
|
2010
2182
|
rb_define_method(c, "eval", context_eval, -1);
|
|
2183
|
+
rb_define_method(c, "eval_await", context_eval_await, -1);
|
|
2011
2184
|
rb_define_method(c, "heap_stats", context_heap_stats, 0);
|
|
2012
2185
|
rb_define_method(c, "heap_snapshot", context_heap_snapshot, 0);
|
|
2013
2186
|
rb_define_method(c, "perform_microtask_checkpoint", context_perform_microtask_checkpoint, 0);
|