forked from ROMEO/nexosim
Make execution failible, impl deadlock detection
TODO: return the list of models involved in a deadlock. Note that Many execution errors are not implemented at all at the moment and will need separate PRs, namely: - Terminated - ModelError - Panic
This commit is contained in:
@ -10,14 +10,20 @@ import "google/protobuf/empty.proto";
|
||||
enum ErrorCode {
|
||||
INTERNAL_ERROR = 0;
|
||||
SIMULATION_NOT_STARTED = 1;
|
||||
MISSING_ARGUMENT = 2;
|
||||
INVALID_TIME = 3;
|
||||
INVALID_DURATION = 4;
|
||||
INVALID_MESSAGE = 5;
|
||||
INVALID_KEY = 6;
|
||||
SOURCE_NOT_FOUND = 10;
|
||||
SINK_NOT_FOUND = 11;
|
||||
SIMULATION_TIME_OUT_OF_RANGE = 12;
|
||||
SIMULATION_TERMINATED = 2;
|
||||
SIMULATION_DEADLOCK = 3;
|
||||
SIMULATION_MODEL_ERROR = 4;
|
||||
SIMULATION_PANIC = 5;
|
||||
SIMULATION_BAD_QUERY = 6;
|
||||
SIMULATION_TIME_OUT_OF_RANGE = 7;
|
||||
MISSING_ARGUMENT = 10;
|
||||
INVALID_TIME = 11;
|
||||
INVALID_DURATION = 12;
|
||||
INVALID_PERIOD = 13;
|
||||
INVALID_MESSAGE = 14;
|
||||
INVALID_KEY = 15;
|
||||
SOURCE_NOT_FOUND = 20;
|
||||
SINK_NOT_FOUND = 21;
|
||||
}
|
||||
|
||||
message Error {
|
||||
|
@ -338,14 +338,20 @@ pub mod any_request {
|
||||
pub enum ErrorCode {
|
||||
InternalError = 0,
|
||||
SimulationNotStarted = 1,
|
||||
MissingArgument = 2,
|
||||
InvalidTime = 3,
|
||||
InvalidDuration = 4,
|
||||
InvalidMessage = 5,
|
||||
InvalidKey = 6,
|
||||
SourceNotFound = 10,
|
||||
SinkNotFound = 11,
|
||||
SimulationTimeOutOfRange = 12,
|
||||
SimulationTerminated = 2,
|
||||
SimulationDeadlock = 3,
|
||||
SimulationModelError = 4,
|
||||
SimulationPanic = 5,
|
||||
SimulationBadQuery = 6,
|
||||
SimulationTimeOutOfRange = 22,
|
||||
MissingArgument = 7,
|
||||
InvalidTime = 8,
|
||||
InvalidDuration = 9,
|
||||
InvalidPeriod = 10,
|
||||
InvalidMessage = 11,
|
||||
InvalidKey = 12,
|
||||
SourceNotFound = 20,
|
||||
SinkNotFound = 21,
|
||||
}
|
||||
impl ErrorCode {
|
||||
/// String value of the enum field names used in the ProtoBuf definition.
|
||||
@ -356,14 +362,20 @@ impl ErrorCode {
|
||||
match self {
|
||||
ErrorCode::InternalError => "INTERNAL_ERROR",
|
||||
ErrorCode::SimulationNotStarted => "SIMULATION_NOT_STARTED",
|
||||
ErrorCode::SimulationTerminated => "SIMULATION_TERMINATED",
|
||||
ErrorCode::SimulationDeadlock => "SIMULATION_DEADLOCK",
|
||||
ErrorCode::SimulationModelError => "SIMULATION_MODEL_ERROR",
|
||||
ErrorCode::SimulationPanic => "SIMULATION_PANIC",
|
||||
ErrorCode::SimulationBadQuery => "SIMULATION_BAD_QUERY",
|
||||
ErrorCode::SimulationTimeOutOfRange => "SIMULATION_TIME_OUT_OF_RANGE",
|
||||
ErrorCode::MissingArgument => "MISSING_ARGUMENT",
|
||||
ErrorCode::InvalidTime => "INVALID_TIME",
|
||||
ErrorCode::InvalidDuration => "INVALID_DURATION",
|
||||
ErrorCode::InvalidPeriod => "INVALID_PERIOD",
|
||||
ErrorCode::InvalidMessage => "INVALID_MESSAGE",
|
||||
ErrorCode::InvalidKey => "INVALID_KEY",
|
||||
ErrorCode::SourceNotFound => "SOURCE_NOT_FOUND",
|
||||
ErrorCode::SinkNotFound => "SINK_NOT_FOUND",
|
||||
ErrorCode::SimulationTimeOutOfRange => "SIMULATION_TIME_OUT_OF_RANGE",
|
||||
}
|
||||
}
|
||||
/// Creates an enum from field names used in the ProtoBuf definition.
|
||||
@ -371,14 +383,20 @@ impl ErrorCode {
|
||||
match value {
|
||||
"INTERNAL_ERROR" => Some(Self::InternalError),
|
||||
"SIMULATION_NOT_STARTED" => Some(Self::SimulationNotStarted),
|
||||
"SIMULATION_TERMINATED" => Some(Self::SimulationTerminated),
|
||||
"SIMULATION_DEADLOCK" => Some(Self::SimulationDeadlock),
|
||||
"SIMULATION_MODEL_ERROR" => Some(Self::SimulationModelError),
|
||||
"SIMULATION_PANIC" => Some(Self::SimulationPanic),
|
||||
"SIMULATION_BAD_QUERY" => Some(Self::SimulationBadQuery),
|
||||
"SIMULATION_TIME_OUT_OF_RANGE" => Some(Self::SimulationTimeOutOfRange),
|
||||
"MISSING_ARGUMENT" => Some(Self::MissingArgument),
|
||||
"INVALID_TIME" => Some(Self::InvalidTime),
|
||||
"INVALID_DURATION" => Some(Self::InvalidDuration),
|
||||
"INVALID_PERIOD" => Some(Self::InvalidPeriod),
|
||||
"INVALID_MESSAGE" => Some(Self::InvalidMessage),
|
||||
"INVALID_KEY" => Some(Self::InvalidKey),
|
||||
"SOURCE_NOT_FOUND" => Some(Self::SourceNotFound),
|
||||
"SINK_NOT_FOUND" => Some(Self::SinkNotFound),
|
||||
"SIMULATION_TIME_OUT_OF_RANGE" => Some(Self::SimulationTimeOutOfRange),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ use prost_types::Timestamp;
|
||||
use tai_time::MonotonicTime;
|
||||
|
||||
use super::codegen::simulation::{Error, ErrorCode};
|
||||
use crate::simulation::ExecutionError;
|
||||
|
||||
pub(crate) use controller_service::ControllerService;
|
||||
pub(crate) use init_service::InitService;
|
||||
@ -29,6 +30,21 @@ fn simulation_not_started_error() -> Error {
|
||||
)
|
||||
}
|
||||
|
||||
/// Map an `ExecutionError` to a Protobuf error.
|
||||
fn map_execution_error(error: ExecutionError) -> Error {
|
||||
let error_code = match error {
|
||||
ExecutionError::Deadlock(_) => ErrorCode::SimulationDeadlock,
|
||||
ExecutionError::ModelError { .. } => ErrorCode::SimulationModelError,
|
||||
ExecutionError::Panic(_) => ErrorCode::SimulationPanic,
|
||||
ExecutionError::BadQuery => ErrorCode::SimulationBadQuery,
|
||||
ExecutionError::Terminated => ErrorCode::SimulationTerminated,
|
||||
ExecutionError::InvalidTargetTime(_) => ErrorCode::InvalidTime,
|
||||
};
|
||||
let error_message = error.to_string();
|
||||
|
||||
to_error(error_code, error_message)
|
||||
}
|
||||
|
||||
/// Attempts a cast from a `MonotonicTime` to a protobuf `Timestamp`.
|
||||
///
|
||||
/// This will fail if the time is outside the protobuf-specified range for
|
||||
|
@ -8,8 +8,8 @@ use crate::simulation::Simulation;
|
||||
|
||||
use super::super::codegen::simulation::*;
|
||||
use super::{
|
||||
monotonic_to_timestamp, simulation_not_started_error, timestamp_to_monotonic, to_error,
|
||||
to_positive_duration, to_strictly_positive_duration,
|
||||
map_execution_error, monotonic_to_timestamp, simulation_not_started_error,
|
||||
timestamp_to_monotonic, to_error, to_positive_duration, to_strictly_positive_duration,
|
||||
};
|
||||
|
||||
/// Protobuf-based simulation manager.
|
||||
@ -61,18 +61,19 @@ impl ControllerService {
|
||||
/// processed events have completed.
|
||||
pub(crate) fn step(&mut self, _request: StepRequest) -> StepReply {
|
||||
let reply = match self {
|
||||
Self::Started { simulation, .. } => {
|
||||
simulation.step();
|
||||
|
||||
if let Some(timestamp) = monotonic_to_timestamp(simulation.time()) {
|
||||
step_reply::Result::Time(timestamp)
|
||||
} else {
|
||||
step_reply::Result::Error(to_error(
|
||||
ErrorCode::SimulationTimeOutOfRange,
|
||||
"the final simulation time is out of range",
|
||||
))
|
||||
Self::Started { simulation, .. } => match simulation.step() {
|
||||
Ok(()) => {
|
||||
if let Some(timestamp) = monotonic_to_timestamp(simulation.time()) {
|
||||
step_reply::Result::Time(timestamp)
|
||||
} else {
|
||||
step_reply::Result::Error(to_error(
|
||||
ErrorCode::SimulationTimeOutOfRange,
|
||||
"the final simulation time is out of range",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => step_reply::Result::Error(map_execution_error(e)),
|
||||
},
|
||||
Self::NotStarted => step_reply::Result::Error(simulation_not_started_error()),
|
||||
};
|
||||
|
||||
@ -117,7 +118,7 @@ impl ControllerService {
|
||||
"the specified deadline lies in the past",
|
||||
))?;
|
||||
|
||||
simulation.step_by(duration);
|
||||
simulation.step_by(duration).map_err(map_execution_error)?;
|
||||
}
|
||||
};
|
||||
|
||||
@ -221,7 +222,7 @@ impl ControllerService {
|
||||
}
|
||||
});
|
||||
|
||||
simulation.process(action);
|
||||
simulation.process(action).map_err(map_execution_error)?;
|
||||
|
||||
Ok(key_id)
|
||||
}(),
|
||||
@ -315,9 +316,7 @@ impl ControllerService {
|
||||
)
|
||||
})?;
|
||||
|
||||
simulation.process(event);
|
||||
|
||||
Ok(())
|
||||
simulation.process(event).map_err(map_execution_error)
|
||||
}(),
|
||||
Self::NotStarted => Err(simulation_not_started_error()),
|
||||
};
|
||||
@ -360,11 +359,11 @@ impl ControllerService {
|
||||
)
|
||||
})?;
|
||||
|
||||
simulation.process(query);
|
||||
simulation.process(query).map_err(map_execution_error)?;
|
||||
|
||||
let replies = promise.take_collect().ok_or(to_error(
|
||||
ErrorCode::InternalError,
|
||||
"a reply to the query was expected but none was available".to_string(),
|
||||
ErrorCode::SimulationBadQuery,
|
||||
"a reply to the query was expected but none was available; maybe the target model was not added to the simulation?".to_string(),
|
||||
))?;
|
||||
|
||||
replies.map_err(|e| {
|
||||
|
@ -5,7 +5,7 @@ use crate::registry::EndpointRegistry;
|
||||
use crate::simulation::SimInit;
|
||||
use crate::simulation::Simulation;
|
||||
|
||||
use super::{timestamp_to_monotonic, to_error};
|
||||
use super::{map_execution_error, timestamp_to_monotonic, to_error};
|
||||
|
||||
use super::super::codegen::simulation::*;
|
||||
|
||||
@ -69,7 +69,12 @@ impl InitService {
|
||||
.ok_or_else(|| {
|
||||
to_error(ErrorCode::InvalidTime, "out-of-range nanosecond field")
|
||||
})
|
||||
.map(|start_time| (sim_init.init(start_time), registry))
|
||||
.and_then(|start_time| {
|
||||
sim_init
|
||||
.init(start_time)
|
||||
.map_err(|e| map_execution_error(e))
|
||||
.map(|sim| (sim, registry))
|
||||
})
|
||||
});
|
||||
|
||||
let (reply, bench) = match reply {
|
||||
|
Reference in New Issue
Block a user