Skip to main content

tokio/runtime/scheduler/multi_thread/
worker.rs

1//! A scheduler is initialized with a fixed number of workers. Each worker is
2//! driven by a thread. Each worker has a "core" which contains data such as the
3//! run queue and other state. When `block_in_place` is called, the worker's
4//! "core" is handed off to a new thread allowing the scheduler to continue to
5//! make progress while the originating thread blocks.
6//!
7//! # Shutdown
8//!
9//! Shutting down the runtime involves the following steps:
10//!
11//!  1. The Shared::close method is called. This closes the inject queue and
12//!     `OwnedTasks` instance and wakes up all worker threads.
13//!
14//!  2. Each worker thread observes the close signal next time it runs
15//!     Core::maintenance by checking whether the inject queue is closed.
16//!     The `Core::is_shutdown` flag is set to true.
17//!
18//!  3. The worker thread calls `pre_shutdown` in parallel. Here, the worker
19//!     will keep removing tasks from `OwnedTasks` until it is empty. No new
20//!     tasks can be pushed to the `OwnedTasks` during or after this step as it
21//!     was closed in step 1.
22//!
23//!  5. The workers call Shared::shutdown to enter the single-threaded phase of
24//!     shutdown. These calls will push their core to `Shared::shutdown_cores`,
25//!     and the last thread to push its core will finish the shutdown procedure.
26//!
27//!  6. The local run queue of each core is emptied, then the inject queue is
28//!     emptied.
29//!
30//! At this point, shutdown has completed. It is not possible for any of the
31//! collections to contain any tasks at this point, as each collection was
32//! closed first, then emptied afterwards.
33//!
34//! ## Spawns during shutdown
35//!
36//! When spawning tasks during shutdown, there are two cases:
37//!
38//!  * The spawner observes the `OwnedTasks` being open, and the inject queue is
39//!    closed.
40//!  * The spawner observes the `OwnedTasks` being closed and doesn't check the
41//!    inject queue.
42//!
43//! The first case can only happen if the `OwnedTasks::bind` call happens before
44//! or during step 1 of shutdown. In this case, the runtime will clean up the
45//! task in step 3 of shutdown.
46//!
47//! In the latter case, the task was not spawned and the task is immediately
48//! cancelled by the spawner.
49//!
50//! The correctness of shutdown requires both the inject queue and `OwnedTasks`
51//! collection to have a closed bit. With a close bit on only the inject queue,
52//! spawning could run in to a situation where a task is successfully bound long
53//! after the runtime has shut down. With a close bit on only the `OwnedTasks`,
54//! the first spawning situation could result in the notification being pushed
55//! to the inject queue after step 6 of shutdown, which would leave a task in
56//! the inject queue indefinitely. This would be a ref-count cycle and a memory
57//! leak.
58
59use crate::loom::sync::{Arc, Mutex};
60use crate::runtime;
61use crate::runtime::scheduler::multi_thread::{
62    idle, park, queue, Counters, Handle, Idle, Overflow, Parker, Stats, TraceStatus, Unparker,
63};
64use crate::runtime::scheduler::{inject, Defer, Lock};
65use crate::runtime::task::OwnedTasks;
66use crate::runtime::{
67    blocking, driver, scheduler, task, Config, SchedulerMetrics, TimerFlavor, WorkerMetrics,
68};
69use crate::runtime::{context, TaskHooks};
70use crate::task::coop;
71use crate::util::atomic_cell::AtomicCell;
72use crate::util::rand::{FastRand, RngSeedGenerator};
73
74use std::cell::RefCell;
75use std::task::Waker;
76use std::thread;
77use std::time::{Duration, Instant};
78
79mod metrics;
80
81cfg_taskdump! {
82    mod taskdump;
83}
84
85cfg_not_taskdump! {
86    mod taskdump_mock;
87}
88
89#[cfg(all(tokio_unstable, feature = "time"))]
90use crate::loom::sync::atomic::AtomicBool;
91
92#[cfg(all(tokio_unstable, feature = "time"))]
93use crate::runtime::time_alt;
94
95use crate::runtime::metrics::ScheduleLatencyInstant;
96#[cfg(all(tokio_unstable, feature = "time"))]
97use crate::runtime::scheduler::util;
98
99/// A scheduler worker
100pub(super) struct Worker {
101    /// Reference to scheduler's handle
102    handle: Arc<Handle>,
103
104    /// Index holding this worker's remote state
105    index: usize,
106
107    /// Used to hand-off a worker's core to another thread.
108    core: AtomicCell<Core>,
109}
110
111/// Core data
112struct Core {
113    /// Used to schedule bookkeeping tasks every so often.
114    tick: u32,
115
116    /// When a task is scheduled from a worker, it is stored in this slot. The
117    /// worker will check this slot for a task **before** checking the run
118    /// queue. This effectively results in the **last** scheduled task to be run
119    /// next (LIFO). This is an optimization for improving locality which
120    /// benefits message passing patterns and helps to reduce latency.
121    lifo_slot: Option<Notified>,
122
123    /// When `true`, locally scheduled tasks go to the LIFO slot. When `false`,
124    /// they go to the back of the `run_queue`.
125    lifo_enabled: bool,
126
127    /// The worker-local run queue.
128    run_queue: queue::Local<Arc<Handle>>,
129
130    #[cfg(all(tokio_unstable, feature = "time"))]
131    time_context: time_alt::LocalContext,
132
133    /// True if the worker is currently searching for more work. Searching
134    /// involves attempting to steal from other workers.
135    is_searching: bool,
136
137    /// True if the scheduler is being shutdown
138    is_shutdown: bool,
139
140    /// True if the scheduler is being traced
141    is_traced: bool,
142
143    /// Whether or not the worker has just returned from a park in which we
144    /// parked on the I/O driver.
145    had_driver: park::HadDriver,
146
147    /// If `true`, the worker should eagerly notify another worker when polling
148    /// the first task after returning from a park in which it parked on the I/O
149    /// or time driver.
150    enable_eager_driver_handoff: bool,
151
152    /// Parker
153    ///
154    /// Stored in an `Option` as the parker is added / removed to make the
155    /// borrow checker happy.
156    park: Option<Parker>,
157
158    /// Per-worker runtime stats
159    stats: Stats,
160
161    /// How often to check the global queue
162    global_queue_interval: u32,
163
164    /// Fast random number generator.
165    rand: FastRand,
166}
167
168/// State shared across all workers
169pub(crate) struct Shared {
170    /// Per-worker remote state. All other workers have access to this and is
171    /// how they communicate between each other.
172    remotes: Box<[Remote]>,
173
174    /// Global task queue used for:
175    ///  1. Submit work to the scheduler while **not** currently on a worker thread.
176    ///  2. Submit work to the scheduler when a worker run queue is saturated
177    pub(super) inject: inject::Shared<Arc<Handle>>,
178
179    /// Coordinates idle workers
180    idle: Idle,
181
182    /// Collection of all active tasks spawned onto this executor.
183    pub(crate) owned: OwnedTasks<Arc<Handle>>,
184
185    /// Data synchronized by the scheduler mutex
186    pub(super) synced: Mutex<Synced>,
187
188    /// Cores that have observed the shutdown signal
189    ///
190    /// The core is **not** placed back in the worker to avoid it from being
191    /// stolen by a thread that was spawned as part of `block_in_place`.
192    #[allow(clippy::vec_box)] // we're moving an already-boxed value
193    shutdown_cores: Mutex<Vec<Box<Core>>>,
194
195    /// The number of cores that have observed the trace signal.
196    pub(super) trace_status: TraceStatus,
197
198    /// Scheduler configuration options
199    config: Config,
200
201    /// Collects metrics from the runtime.
202    pub(super) scheduler_metrics: SchedulerMetrics,
203
204    pub(super) worker_metrics: Box<[WorkerMetrics]>,
205
206    /// Startup time of this scheduler.
207    ///
208    /// This instant is used as the basis of task `scheduled_at` measurements.
209    started_at: Option<Instant>,
210
211    /// Only held to trigger some code on drop. This is used to get internal
212    /// runtime metrics that can be useful when doing performance
213    /// investigations. This does nothing (empty struct, no drop impl) unless
214    /// the `tokio_internal_mt_counters` `cfg` flag is set.
215    _counters: Counters,
216}
217
218/// Data synchronized by the scheduler mutex
219pub(crate) struct Synced {
220    /// Synchronized state for `Idle`.
221    pub(super) idle: idle::Synced,
222
223    /// Synchronized state for `Inject`.
224    pub(crate) inject: inject::Synced,
225
226    #[cfg(all(tokio_unstable, feature = "time"))]
227    /// Timers pending to be registered.
228    /// This is used to register a timer but the [`Core`]
229    /// is not available in the current thread.
230    inject_timers: Vec<time_alt::EntryHandle>,
231}
232
233/// Used to communicate with a worker from other threads.
234struct Remote {
235    /// Steals tasks from this worker.
236    pub(super) steal: queue::Steal<Arc<Handle>>,
237
238    /// Unparks the associated worker thread
239    unpark: Unparker,
240}
241
242/// Thread-local context
243pub(crate) struct Context {
244    /// Worker
245    worker: Arc<Worker>,
246
247    /// Core data
248    core: RefCell<Option<Box<Core>>>,
249
250    /// Tasks to wake after resource drivers are polled. This is mostly to
251    /// handle yielded tasks.
252    pub(crate) defer: Defer,
253}
254
255/// Starts the workers
256pub(crate) struct Launch(Vec<Arc<Worker>>);
257
258/// Running a task may consume the core. If the core is still available when
259/// running the task completes, it is returned. Otherwise, the worker will need
260/// to stop processing.
261type RunResult = Result<Box<Core>, ()>;
262
263/// A notified task handle
264type Notified = task::Notified<Arc<Handle>>;
265
266/// Value picked out of thin-air. Running the LIFO slot a handful of times
267/// seems sufficient to benefit from locality. More than 3 times probably is
268/// over-weighting. The value can be tuned in the future with data that shows
269/// improvements.
270const MAX_LIFO_POLLS_PER_TICK: usize = 3;
271
272#[allow(clippy::too_many_arguments)]
273pub(super) fn create(
274    size: usize,
275    park: Parker,
276    driver_handle: driver::Handle,
277    blocking_spawner: blocking::Spawner,
278    seed_generator: RngSeedGenerator,
279    config: Config,
280    timer_flavor: TimerFlavor,
281    name: Option<String>,
282) -> (Arc<Handle>, Launch) {
283    let mut cores = Vec::with_capacity(size);
284    let mut remotes = Vec::with_capacity(size);
285    let mut worker_metrics = Vec::with_capacity(size);
286
287    // Create the local queues
288    for _ in 0..size {
289        let (steal, run_queue) = queue::local();
290
291        let park = park.clone();
292        let unpark = park.unpark();
293        let metrics = WorkerMetrics::from_config(&config);
294        let stats = Stats::new(&metrics);
295
296        cores.push(Box::new(Core {
297            tick: 0,
298            lifo_slot: None,
299            lifo_enabled: !config.disable_lifo_slot,
300            run_queue,
301            #[cfg(all(tokio_unstable, feature = "time"))]
302            time_context: time_alt::LocalContext::new(),
303            is_searching: false,
304            is_shutdown: false,
305            is_traced: false,
306            enable_eager_driver_handoff: config.enable_eager_driver_handoff,
307            had_driver: park::HadDriver::No,
308            park: Some(park),
309            global_queue_interval: stats.tuned_global_queue_interval(&config),
310            stats,
311            rand: FastRand::from_seed(config.seed_generator.next_seed()),
312        }));
313
314        remotes.push(Remote { steal, unpark });
315        worker_metrics.push(metrics);
316    }
317
318    let (idle, idle_synced) = Idle::new(size);
319    let (inject, inject_synced) = inject::Shared::new();
320    let started_at = config
321        .metrics_schedule_latency_histogram
322        .as_ref()
323        .map(|_| Instant::now());
324
325    let remotes_len = remotes.len();
326    let handle = Arc::new(Handle {
327        name,
328        task_hooks: TaskHooks::from_config(&config),
329        shared: Shared {
330            remotes: remotes.into_boxed_slice(),
331            inject,
332            idle,
333            owned: OwnedTasks::new(size),
334            synced: Mutex::new(Synced {
335                idle: idle_synced,
336                inject: inject_synced,
337                #[cfg(all(tokio_unstable, feature = "time"))]
338                inject_timers: Vec::new(),
339            }),
340            shutdown_cores: Mutex::new(vec![]),
341            trace_status: TraceStatus::new(remotes_len),
342            config,
343            scheduler_metrics: SchedulerMetrics::new(),
344            worker_metrics: worker_metrics.into_boxed_slice(),
345            started_at,
346            _counters: Counters,
347        },
348        driver: driver_handle,
349        blocking_spawner,
350        seed_generator,
351        timer_flavor,
352        #[cfg(all(tokio_unstable, feature = "time"))]
353        is_shutdown: AtomicBool::new(false),
354    });
355
356    let mut launch = Launch(vec![]);
357
358    for (index, core) in cores.drain(..).enumerate() {
359        launch.0.push(Arc::new(Worker {
360            handle: handle.clone(),
361            index,
362            core: AtomicCell::new(Some(core)),
363        }));
364    }
365
366    (handle, launch)
367}
368
369#[track_caller]
370pub(crate) fn block_in_place<F, R>(f: F) -> R
371where
372    F: FnOnce() -> R,
373{
374    // Try to steal the worker core back
375    struct Reset {
376        take_core: bool,
377        budget: coop::Budget,
378    }
379
380    impl Drop for Reset {
381        fn drop(&mut self) {
382            with_current(|maybe_cx| {
383                if let Some(cx) = maybe_cx {
384                    if self.take_core {
385                        let core = cx.worker.core.take();
386
387                        if core.is_some() {
388                            cx.worker.handle.shared.worker_metrics[cx.worker.index]
389                                .set_thread_id(thread::current().id());
390                        }
391
392                        let mut cx_core = cx.core.borrow_mut();
393                        assert!(cx_core.is_none());
394                        *cx_core = core;
395                    }
396
397                    // Reset the task budget as we are re-entering the
398                    // runtime.
399                    coop::set(self.budget);
400                }
401            });
402        }
403    }
404
405    let mut had_entered = false;
406    let mut take_core = false;
407
408    let setup_result = with_current(|maybe_cx| {
409        match (
410            crate::runtime::context::current_enter_context(),
411            maybe_cx.is_some(),
412        ) {
413            (context::EnterRuntime::Entered { .. }, true) => {
414                // We are on a thread pool runtime thread, so we just need to
415                // set up blocking.
416                had_entered = true;
417            }
418            (
419                context::EnterRuntime::Entered {
420                    allow_block_in_place,
421                },
422                false,
423            ) => {
424                // We are on an executor, but _not_ on the thread pool.  That is
425                // _only_ okay if we are in a thread pool runtime's block_on
426                // method:
427                if allow_block_in_place {
428                    had_entered = true;
429                    return Ok(());
430                } else {
431                    // This probably means we are on the current_thread runtime or in a
432                    // LocalSet, where it is _not_ okay to block.
433                    return Err(
434                        "can call blocking only when running on the multi-threaded runtime",
435                    );
436                }
437            }
438            (context::EnterRuntime::NotEntered, true) => {
439                // This is a nested call to block_in_place (we already exited).
440                // All the necessary setup has already been done.
441                return Ok(());
442            }
443            (context::EnterRuntime::NotEntered, false) => {
444                // We are outside of the tokio runtime, so blocking is fine.
445                // We can also skip all of the thread pool blocking setup steps.
446                return Ok(());
447            }
448        }
449
450        let cx = maybe_cx.expect("no .is_some() == false cases above should lead here");
451
452        // Since deferred tasks don't stay on `core`, make sure to wake them
453        // before blocking.
454        cx.defer.wake();
455
456        // Get the worker core. If none is set, then blocking is fine!
457        let mut core = match cx.core.borrow_mut().take() {
458            Some(core) => core,
459            None => return Ok(()),
460        };
461
462        // If we heavily call `spawn_blocking`, there might be no available thread to
463        // run this core. Except for the task in the lifo_slot, all tasks can be
464        // stolen, so we move the task out of the lifo_slot to the run_queue.
465        if let Some(task) = core.lifo_slot.take() {
466            core.run_queue
467                .push_back_or_overflow(task, &*cx.worker.handle, &mut core.stats);
468        }
469
470        // We are taking the core from the context and sending it to another
471        // thread.
472        take_core = true;
473
474        // The parker should be set here
475        assert!(core.park.is_some());
476
477        // In order to block, the core must be sent to another thread for
478        // execution.
479        //
480        // First, move the core back into the worker's shared core slot.
481        cx.worker.core.set(core);
482
483        // Next, clone the worker handle and send it to a new thread for
484        // processing.
485        //
486        // Once the blocking task is done executing, we will attempt to
487        // steal the core back.
488        let worker = cx.worker.clone();
489        runtime::spawn_blocking(move || run(worker));
490        Ok(())
491    });
492
493    if let Err(panic_message) = setup_result {
494        panic!("{}", panic_message);
495    }
496
497    if had_entered {
498        // Unset the current task's budget. Blocking sections are not
499        // constrained by task budgets.
500        let _reset = Reset {
501            take_core,
502            budget: coop::stop(),
503        };
504
505        crate::runtime::context::exit_runtime(f)
506    } else {
507        f()
508    }
509}
510
511impl Launch {
512    pub(crate) fn launch(mut self) {
513        for worker in self.0.drain(..) {
514            runtime::spawn_blocking(move || run(worker));
515        }
516    }
517}
518
519fn run(worker: Arc<Worker>) {
520    #[allow(dead_code)]
521    struct AbortOnPanic;
522
523    impl Drop for AbortOnPanic {
524        fn drop(&mut self) {
525            if std::thread::panicking() {
526                eprintln!("worker thread panicking; aborting process");
527                std::process::abort();
528            }
529        }
530    }
531
532    // Catching panics on worker threads in tests is quite tricky. Instead, when
533    // debug assertions are enabled, we just abort the process.
534    #[cfg(debug_assertions)]
535    let _abort_on_panic = AbortOnPanic;
536
537    // Acquire a core. If this fails, then another thread is running this
538    // worker and there is nothing further to do.
539    let core = match worker.core.take() {
540        Some(core) => core,
541        None => return,
542    };
543
544    worker.handle.shared.worker_metrics[worker.index].set_thread_id(thread::current().id());
545
546    let handle = scheduler::Handle::MultiThread(worker.handle.clone());
547
548    crate::runtime::context::enter_runtime(&handle, true, |_| {
549        // Set the worker context.
550        let cx = scheduler::Context::MultiThread(Context {
551            worker,
552            core: RefCell::new(None),
553            defer: Defer::new(),
554        });
555
556        context::set_scheduler(&cx, || {
557            let cx = cx.expect_multi_thread();
558
559            // This should always be an error. It only returns a `Result` to support
560            // using `?` to short circuit.
561            assert!(cx.run(core).is_err());
562
563            // Check if there are any deferred tasks to notify. This can happen when
564            // the worker core is lost due to `block_in_place()` being called from
565            // within the task.
566            cx.defer.wake();
567        });
568    });
569}
570
571impl Context {
572    fn run(&self, mut core: Box<Core>) -> RunResult {
573        // Reset `lifo_enabled` here in case the core was previously stolen from
574        // a task that had the LIFO slot disabled.
575        self.reset_lifo_enabled(&mut core);
576
577        // Start as "processing" tasks as polling tasks from the local queue
578        // will be one of the first things we do.
579        core.stats.start_processing_scheduled_tasks();
580
581        while !core.is_shutdown {
582            self.assert_lifo_enabled_is_correct(&core);
583
584            if core.is_traced {
585                core = self.worker.handle.trace_core(core);
586            }
587
588            // Increment the tick
589            core.tick();
590
591            // Run maintenance, if needed
592            core = self.maintenance(core);
593
594            // First, check work available to the current worker.
595            if let Some(task) = core.next_task(&self.worker) {
596                core = self.run_task(task, core)?;
597                continue;
598            }
599
600            // We consumed all work in the queues and will start searching for work.
601            core.stats.end_processing_scheduled_tasks();
602
603            // There is no more **local** work to process, try to steal work
604            // from other workers.
605            if let Some(task) = core.steal_work(&self.worker) {
606                // Found work, switch back to processing
607                core.stats.start_processing_scheduled_tasks();
608                core = self.run_task(task, core)?;
609            } else {
610                // Wait for work
611                core = if !self.defer.is_empty() {
612                    self.park_yield(core)
613                } else {
614                    self.park(core)
615                };
616                core.stats.start_processing_scheduled_tasks();
617            }
618        }
619
620        #[cfg(all(tokio_unstable, feature = "time"))]
621        {
622            match self.worker.handle.timer_flavor {
623                TimerFlavor::Traditional => {}
624                TimerFlavor::Alternative => {
625                    util::time_alt::shutdown_local_timers(
626                        &mut core.time_context.wheel,
627                        &mut core.time_context.canc_rx,
628                        self.worker.handle.take_remote_timers(),
629                        &self.worker.handle.driver,
630                    );
631                }
632            }
633        }
634
635        core.pre_shutdown(&self.worker);
636        // Signal shutdown
637        self.worker.handle.shutdown_core(core);
638        Err(())
639    }
640
641    fn run_task(&self, task: Notified, mut core: Box<Core>) -> RunResult {
642        #[cfg(tokio_unstable)]
643        let task_meta = task.task_meta();
644
645        let task = self.worker.handle.shared.owned.assert_owner(task);
646
647        // Make sure the worker is not in the **searching** state. This enables
648        // another idle worker to try to steal work.
649        let notified_parked_worker = core.transition_from_searching(&self.worker);
650
651        // If the setting to wake eagerly when releasing the I/O driver is
652        // enabled, and this worker had the driver, wake a parked worker to come
653        // grab it from us.
654        //
655        // Note that this is only done when we are *actually* about to poll a
656        // task, rather than whenever the worker has unparked. When the worker
657        // has been unparked, it may not actually have any tasks to poll, and if
658        // it's still holding the I/O driver, it should just go back to polling
659        // the driver again, rather than trying to wake someone else spuriously.
660        //
661        // Note that this explicitly checks `cfg!(tokio_unstable)` in addition,
662        // as that should result in this whole expression being eliminated at
663        // compile-time when unstable features are disabled.
664        if cfg!(tokio_unstable)
665            && core.enable_eager_driver_handoff
666            && core.had_driver == park::HadDriver::Yes
667            && !notified_parked_worker
668        // don't do it a second time
669        {
670            core.had_driver = park::HadDriver::No;
671            self.worker.handle.notify_parked_local();
672        }
673
674        self.assert_lifo_enabled_is_correct(&core);
675
676        // Measure the poll start time. Note that we may end up polling other
677        // tasks under this measurement. In this case, the tasks came from the
678        // LIFO slot and are considered part of the current task for scheduling
679        // purposes. These tasks inherent the "parent"'s limits.
680        core.stats.start_poll(
681            task.get_scheduled_at()
682                .prepare(self.worker.handle.shared.started_at),
683        );
684
685        // Make the core available to the runtime context
686        *self.core.borrow_mut() = Some(core);
687
688        // Run the task
689        coop::budget(|| {
690            // Unlike the poll time above, poll start callback is attached to the task id,
691            // so it is tightly associated with the actual poll invocation.
692            #[cfg(tokio_unstable)]
693            self.worker
694                .handle
695                .task_hooks
696                .poll_start_callback(&task_meta);
697
698            task.run();
699
700            #[cfg(tokio_unstable)]
701            self.worker.handle.task_hooks.poll_stop_callback(&task_meta);
702
703            let mut lifo_polls = 0;
704
705            // As long as there is budget remaining and a task exists in the
706            // `lifo_slot`, then keep running.
707            loop {
708                // Check if we still have the core. If not, the core was stolen
709                // by another worker.
710                let mut core = match self.core.borrow_mut().take() {
711                    Some(core) => core,
712                    None => {
713                        // In this case, we cannot call `reset_lifo_enabled()`
714                        // because the core was stolen. The stealer will handle
715                        // that at the top of `Context::run`
716                        return Err(());
717                    }
718                };
719
720                // Check for a task in the LIFO slot
721                let task = match core.lifo_slot.take() {
722                    Some(task) => task,
723                    None => {
724                        self.reset_lifo_enabled(&mut core);
725                        core.stats.end_poll();
726                        return Ok(core);
727                    }
728                };
729
730                if !coop::has_budget_remaining() {
731                    core.stats.end_poll();
732
733                    // Not enough budget left to run the LIFO task, push it to
734                    // the back of the queue and return.
735                    core.run_queue.push_back_or_overflow(
736                        task,
737                        &*self.worker.handle,
738                        &mut core.stats,
739                    );
740                    // If we hit this point, the LIFO slot should be enabled.
741                    // There is no need to reset it.
742                    debug_assert!(core.lifo_enabled);
743                    return Ok(core);
744                }
745
746                // Track that we are about to run a task from the LIFO slot.
747                lifo_polls += 1;
748                super::counters::inc_lifo_schedules();
749
750                // Disable the LIFO slot if we reach our limit
751                //
752                // In ping-ping style workloads where task A notifies task B,
753                // which notifies task A again, continuously prioritizing the
754                // LIFO slot can cause starvation as these two tasks will
755                // repeatedly schedule the other. To mitigate this, we limit the
756                // number of times the LIFO slot is prioritized.
757                if lifo_polls >= MAX_LIFO_POLLS_PER_TICK {
758                    core.lifo_enabled = false;
759                    super::counters::inc_lifo_capped();
760                }
761
762                // Run the LIFO task, then loop
763                *self.core.borrow_mut() = Some(core);
764                let task = self.worker.handle.shared.owned.assert_owner(task);
765
766                #[cfg(tokio_unstable)]
767                let task_meta = task.task_meta();
768
769                #[cfg(tokio_unstable)]
770                self.worker
771                    .handle
772                    .task_hooks
773                    .poll_start_callback(&task_meta);
774
775                task.run();
776
777                #[cfg(tokio_unstable)]
778                self.worker.handle.task_hooks.poll_stop_callback(&task_meta);
779            }
780        })
781    }
782
783    fn reset_lifo_enabled(&self, core: &mut Core) {
784        core.lifo_enabled = !self.worker.handle.shared.config.disable_lifo_slot;
785    }
786
787    fn assert_lifo_enabled_is_correct(&self, core: &Core) {
788        debug_assert_eq!(
789            core.lifo_enabled,
790            !self.worker.handle.shared.config.disable_lifo_slot
791        );
792    }
793
794    fn maintenance(&self, mut core: Box<Core>) -> Box<Core> {
795        if core.tick % self.worker.handle.shared.config.event_interval == 0 {
796            super::counters::inc_num_maintenance();
797
798            core.stats.end_processing_scheduled_tasks();
799
800            // Call `park` with a 0 timeout. This enables the I/O driver, timer, ...
801            // to run without actually putting the thread to sleep.
802            core = self.park_yield(core);
803
804            // Run regularly scheduled maintenance
805            core.maintenance(&self.worker);
806
807            core.stats.start_processing_scheduled_tasks();
808        }
809
810        core
811    }
812
813    /// Parks the worker thread while waiting for tasks to execute.
814    ///
815    /// This function checks if indeed there's no more work left to be done before parking.
816    /// Also important to notice that, before parking, the worker thread will try to take
817    /// ownership of the Driver (IO/Time) and dispatch any events that might have fired.
818    /// Whenever a worker thread executes the Driver loop, all waken tasks are scheduled
819    /// in its own local queue until the queue saturates (ntasks > `LOCAL_QUEUE_CAPACITY`).
820    /// When the local queue is saturated, the overflow tasks are added to the injection queue
821    /// from where other workers can pick them up.
822    /// Also, we rely on the workstealing algorithm to spread the tasks amongst workers
823    /// after all the IOs get dispatched
824    fn park(&self, mut core: Box<Core>) -> Box<Core> {
825        if let Some(f) = &self.worker.handle.shared.config.before_park {
826            f();
827        }
828
829        if core.transition_to_parked(&self.worker) {
830            while !core.is_shutdown && !core.is_traced {
831                core.stats.about_to_park();
832                core.stats
833                    .submit(&self.worker.handle.shared.worker_metrics[self.worker.index]);
834
835                core = self.park_internal(core, None);
836
837                core.stats.unparked();
838
839                // Run regularly scheduled maintenance
840                core.maintenance(&self.worker);
841
842                if core.transition_from_parked(&self.worker) {
843                    break;
844                }
845            }
846        }
847
848        if let Some(f) = &self.worker.handle.shared.config.after_unpark {
849            f();
850        }
851        core
852    }
853
854    fn park_yield(&self, core: Box<Core>) -> Box<Core> {
855        self.park_internal(core, Some(Duration::from_millis(0)))
856    }
857
858    fn park_internal(&self, mut core: Box<Core>, duration: Option<Duration>) -> Box<Core> {
859        self.assert_lifo_enabled_is_correct(&core);
860
861        // Take the parker out of core
862        let mut park = core.park.take().expect("park missing");
863        // Store `core` in context
864        *self.core.borrow_mut() = Some(core);
865
866        #[cfg(feature = "time")]
867        let (duration, auto_advance_duration) = match self.worker.handle.timer_flavor {
868            TimerFlavor::Traditional => (duration, None::<Duration>),
869            #[cfg(tokio_unstable)]
870            TimerFlavor::Alternative => {
871                // Must happens after taking out the parker, as the `Handle::schedule_local`
872                // will delay the notify if the parker taken out.
873                //
874                // See comments in `Handle::schedule_local` for more details.
875                let MaintainLocalTimer {
876                    park_duration: duration,
877                    auto_advance_duration,
878                } = self.maintain_local_timers_before_parking(duration);
879                (duration, auto_advance_duration)
880            }
881        };
882
883        // Park thread
884        let had_driver = if let Some(timeout) = duration {
885            park.park_timeout(&self.worker.handle.driver, timeout)
886        } else {
887            park.park(&self.worker.handle.driver)
888        };
889
890        self.defer.wake();
891
892        #[cfg(feature = "time")]
893        match self.worker.handle.timer_flavor {
894            TimerFlavor::Traditional => {
895                // suppress unused variable warning
896                let _ = auto_advance_duration;
897            }
898            #[cfg(tokio_unstable)]
899            TimerFlavor::Alternative => {
900                // Must happens before placing back the parker, as the `Handle::schedule_local`
901                // will delay the notify if the parker is still in `core`.
902                //
903                // See comments in `Handle::schedule_local` for more details.
904                self.maintain_local_timers_after_parking(auto_advance_duration);
905            }
906        }
907
908        // Remove `core` from context
909        core = self.core.borrow_mut().take().expect("core missing");
910
911        // Place `park` back in `core`
912        core.park = Some(park);
913        core.had_driver = had_driver;
914
915        if core.should_notify_others() {
916            self.worker.handle.notify_parked_local();
917        }
918        core
919    }
920
921    pub(crate) fn defer(&self, waker: &Waker) {
922        if self.core.borrow().is_none() {
923            // If there is no core, then the worker is currently in a block_in_place. In this case,
924            // we cannot use the defer queue as we aren't really in the current runtime.
925            waker.wake_by_ref();
926        } else {
927            self.defer.defer(waker);
928        }
929    }
930
931    #[cfg(all(tokio_unstable, feature = "time"))]
932    /// Maintain local timers before parking the resource driver.
933    ///
934    /// * Remove cancelled timers from the local timer wheel.
935    /// * Register remote timers to the local timer wheel.
936    /// * Adjust the park duration based on
937    ///   * the next timer expiration time.
938    ///   * whether auto-advancing is required (feature = "test-util").
939    ///
940    /// # Returns
941    ///
942    /// `(Box<Core>, park_duration, auto_advance_duration)`
943    fn maintain_local_timers_before_parking(
944        &self,
945        park_duration: Option<Duration>,
946    ) -> MaintainLocalTimer {
947        let handle = &self.worker.handle;
948        let mut wake_queue = time_alt::WakeQueue::new();
949
950        let (should_yield, next_timer) = with_current(|maybe_cx| {
951            let cx = maybe_cx.expect("function should be called when core is present");
952            assert_eq!(
953                Arc::as_ptr(&cx.worker.handle),
954                Arc::as_ptr(&self.worker.handle),
955                "function should be called on the exact same worker"
956            );
957
958            let mut maybe_core = cx.core.borrow_mut();
959            let core = maybe_core.as_mut().expect("core missing");
960            let time_cx = &mut core.time_context;
961
962            util::time_alt::process_registration_queue(
963                &mut time_cx.registration_queue,
964                &mut time_cx.wheel,
965                &time_cx.canc_tx,
966                &mut wake_queue,
967            );
968            util::time_alt::insert_inject_timers(
969                &mut time_cx.wheel,
970                &time_cx.canc_tx,
971                handle.take_remote_timers(),
972                &mut wake_queue,
973            );
974            util::time_alt::remove_cancelled_timers(&mut time_cx.wheel, &mut time_cx.canc_rx);
975            let should_yield = !wake_queue.is_empty();
976
977            let next_timer = util::time_alt::next_expiration_time(&time_cx.wheel, &handle.driver);
978
979            (should_yield, next_timer)
980        });
981
982        wake_queue.wake_all();
983
984        if should_yield {
985            MaintainLocalTimer {
986                park_duration: Some(Duration::from_millis(0)),
987                auto_advance_duration: None,
988            }
989        } else {
990            // get the minimum duration
991            let dur = util::time_alt::min_duration(park_duration, next_timer);
992            if util::time_alt::pre_auto_advance(&handle.driver, dur) {
993                MaintainLocalTimer {
994                    park_duration: Some(Duration::ZERO),
995                    auto_advance_duration: dur,
996                }
997            } else {
998                MaintainLocalTimer {
999                    park_duration: dur,
1000                    auto_advance_duration: None,
1001                }
1002            }
1003        }
1004    }
1005
1006    #[cfg(all(tokio_unstable, feature = "time"))]
1007    /// Maintain local timers after unparking the resource driver.
1008    ///
1009    /// * Auto-advance time, if required (feature = "test-util").
1010    /// * Process expired timers.
1011    fn maintain_local_timers_after_parking(&self, auto_advance_duration: Option<Duration>) {
1012        let handle = &self.worker.handle;
1013        let mut wake_queue = time_alt::WakeQueue::new();
1014
1015        with_current(|maybe_cx| {
1016            let cx = maybe_cx.expect("function should be called when core is present");
1017            assert_eq!(
1018                Arc::as_ptr(&cx.worker.handle),
1019                Arc::as_ptr(&self.worker.handle),
1020                "function should be called on the exact same worker"
1021            );
1022
1023            let mut maybe_core = cx.core.borrow_mut();
1024            let core = maybe_core.as_mut().expect("core missing");
1025            let time_cx = &mut core.time_context;
1026
1027            util::time_alt::post_auto_advance(&handle.driver, auto_advance_duration);
1028            util::time_alt::process_expired_timers(
1029                &mut time_cx.wheel,
1030                &handle.driver,
1031                &mut wake_queue,
1032            );
1033        });
1034
1035        wake_queue.wake_all();
1036    }
1037
1038    #[cfg(all(tokio_unstable, feature = "time"))]
1039    fn with_core<F, R>(&self, f: F) -> R
1040    where
1041        F: FnOnce(Option<&mut Core>) -> R,
1042    {
1043        match self.core.borrow_mut().as_mut() {
1044            Some(core) => f(Some(core)),
1045            None => f(None),
1046        }
1047    }
1048
1049    #[cfg(all(tokio_unstable, feature = "time"))]
1050    pub(crate) fn with_time_temp_local_context<F, R>(&self, f: F) -> R
1051    where
1052        F: FnOnce(Option<time_alt::TempLocalContext<'_>>) -> R,
1053    {
1054        self.with_core(|maybe_core| match maybe_core {
1055            Some(core) if core.is_shutdown => f(Some(time_alt::TempLocalContext::new_shutdown())),
1056            Some(core) => f(Some(time_alt::TempLocalContext::new_running(
1057                &mut core.time_context,
1058            ))),
1059            None => f(None),
1060        })
1061    }
1062
1063    #[cfg(tokio_unstable)]
1064    pub(crate) fn worker_index(&self) -> usize {
1065        self.worker.index
1066    }
1067}
1068
1069impl Core {
1070    /// Increment the tick
1071    fn tick(&mut self) {
1072        self.tick = self.tick.wrapping_add(1);
1073    }
1074
1075    /// Return the next notified task available to this worker.
1076    fn next_task(&mut self, worker: &Worker) -> Option<Notified> {
1077        if self.tick % self.global_queue_interval == 0 {
1078            // Update the global queue interval, if needed
1079            self.tune_global_queue_interval(worker);
1080
1081            worker
1082                .handle
1083                .next_remote_task()
1084                .or_else(|| self.next_local_task())
1085        } else {
1086            let maybe_task = self.next_local_task();
1087
1088            if maybe_task.is_some() {
1089                return maybe_task;
1090            }
1091
1092            if worker.inject().is_empty() {
1093                return None;
1094            }
1095
1096            let cap = usize::min(
1097                // Other threads can only **remove** tasks from the current
1098                // worker's `run_queue`. So, we can be confident that by the
1099                // time we call `run_queue.push_back` below, there will be *at
1100                // least* `cap` available slots in the queue.
1101                //
1102                // Note that even though `next_local_task()` just returned
1103                // `None`, this may be different from `max_capacity()` if
1104                // another worker is currently stealing tasks from us.
1105                self.run_queue.remaining_slots(),
1106                // We want to make sure that all of the tasks we take end up in
1107                // the first half of the local queue. This ensures that the
1108                // tasks do not get pushed to the inject queue again if overflow
1109                // occurs, as overflow only affects tasks in the second half of
1110                // the local queue.
1111                //
1112                // Note that even if there are concurrent stealers, we do not
1113                // need to consider the value of `remaining_slots()` because a
1114                // future call to `push_overflow()` can only succeed once that
1115                // concurrent stealer has finished stealing, so at that point
1116                // the tasks we are adding now will be in the first half.
1117                self.run_queue.max_capacity() / 2,
1118            );
1119
1120            // The worker is currently idle, pull a batch of work from the
1121            // injection queue. We don't want to pull *all* the work so other
1122            // workers can also get some.
1123            let n = usize::min(
1124                worker.inject().len() / worker.handle.shared.remotes.len() + 1,
1125                cap,
1126            );
1127
1128            // Take at least one task since the first task is returned directly
1129            // and not pushed onto the local queue.
1130            let n = usize::max(1, n);
1131
1132            let mut synced = worker.handle.shared.synced.lock();
1133            // safety: passing in the correct `inject::Synced`.
1134            let mut tasks = unsafe { worker.inject().pop_n(&mut synced.inject, n) };
1135
1136            // Pop the first task to return immediately
1137            let ret = tasks.next();
1138
1139            // Push the rest of the on the run queue
1140            self.run_queue.push_back(tasks);
1141
1142            ret
1143        }
1144    }
1145
1146    fn next_local_task(&mut self) -> Option<Notified> {
1147        self.lifo_slot.take().or_else(|| self.run_queue.pop())
1148    }
1149
1150    /// Function responsible for stealing tasks from another worker
1151    ///
1152    /// Note: Only if less than half the workers are searching for tasks to steal
1153    /// a new worker will actually try to steal. The idea is to make sure not all
1154    /// workers will be trying to steal at the same time.
1155    fn steal_work(&mut self, worker: &Worker) -> Option<Notified> {
1156        if !self.transition_to_searching(worker) {
1157            return None;
1158        }
1159
1160        let num = worker.handle.shared.remotes.len();
1161        // Start from a random worker
1162        let start = self.rand.fastrand_n(num as u32) as usize;
1163
1164        for i in 0..num {
1165            let i = (start + i) % num;
1166
1167            // Don't steal from ourself! We know we don't have work.
1168            if i == worker.index {
1169                continue;
1170            }
1171
1172            let target = &worker.handle.shared.remotes[i];
1173            if let Some(task) = target
1174                .steal
1175                .steal_into(&mut self.run_queue, &mut self.stats)
1176            {
1177                return Some(task);
1178            }
1179        }
1180
1181        // Fallback on checking the global queue
1182        worker.handle.next_remote_task()
1183    }
1184
1185    fn transition_to_searching(&mut self, worker: &Worker) -> bool {
1186        if !self.is_searching {
1187            self.is_searching = worker.handle.shared.idle.transition_worker_to_searching();
1188        }
1189
1190        self.is_searching
1191    }
1192
1193    fn transition_from_searching(&mut self, worker: &Worker) -> bool {
1194        if !self.is_searching {
1195            return false;
1196        }
1197
1198        self.is_searching = false;
1199        worker.handle.transition_worker_from_searching()
1200    }
1201
1202    fn has_tasks(&self) -> bool {
1203        self.lifo_slot.is_some() || self.run_queue.has_tasks()
1204    }
1205
1206    fn should_notify_others(&self) -> bool {
1207        // If there are tasks available to steal, but this worker is not
1208        // looking for tasks to steal, notify another worker.
1209        if self.is_searching {
1210            return false;
1211        }
1212        self.lifo_slot.is_some() as usize + self.run_queue.len() > 1
1213    }
1214
1215    /// Prepares the worker state for parking.
1216    ///
1217    /// Returns true if the transition happened, false if there is work to do first.
1218    fn transition_to_parked(&mut self, worker: &Worker) -> bool {
1219        // Workers should not park if they have work to do
1220        if self.has_tasks() || self.is_traced {
1221            return false;
1222        }
1223
1224        // When the final worker transitions **out** of searching to parked, it
1225        // must check all the queues one last time in case work materialized
1226        // between the last work scan and transitioning out of searching.
1227        let is_last_searcher = worker.handle.shared.idle.transition_worker_to_parked(
1228            &worker.handle.shared,
1229            worker.index,
1230            self.is_searching,
1231        );
1232
1233        // The worker is no longer searching. Setting this is the local cache
1234        // only.
1235        self.is_searching = false;
1236
1237        if is_last_searcher {
1238            worker.handle.notify_if_work_pending();
1239        }
1240
1241        true
1242    }
1243
1244    /// Returns `true` if the transition happened.
1245    fn transition_from_parked(&mut self, worker: &Worker) -> bool {
1246        // If a task is in the lifo slot/run queue, then we must unpark regardless of
1247        // being notified
1248        if self.has_tasks() {
1249            // When a worker wakes, it should only transition to the "searching"
1250            // state when the wake originates from another worker *or* a new task
1251            // is pushed. We do *not* want the worker to transition to "searching"
1252            // when it wakes when the I/O driver receives new events.
1253            self.is_searching = !worker
1254                .handle
1255                .shared
1256                .idle
1257                .unpark_worker_by_id(&worker.handle.shared, worker.index);
1258            return true;
1259        }
1260
1261        if worker
1262            .handle
1263            .shared
1264            .idle
1265            .is_parked(&worker.handle.shared, worker.index)
1266        {
1267            return false;
1268        }
1269
1270        // When unparked, the worker is in the searching state.
1271        self.is_searching = true;
1272        true
1273    }
1274
1275    /// Runs maintenance work such as checking the pool's state.
1276    fn maintenance(&mut self, worker: &Worker) {
1277        self.stats
1278            .submit(&worker.handle.shared.worker_metrics[worker.index]);
1279
1280        if !self.is_shutdown {
1281            // Check if the scheduler has been shutdown
1282            let synced = worker.handle.shared.synced.lock();
1283            self.is_shutdown = worker.inject().is_closed(&synced.inject);
1284        }
1285
1286        if !self.is_traced {
1287            // Check if the worker should be tracing.
1288            self.is_traced = worker.handle.shared.trace_status.trace_requested();
1289        }
1290    }
1291
1292    /// Signals all tasks to shut down, and waits for them to complete. Must run
1293    /// before we enter the single-threaded phase of shutdown processing.
1294    fn pre_shutdown(&mut self, worker: &Worker) {
1295        // Start from a random inner list
1296        let start = self
1297            .rand
1298            .fastrand_n(worker.handle.shared.owned.get_shard_size() as u32);
1299        // Signal to all tasks to shut down.
1300        worker
1301            .handle
1302            .shared
1303            .owned
1304            .close_and_shutdown_all(start as usize);
1305
1306        self.stats
1307            .submit(&worker.handle.shared.worker_metrics[worker.index]);
1308    }
1309
1310    /// Shuts down the core.
1311    fn shutdown(&mut self, handle: &Handle) {
1312        // Take the core
1313        let mut park = self.park.take().expect("park missing");
1314
1315        // Drain the queue
1316        while self.next_local_task().is_some() {}
1317
1318        park.shutdown(&handle.driver);
1319    }
1320
1321    fn tune_global_queue_interval(&mut self, worker: &Worker) {
1322        let next = self
1323            .stats
1324            .tuned_global_queue_interval(&worker.handle.shared.config);
1325
1326        // Smooth out jitter
1327        if u32::abs_diff(self.global_queue_interval, next) > 2 {
1328            self.global_queue_interval = next;
1329        }
1330    }
1331}
1332
1333impl Worker {
1334    /// Returns a reference to the scheduler's injection queue.
1335    fn inject(&self) -> &inject::Shared<Arc<Handle>> {
1336        &self.handle.shared.inject
1337    }
1338}
1339
1340impl Handle {
1341    pub(super) fn schedule_task(&self, task: Notified, is_yield: bool) {
1342        if self
1343            .shared
1344            .config
1345            .metrics_schedule_latency_histogram
1346            .is_some()
1347        {
1348            task.set_scheduled_at(ScheduleLatencyInstant::new(self.shared.started_at));
1349        }
1350
1351        with_current(|maybe_cx| {
1352            if let Some(cx) = maybe_cx {
1353                // Make sure the task is part of the **current** scheduler.
1354                if self.ptr_eq(&cx.worker.handle) {
1355                    // And the current thread still holds a core
1356                    if let Some(core) = cx.core.borrow_mut().as_mut() {
1357                        self.schedule_local(core, task, is_yield);
1358                        return;
1359                    }
1360                }
1361            }
1362
1363            // Otherwise, use the inject queue.
1364            self.push_remote_task(task);
1365            self.notify_parked_remote();
1366        });
1367    }
1368
1369    // Separated case to reduce LLVM codegen in `Handle::bind_new_task`.
1370    pub(super) fn schedule_option_task_without_yield(&self, task: Option<Notified>) {
1371        if let Some(task) = task {
1372            self.schedule_task(task, false);
1373        }
1374    }
1375
1376    fn schedule_local(&self, core: &mut Core, task: Notified, is_yield: bool) {
1377        core.stats.inc_local_schedule_count();
1378
1379        // Spawning from the worker thread. If scheduling a "yield" then the
1380        // task must always be pushed to the back of the queue, enabling other
1381        // tasks to be executed. If **not** a yield, then there is more
1382        // flexibility and the task may go to the front of the queue.
1383        let should_notify = if is_yield || !core.lifo_enabled {
1384            core.run_queue
1385                .push_back_or_overflow(task, self, &mut core.stats);
1386            true
1387        } else {
1388            // Push to the LIFO slot
1389            let prev = core.lifo_slot.take();
1390            let ret = prev.is_some();
1391
1392            if let Some(prev) = prev {
1393                core.run_queue
1394                    .push_back_or_overflow(prev, self, &mut core.stats);
1395            }
1396
1397            core.lifo_slot = Some(task);
1398
1399            ret
1400        };
1401
1402        // Only notify if not currently parked. If `park` is `None`, then the
1403        // scheduling is from a resource driver. As notifications often come in
1404        // batches, the notification is delayed until the park is complete.
1405        if should_notify && core.park.is_some() {
1406            self.notify_parked_local();
1407        }
1408    }
1409
1410    fn next_remote_task(&self) -> Option<Notified> {
1411        if self.shared.inject.is_empty() {
1412            return None;
1413        }
1414
1415        let mut synced = self.shared.synced.lock();
1416        // safety: passing in correct `idle::Synced`
1417        unsafe { self.shared.inject.pop(&mut synced.inject) }
1418    }
1419
1420    fn push_remote_task(&self, task: Notified) {
1421        self.shared.scheduler_metrics.inc_remote_schedule_count();
1422
1423        let mut synced = self.shared.synced.lock();
1424        // safety: passing in correct `idle::Synced`
1425        unsafe {
1426            self.shared.inject.push(&mut synced.inject, task);
1427        }
1428    }
1429
1430    #[cfg(all(tokio_unstable, feature = "time"))]
1431    pub(crate) fn push_remote_timer(&self, hdl: time_alt::EntryHandle) {
1432        assert_eq!(self.timer_flavor, TimerFlavor::Alternative);
1433        {
1434            let mut synced = self.shared.synced.lock();
1435            synced.inject_timers.push(hdl);
1436        }
1437        self.notify_parked_remote();
1438    }
1439
1440    #[cfg(all(tokio_unstable, feature = "time"))]
1441    pub(crate) fn take_remote_timers(&self) -> Vec<time_alt::EntryHandle> {
1442        assert_eq!(self.timer_flavor, TimerFlavor::Alternative);
1443        // It's ok to lost the race, as another worker is
1444        // draining the inject_timers.
1445        match self.shared.synced.try_lock() {
1446            Some(mut synced) => std::mem::take(&mut synced.inject_timers),
1447            None => Vec::new(),
1448        }
1449    }
1450
1451    pub(super) fn close(&self) {
1452        if self
1453            .shared
1454            .inject
1455            .close(&mut self.shared.synced.lock().inject)
1456        {
1457            self.notify_all();
1458        }
1459    }
1460
1461    /// Notify a parked worker.
1462    ///
1463    /// Returns `true` if a worker was notified, `false` otherwise.
1464    fn notify_parked_local(&self) -> bool {
1465        super::counters::inc_num_inc_notify_local();
1466
1467        if let Some(index) = self.shared.idle.worker_to_notify(&self.shared) {
1468            super::counters::inc_num_unparks_local();
1469            self.shared.remotes[index].unpark.unpark(&self.driver);
1470            true
1471        } else {
1472            false
1473        }
1474    }
1475
1476    fn notify_parked_remote(&self) {
1477        if let Some(index) = self.shared.idle.worker_to_notify(&self.shared) {
1478            self.shared.remotes[index].unpark.unpark(&self.driver);
1479        }
1480    }
1481
1482    pub(super) fn notify_all(&self) {
1483        for remote in &self.shared.remotes[..] {
1484            remote.unpark.unpark(&self.driver);
1485        }
1486    }
1487
1488    fn notify_if_work_pending(&self) {
1489        for remote in &self.shared.remotes[..] {
1490            if !remote.steal.is_empty() {
1491                self.notify_parked_local();
1492                return;
1493            }
1494        }
1495
1496        if !self.shared.inject.is_empty() {
1497            self.notify_parked_local();
1498        }
1499    }
1500
1501    /// Returns `true` if another parked worker was notified, `false` otherwise.
1502    fn transition_worker_from_searching(&self) -> bool {
1503        if self.shared.idle.transition_worker_from_searching() {
1504            // We are the final searching worker. Because work was found, we
1505            // need to notify another worker.
1506            self.notify_parked_local()
1507        } else {
1508            false
1509        }
1510    }
1511
1512    /// Signals that a worker has observed the shutdown signal and has replaced
1513    /// its core back into its handle.
1514    ///
1515    /// If all workers have reached this point, the final cleanup is performed.
1516    fn shutdown_core(&self, core: Box<Core>) {
1517        let mut cores = self.shared.shutdown_cores.lock();
1518        cores.push(core);
1519
1520        if cores.len() != self.shared.remotes.len() {
1521            return;
1522        }
1523
1524        debug_assert!(self.shared.owned.is_empty());
1525
1526        for mut core in cores.drain(..) {
1527            core.shutdown(self);
1528        }
1529
1530        // Drain the injection queue
1531        //
1532        // We already shut down every task, so we can simply drop the tasks.
1533        while let Some(task) = self.next_remote_task() {
1534            drop(task);
1535        }
1536    }
1537
1538    fn ptr_eq(&self, other: &Handle) -> bool {
1539        std::ptr::eq(self, other)
1540    }
1541}
1542
1543impl Overflow<Arc<Handle>> for Handle {
1544    fn push(&self, task: task::Notified<Arc<Handle>>) {
1545        self.push_remote_task(task);
1546    }
1547
1548    fn push_batch<I>(&self, iter: I)
1549    where
1550        I: Iterator<Item = task::Notified<Arc<Handle>>>,
1551    {
1552        unsafe {
1553            self.shared.inject.push_batch(self, iter);
1554        }
1555    }
1556}
1557
1558pub(crate) struct InjectGuard<'a> {
1559    lock: crate::loom::sync::MutexGuard<'a, Synced>,
1560}
1561
1562impl<'a> AsMut<inject::Synced> for InjectGuard<'a> {
1563    fn as_mut(&mut self) -> &mut inject::Synced {
1564        &mut self.lock.inject
1565    }
1566}
1567
1568impl<'a> Lock<inject::Synced> for &'a Handle {
1569    type Handle = InjectGuard<'a>;
1570
1571    fn lock(self) -> Self::Handle {
1572        InjectGuard {
1573            lock: self.shared.synced.lock(),
1574        }
1575    }
1576}
1577
1578#[cfg(all(tokio_unstable, feature = "time"))]
1579/// Returned by [`Context::maintain_local_timers_before_parking`].
1580struct MaintainLocalTimer {
1581    park_duration: Option<Duration>,
1582    auto_advance_duration: Option<Duration>,
1583}
1584
1585#[track_caller]
1586fn with_current<R>(f: impl FnOnce(Option<&Context>) -> R) -> R {
1587    use scheduler::Context::MultiThread;
1588
1589    context::with_scheduler(|ctx| match ctx {
1590        Some(MultiThread(ctx)) => f(Some(ctx)),
1591        _ => f(None),
1592    })
1593}