Skip to main content

sim_lib_control/
policy.rs

1use std::{
2    collections::BTreeSet,
3    sync::{Arc, Mutex},
4};
5
6use sim_kernel::{
7    Cx, Error, Ref, Result,
8    control::{
9        ControlAbort, ControlCapture, ControlPolicy, ControlPolicyRef, ControlPrompt,
10        ControlResume, aborted_control_result, captured_control_result, resumed_control_result,
11    },
12};
13
14/// A control policy that allows each captured continuation to resume once.
15///
16/// Implements the kernel [`ControlPolicy`] contract: prompt entry and abort are
17/// unrestricted, but a continuation that has already been resumed cannot be
18/// resumed again. This is the default policy installed by the control lib.
19pub struct OneShotControlPolicy {
20    resumed: Mutex<BTreeSet<Ref>>,
21}
22
23impl OneShotControlPolicy {
24    /// Builds a one-shot policy with no continuations yet resumed.
25    pub fn new() -> Self {
26        Self {
27            resumed: Mutex::new(BTreeSet::new()),
28        }
29    }
30}
31
32impl Default for OneShotControlPolicy {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl ControlPolicy for OneShotControlPolicy {
39    fn name(&self) -> &'static str {
40        "one-shot-control"
41    }
42
43    fn enter_prompt(&self, _cx: &mut Cx, _prompt: &ControlPrompt) -> Result<()> {
44        Ok(())
45    }
46
47    fn capture(&self, cx: &mut Cx, capture: &ControlCapture) -> Result<Ref> {
48        captured_control_result(cx, capture.continuation.clone(), capture.value.clone())
49    }
50
51    fn abort(&self, cx: &mut Cx, abort: &ControlAbort) -> Result<Ref> {
52        aborted_control_result(cx, abort.prompt.clone(), abort.value.clone())
53    }
54
55    fn resume(&self, cx: &mut Cx, resume: &ControlResume) -> Result<Ref> {
56        let mut resumed = self
57            .resumed
58            .lock()
59            .map_err(|_| Error::PoisonedLock("one-shot control policy"))?;
60        if !resumed.insert(resume.continuation.clone()) {
61            return Err(Error::Eval(
62                "one-shot control continuation already resumed".to_owned(),
63            ));
64        }
65        resumed_control_result(cx, resume.continuation.clone(), resume.value.clone())
66    }
67}
68
69/// A one-shot control policy scoped to a named delimited segment.
70///
71/// Wraps an [`OneShotControlPolicy`] and tags it with a segment [`Ref`],
72/// associating its capture/resume bookkeeping with one delimited region.
73pub struct SegmentedControlPolicy {
74    segment: Ref,
75    one_shot: OneShotControlPolicy,
76}
77
78impl SegmentedControlPolicy {
79    /// Builds a segmented policy bound to `segment`.
80    pub fn new(segment: Ref) -> Self {
81        Self {
82            segment,
83            one_shot: OneShotControlPolicy::new(),
84        }
85    }
86
87    /// Returns the segment this policy is scoped to.
88    pub fn segment(&self) -> &Ref {
89        &self.segment
90    }
91}
92
93impl ControlPolicy for SegmentedControlPolicy {
94    fn name(&self) -> &'static str {
95        "segmented-control"
96    }
97
98    fn enter_prompt(&self, cx: &mut Cx, prompt: &ControlPrompt) -> Result<()> {
99        self.one_shot.enter_prompt(cx, prompt)
100    }
101
102    fn capture(&self, cx: &mut Cx, capture: &ControlCapture) -> Result<Ref> {
103        self.one_shot.capture(cx, capture)
104    }
105
106    fn abort(&self, cx: &mut Cx, abort: &ControlAbort) -> Result<Ref> {
107        self.one_shot.abort(cx, abort)
108    }
109
110    fn resume(&self, cx: &mut Cx, resume: &ControlResume) -> Result<Ref> {
111        self.one_shot.resume(cx, resume)
112    }
113}
114
115/// Builds a shared [`OneShotControlPolicy`] as a kernel
116/// [`ControlPolicyRef`](sim_kernel::control::ControlPolicyRef).
117pub fn one_shot_control_policy() -> ControlPolicyRef {
118    Arc::new(OneShotControlPolicy::new())
119}
120
121/// Builds a shared [`SegmentedControlPolicy`] bound to `segment` as a kernel
122/// [`ControlPolicyRef`](sim_kernel::control::ControlPolicyRef).
123pub fn segmented_control_policy(segment: Ref) -> ControlPolicyRef {
124    Arc::new(SegmentedControlPolicy::new(segment))
125}
126
127/// Installs the default [`one_shot_control_policy`] into `cx` as the active
128/// control policy.
129pub fn install_control_policy(cx: &mut Cx) {
130    cx.set_control_policy(one_shot_control_policy());
131}