use std::{
collections::BTreeSet,
sync::{Arc, Mutex},
};
use sim_kernel::{
Cx, Error, Ref, Result,
control::{
ControlAbort, ControlCapture, ControlPolicy, ControlPolicyRef, ControlPrompt,
ControlResume, aborted_control_result, captured_control_result, resumed_control_result,
},
};
pub struct OneShotControlPolicy {
resumed: Mutex<BTreeSet<Ref>>,
}
impl OneShotControlPolicy {
pub fn new() -> Self {
Self {
resumed: Mutex::new(BTreeSet::new()),
}
}
}
impl Default for OneShotControlPolicy {
fn default() -> Self {
Self::new()
}
}
impl ControlPolicy for OneShotControlPolicy {
fn name(&self) -> &'static str {
"one-shot-control"
}
fn enter_prompt(&self, _cx: &mut Cx, _prompt: &ControlPrompt) -> Result<()> {
Ok(())
}
fn capture(&self, cx: &mut Cx, capture: &ControlCapture) -> Result<Ref> {
captured_control_result(cx, capture.continuation.clone(), capture.value.clone())
}
fn abort(&self, cx: &mut Cx, abort: &ControlAbort) -> Result<Ref> {
aborted_control_result(cx, abort.prompt.clone(), abort.value.clone())
}
fn resume(&self, cx: &mut Cx, resume: &ControlResume) -> Result<Ref> {
let mut resumed = self
.resumed
.lock()
.map_err(|_| Error::PoisonedLock("one-shot control policy"))?;
if !resumed.insert(resume.continuation.clone()) {
return Err(Error::Eval(
"one-shot control continuation already resumed".to_owned(),
));
}
resumed_control_result(cx, resume.continuation.clone(), resume.value.clone())
}
}
pub struct SegmentedControlPolicy {
segment: Ref,
one_shot: OneShotControlPolicy,
}
impl SegmentedControlPolicy {
pub fn new(segment: Ref) -> Self {
Self {
segment,
one_shot: OneShotControlPolicy::new(),
}
}
pub fn segment(&self) -> &Ref {
&self.segment
}
}
impl ControlPolicy for SegmentedControlPolicy {
fn name(&self) -> &'static str {
"segmented-control"
}
fn enter_prompt(&self, cx: &mut Cx, prompt: &ControlPrompt) -> Result<()> {
self.one_shot.enter_prompt(cx, prompt)
}
fn capture(&self, cx: &mut Cx, capture: &ControlCapture) -> Result<Ref> {
self.one_shot.capture(cx, capture)
}
fn abort(&self, cx: &mut Cx, abort: &ControlAbort) -> Result<Ref> {
self.one_shot.abort(cx, abort)
}
fn resume(&self, cx: &mut Cx, resume: &ControlResume) -> Result<Ref> {
self.one_shot.resume(cx, resume)
}
}
pub fn one_shot_control_policy() -> ControlPolicyRef {
Arc::new(OneShotControlPolicy::new())
}
pub fn segmented_control_policy(segment: Ref) -> ControlPolicyRef {
Arc::new(SegmentedControlPolicy::new(segment))
}
pub fn install_control_policy(cx: &mut Cx) {
cx.set_control_policy(one_shot_control_policy());
}