1use std::collections::BTreeMap;
7use std::sync::Mutex;
8
9use crate::coroutine::Value;
10use crate::effect::{EffectFailure, EffectHandler, EffectResult};
11use crate::vm::{ObsEvent, StepResult, VMError, VM};
12
13struct SiteRunner {
14 vm: Mutex<VM>,
15 handler: Box<dyn EffectHandler>,
16}
17
18pub struct NestedVMHandler {
20 sites: BTreeMap<String, SiteRunner>,
21 max_rounds_per_step: usize,
22}
23
24impl NestedVMHandler {
25 #[must_use]
27 pub fn new() -> Self {
28 Self {
29 sites: BTreeMap::new(),
30 max_rounds_per_step: 1,
31 }
32 }
33
34 #[must_use]
36 pub fn with_rounds_per_step(mut self, rounds: usize) -> Self {
37 self.max_rounds_per_step = rounds.max(1);
38 self
39 }
40
41 #[must_use]
43 pub fn rounds_per_step(&self) -> usize {
44 self.max_rounds_per_step
45 }
46
47 pub fn add_site(&mut self, name: impl Into<String>, vm: VM, handler: Box<dyn EffectHandler>) {
49 self.sites.insert(
50 name.into(),
51 SiteRunner {
52 vm: Mutex::new(vm),
53 handler,
54 },
55 );
56 }
57
58 #[must_use]
64 pub fn site_trace(&self, name: &str) -> Option<Vec<ObsEvent>> {
65 self.sites.get(name).map(|site| {
66 site.vm
67 .lock()
68 .unwrap_or_else(|poisoned| poisoned.into_inner())
69 .trace()
70 .to_vec()
71 })
72 }
73
74 #[must_use]
80 pub fn site_all_done(&self, name: &str) -> Option<bool> {
81 self.sites.get(name).map(|site| {
82 site.vm
83 .lock()
84 .unwrap_or_else(|poisoned| poisoned.into_inner())
85 .all_done()
86 })
87 }
88
89 fn step_site(&self, name: &str) -> Result<(), String> {
90 let site = self
91 .sites
92 .get(name)
93 .ok_or_else(|| format!("unknown site: {name}"))?;
94
95 let mut vm = site
96 .vm
97 .lock()
98 .unwrap_or_else(|poisoned| poisoned.into_inner());
99 let handler = site.handler.as_ref();
100
101 for _ in 0..self.max_rounds_per_step {
102 match vm.step_round(handler, 1) {
103 Ok(StepResult::Continue) => {}
104 Ok(StepResult::AllDone | StepResult::Stuck) => break,
105 Err(VMError::Fault { fault, .. }) => {
106 return Err(format!("inner vm fault: {fault}"));
107 }
108 Err(e) => return Err(e.to_string()),
109 }
110 }
111
112 Ok(())
113 }
114}
115
116impl Default for NestedVMHandler {
117 fn default() -> Self {
118 Self::new()
119 }
120}
121
122impl EffectHandler for NestedVMHandler {
123 fn handle_send(
124 &self,
125 role: &str,
126 _partner: &str,
127 _label: &str,
128 _state: &[Value],
129 ) -> EffectResult<Value> {
130 match self.step_site(role) {
131 Ok(()) => EffectResult::success(Value::Unit),
132 Err(message) => EffectResult::failure(EffectFailure::contract_violation(message)),
133 }
134 }
135
136 fn handle_recv(
137 &self,
138 role: &str,
139 _partner: &str,
140 _label: &str,
141 _state: &mut Vec<Value>,
142 _payload: &Value,
143 ) -> EffectResult<()> {
144 match self.step_site(role) {
145 Ok(()) => EffectResult::success(()),
146 Err(message) => EffectResult::failure(EffectFailure::contract_violation(message)),
147 }
148 }
149
150 fn handle_choose(
151 &self,
152 _role: &str,
153 _partner: &str,
154 labels: &[String],
155 _state: &[Value],
156 ) -> EffectResult<String> {
157 match labels.first().cloned() {
158 Some(label) => EffectResult::success(label),
159 None => EffectResult::failure(EffectFailure::invalid_input("no labels available")),
160 }
161 }
162
163 fn step(&self, role: &str, _state: &mut Vec<Value>) -> EffectResult<()> {
164 match self.step_site(role) {
165 Ok(()) => EffectResult::success(()),
166 Err(message) => EffectResult::failure(EffectFailure::contract_violation(message)),
167 }
168 }
169}