use super::{compat, idle, Builder};
use tokio_02::{
runtime::Handle as Handle02,
task::{JoinHandle, LocalSet},
};
use tokio_executor_01 as executor_01;
use tokio_reactor_01 as reactor_01;
use tokio_timer_02 as timer_02;
use futures_01::future::Future as Future01;
use futures_util::{compat::Future01CompatExt, future::FutureExt};
use std::cell::RefCell;
use std::error::Error;
use std::fmt;
use std::future::Future;
use std::io;
#[cfg_attr(docsrs, doc(cfg(feature = "rt-current-thread")))]
#[derive(Debug)]
pub struct Runtime {
inner: tokio_02::runtime::Runtime,
local: LocalSet,
idle: idle::Idle,
idle_rx: idle::Rx,
compat: compat::Background,
}
#[cfg_attr(docsrs, doc(cfg(feature = "rt-current-thread")))]
#[derive(Debug, Clone)]
pub struct Handle {
inner: Handle02,
idle: idle::Idle,
}
thread_local! {
static CURRENT_IDLE: RefCell<Option<idle::Idle>> = RefCell::new(None);
}
impl Handle {
pub fn spawn<F>(&self, future: F) -> Result<(), executor_01::SpawnError>
where
F: Future01<Item = (), Error = ()> + Send + 'static,
{
let future = future.compat().map(|_| ());
self.spawn_std(future)
}
pub fn spawn_std<F>(&self, future: F) -> Result<(), executor_01::SpawnError>
where
F: Future<Output = ()> + Send + 'static,
{
let idle = self.idle.reserve();
self.inner.spawn(idle.with(future));
Ok(())
}
pub fn spawn_handle<F>(&self, future: F) -> JoinHandle<Result<F::Item, F::Error>>
where
F: Future01 + Send + 'static,
F::Item: Send + 'static,
F::Error: Send + 'static,
{
let future = Box::pin(future.compat());
self.spawn_handle_std(future)
}
pub fn spawn_handle_std<F>(&self, future: F) -> JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send + 'static,
{
self.inner.spawn(future)
}
pub fn status(&self) -> Result<(), executor_01::SpawnError> {
Ok(())
}
}
#[derive(Debug)]
#[cfg_attr(docsrs, doc(cfg(feature = "rt-current-thread")))]
pub struct RunError {
inner: (),
}
impl fmt::Display for RunError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "RunError")
}
}
impl Error for RunError {}
impl Runtime {
pub fn new() -> io::Result<Runtime> {
Builder::new().build()
}
pub(super) fn new2(
inner: tokio_02::runtime::Runtime,
idle: idle::Idle,
idle_rx: idle::Rx,
compat: compat::Background,
) -> Self {
Self {
inner,
idle,
idle_rx,
compat,
local: tokio_02::task::LocalSet::new(),
}
}
pub fn handle(&self) -> Handle {
let inner = self.inner.handle().clone();
let idle = self.idle.clone();
Handle { inner, idle }
}
pub fn spawn<F>(&mut self, future: F) -> &mut Self
where
F: Future01<Item = (), Error = ()> + 'static,
{
let future = future.compat().map(|_| ());
self.spawn_std(future)
}
pub fn spawn_std<F>(&mut self, future: F) -> &mut Self
where
F: Future<Output = ()> + 'static,
{
let idle = self.idle.reserve();
self.local.spawn_local(idle.with(future));
self
}
pub fn block_on<F>(&mut self, f: F) -> Result<F::Item, F::Error>
where
F: Future01,
{
self.block_on_std(f.compat())
}
pub fn block_on_std<F>(&mut self, f: F) -> F::Output
where
F: Future,
{
let handle = self.inner.handle().clone();
let Runtime {
ref mut local,
ref mut inner,
ref compat,
ref idle,
..
} = *self;
let mut spawner = compat::CompatSpawner::new(handle, &idle);
let mut enter = executor_01::enter().unwrap();
let _reactor = reactor_01::set_default(compat.reactor());
let _timer = timer_02::timer::set_default(compat.timer());
let track = self.idle.reserve();
executor_01::with_default(&mut spawner, &mut enter, |_enter| {
Self::with_idle(idle, move || local.block_on(inner, track.with(f)))
})
}
pub fn run(&mut self) -> Result<(), RunError> {
let handle = self.inner.handle().clone();
let Runtime {
ref mut local,
ref mut inner,
ref compat,
ref mut idle_rx,
ref idle,
..
} = *self;
let mut spawner = compat::CompatSpawner::new(handle, &idle);
let mut enter = executor_01::enter().unwrap();
let _reactor = reactor_01::set_default(compat.reactor());
let _timer = timer_02::timer::set_default(compat.timer());
executor_01::with_default(&mut spawner, &mut enter, |_enter| {
Self::with_idle(idle, move || local.block_on(inner, idle_rx.idle()))
});
Ok(())
}
fn with_idle<T>(idle: &idle::Idle, f: impl FnOnce() -> T) -> T {
struct Reset<'a>(&'a RefCell<Option<idle::Idle>>);
impl<'a> Drop for Reset<'a> {
fn drop(&mut self) {
self.0.borrow_mut().take();
}
}
let idle = idle.clone();
CURRENT_IDLE.with(move |c| {
let was_empty = c.borrow_mut().replace(idle).is_none();
assert!(was_empty, "entered current_thread runtime twice!");
let _reset = Reset(c);
f()
})
}
pub(super) fn reserve_idle() -> Option<idle::Track> {
CURRENT_IDLE
.try_with(|c| {
let idle = c.borrow();
Some(idle.as_ref()?.reserve())
})
.ok()
.and_then(|x| x)
}
pub(super) fn is_current() -> bool {
CURRENT_IDLE
.try_with(|c| c.borrow().is_some())
.unwrap_or(false)
}
pub fn enter<F, R>(&self, f: F) -> R
where
F: FnOnce() -> R,
{
let handle = self.inner.handle().clone();
let Runtime {
ref inner,
ref compat,
ref idle,
..
} = *self;
let mut spawner = compat::CompatSpawner::new(handle, &idle);
let mut enter = executor_01::enter().unwrap();
let _reactor = reactor_01::set_default(compat.reactor());
let _timer = timer_02::timer::set_default(compat.timer());
executor_01::with_default(&mut spawner, &mut enter, |_enter| {
Self::with_idle(idle, || inner.enter(f))
})
}
}