runtime: create reactor per worker (#660)

This commit is contained in:
Stjepan Glavina
2018-10-02 18:19:27 -07:00
committed by Carl Lerche
parent 886511c0a6
commit d35d0518f5
6 changed files with 60 additions and 52 deletions
+1
View File
@@ -54,6 +54,7 @@ appveyor = { repository = "carllerche/tokio", id = "s83yxhy9qeb58va7" }
[dependencies] [dependencies]
bytes = "0.4" bytes = "0.4"
num_cpus = "1.8.0"
tokio-codec = { version = "0.1.0", path = "tokio-codec" } tokio-codec = { version = "0.1.0", path = "tokio-codec" }
tokio-current-thread = { version = "0.1.3", path = "tokio-current-thread" } tokio-current-thread = { version = "0.1.3", path = "tokio-current-thread" }
tokio-io = { version = "0.1.6", path = "tokio-io" } tokio-io = { version = "0.1.6", path = "tokio-io" }
+1
View File
@@ -76,6 +76,7 @@ extern crate bytes;
#[macro_use] #[macro_use]
extern crate futures; extern crate futures;
extern crate mio; extern crate mio;
extern crate num_cpus;
extern crate tokio_current_thread; extern crate tokio_current_thread;
extern crate tokio_io; extern crate tokio_io;
extern crate tokio_executor; extern crate tokio_executor;
+40 -25
View File
@@ -3,11 +3,12 @@ use runtime::{Inner, Runtime};
use reactor::Reactor; use reactor::Reactor;
use std::io; use std::io;
use std::sync::Mutex;
use std::time::Duration; use std::time::Duration;
use num_cpus;
use tokio_reactor; use tokio_reactor;
use tokio_threadpool::Builder as ThreadPoolBuilder; use tokio_threadpool::Builder as ThreadPoolBuilder;
use tokio_threadpool::park::DefaultPark;
use tokio_timer::clock::{self, Clock}; use tokio_timer::clock::{self, Clock};
use tokio_timer::timer::{self, Timer}; use tokio_timer::timer::{self, Timer};
@@ -51,6 +52,9 @@ pub struct Builder {
/// Thread pool specific builder /// Thread pool specific builder
threadpool_builder: ThreadPoolBuilder, threadpool_builder: ThreadPoolBuilder,
/// The number of worker threads
core_threads: usize,
/// The clock to use /// The clock to use
clock: Clock, clock: Clock,
} }
@@ -61,11 +65,15 @@ impl Builder {
/// ///
/// Configuration methods can be chained on the return value. /// Configuration methods can be chained on the return value.
pub fn new() -> Builder { pub fn new() -> Builder {
let core_threads = num_cpus::get().max(1);
let mut threadpool_builder = ThreadPoolBuilder::new(); let mut threadpool_builder = ThreadPoolBuilder::new();
threadpool_builder.name_prefix("tokio-runtime-worker-"); threadpool_builder.name_prefix("tokio-runtime-worker-");
threadpool_builder.pool_size(core_threads);
Builder { Builder {
threadpool_builder, threadpool_builder,
core_threads,
clock: Clock::new(), clock: Clock::new(),
} }
} }
@@ -110,6 +118,7 @@ impl Builder {
/// # } /// # }
/// ``` /// ```
pub fn core_threads(&mut self, val: usize) -> &mut Self { pub fn core_threads(&mut self, val: usize) -> &mut Self {
self.core_threads = val;
self.threadpool_builder.pool_size(val); self.threadpool_builder.pool_size(val);
self self
} }
@@ -243,44 +252,50 @@ impl Builder {
/// # } /// # }
/// ``` /// ```
pub fn build(&mut self) -> io::Result<Runtime> { pub fn build(&mut self) -> io::Result<Runtime> {
use std::collections::HashMap; // TODO(stjepang): Once we remove the `threadpool_builder` method, remove this line too.
use std::sync::{Arc, Mutex}; self.threadpool_builder.pool_size(self.core_threads);
let mut reactor_handles = Vec::new();
let mut timer_handles = Vec::new();
let mut timers = Vec::new();
for _ in 0..self.core_threads {
// Create a new reactor.
let reactor = Reactor::new()?;
reactor_handles.push(reactor.handle());
// Create a new timer.
let timer = Timer::new_with_now(reactor, self.clock.clone());
timer_handles.push(timer.handle());
timers.push(Mutex::new(Some(timer)));
}
// Get a handle to the clock for the runtime. // Get a handle to the clock for the runtime.
let clock1 = self.clock.clone(); let clock = self.clock.clone();
let clock2 = clock1.clone();
let timers = Arc::new(Mutex::new(HashMap::<_, timer::Handle>::new())); // Get a handle to the first reactor.
let t1 = timers.clone(); let reactor = reactor_handles[0].clone();
// Spawn a reactor on a background thread.
let reactor = Reactor::new()?.background()?;
// Get a handle to the reactor.
let reactor_handle = reactor.handle().clone();
let pool = self.threadpool_builder let pool = self.threadpool_builder
.around_worker(move |w, enter| { .around_worker(move |w, enter| {
let timer_handle = t1.lock().unwrap() let index = w.id().to_usize();
.get(w.id()).unwrap()
.clone();
tokio_reactor::with_default(&reactor_handle, enter, |enter| { tokio_reactor::with_default(&reactor_handles[index], enter, |enter| {
clock::with_default(&clock1, enter, |enter| { clock::with_default(&clock, enter, |enter| {
timer::with_default(&timer_handle, enter, |_| { timer::with_default(&timer_handles[index], enter, |_| {
w.run(); w.run();
}); });
}) })
}); });
}) })
.custom_park(move |worker_id| { .custom_park(move |worker_id| {
// Create a new timer let index = worker_id.to_usize();
let timer = Timer::new_with_now(DefaultPark::new(), clock2.clone());
timers.lock().unwrap() timers[index]
.insert(worker_id.clone(), timer.handle()); .lock()
.unwrap()
timer .take()
.unwrap()
}) })
.build(); .build();
+7 -14
View File
@@ -121,7 +121,7 @@ pub use self::builder::Builder;
pub use self::shutdown::Shutdown; pub use self::shutdown::Shutdown;
pub use self::task_executor::TaskExecutor; pub use self::task_executor::TaskExecutor;
use reactor::{Background, Handle}; use reactor::Handle;
use std::io; use std::io;
@@ -152,8 +152,8 @@ pub struct Runtime {
#[derive(Debug)] #[derive(Debug)]
struct Inner { struct Inner {
/// Reactor running on a background thread. /// A handle to one of the per-worker reactors.
reactor: Background, reactor: Handle,
/// Task execution pool. /// Task execution pool.
pool: threadpool::ThreadPool, pool: threadpool::ThreadPool,
@@ -254,6 +254,7 @@ impl Runtime {
#[deprecated(since = "0.1.5", note = "use `reactor` instead")] #[deprecated(since = "0.1.5", note = "use `reactor` instead")]
#[doc(hidden)] #[doc(hidden)]
pub fn handle(&self) -> &Handle { pub fn handle(&self) -> &Handle {
#[allow(deprecated)]
self.reactor() self.reactor()
} }
@@ -275,8 +276,9 @@ impl Runtime {
/// ///
/// // use `reactor_handle` /// // use `reactor_handle`
/// ``` /// ```
#[deprecated(since = "0.1.11", note = "there is now a reactor per worker thread")]
pub fn reactor(&self) -> &Handle { pub fn reactor(&self) -> &Handle {
self.inner().reactor.handle() &self.inner().reactor
} }
/// Return a handle to the runtime's executor. /// Return a handle to the runtime's executor.
@@ -424,16 +426,7 @@ impl Runtime {
/// [mod]: index.html /// [mod]: index.html
pub fn shutdown_on_idle(mut self) -> Shutdown { pub fn shutdown_on_idle(mut self) -> Shutdown {
let inner = self.inner.take().unwrap(); let inner = self.inner.take().unwrap();
let inner = inner.pool.shutdown_on_idle();
let inner = Box::new({
let pool = inner.pool;
let reactor = inner.reactor;
pool.shutdown_on_idle().and_then(|_| {
reactor.shutdown_on_idle()
})
});
Shutdown { inner } Shutdown { inner }
} }
+3 -13
View File
@@ -1,4 +1,5 @@
use runtime::Inner; use runtime::Inner;
use tokio_threadpool as threadpool;
use std::fmt; use std::fmt;
@@ -6,23 +7,12 @@ use futures::{Future, Poll};
/// A future that resolves when the Tokio `Runtime` is shut down. /// A future that resolves when the Tokio `Runtime` is shut down.
pub struct Shutdown { pub struct Shutdown {
pub(super) inner: Box<Future<Item = (), Error = ()> + Send>, pub(super) inner: threadpool::Shutdown,
} }
impl Shutdown { impl Shutdown {
pub(super) fn shutdown_now(inner: Inner) -> Self { pub(super) fn shutdown_now(inner: Inner) -> Self {
let inner = Box::new({ let inner = inner.pool.shutdown_now();
let pool = inner.pool;
let reactor = inner.reactor;
pool.shutdown_now().and_then(|_| {
reactor.shutdown_now()
.then(|_| {
Ok(())
})
})
});
Shutdown { inner } Shutdown { inner }
} }
} }
+8
View File
@@ -892,4 +892,12 @@ impl WorkerId {
pub(crate) fn new(idx: usize) -> WorkerId { pub(crate) fn new(idx: usize) -> WorkerId {
WorkerId(idx) WorkerId(idx)
} }
/// Returns this identifier represented as an integer.
///
/// Worker identifiers in a single thread pool are guaranteed to correspond to integers in the
/// range `0..pool_size`.
pub fn to_usize(&self) -> usize {
self.0
}
} }