Skip to content

Commit 911a0ef

Browse files
authored
rt: internally split Handle into two structs (#4629)
Previously, `runtime::Handle` was a single struct composed of the internal handles for each runtime component. This patch splits the `Handle` struct into a `HandleInner` which contains everything **except** the task scheduler handle. Now, `HandleInner` is passed to the task scheduler during creation and the task scheduler is responsible for storing it. `Handle` only needs to hold the scheduler handle and can access the rest of the component handles by querying the task scheduler. The motivation for this change is it now enables the multi-threaded scheduler to have direct access to the blocking spawner handle. Previously, when spawning a new thread, the multi-threaded scheduler had to access the blocking spawner by accessing a thread-local variable. Now, in theory, the multi-threaded scheduler can use `HandleInner` directly. However, this change hasn't been done in this PR yet. Also, now the `Handle` struct is much smaller. This change is intended to make it easier for the multi-threaded scheduler to shutdown idle threads and respawn them on demand.
1 parent d590a36 commit 911a0ef

File tree

16 files changed

+233
-164
lines changed

16 files changed

+233
-164
lines changed

tokio/src/runtime/basic_scheduler.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::park::{Park, Unpark};
55
use crate::runtime::context::EnterGuard;
66
use crate::runtime::driver::Driver;
77
use crate::runtime::task::{self, JoinHandle, OwnedTasks, Schedule, Task};
8-
use crate::runtime::Callback;
8+
use crate::runtime::{Callback, HandleInner};
99
use crate::runtime::{MetricsBatch, SchedulerMetrics, WorkerMetrics};
1010
use crate::sync::notify::Notify;
1111
use crate::util::atomic_cell::AtomicCell;
@@ -78,6 +78,9 @@ struct Shared {
7878
/// Indicates whether the blocked on thread was woken.
7979
woken: AtomicBool,
8080

81+
/// Handle to I/O driver, timer, blocking pool, ...
82+
handle_inner: HandleInner,
83+
8184
/// Callback for a worker parking itself
8285
before_park: Option<Callback>,
8386

@@ -119,6 +122,7 @@ scoped_thread_local!(static CURRENT: Context);
119122
impl BasicScheduler {
120123
pub(crate) fn new(
121124
driver: Driver,
125+
handle_inner: HandleInner,
122126
before_park: Option<Callback>,
123127
after_unpark: Option<Callback>,
124128
) -> BasicScheduler {
@@ -130,6 +134,7 @@ impl BasicScheduler {
130134
owned: OwnedTasks::new(),
131135
unpark,
132136
woken: AtomicBool::new(false),
137+
handle_inner,
133138
before_park,
134139
after_unpark,
135140
scheduler_metrics: SchedulerMetrics::new(),
@@ -397,6 +402,10 @@ impl Spawner {
397402
pub(crate) fn reset_woken(&self) -> bool {
398403
self.shared.woken.swap(false, AcqRel)
399404
}
405+
406+
pub(crate) fn as_handle_inner(&self) -> &HandleInner {
407+
&self.shared.handle_inner
408+
}
400409
}
401410

402411
cfg_metrics! {

tokio/src/runtime/blocking/mod.rs

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,3 @@ use crate::runtime::Builder;
2121
pub(crate) fn create_blocking_pool(builder: &Builder, thread_cap: usize) -> BlockingPool {
2222
BlockingPool::new(builder, thread_cap)
2323
}
24-
25-
/*
26-
cfg_not_blocking_impl! {
27-
use crate::runtime::Builder;
28-
use std::time::Duration;
29-
30-
#[derive(Debug, Clone)]
31-
pub(crate) struct BlockingPool {}
32-
33-
pub(crate) use BlockingPool as Spawner;
34-
35-
pub(crate) fn create_blocking_pool(_builder: &Builder, _thread_cap: usize) -> BlockingPool {
36-
BlockingPool {}
37-
}
38-
39-
impl BlockingPool {
40-
pub(crate) fn spawner(&self) -> &BlockingPool {
41-
self
42-
}
43-
44-
pub(crate) fn shutdown(&mut self, _duration: Option<Duration>) {
45-
}
46-
}
47-
}
48-
*/

tokio/src/runtime/blocking/pool.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::runtime::blocking::shutdown;
77
use crate::runtime::builder::ThreadNameFn;
88
use crate::runtime::context;
99
use crate::runtime::task::{self, JoinHandle};
10-
use crate::runtime::{Builder, Callback, Handle};
10+
use crate::runtime::{Builder, Callback, ToHandle};
1111

1212
use std::collections::{HashMap, VecDeque};
1313
use std::fmt;
@@ -129,7 +129,7 @@ cfg_fs! {
129129
R: Send + 'static,
130130
{
131131
let rt = context::current();
132-
rt.spawn_mandatory_blocking(func)
132+
rt.as_inner().spawn_mandatory_blocking(&rt, func)
133133
}
134134
}
135135

@@ -220,7 +220,7 @@ impl fmt::Debug for BlockingPool {
220220
// ===== impl Spawner =====
221221

222222
impl Spawner {
223-
pub(crate) fn spawn(&self, task: Task, rt: &Handle) -> Result<(), ()> {
223+
pub(crate) fn spawn(&self, task: Task, rt: &dyn ToHandle) -> Result<(), ()> {
224224
let mut shared = self.inner.shared.lock();
225225

226226
if shared.shutdown {
@@ -283,7 +283,7 @@ impl Spawner {
283283
fn spawn_thread(
284284
&self,
285285
shutdown_tx: shutdown::Sender,
286-
rt: &Handle,
286+
rt: &dyn ToHandle,
287287
id: usize,
288288
) -> std::io::Result<thread::JoinHandle<()>> {
289289
let mut builder = thread::Builder::new().name((self.inner.thread_name)());
@@ -292,12 +292,12 @@ impl Spawner {
292292
builder = builder.stack_size(stack_size);
293293
}
294294

295-
let rt = rt.clone();
295+
let rt = rt.to_handle();
296296

297297
builder.spawn(move || {
298298
// Only the reference should be moved into the closure
299299
let _enter = crate::runtime::context::enter(rt.clone());
300-
rt.blocking_spawner.inner.run(id);
300+
rt.as_inner().blocking_spawner.inner.run(id);
301301
drop(shutdown_tx);
302302
})
303303
}

tokio/src/runtime/builder.rs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -555,32 +555,37 @@ impl Builder {
555555
}
556556

557557
fn build_basic_runtime(&mut self) -> io::Result<Runtime> {
558-
use crate::runtime::{BasicScheduler, Kind};
558+
use crate::runtime::{BasicScheduler, HandleInner, Kind};
559559

560560
let (driver, resources) = driver::Driver::new(self.get_cfg())?;
561561

562+
// Blocking pool
563+
let blocking_pool = blocking::create_blocking_pool(self, self.max_blocking_threads);
564+
let blocking_spawner = blocking_pool.spawner().clone();
565+
566+
let handle_inner = HandleInner {
567+
io_handle: resources.io_handle,
568+
time_handle: resources.time_handle,
569+
signal_handle: resources.signal_handle,
570+
clock: resources.clock,
571+
blocking_spawner,
572+
};
573+
562574
// And now put a single-threaded scheduler on top of the timer. When
563575
// there are no futures ready to do something, it'll let the timer or
564576
// the reactor to generate some new stimuli for the futures to continue
565577
// in their life.
566-
let scheduler =
567-
BasicScheduler::new(driver, self.before_park.clone(), self.after_unpark.clone());
578+
let scheduler = BasicScheduler::new(
579+
driver,
580+
handle_inner,
581+
self.before_park.clone(),
582+
self.after_unpark.clone(),
583+
);
568584
let spawner = Spawner::Basic(scheduler.spawner().clone());
569585

570-
// Blocking pool
571-
let blocking_pool = blocking::create_blocking_pool(self, self.max_blocking_threads);
572-
let blocking_spawner = blocking_pool.spawner().clone();
573-
574586
Ok(Runtime {
575587
kind: Kind::CurrentThread(scheduler),
576-
handle: Handle {
577-
spawner,
578-
io_handle: resources.io_handle,
579-
time_handle: resources.time_handle,
580-
signal_handle: resources.signal_handle,
581-
clock: resources.clock,
582-
blocking_spawner,
583-
},
588+
handle: Handle { spawner },
584589
blocking_pool,
585590
})
586591
}
@@ -662,30 +667,32 @@ cfg_rt_multi_thread! {
662667
impl Builder {
663668
fn build_threaded_runtime(&mut self) -> io::Result<Runtime> {
664669
use crate::loom::sys::num_cpus;
665-
use crate::runtime::{Kind, ThreadPool};
666-
use crate::runtime::park::Parker;
670+
use crate::runtime::{Kind, HandleInner, ThreadPool};
667671

668672
let core_threads = self.worker_threads.unwrap_or_else(num_cpus);
669673

670674
let (driver, resources) = driver::Driver::new(self.get_cfg())?;
671675

672-
let (scheduler, launch) = ThreadPool::new(core_threads, Parker::new(driver), self.before_park.clone(), self.after_unpark.clone());
673-
let spawner = Spawner::ThreadPool(scheduler.spawner().clone());
674-
675676
// Create the blocking pool
676677
let blocking_pool = blocking::create_blocking_pool(self, self.max_blocking_threads + core_threads);
677678
let blocking_spawner = blocking_pool.spawner().clone();
678679

679-
// Create the runtime handle
680-
let handle = Handle {
681-
spawner,
680+
let handle_inner = HandleInner {
682681
io_handle: resources.io_handle,
683682
time_handle: resources.time_handle,
684683
signal_handle: resources.signal_handle,
685684
clock: resources.clock,
686685
blocking_spawner,
687686
};
688687

688+
let (scheduler, launch) = ThreadPool::new(core_threads, driver, handle_inner, self.before_park.clone(), self.after_unpark.clone());
689+
let spawner = Spawner::ThreadPool(scheduler.spawner().clone());
690+
691+
// Create the runtime handle
692+
let handle = Handle {
693+
spawner,
694+
};
695+
689696
// Spawn the thread pool workers
690697
let _enter = crate::runtime::context::enter(handle.clone());
691698
launch.launch();

tokio/src/runtime/context.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ cfg_io_driver! {
2626
pub(crate) fn io_handle() -> crate::runtime::driver::IoHandle {
2727
match CONTEXT.try_with(|ctx| {
2828
let ctx = ctx.borrow();
29-
ctx.as_ref().expect(crate::util::error::CONTEXT_MISSING_ERROR).io_handle.clone()
29+
ctx.as_ref().expect(crate::util::error::CONTEXT_MISSING_ERROR).as_inner().io_handle.clone()
3030
}) {
3131
Ok(io_handle) => io_handle,
3232
Err(_) => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR),
@@ -39,7 +39,7 @@ cfg_signal_internal! {
3939
pub(crate) fn signal_handle() -> crate::runtime::driver::SignalHandle {
4040
match CONTEXT.try_with(|ctx| {
4141
let ctx = ctx.borrow();
42-
ctx.as_ref().expect(crate::util::error::CONTEXT_MISSING_ERROR).signal_handle.clone()
42+
ctx.as_ref().expect(crate::util::error::CONTEXT_MISSING_ERROR).as_inner().signal_handle.clone()
4343
}) {
4444
Ok(signal_handle) => signal_handle,
4545
Err(_) => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR),
@@ -51,7 +51,7 @@ cfg_time! {
5151
pub(crate) fn time_handle() -> crate::runtime::driver::TimeHandle {
5252
match CONTEXT.try_with(|ctx| {
5353
let ctx = ctx.borrow();
54-
ctx.as_ref().expect(crate::util::error::CONTEXT_MISSING_ERROR).time_handle.clone()
54+
ctx.as_ref().expect(crate::util::error::CONTEXT_MISSING_ERROR).as_inner().time_handle.clone()
5555
}) {
5656
Ok(time_handle) => time_handle,
5757
Err(_) => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR),
@@ -60,7 +60,7 @@ cfg_time! {
6060

6161
cfg_test_util! {
6262
pub(crate) fn clock() -> Option<crate::runtime::driver::Clock> {
63-
match CONTEXT.try_with(|ctx| (*ctx.borrow()).as_ref().map(|ctx| ctx.clock.clone())) {
63+
match CONTEXT.try_with(|ctx| (*ctx.borrow()).as_ref().map(|ctx| ctx.as_inner().clock.clone())) {
6464
Ok(clock) => clock,
6565
Err(_) => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR),
6666
}

0 commit comments

Comments
 (0)