diff options
Diffstat (limited to 'crates/shirabe-external-packages/src/symfony/console/signal_registry')
| -rw-r--r-- | crates/shirabe-external-packages/src/symfony/console/signal_registry/mod.rs | 3 | ||||
| -rw-r--r-- | crates/shirabe-external-packages/src/symfony/console/signal_registry/signal_registry.rs | 100 |
2 files changed, 103 insertions, 0 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/console/signal_registry/mod.rs b/crates/shirabe-external-packages/src/symfony/console/signal_registry/mod.rs new file mode 100644 index 0000000..a07ca01 --- /dev/null +++ b/crates/shirabe-external-packages/src/symfony/console/signal_registry/mod.rs @@ -0,0 +1,3 @@ +pub mod signal_registry; + +pub use signal_registry::*; diff --git a/crates/shirabe-external-packages/src/symfony/console/signal_registry/signal_registry.rs b/crates/shirabe-external-packages/src/symfony/console/signal_registry/signal_registry.rs new file mode 100644 index 0000000..bd6b989 --- /dev/null +++ b/crates/shirabe-external-packages/src/symfony/console/signal_registry/signal_registry.rs @@ -0,0 +1,100 @@ +use indexmap::IndexMap; + +/// A signal handler receives the signal number and whether a further handler follows. +pub type SignalHandler = Box<dyn Fn(i64, bool)>; + +pub struct SignalRegistry { + // signal number => list of handlers + signal_handlers: IndexMap<i64, Vec<SignalHandler>>, +} + +impl std::fmt::Debug for SignalRegistry { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SignalRegistry") + .field("signal_handlers", &self.signal_handlers.keys()) + .finish_non_exhaustive() + } +} + +impl Default for SignalRegistry { + fn default() -> Self { + Self::new() + } +} + +impl SignalRegistry { + pub fn new() -> Self { + if shirabe_php_shim::function_exists("pcntl_async_signals") { + shirabe_php_shim::pcntl_async_signals(true); + } + + Self { + signal_handlers: IndexMap::new(), + } + } + + pub fn register(&mut self, signal: i64, signal_handler: SignalHandler) { + if !self.signal_handlers.contains_key(&signal) { + let previous_callback = shirabe_php_shim::pcntl_signal_get_handler(signal); + + if shirabe_php_shim::is_callable(&previous_callback) { + // $this->signalHandlers[$signal][] = $previousCallback; + // The previous handler is an opaque PHP callable obtained from pcntl; + // it is invoked through the runtime callable mechanism. + self.signal_handlers + .entry(signal) + .or_default() + .push(Box::new(move |signal, has_next| { + shirabe_php_shim::call_php_callable( + &previous_callback, + &[ + shirabe_php_shim::PhpMixed::Int(signal), + shirabe_php_shim::PhpMixed::Bool(has_next), + ], + ); + })); + } + } + + self.signal_handlers + .entry(signal) + .or_default() + .push(signal_handler); + + // pcntl_signal($signal, [$this, 'handle']) + // TODO(plugin): the PHP callback `[$this, 'handle']` captures the registry + // instance. Wiring this object method as a C-level signal handler requires the + // runtime callable mechanism; see review notes. + shirabe_php_shim::pcntl_signal(signal, shirabe_php_shim::PhpMixed::Null); + } + + pub fn is_supported() -> bool { + if !shirabe_php_shim::function_exists("pcntl_signal") { + return false; + } + + if shirabe_php_shim::explode( + ",", + &shirabe_php_shim::ini_get("disable_functions").unwrap_or_default(), + ) + .contains(&"pcntl_signal".to_string()) + { + return false; + } + + true + } + + pub fn handle(&self, signal: i64) { + let handlers = match self.signal_handlers.get(&signal) { + Some(handlers) => handlers, + None => return, + }; + let count = handlers.len(); + + for (i, signal_handler) in handlers.iter().enumerate() { + let has_next = i != count - 1; + signal_handler(signal, has_next); + } + } +} |
