1#[derive(Debug, thiserror::Error)]
3pub enum VMError {
4 #[error("coroutine {coro_id} faulted: {fault}")]
6 Fault {
7 coro_id: usize,
9 fault: Fault,
11 },
12 #[error("max sessions ({max}) exceeded")]
14 TooManySessions {
15 max: usize,
17 },
18 #[error("max coroutines ({max}) exceeded")]
20 TooManyCoroutines {
21 max: usize,
23 },
24 #[error("session {0} not found")]
26 SessionNotFound(SessionId),
27 #[error("effect handler error: {0}")]
29 HandlerError(String),
30 #[error("persistence error: {0}")]
32 PersistenceError(String),
33 #[error("invalid concurrency level: {n}")]
35 InvalidConcurrency {
36 n: usize,
38 },
39 #[error("invalid VM config: {reason}")]
41 InvalidConfig {
42 reason: String,
44 },
45 #[error("thread pool build failed: {message}")]
47 ThreadPoolBuild {
48 message: String,
50 },
51 #[error("invalid code image: {reason}")]
53 InvalidCodeImage {
54 reason: String,
56 },
57}
58
59pub(crate) enum CoroUpdate {
63 AdvancePc,
65 SetPc(PC),
67 Block(BlockReason),
69 AdvancePcBlock(BlockReason),
71 Halt,
73 AdvancePcWriteReg { reg: u16, val: Value },
75}
76
77pub(crate) enum TypeUpdate {
79 Advance(LocalTypeR),
81 AdvanceWithOriginal(LocalTypeR, LocalTypeR),
83 Remove,
85}
86
87pub(crate) fn resolve_type_update(
89 cont: &LocalTypeR,
90 original: &LocalTypeR,
91 ep: &Endpoint,
92) -> (LocalTypeR, Option<(Endpoint, TypeUpdate)>) {
93 let (resolved, new_scope) = unfold_if_var_with_scope(cont, original);
94 let update = if let Some(mu) = new_scope {
95 Some((
96 ep.clone(),
97 TypeUpdate::AdvanceWithOriginal(resolved.clone(), mu),
98 ))
99 } else {
100 Some((ep.clone(), TypeUpdate::Advance(resolved.clone())))
101 };
102 (resolved, update)
103}
104
105pub(crate) struct StepPack {
110 pub(crate) coro_update: CoroUpdate,
112 pub(crate) type_update: Option<(Endpoint, TypeUpdate)>,
114 pub(crate) events: Vec<ObsEvent>,
116}
117
118#[derive(Clone, Copy)]
119pub(crate) struct GuardAcquireInput<'a> {
120 pub coro_idx: usize,
121 pub endpoint: &'a Endpoint,
122 pub role: &'a str,
123 pub sid: SessionId,
124 pub layer: &'a str,
125 pub dst: u16,
126}
127
128#[derive(Clone, Copy)]
129pub(crate) struct GuardReleaseInput<'a> {
130 pub coro_idx: usize,
131 pub endpoint: &'a Endpoint,
132 pub role: &'a str,
133 pub sid: SessionId,
134 pub layer: &'a str,
135 pub evidence: u16,
136}
137
138pub(crate) enum ExecOutcome {
140 Continue,
142 Blocked(BlockReason),
144 Halted,
146}
147
148#[derive(Debug, Serialize, Deserialize)]
156pub struct VM<I = (), G = (), P = NoopPersistence, Nu = DefaultVerificationModel>
157where
158 P: PersistenceModel,
159{
160 config: VMConfig,
161 code: Option<Program>,
162 programs: Vec<Program>,
163 identity_model: PhantomData<I>,
164 guard_model: PhantomData<G>,
165 persistence_model: PhantomData<P>,
166 persistent: P::PState,
167 verification: Nu,
168 #[serde(default)]
169 communication_consumption: DefaultCommunicationConsumption,
170 #[serde(default)]
171 communication_consumption_artifacts: Vec<CommunicationConsumptionArtifact>,
172 coroutines: Vec<Coroutine>,
173 sessions: SessionStore,
174 arena: Arena,
175 resource_states: BTreeMap<ScopeId, ResourceState>,
176 sched: Scheduler,
177 monitor: SessionMonitor,
178 obs_trace: Vec<ObsEvent>,
179 role_symbols: SymbolTable,
180 label_symbols: SymbolTable,
181 clock: SimClock,
182 next_coro_id: usize,
183 next_session_id: SessionId,
184 paused_roles: BTreeSet<String>,
185 guard_layer: InMemoryGuardLayer,
186 effect_trace: Vec<EffectTraceEntry>,
187 next_effect_id: u64,
188 output_condition_checks: Vec<OutputConditionCheck>,
189 crashed_sites: BTreeSet<SiteId>,
190 partitioned_edges: BTreeSet<(SiteId, SiteId)>,
191 corrupted_edges: BTreeMap<(SiteId, SiteId), CorruptionType>,
192 timed_out_sites: BTreeMap<SiteId, u64>,
193 last_sched_step: Option<SchedStepDebug>,
194 handler_identity_anchor: Option<String>,
195}
196
197pub type VMState<I = (), G = (), P = NoopPersistence, Nu = DefaultVerificationModel> =
199 VM<I, G, P, Nu>;
200
201impl<I, G, P, Nu> VM<I, G, P, Nu>
202where
203 P: PersistenceModel,
204{
205 #[must_use]
207 pub fn new_with_models(config: VMConfig) -> Self
208 where
209 P::PState: Default,
210 Nu: VerificationModel + Default,
211 {
212 config.assert_invariants();
213 let tick_duration = config.tick_duration;
214 let communication_replay_mode = config.communication_replay_mode;
215 let sched = Scheduler::new(config.sched_policy.clone());
216 let mut guard_resources = BTreeMap::new();
217 for layer in &config.guard_layers {
218 guard_resources.insert(layer.id.clone(), Value::Unit);
219 }
220 Self {
221 config,
222 code: None,
223 programs: Vec::new(),
224 identity_model: PhantomData,
225 guard_model: PhantomData,
226 persistence_model: PhantomData,
227 persistent: P::PState::default(),
228 verification: Nu::default(),
229 communication_consumption: DefaultCommunicationConsumption::new(
230 communication_replay_mode,
231 ),
232 communication_consumption_artifacts: Vec::new(),
233 coroutines: Vec::new(),
234 sessions: SessionStore::new(),
235 arena: Arena::default(),
236 resource_states: BTreeMap::new(),
237 sched,
238 monitor: SessionMonitor::default(),
239 obs_trace: Vec::new(),
240 role_symbols: SymbolTable::new(),
241 label_symbols: SymbolTable::new(),
242 clock: SimClock::new(tick_duration),
243 next_coro_id: 0,
244 next_session_id: 0,
245 paused_roles: BTreeSet::new(),
246 guard_layer: InMemoryGuardLayer {
247 resources: guard_resources
248 .into_iter()
249 .map(|(k, v)| (LayerId(k), v))
250 .collect(),
251 },
252 effect_trace: Vec::new(),
253 next_effect_id: 0,
254 output_condition_checks: Vec::new(),
255 crashed_sites: BTreeSet::new(),
256 partitioned_edges: BTreeSet::new(),
257 corrupted_edges: BTreeMap::new(),
258 timed_out_sites: BTreeMap::new(),
259 last_sched_step: None,
260 handler_identity_anchor: None,
261 }
262 }
263
264 #[must_use]
266 pub fn persistent_state(&self) -> &P::PState {
267 &self.persistent
268 }
269
270 pub fn persistent_state_mut(&mut self) -> &mut P::PState {
272 &mut self.persistent
273 }
274
275 fn apply_open_delta(&mut self, sid: SessionId) -> Result<(), String> {
276 let delta = P::open_delta(sid);
277 P::apply(&mut self.persistent, &delta)
278 }
279
280 fn apply_close_delta(&mut self, sid: SessionId) -> Result<(), String> {
281 let delta = P::close_delta(sid);
282 P::apply(&mut self.persistent, &delta)
283 }
284
285 fn apply_invoke_delta(&mut self, sid: SessionId, action: &str) -> Result<(), String> {
286 if let Some(delta) = P::invoke_delta(sid, action) {
287 P::apply(&mut self.persistent, &delta)?;
288 }
289 Ok(())
290 }
291
292 #[must_use]
294 pub fn bridge_guard_layer_for_participant<B>(
295 &self,
296 bridge: &B,
297 participant: &I::ParticipantId,
298 ) -> LayerId
299 where
300 I: IdentityModel,
301 G: GuardLayer,
302 B: IdentityGuardBridge<I, G>,
303 {
304 bridge.guard_layer_for_participant(participant)
305 }
306
307 #[must_use]
309 pub fn bridge_verifying_key_for_participant<B>(
310 &self,
311 bridge: &B,
312 participant: &I::ParticipantId,
313 ) -> Nu::VerifyingKey
314 where
315 I: IdentityModel,
316 Nu: VerificationModel,
317 B: IdentityVerificationBridge<I, Nu>,
318 {
319 bridge.verification_key_for_participant(participant)
320 }
321}