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. The variant `ResumableRef::Fresh` indicates this
56/// resumable has never been invoked/resumed to. `ResumableRef::Invoked` indicates this resumable has been
57/// invoked/resumed to at least once.
58pub enum ResumableRef {
59    Fresh(FreshResumableRef),
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. `RunState::Finished` represents a resumable that has
76/// executed completely. `RunState::Resumable` represents a resumable that has ran out of fuel during execution, missing
77/// at least `required_fuel` units of fuel to continue further execution.
78pub enum RunState {
79    Finished(Vec<Value>),
80    Resumable {
81        resumable_ref: ResumableRef,
82        required_fuel: NonZeroU32,
83    },
84}
85
86#[cfg(test)]
87mod test {
88    use crate::{addrs::FuncAddr, value_stack::Stack};
89
90    use super::{Dormitory, Resumable};
91
92    /// Test that a dormitory can be constructed and that a resumable can be inserted
93    #[test]
94    fn dormitory_constructor() {
95        let dorm = Dormitory::new();
96
97        let resumable = Resumable {
98            stack: Stack::new(),
99            pc: 11,
100            stp: 13,
101            current_func_addr: FuncAddr::INVALID,
102            maybe_fuel: None,
103        };
104
105        dorm.insert(resumable);
106    }
107}