      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110

    //! Threadpool

    mod atomic_cell;
    use atomic_cell::AtomicCell;

    mod idle;
    use self::idle::Idle;

    mod worker;
    pub(crate) use worker::Launch;

    pub(crate) use worker::block_in_place;

    use crate::loom::sync::Arc;
    use crate::runtime::task::{self, JoinHandle};
    use crate::runtime::Parker;

    use std::fmt;
    use std::future::Future;

    /// Work-stealing based thread pool for executing futures.
    pub(crate) struct ThreadPool {
        spawner: Spawner,
    }

    /// Submit futures to the associated thread pool for execution.
    ///
    /// A `Spawner` instance is a handle to a single thread pool that allows the owner
    /// of the handle to spawn futures onto the thread pool.
    ///
    /// The `Spawner` handle is *only* used for spawning new futures. It does not
    /// impact the lifecycle of the thread pool in any way. The thread pool may
    /// shutdown while there are outstanding `Spawner` instances.
    ///
    /// `Spawner` instances are obtained by calling [`ThreadPool::spawner`].
    ///
    /// [`ThreadPool::spawner`]: method@ThreadPool::spawner
    #[derive(Clone)]
    pub(crate) struct Spawner {
        shared: Arc<worker::Shared>,
    }

    // ===== impl ThreadPool =====

    impl ThreadPool {
        pub(crate) fn new(size: usize, parker: Parker) -> (ThreadPool, Launch) {
            let (shared, launch) = worker::create(size, parker);
            let spawner = Spawner { shared };
            let thread_pool = ThreadPool { spawner };

            (thread_pool, launch)
        }

        /// Returns reference to `Spawner`.
        ///
        /// The `Spawner` handle can be cloned and enables spawning tasks from other
        /// threads.
        pub(crate) fn spawner(&self) -> &Spawner {
            &self.spawner
        }

        /// Blocks the current thread waiting for the future to complete.
        ///
        /// The future will execute on the current thread, but all spawned tasks
        /// will be executed on the thread pool.
        pub(crate) fn block_on<F>(&self, future: F) -> F::Output
        where
            F: Future,
        {
            let mut enter = crate::runtime::enter(true);
            enter.block_on(future).expect("failed to park thread")
        }
    }

    impl fmt::Debug for ThreadPool {
        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
            fmt.debug_struct("ThreadPool").finish()
        }
    }

    impl Drop for ThreadPool {
        fn drop(&mut self) {
            self.spawner.shutdown();
        }
    }

    // ==== impl Spawner =====

    impl Spawner {
        /// Spawns a future onto the thread pool
        pub(crate) fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
        where
            F: Future + Send + 'static,
            F::Output: Send + 'static,
        {
            let (task, handle) = task::joinable(future);
            self.shared.schedule(task, false);
            handle
        }

        pub(crate) fn shutdown(&mut self) {
            self.shared.close();
        }
    }

    impl fmt::Debug for Spawner {
        fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
            fmt.debug_struct("Spawner").finish()
        }
    }