1 #![cfg(not(miri))]
2
3 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst};
4 use wasmtime::*;
5
interruptible_store() -> Store<()>6 fn interruptible_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
hugely_recursive_module(engine: &Engine) -> wasmtime::Result<Module>13 fn hugely_recursive_module(engine: &Engine) -> wasmtime::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]
loops_interruptible() -> wasmtime::Result<()>30 fn loops_interruptible() -> wasmtime::Result<()> {
31 let mut store = interruptible_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]
functions_interruptible() -> wasmtime::Result<()>42 fn functions_interruptible() -> wasmtime::Result<()> {
43 let mut store = interruptible_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]
loop_interrupt_from_afar() -> wasmtime::Result<()>57 fn loop_interrupt_from_afar() -> wasmtime::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 = interruptible_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]
function_interrupt_from_afar() -> wasmtime::Result<()>104 fn function_interrupt_from_afar() -> wasmtime::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 = interruptible_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