forked from ROMEO/nexosim
Unix socket server support + rename grpc -> server
This commit is contained in:
parent
81c1d61290
commit
e526071a29
@ -30,7 +30,7 @@ keywords = [
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
# gRPC service.
|
# gRPC service.
|
||||||
grpc = [
|
server = [
|
||||||
"dep:bytes",
|
"dep:bytes",
|
||||||
"dep:ciborium",
|
"dep:ciborium",
|
||||||
"dep:prost",
|
"dep:prost",
|
||||||
@ -38,6 +38,7 @@ grpc = [
|
|||||||
"dep:serde",
|
"dep:serde",
|
||||||
"dep:tonic",
|
"dep:tonic",
|
||||||
"dep:tokio",
|
"dep:tokio",
|
||||||
|
"dep:tokio-stream",
|
||||||
"dep:tonic",
|
"dep:tonic",
|
||||||
"tai-time/serde",
|
"tai-time/serde",
|
||||||
]
|
]
|
||||||
@ -84,6 +85,9 @@ tracing = { version = "0.1.40", default-features = false, features = [
|
|||||||
], optional = true }
|
], optional = true }
|
||||||
tracing-subscriber = { version = "0.3.18", optional = true }
|
tracing-subscriber = { version = "0.3.18", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
tokio-stream = { version = "0.1.10", features = ["net"], optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
futures-executor = "0.3"
|
futures-executor = "0.3"
|
||||||
@ -93,15 +97,15 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
|||||||
loom = "0.7"
|
loom = "0.7"
|
||||||
waker-fn = "1.1"
|
waker-fn = "1.1"
|
||||||
|
|
||||||
[target.'cfg(nexosim_grpc_codegen)'.build-dependencies]
|
[target.'cfg(nexosim_server_codegen)'.build-dependencies]
|
||||||
tonic-build = { version = "0.12" }
|
tonic-build = { version = "0.12" }
|
||||||
|
|
||||||
[lints.rust]
|
[lints.rust]
|
||||||
# `nexosim_loom` flag: run loom-based tests.
|
# `nexosim_loom` flag: run loom-based tests.
|
||||||
# `nexosim_grpc_codegen` flag: regenerate gRPC code from .proto definitions.
|
# `nexosim_server_codegen` flag: regenerate gRPC code from .proto definitions.
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = [
|
unexpected_cfgs = { level = "warn", check-cfg = [
|
||||||
'cfg(nexosim_loom)',
|
'cfg(nexosim_loom)',
|
||||||
'cfg(nexosim_grpc_codegen)',
|
'cfg(nexosim_server_codegen)',
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
#[cfg(nexosim_grpc_codegen)]
|
#[cfg(nexosim_server_codegen)]
|
||||||
tonic_build::configure()
|
tonic_build::configure()
|
||||||
.build_client(false)
|
.build_client(false)
|
||||||
.out_dir("src/grpc/codegen/")
|
.out_dir("src/server/codegen/")
|
||||||
.compile_protos(&["simulation.proto"], &["src/grpc/api/"])?;
|
.compile_protos(&["simulation.proto"], &["src/server/api/"])?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -399,14 +399,14 @@
|
|||||||
//!
|
//!
|
||||||
//! See the [`tracing`] module for more information.
|
//! See the [`tracing`] module for more information.
|
||||||
//!
|
//!
|
||||||
//! ## gRPC server
|
//! ## Server
|
||||||
//!
|
//!
|
||||||
//! The `grpc` feature provides a gRPC server for remote control and monitoring,
|
//! The `server` feature provides a gRPC server for remote control and monitoring,
|
||||||
//! e.g. from a Python client. It can be activated with:
|
//! e.g. from a Python client. It can be activated with:
|
||||||
//!
|
//!
|
||||||
//! ```toml
|
//! ```toml
|
||||||
//! [dependencies]
|
//! [dependencies]
|
||||||
//! nexosim = { version = "0.3.0-beta.0", features = ["grpc"] }
|
//! nexosim = { version = "0.3.0-beta.0", features = ["server"] }
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! # Other resources
|
//! # Other resources
|
||||||
@ -449,10 +449,10 @@ pub mod simulation;
|
|||||||
pub mod time;
|
pub mod time;
|
||||||
pub(crate) mod util;
|
pub(crate) mod util;
|
||||||
|
|
||||||
#[cfg(feature = "grpc")]
|
#[cfg(feature = "server")]
|
||||||
pub mod grpc;
|
|
||||||
#[cfg(feature = "grpc")]
|
|
||||||
pub mod registry;
|
pub mod registry;
|
||||||
|
#[cfg(feature = "server")]
|
||||||
|
pub mod server;
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
pub mod tracing;
|
pub mod tracing;
|
||||||
|
@ -6,3 +6,6 @@ mod run;
|
|||||||
mod services;
|
mod services;
|
||||||
|
|
||||||
pub use run::run;
|
pub use run::run;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub use run::run_local;
|
@ -1,6 +1,7 @@
|
|||||||
//! gRPC simulation service.
|
//! Simulation server.
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::sync::MutexGuard;
|
use std::sync::MutexGuard;
|
||||||
@ -16,7 +17,7 @@ use super::key_registry::KeyRegistry;
|
|||||||
use super::services::InitService;
|
use super::services::InitService;
|
||||||
use super::services::{ControllerService, MonitorService, SchedulerService};
|
use super::services::{ControllerService, MonitorService, SchedulerService};
|
||||||
|
|
||||||
/// Runs a gRPC simulation server.
|
/// Runs a simulation from a network server.
|
||||||
///
|
///
|
||||||
/// The first argument is a closure that takes an initialization configuration
|
/// The first argument is a closure that takes an initialization configuration
|
||||||
/// and is called every time the simulation is (re)started by the remote client.
|
/// and is called every time the simulation is (re)started by the remote client.
|
||||||
@ -30,7 +31,7 @@ where
|
|||||||
run_service(GrpcSimulationService::new(sim_gen), addr)
|
run_service(GrpcSimulationService::new(sim_gen), addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Monomorphization of the networking code.
|
/// Monomorphization of the network server.
|
||||||
///
|
///
|
||||||
/// Keeping this as a separate monomorphized fragment can even triple
|
/// Keeping this as a separate monomorphized fragment can even triple
|
||||||
/// compilation speed for incremental release builds.
|
/// compilation speed for incremental release builds.
|
||||||
@ -38,8 +39,8 @@ fn run_service(
|
|||||||
service: GrpcSimulationService,
|
service: GrpcSimulationService,
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
// Use 2 threads so that the even if the controller service is blocked due
|
// Use 2 threads so that even if the controller service is blocked due to
|
||||||
// to ongoing simulation execution, other services can still be used
|
// ongoing simulation execution, other services can still be used
|
||||||
// concurrently.
|
// concurrently.
|
||||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||||
.worker_threads(2)
|
.worker_threads(2)
|
||||||
@ -56,6 +57,82 @@ fn run_service(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs a simulation locally from a Unix Domain Sockets server.
|
||||||
|
///
|
||||||
|
/// The first argument is a closure that takes an initialization configuration
|
||||||
|
/// and is called every time the simulation is (re)started by the remote client.
|
||||||
|
/// It must create a new simulation, complemented by a registry that exposes the
|
||||||
|
/// public event and query interface.
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub fn run_local<F, I, P>(sim_gen: F, path: P) -> Result<(), Box<dyn std::error::Error>>
|
||||||
|
where
|
||||||
|
F: FnMut(I) -> Result<(Simulation, EndpointRegistry), SimulationError> + Send + 'static,
|
||||||
|
I: DeserializeOwned,
|
||||||
|
P: AsRef<Path>,
|
||||||
|
{
|
||||||
|
let path = path.as_ref();
|
||||||
|
run_local_service(GrpcSimulationService::new(sim_gen), path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Monomorphization of the Unix Domain Sockets server.
|
||||||
|
///
|
||||||
|
/// Keeping this as a separate monomorphized fragment can even triple
|
||||||
|
/// compilation speed for incremental release builds.
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn run_local_service(
|
||||||
|
service: GrpcSimulationService,
|
||||||
|
path: &Path,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
use std::fs;
|
||||||
|
use std::io;
|
||||||
|
use std::os::unix::fs::FileTypeExt;
|
||||||
|
|
||||||
|
use tokio::net::UnixListener;
|
||||||
|
use tokio_stream::wrappers::UnixListenerStream;
|
||||||
|
|
||||||
|
// Unlink the socket if it already exists to prevent an `AddrInUse` error.
|
||||||
|
match fs::metadata(path) {
|
||||||
|
// The path is valid: make sure it actually points to a socket.
|
||||||
|
Ok(socket_meta) => {
|
||||||
|
if !socket_meta.file_type().is_socket() {
|
||||||
|
return Err(Box::new(io::Error::new(
|
||||||
|
io::ErrorKind::AlreadyExists,
|
||||||
|
"the specified path points to an existing non-socket file",
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::remove_file(path)?;
|
||||||
|
}
|
||||||
|
// Nothing to do: the socket does not exist yet.
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::NotFound => {}
|
||||||
|
// We don't have permission to use the socket.
|
||||||
|
Err(e) => return Err(Box::new(e)),
|
||||||
|
}
|
||||||
|
|
||||||
|
// (Re-)Create the socket.
|
||||||
|
fs::create_dir_all(path.parent().unwrap())?;
|
||||||
|
|
||||||
|
// Use 2 threads so that even if the controller service is blocked due to
|
||||||
|
// ongoing simulation execution, other services can still be used
|
||||||
|
// concurrently.
|
||||||
|
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.worker_threads(2)
|
||||||
|
.enable_io()
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
rt.block_on(async move {
|
||||||
|
let uds = UnixListener::bind(path)?;
|
||||||
|
let uds_stream = UnixListenerStream::new(uds);
|
||||||
|
|
||||||
|
Server::builder()
|
||||||
|
.add_service(simulation_server::SimulationServer::new(service))
|
||||||
|
.serve_with_incoming(uds_stream)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
struct GrpcSimulationService {
|
struct GrpcSimulationService {
|
||||||
init_service: Mutex<InitService>,
|
init_service: Mutex<InitService>,
|
||||||
controller_service: Mutex<ControllerService>,
|
controller_service: Mutex<ControllerService>,
|
@ -1,8 +1,8 @@
|
|||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::grpc::key_registry::{KeyRegistry, KeyRegistryId};
|
|
||||||
use crate::registry::EventSourceRegistry;
|
use crate::registry::EventSourceRegistry;
|
||||||
|
use crate::server::key_registry::{KeyRegistry, KeyRegistryId};
|
||||||
use crate::simulation::Scheduler;
|
use crate::simulation::Scheduler;
|
||||||
|
|
||||||
use super::super::codegen::simulation::*;
|
use super::super::codegen::simulation::*;
|
@ -537,7 +537,7 @@ impl Simulation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a scheduler handle.
|
/// Returns a scheduler handle.
|
||||||
#[cfg(feature = "grpc")]
|
#[cfg(feature = "server")]
|
||||||
pub(crate) fn scheduler(&self) -> Scheduler {
|
pub(crate) fn scheduler(&self) -> Scheduler {
|
||||||
Scheduler::new(
|
Scheduler::new(
|
||||||
self.scheduler_queue.clone(),
|
self.scheduler_queue.clone(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user