Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/tests/all/iloop.rs
1690 views
1
#![cfg(not(miri))]
2
3
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
4
use wasmtime::*;
5
6
fn interruptable_store() -> Store<()> {
7
let engine = Engine::new(Config::new().epoch_interruption(true)).unwrap();
8
let mut store = Store::new(&engine, ());
9
store.set_epoch_deadline(1);
10
store
11
}
12
13
fn hugely_recursive_module(engine: &Engine) -> anyhow::Result<Module> {
14
let mut wat = String::new();
15
wat.push_str(
16
r#"
17
(import "" "" (func))
18
(func (export "loop") call 2 call 2)
19
"#,
20
);
21
for i in 0..100 {
22
wat.push_str(&format!("(func call {0} call {0})\n", i + 3));
23
}
24
wat.push_str("(func call 0)\n");
25
26
Module::new(engine, &wat)
27
}
28
29
#[test]
30
fn loops_interruptable() -> anyhow::Result<()> {
31
let mut store = interruptable_store();
32
let module = Module::new(store.engine(), r#"(func (export "loop") (loop br 0))"#)?;
33
let instance = Instance::new(&mut store, &module, &[])?;
34
let iloop = instance.get_typed_func::<(), ()>(&mut store, "loop")?;
35
store.engine().increment_epoch();
36
let trap = iloop.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
37
assert_eq!(trap, Trap::Interrupt);
38
Ok(())
39
}
40
41
#[test]
42
fn functions_interruptable() -> anyhow::Result<()> {
43
let mut store = interruptable_store();
44
let module = hugely_recursive_module(store.engine())?;
45
let func = Func::wrap(&mut store, || {});
46
let instance = Instance::new(&mut store, &module, &[func.into()])?;
47
let iloop = instance.get_typed_func::<(), ()>(&mut store, "loop")?;
48
store.engine().increment_epoch();
49
let trap = iloop.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
50
assert_eq!(trap, Trap::Interrupt);
51
Ok(())
52
}
53
54
const NUM_HITS: usize = 100_000;
55
56
#[test]
57
fn loop_interrupt_from_afar() -> anyhow::Result<()> {
58
// Create an instance which calls an imported function on each iteration of
59
// the loop so we can count the number of loop iterations we've executed so
60
// far.
61
static HITS: AtomicUsize = AtomicUsize::new(0);
62
static STOP: AtomicBool = AtomicBool::new(false);
63
let mut store = interruptable_store();
64
let module = Module::new(
65
store.engine(),
66
r#"
67
(import "" "" (func))
68
69
(func (export "loop")
70
(loop
71
call 0
72
br 0)
73
)
74
"#,
75
)?;
76
let func = Func::wrap(&mut store, || {
77
HITS.fetch_add(1, SeqCst);
78
});
79
let instance = Instance::new(&mut store, &module, &[func.into()])?;
80
81
// Use the engine to wait for it to enter the loop long enough and then we
82
// signal an interrupt happens.
83
let engine = store.engine().clone();
84
let thread = std::thread::spawn(move || {
85
while HITS.load(SeqCst) <= NUM_HITS && !STOP.load(SeqCst) {
86
// continue ...
87
}
88
println!("interrupting");
89
engine.increment_epoch();
90
});
91
92
// Enter the infinitely looping function and assert that our interrupt
93
// handle does indeed actually interrupt the function.
94
let iloop = instance.get_typed_func::<(), ()>(&mut store, "loop")?;
95
let trap = iloop.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
96
STOP.store(true, SeqCst);
97
thread.join().unwrap();
98
assert!(HITS.load(SeqCst) > NUM_HITS);
99
assert_eq!(trap, Trap::Interrupt);
100
Ok(())
101
}
102
103
#[test]
104
fn function_interrupt_from_afar() -> anyhow::Result<()> {
105
// Create an instance which calls an imported function on each iteration of
106
// the loop so we can count the number of loop iterations we've executed so
107
// far.
108
static HITS: AtomicUsize = AtomicUsize::new(0);
109
static STOP: AtomicBool = AtomicBool::new(false);
110
111
let mut store = interruptable_store();
112
let module = hugely_recursive_module(store.engine())?;
113
let func = Func::wrap(&mut store, || {
114
HITS.fetch_add(1, SeqCst);
115
});
116
let instance = Instance::new(&mut store, &module, &[func.into()])?;
117
118
// Use the instance's interrupt handle to wait for it to enter the loop long
119
// enough and then we signal an interrupt happens.
120
let engine = store.engine().clone();
121
let thread = std::thread::spawn(move || {
122
while HITS.load(SeqCst) <= NUM_HITS && !STOP.load(SeqCst) {
123
// continue ...
124
}
125
engine.increment_epoch();
126
});
127
128
// Enter the infinitely looping function and assert that our interrupt
129
// handle does indeed actually interrupt the function.
130
let iloop = instance.get_typed_func::<(), ()>(&mut store, "loop")?;
131
let trap = iloop.call(&mut store, ()).unwrap_err().downcast::<Trap>()?;
132
STOP.store(true, SeqCst);
133
thread.join().unwrap();
134
assert!(HITS.load(SeqCst) > NUM_HITS);
135
assert_eq!(trap, Trap::Interrupt);
136
Ok(())
137
}
138
139