提交 790c2037 authored 作者: Serhij S's avatar Serhij S

reload handler and live reload

上级 ae339ed9
...@@ -77,10 +77,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> { ...@@ -77,10 +77,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
controller.spawn_worker(DataGenerator {})?; controller.spawn_worker(DataGenerator {})?;
controller.spawn_worker(DataParser {})?; controller.spawn_worker(DataParser {})?;
controller.spawn_worker(VeryBlocking {})?; controller.spawn_worker(VeryBlocking {})?;
controller.register_signals_with_shutdown_handler( controller.register_signals_with_handlers(
move |context| { move |context| {
context.hub().send(Message::Terminate); context.hub().send(Message::Terminate);
}, },
|_| {
info!("Allowing reload");
Ok(())
},
SHUTDOWN_TIMEOUT, SHUTDOWN_TIMEOUT,
)?; )?;
info!("controller started"); info!("controller started");
......
...@@ -20,10 +20,10 @@ pub use roboplc_derive::WorkerOpts; ...@@ -20,10 +20,10 @@ pub use roboplc_derive::WorkerOpts;
use rtsc::data_policy::DataDeliveryPolicy; use rtsc::data_policy::DataDeliveryPolicy;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use signal_hook::{ use signal_hook::{
consts::{SIGINT, SIGTERM}, consts::{SIGINT, SIGTERM, SIGUSR2},
iterator::Signals, iterator::Signals,
}; };
use tracing::error; use tracing::{error, warn};
/// Controller prelude /// Controller prelude
pub mod prelude { pub mod prelude {
...@@ -184,10 +184,10 @@ where ...@@ -184,10 +184,10 @@ where
self.supervisor.spawn(Builder::new().name(name), f)?; self.supervisor.spawn(Builder::new().name(name), f)?;
Ok(()) Ok(())
} }
/// Registers SIGINT and SIGTERM signals to a thread which terminates the controller with a /// Registers SIGINT, SIGTERM and SIGUSR2 signals to a thread which terminates the controller
/// dummy handler (see [`Controller::register_signals_with_shutdown_handler()`]). /// with dummy handlers (see [`Controller::register_signals_with_handlers()`]).
pub fn register_signals(&mut self, shutdown_timeout: Duration) -> Result<()> { pub fn register_signals(&mut self, shutdown_timeout: Duration) -> Result<()> {
self.register_signals_with_shutdown_handler(|_| {}, shutdown_timeout) self.register_signals_with_handlers(|_| {}, |_| Ok(()), shutdown_timeout)
} }
/// Registers SIGINT and SIGTERM signals to a thread which terminates the controller. /// Registers SIGINT and SIGTERM signals to a thread which terminates the controller.
/// ///
...@@ -202,15 +202,21 @@ where ...@@ -202,15 +202,21 @@ where
/// ///
/// The thread is automatically spawned with FIFO scheduling and the highest priority on CPU 0 /// The thread is automatically spawned with FIFO scheduling and the highest priority on CPU 0
/// or falled back to non-realtime. /// or falled back to non-realtime.
pub fn register_signals_with_shutdown_handler<H>( ///
/// If the reload handler function returns error, the reload process is aborted. Otherwise, the
/// executable is reloaded (see [`crate::reload_executable()`]).
pub fn register_signals_with_handlers<SH, RH>(
&mut self, &mut self,
handle_fn: H, shutdown_handler_fn: SH,
reload_handler_fn: RH,
#[allow(unused_variables)] shutdown_timeout: Duration, #[allow(unused_variables)] shutdown_timeout: Duration,
) -> Result<()> ) -> Result<()>
where where
H: Fn(&Context<D, V>) + Send + Sync + 'static, SH: Fn(&Context<D, V>) + Send + Sync + 'static,
RH: Fn(&Context<D, V>) -> Result<()> + Send + Sync + 'static,
{ {
let handler = Arc::new(handle_fn); let shutdown_handler = Arc::new(shutdown_handler_fn);
let reload_handler = Arc::new(reload_handler_fn);
let mut builder = Builder::new().name("RoboPLCSigRT").rt_params( let mut builder = Builder::new().name("RoboPLCSigRT").rt_params(
RTParams::new() RTParams::new()
.set_priority(99) .set_priority(99)
...@@ -219,19 +225,27 @@ where ...@@ -219,19 +225,27 @@ where
); );
builder.park_on_errors = true; builder.park_on_errors = true;
macro_rules! sig_handler { macro_rules! sig_handler {
($handler: expr) => {{ ($shutdown_handler: expr, $reload_handler: expr) => {{
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
{ {
let context = self.context(); let context = self.context();
let mut signals = Signals::new([SIGTERM, SIGINT])?; let mut signals = Signals::new([SIGTERM, SIGINT, SIGUSR2])?;
move || { move || {
if let Some(sig) = signals.forever().next() { if let Some(sig) = signals.forever().next() {
match sig { match sig {
SIGTERM | SIGINT => { SIGTERM | SIGINT => {
suicide(shutdown_timeout, true); suicide(shutdown_timeout, true);
$handler(&context); $shutdown_handler(&context);
context.terminate(); context.terminate();
} }
SIGUSR2 => {
warn!("Performing live reload");
if let Err(e) = $reload_handler(&context) {
error!(error=%e, "reload handler");
} else if let Err(e) = crate::reload_executable() {
panic!("Live reload failed: {}", e);
}
}
_ => unreachable!(), _ => unreachable!(),
} }
} }
...@@ -244,8 +258,9 @@ where ...@@ -244,8 +258,9 @@ where
}}; }};
} }
#[allow(unused_variables)] #[allow(unused_variables)]
let h = handler.clone(); let sh = shutdown_handler.clone();
if let Err(e) = self.supervisor.spawn(builder.clone(), sig_handler!(h)) { let rh = reload_handler.clone();
if let Err(e) = self.supervisor.spawn(builder.clone(), sig_handler!(sh, rh)) {
if !matches!(e, Error::RTSchedSetSchduler(_)) { if !matches!(e, Error::RTSchedSetSchduler(_)) {
return Err(e); return Err(e);
} }
...@@ -254,7 +269,8 @@ where ...@@ -254,7 +269,8 @@ where
} }
// fall-back to non-rt handler // fall-back to non-rt handler
let builder = builder.name("RoboPLCSig").rt_params(RTParams::new()); let builder = builder.name("RoboPLCSig").rt_params(RTParams::new());
self.supervisor.spawn(builder, sig_handler!(handler))?; self.supervisor
.spawn(builder, sig_handler!(shutdown_handler, reload_handler))?;
Ok(()) Ok(())
} }
fn context(&self) -> Context<D, V> { fn context(&self) -> Context<D, V> {
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论