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