mini_racer_rs 1.0.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 +7 -0
- data/AI-DISCLOSURE.md +13 -0
- data/CHANGELOG.md +45 -0
- data/COMPATIBILITY.md +235 -0
- data/Cargo.lock +1263 -0
- data/Cargo.toml +62 -0
- data/LICENSE +21 -0
- data/README.md +254 -0
- data/THIRD_PARTY_NOTICES.md +4501 -0
- data/crates/mini_racer_engine/Cargo.toml +25 -0
- data/crates/mini_racer_engine/build.rs +13 -0
- data/crates/mini_racer_engine/src/context.rs +802 -0
- data/crates/mini_racer_engine/src/control.rs +239 -0
- data/crates/mini_racer_engine/src/error.rs +88 -0
- data/crates/mini_racer_engine/src/fast_calls.rs +681 -0
- data/crates/mini_racer_engine/src/lib.rs +20 -0
- data/crates/mini_racer_engine/src/notify.rs +107 -0
- data/crates/mini_racer_engine/src/process/code_cache.rs +412 -0
- data/crates/mini_racer_engine/src/process/fork.rs +261 -0
- data/crates/mini_racer_engine/src/process/platform.rs +194 -0
- data/crates/mini_racer_engine/src/process/tasks.rs +96 -0
- data/crates/mini_racer_engine/src/process/watchdog.rs +185 -0
- data/crates/mini_racer_engine/src/process/workers.rs +165 -0
- data/crates/mini_racer_engine/src/process.rs +69 -0
- data/crates/mini_racer_engine/src/runner.rs +1560 -0
- data/crates/mini_racer_engine/src/serialize.rs +253 -0
- data/crates/mini_racer_engine/src/snapshot.rs +221 -0
- data/crates/mini_racer_engine/src/spin.rs +23 -0
- data/crates/mini_racer_engine/src/sys.rs +566 -0
- data/crates/mini_racer_sync/Cargo.toml +18 -0
- data/crates/mini_racer_sync/src/channel.rs +621 -0
- data/crates/mini_racer_sync/src/fork.rs +116 -0
- data/crates/mini_racer_sync/src/lib.rs +20 -0
- data/crates/mini_racer_sync/src/lock.rs +49 -0
- data/crates/mini_racer_sync/src/parker.rs +44 -0
- data/crates/mini_racer_sync/src/primitives.rs +10 -0
- data/crates/mini_racer_sync/src/tasks.rs +345 -0
- data/crates/mini_racer_sync/src/termination.rs +1234 -0
- data/crates/mini_racer_sync/src/workers.rs +283 -0
- data/crates/mini_racer_wire/Cargo.toml +18 -0
- data/crates/mini_racer_wire/src/decode.rs +926 -0
- data/crates/mini_racer_wire/src/lib.rs +67 -0
- data/crates/mini_racer_wire/src/marker.rs +43 -0
- data/crates/mini_racer_wire/src/text.rs +106 -0
- data/crates/mini_racer_wire/src/write.rs +187 -0
- data/ext/mini_racer_rs/Cargo.toml +26 -0
- data/ext/mini_racer_rs/build.rs +16 -0
- data/ext/mini_racer_rs/extconf.rb +78 -0
- data/ext/mini_racer_rs/src/context.rs +485 -0
- data/ext/mini_racer_rs/src/decode.rs +450 -0
- data/ext/mini_racer_rs/src/encode.rs +333 -0
- data/ext/mini_racer_rs/src/errors.rs +156 -0
- data/ext/mini_racer_rs/src/fork.rs +31 -0
- data/ext/mini_racer_rs/src/gc_root.rs +117 -0
- data/ext/mini_racer_rs/src/guard.rs +7 -0
- data/ext/mini_racer_rs/src/host.rs +339 -0
- data/ext/mini_racer_rs/src/lib.rs +44 -0
- data/ext/mini_racer_rs/src/options.rs +269 -0
- data/ext/mini_racer_rs/src/platform.rs +42 -0
- data/ext/mini_racer_rs/src/rb_raw.rs +368 -0
- data/ext/mini_racer_rs/src/snapshot.rs +149 -0
- data/ext/mini_racer_rs/v8-archives.sha256 +8 -0
- data/lib/mini_racer/shared.rb +420 -0
- data/lib/mini_racer/truffleruby.rb +439 -0
- data/lib/mini_racer/version.rb +6 -0
- data/lib/mini_racer.rb +3 -0
- data/lib/mini_racer_rs/internal_error.rb +7 -0
- data/lib/mini_racer_rs/version.rb +5 -0
- data/lib/mini_racer_rs/vocabulary.rb +105 -0
- data/lib/mini_racer_rs.rb +33 -0
- data/licenses/mini_racer.txt +21 -0
- data/licenses/rust/SOURCES.tsv +6 -0
- data/licenses/rust/std/COPYRIGHT +34 -0
- data/licenses/rust/std/COPYRIGHT-library.html +29357 -0
- data/licenses/rust/std/LICENSE-APACHE +176 -0
- data/licenses/rust/std/LICENSE-MIT +25 -0
- data/licenses/v8/SOURCES.tsv +20 -0
- data/licenses/v8/abseil-cpp/LICENSE +203 -0
- data/licenses/v8/chromium-build/LICENSE +27 -0
- data/licenses/v8/components.tsv +29 -0
- data/licenses/v8/dragonbox/LICENSE-Boost +23 -0
- data/licenses/v8/fast_float/LICENSE-MIT +27 -0
- data/licenses/v8/highway/LICENSE +371 -0
- data/licenses/v8/icu/LICENSE +568 -0
- data/licenses/v8/inspector_protocol/LICENSE +27 -0
- data/licenses/v8/libc++/LICENSE.TXT +311 -0
- data/licenses/v8/libc++abi/LICENSE.TXT +311 -0
- data/licenses/v8/llvm-libc/LICENSE.TXT +278 -0
- data/licenses/v8/rapidhash/LICENSE +34 -0
- data/licenses/v8/simdutf/LICENSE +18 -0
- data/licenses/v8/siphash/LICENSE +116 -0
- data/licenses/v8/utf8-decoder/LICENSE +19 -0
- data/licenses/v8/v8/LICENSE +61 -0
- data/licenses/v8/v8/LICENSE.fdlibm +6 -0
- data/licenses/v8/v8/LICENSE.strongtalk +29 -0
- data/licenses/v8/v8/LICENSE.v8 +26 -0
- metadata +156 -0
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
use std::time::{Duration, Instant};
|
|
2
|
+
|
|
3
|
+
use crate::lock::{lock, wait, wait_timeout};
|
|
4
|
+
use crate::primitives::{Condvar, Mutex};
|
|
5
|
+
use crate::tasks::Queue;
|
|
6
|
+
|
|
7
|
+
struct State<T> {
|
|
8
|
+
queue: Queue<T>,
|
|
9
|
+
paused: bool,
|
|
10
|
+
running: usize,
|
|
11
|
+
idle: usize,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/// One process's worker tasks, as V8's own pool keeps them: run once due,
|
|
15
|
+
/// in posting order, by whichever worker is free.
|
|
16
|
+
///
|
|
17
|
+
/// A fork pauses the queue: `pause` stops tasks from starting and returns
|
|
18
|
+
/// once none is running, so the child finds no task halfway through, and
|
|
19
|
+
/// `resume` lets the workers go on. A forked child never touches its parent's
|
|
20
|
+
/// queue, so it does not matter that the workers sleeping on it are gone.
|
|
21
|
+
pub struct WorkerQueue<T> {
|
|
22
|
+
state: Mutex<State<T>>,
|
|
23
|
+
work: Condvar,
|
|
24
|
+
quiet: Condvar,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
impl<T> Default for WorkerQueue<T> {
|
|
28
|
+
fn default() -> Self {
|
|
29
|
+
Self {
|
|
30
|
+
state: Mutex::new(State {
|
|
31
|
+
queue: Queue::default(),
|
|
32
|
+
paused: false,
|
|
33
|
+
running: 0,
|
|
34
|
+
idle: 0,
|
|
35
|
+
}),
|
|
36
|
+
work: Condvar::new(),
|
|
37
|
+
quiet: Condvar::new(),
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Ends a task's run, even one that unwinds, so a pause never waits for it.
|
|
43
|
+
struct Running<'a, T>(&'a WorkerQueue<T>);
|
|
44
|
+
|
|
45
|
+
impl<T> Drop for Running<'_, T> {
|
|
46
|
+
fn drop(&mut self) {
|
|
47
|
+
let mut state = lock(&self.0.state);
|
|
48
|
+
state.running -= 1;
|
|
49
|
+
let quiet = state.paused && state.running == 0;
|
|
50
|
+
drop(state);
|
|
51
|
+
if quiet {
|
|
52
|
+
self.0.quiet.notify_all();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl<T> WorkerQueue<T> {
|
|
58
|
+
/// Queues `task` to run once `delay` has passed.
|
|
59
|
+
pub fn post(&self, task: T, delay: Duration) {
|
|
60
|
+
let mut state = lock(&self.state);
|
|
61
|
+
state.queue.push(task, true, delay);
|
|
62
|
+
let wake = state.idle > 0 && !state.paused;
|
|
63
|
+
drop(state);
|
|
64
|
+
if !wake {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// Every idle worker then waits for the earliest deadline, so one is
|
|
68
|
+
// still waiting for the next after another takes the first.
|
|
69
|
+
if delay.is_zero() {
|
|
70
|
+
self.work.notify_one();
|
|
71
|
+
} else {
|
|
72
|
+
self.work.notify_all();
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/// Waits until a task is due and the queue is not paused, then hands it
|
|
77
|
+
/// to `run`. A pause waits for `run` to return.
|
|
78
|
+
pub fn run_next(&self, run: impl FnOnce(T)) {
|
|
79
|
+
let task = self.take();
|
|
80
|
+
let _running = Running(self);
|
|
81
|
+
run(task);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
fn take(&self) -> T {
|
|
85
|
+
let mut state = lock(&self.state);
|
|
86
|
+
loop {
|
|
87
|
+
let timeout = if state.paused {
|
|
88
|
+
None
|
|
89
|
+
} else {
|
|
90
|
+
let now = Instant::now();
|
|
91
|
+
if let Some(task) = state.queue.pop(now, false) {
|
|
92
|
+
state.running += 1;
|
|
93
|
+
drop(state);
|
|
94
|
+
return task;
|
|
95
|
+
}
|
|
96
|
+
state
|
|
97
|
+
.queue
|
|
98
|
+
.next_due()
|
|
99
|
+
.map(|due| due.saturating_duration_since(now))
|
|
100
|
+
};
|
|
101
|
+
state.idle += 1;
|
|
102
|
+
state = match timeout {
|
|
103
|
+
Some(timeout) => wait_timeout(&self.work, state, timeout),
|
|
104
|
+
None => wait(&self.work, state),
|
|
105
|
+
};
|
|
106
|
+
state.idle -= 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Starts no task until `resume`, and waits up to `bound` for the running
|
|
111
|
+
/// ones to end. Returns whether none is running.
|
|
112
|
+
pub fn pause(&self, bound: Duration) -> bool {
|
|
113
|
+
let started = Instant::now();
|
|
114
|
+
let mut state = lock(&self.state);
|
|
115
|
+
state.paused = true;
|
|
116
|
+
let quiet = loop {
|
|
117
|
+
if state.running == 0 {
|
|
118
|
+
break true;
|
|
119
|
+
}
|
|
120
|
+
let left = bound.saturating_sub(started.elapsed());
|
|
121
|
+
if left.is_zero() {
|
|
122
|
+
break false;
|
|
123
|
+
}
|
|
124
|
+
state = wait_timeout(&self.quiet, state, left);
|
|
125
|
+
};
|
|
126
|
+
drop(state);
|
|
127
|
+
quiet
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
pub fn resume(&self) {
|
|
131
|
+
lock(&self.state).paused = false;
|
|
132
|
+
self.work.notify_all();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#[cfg(test)]
|
|
137
|
+
#[cfg(not(loom))]
|
|
138
|
+
mod tests {
|
|
139
|
+
use std::sync::atomic::{AtomicBool, Ordering};
|
|
140
|
+
use std::sync::mpsc;
|
|
141
|
+
use std::sync::{Arc, Barrier};
|
|
142
|
+
use std::thread;
|
|
143
|
+
|
|
144
|
+
use super::*;
|
|
145
|
+
|
|
146
|
+
const NOW: Duration = Duration::ZERO;
|
|
147
|
+
|
|
148
|
+
fn worker(queue: &Arc<WorkerQueue<u32>>, ran: &mpsc::Sender<u32>) -> thread::JoinHandle<()> {
|
|
149
|
+
let (queue, ran) = (Arc::clone(queue), ran.clone());
|
|
150
|
+
thread::spawn(move || {
|
|
151
|
+
loop {
|
|
152
|
+
let mut last = false;
|
|
153
|
+
queue.run_next(|task| {
|
|
154
|
+
last = task == 0;
|
|
155
|
+
ran.send(task).expect("receiver");
|
|
156
|
+
});
|
|
157
|
+
if last {
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#[test]
|
|
165
|
+
fn runs_tasks_in_posting_order_then_delayed_ones_by_deadline() {
|
|
166
|
+
let queue = Arc::new(WorkerQueue::default());
|
|
167
|
+
let (ran, order) = mpsc::channel();
|
|
168
|
+
queue.post(3, Duration::from_millis(60));
|
|
169
|
+
queue.post(2, Duration::from_millis(30));
|
|
170
|
+
for task in [1, 4, 5] {
|
|
171
|
+
queue.post(task, NOW);
|
|
172
|
+
}
|
|
173
|
+
let started = Instant::now();
|
|
174
|
+
let thread = worker(&queue, &ran);
|
|
175
|
+
let first: Vec<u32> = order.iter().take(3).collect();
|
|
176
|
+
assert_eq!(first, [1, 4, 5]);
|
|
177
|
+
assert_eq!(order.recv().expect("task"), 2);
|
|
178
|
+
assert!(started.elapsed() >= Duration::from_millis(25));
|
|
179
|
+
assert_eq!(order.recv().expect("task"), 3);
|
|
180
|
+
assert!(started.elapsed() >= Duration::from_millis(55));
|
|
181
|
+
queue.post(0, NOW);
|
|
182
|
+
thread.join().expect("worker");
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
#[test]
|
|
186
|
+
fn a_task_posted_to_idle_workers_runs() {
|
|
187
|
+
let queue = Arc::new(WorkerQueue::default());
|
|
188
|
+
let (ran, order) = mpsc::channel();
|
|
189
|
+
let threads = [worker(&queue, &ran), worker(&queue, &ran)];
|
|
190
|
+
thread::sleep(Duration::from_millis(20));
|
|
191
|
+
queue.post(7, NOW);
|
|
192
|
+
assert_eq!(order.recv().expect("task"), 7);
|
|
193
|
+
queue.post(0, NOW);
|
|
194
|
+
queue.post(0, NOW);
|
|
195
|
+
for thread in threads {
|
|
196
|
+
thread.join().expect("worker");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// A pause waits for the running task, starts nothing until the resume,
|
|
201
|
+
// and the resume runs what was posted meanwhile.
|
|
202
|
+
#[test]
|
|
203
|
+
fn a_pause_waits_for_the_running_task_and_holds_the_rest() {
|
|
204
|
+
let queue = Arc::new(WorkerQueue::<u32>::default());
|
|
205
|
+
let started = Arc::new(Barrier::new(2));
|
|
206
|
+
let release = Arc::new(Barrier::new(2));
|
|
207
|
+
let finished = Arc::new(AtomicBool::new(false));
|
|
208
|
+
let (ran, order) = mpsc::channel();
|
|
209
|
+
let thread = {
|
|
210
|
+
let (queue, started, release) = (
|
|
211
|
+
Arc::clone(&queue),
|
|
212
|
+
Arc::clone(&started),
|
|
213
|
+
Arc::clone(&release),
|
|
214
|
+
);
|
|
215
|
+
let finished = Arc::clone(&finished);
|
|
216
|
+
thread::spawn(move || {
|
|
217
|
+
queue.run_next(|_| {
|
|
218
|
+
started.wait();
|
|
219
|
+
release.wait();
|
|
220
|
+
thread::sleep(Duration::from_millis(20));
|
|
221
|
+
finished.store(true, Ordering::SeqCst);
|
|
222
|
+
});
|
|
223
|
+
queue.run_next(|task| ran.send(task).expect("receiver"));
|
|
224
|
+
})
|
|
225
|
+
};
|
|
226
|
+
queue.post(1, NOW);
|
|
227
|
+
started.wait();
|
|
228
|
+
let pausing = {
|
|
229
|
+
let queue = Arc::clone(&queue);
|
|
230
|
+
thread::spawn(move || queue.pause(Duration::from_secs(10)))
|
|
231
|
+
};
|
|
232
|
+
thread::sleep(Duration::from_millis(20));
|
|
233
|
+
assert!(!pausing.is_finished());
|
|
234
|
+
queue.post(2, NOW);
|
|
235
|
+
release.wait();
|
|
236
|
+
assert!(pausing.join().expect("pause"));
|
|
237
|
+
assert!(finished.load(Ordering::SeqCst));
|
|
238
|
+
assert_eq!(
|
|
239
|
+
order.recv_timeout(Duration::from_millis(50)),
|
|
240
|
+
Err(mpsc::RecvTimeoutError::Timeout)
|
|
241
|
+
);
|
|
242
|
+
queue.resume();
|
|
243
|
+
assert_eq!(order.recv().expect("task"), 2);
|
|
244
|
+
thread.join().expect("worker");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
#[test]
|
|
248
|
+
fn a_pause_past_its_bound_reports_the_running_task() {
|
|
249
|
+
let queue = Arc::new(WorkerQueue::<u32>::default());
|
|
250
|
+
let started = Arc::new(Barrier::new(2));
|
|
251
|
+
let release = Arc::new(Barrier::new(2));
|
|
252
|
+
let thread = {
|
|
253
|
+
let (queue, started, release) = (
|
|
254
|
+
Arc::clone(&queue),
|
|
255
|
+
Arc::clone(&started),
|
|
256
|
+
Arc::clone(&release),
|
|
257
|
+
);
|
|
258
|
+
thread::spawn(move || {
|
|
259
|
+
queue.run_next(|_| {
|
|
260
|
+
started.wait();
|
|
261
|
+
release.wait();
|
|
262
|
+
});
|
|
263
|
+
})
|
|
264
|
+
};
|
|
265
|
+
queue.post(1, NOW);
|
|
266
|
+
started.wait();
|
|
267
|
+
let before = Instant::now();
|
|
268
|
+
assert!(!queue.pause(Duration::from_millis(30)));
|
|
269
|
+
assert!(before.elapsed() >= Duration::from_millis(25));
|
|
270
|
+
release.wait();
|
|
271
|
+
thread.join().expect("worker");
|
|
272
|
+
assert!(queue.pause(Duration::from_millis(30)));
|
|
273
|
+
queue.resume();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#[test]
|
|
277
|
+
fn a_pause_with_nothing_running_returns_at_once() {
|
|
278
|
+
let queue = WorkerQueue::<u32>::default();
|
|
279
|
+
queue.post(1, NOW);
|
|
280
|
+
assert!(queue.pause(Duration::from_secs(10)));
|
|
281
|
+
queue.resume();
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
description = "V8 ValueSerializer wire format codec for mini_racer_rs"
|
|
3
|
+
edition.workspace = true
|
|
4
|
+
license.workspace = true
|
|
5
|
+
name = "mini_racer_wire"
|
|
6
|
+
publish.workspace = true
|
|
7
|
+
repository.workspace = true
|
|
8
|
+
rust-version.workspace = true
|
|
9
|
+
version.workspace = true
|
|
10
|
+
|
|
11
|
+
[dependencies]
|
|
12
|
+
thiserror.workspace = true
|
|
13
|
+
|
|
14
|
+
[dev-dependencies]
|
|
15
|
+
proptest.workspace = true
|
|
16
|
+
|
|
17
|
+
[lints]
|
|
18
|
+
workspace = true
|