xref: /wasmtime-44.0.1/tests/all/iloop.rs (revision ab78bd82)
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