1
0
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:
Serge Barral
2024-10-20 12:35:44 +02:00
parent e7889c8e9b
commit 1cfaa00f9e
22 changed files with 556 additions and 223 deletions

View File

@ -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 {

View File

@ -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,
}
}

View File

@ -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

View File

@ -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| {

View File

@ -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 {