use std::io;
use tokio;
use futures01::Future;
#[derive(Debug)]
pub enum UninitializedExecutor {
Shared(tokio::runtime::TaskExecutor),
Unspawned,
}
impl UninitializedExecutor {
pub fn initialize(self) -> io::Result<Executor> {
self.init_with_name("event.loop")
}
pub fn init_with_name<T: Into<String>>(self, name: T) -> io::Result<Executor> {
match self {
UninitializedExecutor::Shared(executor) => Ok(Executor::Shared(executor)),
UninitializedExecutor::Unspawned => RpcEventLoop::with_name(Some(name.into())).map(Executor::Spawned),
}
}
}
#[derive(Debug)]
pub enum Executor {
Shared(tokio::runtime::TaskExecutor),
Spawned(RpcEventLoop),
}
impl Executor {
pub fn executor(&self) -> tokio::runtime::TaskExecutor {
match *self {
Executor::Shared(ref executor) => executor.clone(),
Executor::Spawned(ref eloop) => eloop.executor(),
}
}
pub fn spawn<F>(&self, future: F)
where
F: Future<Item = (), Error = ()> + Send + 'static,
{
self.executor().spawn(future)
}
pub fn close(self) {
if let Executor::Spawned(eloop) = self {
eloop.close()
}
}
pub fn wait(self) {
if let Executor::Spawned(eloop) = self {
let _ = eloop.wait();
}
}
}
#[derive(Debug)]
pub struct RpcEventLoop {
executor: tokio::runtime::TaskExecutor,
close: Option<futures01::Complete<()>>,
handle: Option<tokio::runtime::Shutdown>,
}
impl Drop for RpcEventLoop {
fn drop(&mut self) {
self.close.take().map(|v| v.send(()));
}
}
impl RpcEventLoop {
pub fn spawn() -> io::Result<Self> {
RpcEventLoop::with_name(None)
}
pub fn with_name(name: Option<String>) -> io::Result<Self> {
let (stop, stopped) = futures01::oneshot();
let mut tb = tokio::runtime::Builder::new();
tb.core_threads(1);
if let Some(name) = name {
tb.name_prefix(name);
}
let mut runtime = tb.build()?;
let executor = runtime.executor();
let terminate = futures01::empty().select(stopped).map(|_| ()).map_err(|_| ());
runtime.spawn(terminate);
let handle = runtime.shutdown_on_idle();
Ok(RpcEventLoop {
executor,
close: Some(stop),
handle: Some(handle),
})
}
pub fn executor(&self) -> tokio::runtime::TaskExecutor {
self.executor.clone()
}
pub fn wait(mut self) -> Result<(), ()> {
self.handle.take().ok_or(())?.wait()
}
pub fn close(mut self) {
let _ = self
.close
.take()
.expect("Close is always set before self is consumed.")
.send(())
.map_err(|e| {
warn!("Event Loop is already finished. {:?}", e);
});
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn make_sure_rpc_event_loop_is_send_and_sync() {
fn is_send_and_sync<T: Send + Sync>() {}
is_send_and_sync::<RpcEventLoop>();
}
}