mirror of https://github.com/fafhrd91/actix-net
feat(rt): accept shared tokio runtimes in with_tokio_rt (#811)
This commit is contained in:
parent
1e07dce27e
commit
2c401e08e4
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
- Minimum supported Rust version (MSRV) is now 1.88.
|
- Minimum supported Rust version (MSRV) is now 1.88.
|
||||||
- Add `SystemRunner::stop_future` and `SystemRunner::into_parts` for awaiting system stop inside `block_on`.
|
- Add `SystemRunner::stop_future` and `SystemRunner::into_parts` for awaiting system stop inside `block_on`.
|
||||||
|
- Allow `{System, Arbiter}::with_tokio_rt` to accept shared Tokio runtimes (e.g. `Arc<tokio::runtime::Runtime>` or `&'static tokio::runtime::Runtime`).
|
||||||
|
|
||||||
## 2.11.0
|
## 2.11.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -105,11 +105,17 @@ impl Arbiter {
|
||||||
|
|
||||||
/// Spawn a new Arbiter using the [Tokio Runtime](tokio-runtime) returned from a closure.
|
/// Spawn a new Arbiter using the [Tokio Runtime](tokio-runtime) returned from a closure.
|
||||||
///
|
///
|
||||||
|
/// The closure may return any type that can be converted into [`Runtime`], such as
|
||||||
|
/// `tokio::runtime::Runtime`, `Arc<tokio::runtime::Runtime>`, or
|
||||||
|
/// `&'static tokio::runtime::Runtime`.
|
||||||
|
///
|
||||||
/// [tokio-runtime]: tokio::runtime::Runtime
|
/// [tokio-runtime]: tokio::runtime::Runtime
|
||||||
|
/// [`Runtime`]: crate::Runtime
|
||||||
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
|
#[cfg(not(all(target_os = "linux", feature = "io-uring")))]
|
||||||
pub fn with_tokio_rt<F>(runtime_factory: F) -> Arbiter
|
pub fn with_tokio_rt<F, R>(runtime_factory: F) -> Arbiter
|
||||||
where
|
where
|
||||||
F: FnOnce() -> tokio::runtime::Runtime + Send + 'static,
|
F: FnOnce() -> R + Send + 'static,
|
||||||
|
R: Into<crate::runtime::Runtime> + Send + 'static,
|
||||||
{
|
{
|
||||||
let sys = System::current();
|
let sys = System::current();
|
||||||
let system_id = sys.id();
|
let system_id = sys.id();
|
||||||
|
|
@ -125,7 +131,7 @@ impl Arbiter {
|
||||||
.spawn({
|
.spawn({
|
||||||
let tx = tx.clone();
|
let tx = tx.clone();
|
||||||
move || {
|
move || {
|
||||||
let rt = crate::runtime::Runtime::from(runtime_factory());
|
let rt = runtime_factory().into();
|
||||||
let hnd = ArbiterHandle::new(tx);
|
let hnd = ArbiterHandle::new(tx);
|
||||||
|
|
||||||
System::set_current(sys);
|
System::set_current(sys);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
use std::{future::Future, io};
|
use std::{future::Future, io, sync::Arc};
|
||||||
|
|
||||||
use tokio::task::{JoinHandle, LocalSet};
|
use tokio::task::{JoinHandle, LocalSet};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum RuntimeInner {
|
||||||
|
Owned(tokio::runtime::Runtime),
|
||||||
|
Shared(Arc<tokio::runtime::Runtime>),
|
||||||
|
Static(&'static tokio::runtime::Runtime),
|
||||||
|
}
|
||||||
|
|
||||||
/// A Tokio-based runtime proxy.
|
/// A Tokio-based runtime proxy.
|
||||||
///
|
///
|
||||||
/// All spawned futures will be executed on the current thread. Therefore, there is no `Send` bound
|
/// All spawned futures will be executed on the current thread. Therefore, there is no `Send` bound
|
||||||
|
|
@ -9,7 +16,7 @@ use tokio::task::{JoinHandle, LocalSet};
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Runtime {
|
pub struct Runtime {
|
||||||
local: LocalSet,
|
local: LocalSet,
|
||||||
rt: tokio::runtime::Runtime,
|
rt: RuntimeInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn default_tokio_runtime() -> io::Result<tokio::runtime::Runtime> {
|
pub(crate) fn default_tokio_runtime() -> io::Result<tokio::runtime::Runtime> {
|
||||||
|
|
@ -26,11 +33,19 @@ impl Runtime {
|
||||||
let rt = default_tokio_runtime()?;
|
let rt = default_tokio_runtime()?;
|
||||||
|
|
||||||
Ok(Runtime {
|
Ok(Runtime {
|
||||||
rt,
|
rt: RuntimeInner::Owned(rt),
|
||||||
local: LocalSet::new(),
|
local: LocalSet::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn tokio_runtime_ref(&self) -> &tokio::runtime::Runtime {
|
||||||
|
match &self.rt {
|
||||||
|
RuntimeInner::Owned(rt) => rt,
|
||||||
|
RuntimeInner::Shared(rt) => rt,
|
||||||
|
RuntimeInner::Static(rt) => rt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Offload a future onto the single-threaded runtime.
|
/// Offload a future onto the single-threaded runtime.
|
||||||
///
|
///
|
||||||
/// The returned join handle can be used to await the future's result.
|
/// The returned join handle can be used to await the future's result.
|
||||||
|
|
@ -114,7 +129,7 @@ impl Runtime {
|
||||||
/// of the Actix runtime. This is because Tokio is responsible for driving the Actix system,
|
/// of the Actix runtime. This is because Tokio is responsible for driving the Actix system,
|
||||||
/// and blocking tasks could delay or deadlock other tasks in run loop.
|
/// and blocking tasks could delay or deadlock other tasks in run loop.
|
||||||
pub fn tokio_runtime(&self) -> &tokio::runtime::Runtime {
|
pub fn tokio_runtime(&self) -> &tokio::runtime::Runtime {
|
||||||
&self.rt
|
self.tokio_runtime_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the provided future, blocking the current thread until the future completes.
|
/// Runs the provided future, blocking the current thread until the future completes.
|
||||||
|
|
@ -135,7 +150,7 @@ impl Runtime {
|
||||||
where
|
where
|
||||||
F: Future,
|
F: Future,
|
||||||
{
|
{
|
||||||
self.local.block_on(&self.rt, f)
|
self.local.block_on(self.tokio_runtime_ref(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +158,25 @@ impl From<tokio::runtime::Runtime> for Runtime {
|
||||||
fn from(rt: tokio::runtime::Runtime) -> Self {
|
fn from(rt: tokio::runtime::Runtime) -> Self {
|
||||||
Self {
|
Self {
|
||||||
local: LocalSet::new(),
|
local: LocalSet::new(),
|
||||||
rt,
|
rt: RuntimeInner::Owned(rt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Arc<tokio::runtime::Runtime>> for Runtime {
|
||||||
|
fn from(rt: Arc<tokio::runtime::Runtime>) -> Self {
|
||||||
|
Self {
|
||||||
|
local: LocalSet::new(),
|
||||||
|
rt: RuntimeInner::Shared(rt),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&'static tokio::runtime::Runtime> for Runtime {
|
||||||
|
fn from(rt: &'static tokio::runtime::Runtime) -> Self {
|
||||||
|
Self {
|
||||||
|
local: LocalSet::new(),
|
||||||
|
rt: RuntimeInner::Static(rt),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,15 +45,21 @@ impl System {
|
||||||
|
|
||||||
/// Create a new System using the [Tokio Runtime](tokio-runtime) returned from a closure.
|
/// Create a new System using the [Tokio Runtime](tokio-runtime) returned from a closure.
|
||||||
///
|
///
|
||||||
|
/// The closure may return any type that can be converted into [`Runtime`], such as
|
||||||
|
/// `tokio::runtime::Runtime`, `Arc<tokio::runtime::Runtime>`, or
|
||||||
|
/// `&'static tokio::runtime::Runtime`.
|
||||||
|
///
|
||||||
/// [tokio-runtime]: tokio::runtime::Runtime
|
/// [tokio-runtime]: tokio::runtime::Runtime
|
||||||
pub fn with_tokio_rt<F>(runtime_factory: F) -> SystemRunner
|
/// [`Runtime`]: crate::Runtime
|
||||||
|
pub fn with_tokio_rt<F, R>(runtime_factory: F) -> SystemRunner
|
||||||
where
|
where
|
||||||
F: FnOnce() -> tokio::runtime::Runtime,
|
F: FnOnce() -> R,
|
||||||
|
R: Into<crate::runtime::Runtime>,
|
||||||
{
|
{
|
||||||
let (stop_tx, stop_rx) = watch::channel(None);
|
let (stop_tx, stop_rx) = watch::channel(None);
|
||||||
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
|
let (sys_tx, sys_rx) = mpsc::unbounded_channel();
|
||||||
|
|
||||||
let rt = crate::runtime::Runtime::from(runtime_factory());
|
let rt = runtime_factory().into();
|
||||||
let sys_arbiter = rt.block_on(async { Arbiter::in_new_system() });
|
let sys_arbiter = rt.block_on(async { Arbiter::in_new_system() });
|
||||||
let system = System::construct(sys_tx, sys_arbiter.clone());
|
let system = System::construct(sys_tx, sys_arbiter.clone());
|
||||||
|
|
||||||
|
|
@ -85,9 +91,10 @@ impl System {
|
||||||
///
|
///
|
||||||
/// [tokio-runtime]: tokio::runtime::Runtime
|
/// [tokio-runtime]: tokio::runtime::Runtime
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn with_tokio_rt<F>(_: F) -> SystemRunner
|
pub fn with_tokio_rt<F, R>(_: F) -> SystemRunner
|
||||||
where
|
where
|
||||||
F: FnOnce() -> tokio::runtime::Runtime,
|
F: FnOnce() -> R,
|
||||||
|
R: Into<crate::runtime::Runtime>,
|
||||||
{
|
{
|
||||||
unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
|
unimplemented!("System::with_tokio_rt is not implemented for io-uring feature yet")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -298,6 +298,65 @@ fn new_system_with_tokio() {
|
||||||
assert_eq!(rx.recv().unwrap(), 42);
|
assert_eq!(rx.recv().unwrap(), 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "io-uring"))]
|
||||||
|
#[test]
|
||||||
|
fn new_system_with_shared_tokio_runtime() {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
let (tx, rx) = channel();
|
||||||
|
|
||||||
|
let rt = Arc::new(
|
||||||
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_io()
|
||||||
|
.enable_time()
|
||||||
|
.worker_threads(2)
|
||||||
|
.max_blocking_threads(2)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let res = System::with_tokio_rt({
|
||||||
|
let rt = rt.clone();
|
||||||
|
move || rt
|
||||||
|
})
|
||||||
|
.block_on(async {
|
||||||
|
actix_rt::time::sleep(Duration::from_millis(1)).await;
|
||||||
|
|
||||||
|
tokio::task::spawn(async move {
|
||||||
|
tx.send(7).unwrap();
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
321usize
|
||||||
|
});
|
||||||
|
|
||||||
|
assert_eq!(res, 321);
|
||||||
|
assert_eq!(rx.recv().unwrap(), 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "io-uring"))]
|
||||||
|
#[test]
|
||||||
|
fn new_system_with_static_tokio_runtime() {
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
static TOKIO: OnceLock<tokio::runtime::Runtime> = OnceLock::new();
|
||||||
|
|
||||||
|
let res = System::with_tokio_rt(|| -> &'static tokio::runtime::Runtime {
|
||||||
|
TOKIO.get_or_init(|| {
|
||||||
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_io()
|
||||||
|
.enable_time()
|
||||||
|
.worker_threads(1)
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.block_on(async { 7usize });
|
||||||
|
|
||||||
|
assert_eq!(res, 7);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "io-uring"))]
|
#[cfg(not(feature = "io-uring"))]
|
||||||
#[test]
|
#[test]
|
||||||
fn new_arbiter_with_tokio() {
|
fn new_arbiter_with_tokio() {
|
||||||
|
|
@ -331,6 +390,45 @@ fn new_arbiter_with_tokio() {
|
||||||
assert!(!counter.load(Ordering::SeqCst));
|
assert!(!counter.load(Ordering::SeqCst));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "io-uring"))]
|
||||||
|
#[test]
|
||||||
|
fn new_arbiter_with_shared_tokio_runtime() {
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = System::new();
|
||||||
|
|
||||||
|
let rt = Arc::new(
|
||||||
|
tokio::runtime::Builder::new_multi_thread()
|
||||||
|
.enable_all()
|
||||||
|
.worker_threads(2)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let arb = Arbiter::with_tokio_rt({
|
||||||
|
let rt = rt.clone();
|
||||||
|
move || rt
|
||||||
|
});
|
||||||
|
|
||||||
|
let flag = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
|
let flag1 = flag.clone();
|
||||||
|
let did_spawn = arb.spawn(async move {
|
||||||
|
actix_rt::time::sleep(Duration::from_millis(1)).await;
|
||||||
|
flag1.store(true, Ordering::SeqCst);
|
||||||
|
Arbiter::current().stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(did_spawn);
|
||||||
|
|
||||||
|
arb.join().unwrap();
|
||||||
|
|
||||||
|
assert!(flag.load(Ordering::SeqCst));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
#[should_panic]
|
||||||
fn no_system_current_panic() {
|
fn no_system_current_panic() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue