wasm/execution/
resumable.rs

1use core::num::NonZeroU32;
2
3use alloc::{
4    sync::{Arc, Weak},
5    vec::Vec,
6};
7
8use crate::{
9    addrs::FuncAddr,
10    core::slotmap::{SlotMap, SlotMapKey},
11    rw_spinlock::RwSpinLock,
12    value_stack::Stack,
13    Value,
14};
15
16#[derive(Debug)]
17pub(crate) struct Resumable {
18    pub(crate) stack: Stack,
19    pub(crate) pc: usize,
20    pub(crate) stp: usize,
21    pub(crate) current_func_addr: FuncAddr,
22    pub(crate) maybe_fuel: Option<u32>,
23}
24
25#[derive(Default)]
26pub(crate) struct Dormitory(pub(crate) Arc<RwSpinLock<SlotMap<Resumable>>>);
27
28impl Dormitory {
29    #[allow(unused)]
30    pub(crate) fn new() -> Self {
31        Self::default()
32    }
33
34    pub(crate) fn insert(&self, resumable: Resumable) -> InvokedResumableRef {
35        let key = self.0.write().insert(resumable);
36
37        InvokedResumableRef {
38            dormitory: Arc::downgrade(&self.0),
39            key,
40        }
41    }
42}
43
44pub struct InvokedResumableRef {
45    pub(crate) dormitory: Weak<RwSpinLock<SlotMap<Resumable>>>,
46    pub(crate) key: SlotMapKey<Resumable>,
47}
48
49pub struct FreshResumableRef {
50    pub(crate) func_addr: FuncAddr,
51    pub(crate) params: Vec<Value>,
52    pub(crate) maybe_fuel: Option<u32>,
53}
54
55/// An object associated to a resumable that is held internally.
56pub enum ResumableRef {
57    /// indicates this resumable has never been invoked/resumed to.
58    Fresh(FreshResumableRef),
59    /// indicates this resumable has been invoked/resumed to at least once.
60    Invoked(InvokedResumableRef),
61}
62
63impl Drop for InvokedResumableRef {
64    fn drop(&mut self) {
65        let Some(dormitory) = self.dormitory.upgrade() else {
66            // Either the dormitory was already dropped or `self` was used to finish execution.
67            return;
68        };
69
70        dormitory.write().remove(&self.key)
71            .expect("that the resumable could not have been removed already, because then this self could not exist or the dormitory weak pointer would have been None");
72    }
73}
74
75/// Represents the state of a possibly interrupted resumable.
76pub enum RunState {
77    /// represents a resumable that has executed completely with return values `values` and possibly remaining fuel
78    /// `maybe_remaining_fuel` (has `Some(remaining_fuel)` for fuel-metered operations and `None` otherwise)
79    Finished {
80        values: Vec<Value>,
81        maybe_remaining_fuel: Option<u32>,
82    },
83    /// represents a resumable that has ran out of fuel during execution, missing at least `required_fuel` units of fuel
84    /// to continue further execution.
85    Resumable {
86        resumable_ref: ResumableRef,
87        required_fuel: NonZeroU32,
88    },
89}
90
91#[cfg(test)]
92mod test {
93    use crate::{addrs::FuncAddr, value_stack::Stack};
94
95    use super::{Dormitory, Resumable};
96
97    /// Test that a dormitory can be constructed and that a resumable can be inserted
98    #[test]
99    fn dormitory_constructor() {
100        let dorm = Dormitory::new();
101
102        let resumable = Resumable {
103            stack: Stack::new(),
104            pc: 11,
105            stp: 13,
106            current_func_addr: FuncAddr::INVALID,
107            maybe_fuel: None,
108        };
109
110        dorm.insert(resumable);
111    }
112}