use crate::{
Engine, Error, Event, Matrix, Result,
runtime::{EngineHandle, RecordingTransaction},
};
use super::{EnvironmentSpec, EnvironmentTransition};
#[must_use]
pub struct EnvironmentExecution {
engine: EngineHandle,
recording: Option<RecordingTransaction>,
pending: Option<Event>,
closed: bool,
submission_count: u64,
}
impl EnvironmentExecution {
pub fn new(engine: &Engine) -> Self {
Self {
engine: engine.handle(),
recording: None,
pending: None,
closed: false,
submission_count: 0,
}
}
fn begin(&mut self) -> Result<()> {
if self.closed {
return Err(Error::failed_precondition(
"environment execution session is closed",
));
}
if self.pending.is_some() {
return Err(Error::failed_precondition(
"environment execution requires wait before another recording",
));
}
if self.recording.is_none() {
self.recording = Some(RecordingTransaction::begin(self.engine.clone())?);
}
Ok(())
}
fn submit(&mut self) -> Result<Event> {
let recording = self.recording.take().ok_or_else(|| {
Error::failed_precondition("environment submit requires an active recording")
})?;
let event = recording.submit()?;
self.submission_count = self
.submission_count
.checked_add(1)
.ok_or_else(|| Error::resource_exhausted("environment submission count exhausted"))?;
self.pending = Some(event.clone());
Ok(event)
}
fn wait(&mut self, event: &Event) -> Result<()> {
let pending = self
.pending
.as_ref()
.ok_or_else(|| Error::failed_precondition("environment wait requires a submitted event"))?;
if !pending.same_as(event) {
return Err(Error::invalid_argument(
"environment wait event does not match its pending submission",
));
}
event.wait()?;
self.pending = None;
Ok(())
}
fn cancel(&mut self) -> Result<()> {
if self.closed {
return Err(Error::failed_precondition(
"environment execution session is closed",
));
}
if self.pending.is_some() {
return Err(Error::failed_precondition(
"environment cannot cancel submitted work",
));
}
self.recording = None;
Ok(())
}
fn close(&mut self) -> Result<()> {
if self.closed {
return Ok(());
}
if let Some(event) = self.pending.clone() {
self.wait(&event)?;
}
self.recording = None;
self.closed = true;
Ok(())
}
pub const fn is_open(&self) -> bool {
!self.closed
}
pub const fn has_active_recording(&self) -> bool {
self.recording.is_some()
}
pub const fn has_pending_event(&self) -> bool {
self.pending.is_some()
}
pub const fn submission_count(&self) -> u64 {
self.submission_count
}
}
pub trait Environment {
fn engine(&self) -> &Engine;
fn spec(&self) -> &EnvironmentSpec;
fn environments(&self) -> u32;
fn observation(&self) -> &Matrix;
fn execution(&self) -> &EnvironmentExecution;
fn execution_mut(&mut self) -> &mut EnvironmentExecution;
fn record_reset(&mut self, seed: u64) -> Result<()>;
fn record_step(&mut self, action: &Matrix) -> Result<EnvironmentTransition>;
fn record_reset_completed(&mut self) -> Result<()>;
fn commit_recorded_state(&mut self) {}
fn rollback_recorded_state(&mut self) {}
fn begin(&mut self) -> Result<()> {
self.execution_mut().begin()
}
fn reset(&mut self, seed: u64) -> Result<()> {
self.begin()?;
if let Err(error) = self.record_reset(seed) {
let _ = self.execution_mut().cancel();
self.rollback_recorded_state();
return Err(error);
}
if let Err(error) = self
.spec()
.validate_reset(self.observation(), self.environments())
{
let _ = self.execution_mut().cancel();
self.rollback_recorded_state();
return Err(error);
}
Ok(())
}
fn step(&mut self, action: &Matrix) -> Result<EnvironmentTransition> {
self.begin()?;
if let Err(error) = self.spec().validate_action(action, self.environments()) {
let _ = self.execution_mut().cancel();
self.rollback_recorded_state();
return Err(error);
}
let transition = match self.record_step(action) {
Ok(transition) => transition,
Err(error) => {
let _ = self.execution_mut().cancel();
self.rollback_recorded_state();
return Err(error);
}
};
if let Err(error) = self
.spec()
.validate_transition(action, &transition, self.environments())
{
let _ = self.execution_mut().cancel();
self.rollback_recorded_state();
return Err(error);
}
Ok(transition)
}
fn reset_completed(&mut self) -> Result<()> {
self.begin()?;
if let Err(error) = self.record_reset_completed() {
let _ = self.execution_mut().cancel();
self.rollback_recorded_state();
return Err(error);
}
Ok(())
}
fn submit(&mut self) -> Result<Event> {
match self.execution_mut().submit() {
Ok(event) => {
self.commit_recorded_state();
Ok(event)
}
Err(error) => {
self.rollback_recorded_state();
Err(error)
}
}
}
fn wait(&mut self, event: &Event) -> Result<()> {
self.execution_mut().wait(event)
}
fn cancel(&mut self) -> Result<()> {
self.execution_mut().cancel()?;
self.rollback_recorded_state();
Ok(())
}
fn close(&mut self) -> Result<()> {
self.execution_mut().close()?;
self.rollback_recorded_state();
Ok(())
}
}