1
0
mirror of https://github.com/fafhrd91/actix-net synced 2025-01-19 00:31:50 +01:00

prevent spawn_fn panic bubbling (#255)

This commit is contained in:
Rob Ede 2021-01-29 14:16:10 +00:00 committed by GitHub
parent 6b86b5efc5
commit 2924419905
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 61 deletions

View File

@ -11,38 +11,25 @@ use crate::{
worker::Worker, worker::Worker,
}; };
/// Builder an actix runtime. /// System builder.
/// ///
/// Either use `Builder::build` to create a system and start actors. Alternatively, use /// Either use `Builder::build` to create a system and start actors. Alternatively, use
/// `Builder::run` to start the Tokio runtime and run a function in its context. /// `Builder::run` to start the Tokio runtime and run a function in its context.
pub struct Builder { pub struct Builder {
/// Name of the System. Defaults to "actix-rt" if unset. /// Name of the System. Defaults to "actix-rt" if unset.
name: Cow<'static, str>, name: Cow<'static, str>,
/// Whether the Arbiter will stop the whole System on uncaught panic. Defaults to false.
stop_on_panic: bool,
} }
impl Builder { impl Builder {
pub(crate) fn new() -> Self { pub(crate) fn new() -> Self {
Builder { Builder {
name: Cow::Borrowed("actix-rt"), name: Cow::Borrowed("actix-rt"),
stop_on_panic: false,
} }
} }
/// Sets the name of the System. /// Sets the name of the System.
pub fn name(mut self, name: impl Into<String>) -> Self { pub fn name(mut self, name: impl Into<Cow<'static, str>>) -> Self {
self.name = Cow::Owned(name.into()); self.name = name.into();
self
}
/// Sets the option 'stop_on_panic' which controls whether the System is stopped when an
/// uncaught panic is thrown from a worker thread.
///
/// Defaults to false.
pub fn stop_on_panic(mut self, stop_on_panic: bool) -> Self {
self.stop_on_panic = stop_on_panic;
self self
} }
@ -55,14 +42,14 @@ impl Builder {
/// This function will start Tokio runtime and will finish once the `System::stop()` message /// This function will start Tokio runtime and will finish once the `System::stop()` message
/// is called. Function `f` is called within Tokio runtime context. /// is called. Function `f` is called within Tokio runtime context.
pub fn run<F>(self, f: F) -> io::Result<()> pub fn run<F>(self, init_fn: F) -> io::Result<()>
where where
F: FnOnce(), F: FnOnce(),
{ {
self.create_runtime(f).run() self.create_runtime(init_fn).run()
} }
fn create_runtime<F>(self, f: F) -> SystemRunner fn create_runtime<F>(self, init_fn: F) -> SystemRunner
where where
F: FnOnce(), F: FnOnce(),
{ {
@ -71,18 +58,14 @@ impl Builder {
let rt = Runtime::new().unwrap(); let rt = Runtime::new().unwrap();
let system = System::construct( let system = System::construct(sys_sender, Worker::new_system(rt.local()));
sys_sender,
Worker::new_system(rt.local()),
self.stop_on_panic,
);
let arb = SystemWorker::new(sys_receiver, stop_tx); // init system worker
let sys_worker = SystemWorker::new(sys_receiver, stop_tx);
rt.spawn(sys_worker);
rt.spawn(arb); // run system init method
rt.block_on(async { init_fn() });
// init system arbiter and run configuration method
rt.block_on(async { f() });
SystemRunner { rt, stop, system } SystemRunner { rt, stop, system }
} }

View File

@ -1,4 +1,5 @@
use std::{ use std::{
borrow::Cow,
cell::RefCell, cell::RefCell,
collections::HashMap, collections::HashMap,
future::Future, future::Future,
@ -24,7 +25,6 @@ pub struct System {
id: usize, id: usize,
tx: mpsc::UnboundedSender<SystemCommand>, tx: mpsc::UnboundedSender<SystemCommand>,
worker: Worker, worker: Worker,
stop_on_panic: bool,
} }
thread_local!( thread_local!(
@ -33,15 +33,10 @@ thread_local!(
impl System { impl System {
/// Constructs new system and sets it as current /// Constructs new system and sets it as current
pub(crate) fn construct( pub(crate) fn construct(sys: mpsc::UnboundedSender<SystemCommand>, worker: Worker) -> Self {
sys: mpsc::UnboundedSender<SystemCommand>,
worker: Worker,
stop_on_panic: bool,
) -> Self {
let sys = System { let sys = System {
tx: sys, tx: sys,
worker, worker,
stop_on_panic,
id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst), id: SYSTEM_COUNT.fetch_add(1, Ordering::SeqCst),
}; };
System::set_current(sys.clone()); System::set_current(sys.clone());
@ -57,9 +52,10 @@ impl System {
/// Create new system. /// Create new system.
/// ///
/// This method panics if it can not create Tokio runtime /// # Panics
/// Panics if underlying Tokio runtime can not be created.
#[allow(clippy::new_ret_no_self)] #[allow(clippy::new_ret_no_self)]
pub fn new(name: impl Into<String>) -> SystemRunner { pub fn new(name: impl Into<Cow<'static, str>>) -> SystemRunner {
Self::builder().name(name).build() Self::builder().name(name).build()
} }
@ -114,12 +110,6 @@ impl System {
&self.tx &self.tx
} }
/// Return status of 'stop_on_panic' option which controls whether the System is stopped when an
/// uncaught panic is thrown from a worker thread.
pub(crate) fn stop_on_panic(&self) -> bool {
self.stop_on_panic
}
/// Get shared reference to system arbiter. /// Get shared reference to system arbiter.
pub fn arbiter(&self) -> &Worker { pub fn arbiter(&self) -> &Worker {
&self.worker &self.worker

View File

@ -246,20 +246,6 @@ struct WorkerRunner {
rx: mpsc::UnboundedReceiver<WorkerCommand>, rx: mpsc::UnboundedReceiver<WorkerCommand>,
} }
impl Drop for WorkerRunner {
fn drop(&mut self) {
// panics can only occur with spawn_fn calls
if thread::panicking() {
if System::current().stop_on_panic() {
eprintln!("Panic in Worker thread, shutting down system.");
System::current().stop_with_code(1)
} else {
eprintln!("Panic in Worker thread.");
}
}
}
}
impl Future for WorkerRunner { impl Future for WorkerRunner {
type Output = (); type Output = ();
@ -277,7 +263,9 @@ impl Future for WorkerRunner {
tokio::task::spawn_local(task_fut); tokio::task::spawn_local(task_fut);
} }
WorkerCommand::ExecuteFn(task_fn) => { WorkerCommand::ExecuteFn(task_fn) => {
tokio::task::spawn_local(async {
task_fn(); task_fn();
});
} }
}, },
} }

View File

@ -1,4 +1,5 @@
use std::{ use std::{
sync::mpsc::sync_channel,
thread, thread,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
@ -107,13 +108,29 @@ fn wait_for_spawns() {
} }
#[test] #[test]
#[should_panic] fn worker_spawn_fn_runs() {
fn worker_drop_panic_fn() { let _ = System::new("test-system");
let (tx, rx) = sync_channel::<u32>(1);
let mut worker = Worker::new();
worker.spawn_fn(move || tx.send(42).unwrap());
let num = rx.recv().unwrap();
assert_eq!(num, 42);
worker.stop();
worker.join().unwrap();
}
#[test]
fn worker_drop_no_panic_fn() {
let _ = System::new("test-system"); let _ = System::new("test-system");
let mut worker = Worker::new(); let mut worker = Worker::new();
worker.spawn_fn(|| panic!("test")); worker.spawn_fn(|| panic!("test"));
worker.stop();
worker.join().unwrap(); worker.join().unwrap();
} }
@ -158,3 +175,15 @@ fn worker_item_storage() {
worker.stop(); worker.stop();
worker.join().unwrap(); worker.join().unwrap();
} }
#[test]
fn system_name_cow_str() {
let _ = System::new("test-system");
System::current().stop();
}
#[test]
fn system_name_cow_string() {
let _ = System::new("test-system".to_owned());
System::current().stop();
}