结合源码理解tokio

简单总结:
初始tokio,初始化线程池(也就是worker),添加了2个future(1个是accept连接的,1个是shutdown清理的)
接受到连接后,执行回调,如果spawn了新的future,就会添加到线程池的全局任务队列,工作线程做完了任务会从全局任务队列批量偷取任务来做
简单来说就是单事件循环+线程池的模型

tokio示例开始

extern crate tokio;

use tokio::prelude::*;
use tokio::io::copy;
use tokio::net::TcpListener;

fn main() {
    // Bind the server's socket.
    let addr = "127.0.0.1:12345".parse().unwrap();
    let listener = TcpListener::bind(&addr)
        .expect("unable to bind TCP listener");

    // Pull out a stream of sockets for incoming connections
    let server = listener.incoming()
        .map_err(|e| eprintln!("accept failed = {:?}", e))
        .for_each(|sock| {
            // Split up the reading and writing parts of the
            // socket.
            let (reader, writer) = sock.split();

            // A future that echos the data and returns how
            // many bytes were copied...
            let bytes_copied = copy(reader, writer);

            // ... after which we'll print what happened.
            let handle_conn = bytes_copied.map(|amt| {
                println!("wrote {:?} bytes", amt)
            }).map_err(|err| {
                eprintln!("IO error {:?}", err)
            });

            // Spawn the future as a concurrent task.
            tokio::spawn(handle_conn)
        });

    // Start the Tokio runtime
    tokio::run(server);
}

相关代码
TcpListener中

    pub fn incoming(self) -> Incoming {
        Incoming::new(self)
    }

impl Stream for Incoming {
    type Item = TcpStream;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Option<Self::Item>, io::Error> {
        let (socket, _) = try_ready!(self.inner.poll_accept());
        Ok(Async::Ready(Some(socket)))
    }
}

    fn for_each<F, U>(self, f: F) -> ForEach<Self, F, U>
        where F: FnMut(Self::Item) -> U,
              U: IntoFuture<Item=(), Error = Self::Error>,
              Self: Sized
    {
        for_each::new(self, f)
    }

pub fn new<S, F, U>(s: S, f: F) -> ForEach<S, F, U>
    where S: Stream,
          F: FnMut(S::Item) -> U,
          U: IntoFuture<Item = (), Error = S::Error>,
{
    ForEach {
        stream: s,
        f: f,
        fut: None,
    }
}

impl<S, F, U> Future for ForEach<S, F, U>
    where S: Stream,
          F: FnMut(S::Item) -> U,
          U: IntoFuture<Item= (), Error = S::Error>,
{
    type Item = ();
    type Error = S::Error;

    fn poll(&mut self) -> Poll<(), S::Error> {
        loop {
            if let Some(mut fut) = self.fut.take() {
                if fut.poll()?.is_not_ready() {
                    self.fut = Some(fut);
                    return Ok(Async::NotReady);
                }
            }

            match try_ready!(self.stream.poll()) {
                Some(e) => self.fut = Some((self.f)(e).into_future()),
                None => return Ok(Async::Ready(())),
            }
        }
    }
}

从上面可以看到,incoming返回Incoming包装后的lisntener,
而incoming实现了stream trait,就拥有了for_each方法,
for_each返回for_each包装后的incoming,
而for_each实现了Future trait, 这个是futures的关键
executor会调用poll,这个时候返回Ok(Async::NotReady),Ok(Async::Ready(()));或者Err(Error()),
从for_each的如下代码回溯

 match try_ready!(self.stream.poll()) {
其实调用了
 pub fn poll_accept(&mut self) -> Poll<(TcpStream, SocketAddr), io::Error> {
        let (io, addr) = try_ready!(self.poll_accept_std());

        let io = mio::net::TcpStream::from_stream(io)?;
        let io = TcpStream::new(io);

        Ok((io, addr).into())
    }

  pub fn poll_accept_std(&mut self) -> Poll<(net::TcpStream, SocketAddr), io::Error> {
        try_ready!(self.io.poll_read_ready(mio::Ready::readable()));

        match self.io.get_ref().accept_std() {
            Ok(pair) => Ok(pair.into()),
            Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
                self.io.clear_read_ready(mio::Ready::readable())?;
                Ok(Async::NotReady)
            }
            Err(e) => Err(e),
        }
    }

而下层其实就是mio针对epoll,kqueue等的封装

从for_each的如下代码回溯

        if let Some(mut fut) = self.fut.take() {
              if fut.poll()?.is_not_ready() {
                  self.fut = Some(fut);
                  return Ok(Async::NotReady);
              }
          }

其实就是调用示例中的
// Split up the reading and writing parts of the
          // socket.
          let (reader, writer) = sock.split();

          // A future that echos the data and returns how
          // many bytes were copied...
          let bytes_copied = copy(reader, writer);

          // ... after which we'll print what happened.
          let handle_conn = bytes_copied.map(|amt| {
              println!("wrote {:?} bytes", amt)
          }).map_err(|err| {
              eprintln!("IO error {:?}", err)
          });

          // Spawn the future as a concurrent task.

          tokio::spawn(handle_conn)

spawn前面的代码其实就是在创建future
而spawn就是把future提交到当前线程,
继续看tokio::spawn
tokio中

pub fn spawn<F>(f: F) -> Spawn
where F: Future<Item = (), Error = ()> + 'static + Send
{
    ::tokio_executor::spawn(f);
    Spawn(())
}
 
pub fn spawn<T>(future: T)
where
    T: Future<Item = (), Error = ()> + Send + 'static,
{
    DefaultExecutor::current().spawn(Box::new(future)).unwrap()
}

    pub fn current() -> DefaultExecutor {
        DefaultExecutor { _dummy: () }
    }

    #[inline]
    fn with_current<F: FnOnce(&mut dyn Executor) -> R, R>(f: F) -> Option<R> {
        EXECUTOR.with(
            |current_executor| match current_executor.replace(State::Active) {
                State::Ready(executor_ptr) => {
                    let executor = unsafe { &mut *executor_ptr };
                    let result = f(executor);
                    current_executor.set(State::Ready(executor_ptr));
                    Some(result)
                }
                State::Empty | State::Active => None,
            },
        )
    }

impl super::Executor for DefaultExecutor {
    fn spawn(
        &mut self,
        future: Box<dyn Future<Item = (), Error = ()> + Send>,
    ) -> Result<(), SpawnError> {
        DefaultExecutor::with_current(|executor| executor.spawn(future))
            .unwrap_or_else(|| Err(SpawnError::shutdown()))
    }


}

thread_local! {
    /// Thread-local tracking the current executor
    static EXECUTOR: Cell<State> = Cell::new(State::Empty)
}

从上看到这个executor是个tls变量,存储着executor,有他来spawn future

tokio::run之前就是构建一个future,接着看tokio::run

/// [mod]: ../index.html
pub fn run<F>(future: F)
where F: Future<Item = (), Error = ()> + Send + 'static,
{
    // Check enter before creating a new Runtime...
    let mut entered = enter().expect("nested tokio::run");
    let mut runtime = Runtime::new().expect("failed to start new Runtime");
    runtime.spawn(future);
    entered
        .block_on(runtime.shutdown_on_idle())
        .expect("shutdown cannot error")
}

继续看runtime的创建,

   pub fn new() -> io::Result<Self> {
        Builder::new().build()
    }

    pub fn new() -> Builder {
        let core_threads = num_cpus::get().max(1);

        let mut threadpool_builder = ThreadPoolBuilder::new();
        threadpool_builder.name_prefix("tokio-runtime-worker-");
        threadpool_builder.pool_size(core_threads);

        Builder {
            threadpool_builder,
            core_threads,
            clock: Clock::new(),
        }
    }

pub fn build(&mut self) -> io::Result<Runtime> {
...
    let pool = self
            .threadpool_builder
            .around_worker(move |w, enter| {
                let index = w.id().to_usize();

                tokio_reactor::with_default(&reactor_handles[index], enter, |enter| {
                    clock::with_default(&clock, enter, |enter| {
                        timer::with_default(&timer_handles[index], enter, |_| {
                            trace::dispatcher::with_default(&dispatch, || {
                                w.run();
                            })
                        });
                    })
                });
            })
            .custom_park(move |worker_id| {
                let index = worker_id.to_usize();

                timers[index]
                    .lock()
                    .unwrap()
                    .take()
                    .unwrap()
            })
            .build();
...

    }

pub fn with_default<F, R>(handle: &Handle, enter: &mut Enter, f: F) -> R
where
    F: FnOnce(&mut Enter) -> R,
{
    // Ensure that the executor is removed from the thread-local context
    // when leaving the scope. This handles cases that involve panicking.
    struct Reset;

    impl Drop for Reset {
        fn drop(&mut self) {
            CURRENT_REACTOR.with(|current| {
                let mut current = current.borrow_mut();
                *current = None;
            });
        }
    }

    // This ensures the value for the current reactor gets reset even if there
    // is a panic.
    let _r = Reset;

    CURRENT_REACTOR.with(|current| {
        {
            let mut current = current.borrow_mut();

            assert!(
                current.is_none(),
                "default Tokio reactor already set \
                 for execution context"
            );

            let handle = match handle.as_priv() {
                Some(handle) => handle,
                None => {
                    panic!("`handle` does not reference a reactor");
                }
            };

            *current = Some(handle.clone());
        }

        f(enter)
    })
}

接着看 runtime.spawn(future);

   pub fn spawn<F>(&self, future: F) -> &Self
    where F: Future<Item = (), Error = ()> + Send + 'static,
    {
        self.inner().pool.spawn(future);
        self
    }


    pub fn spawn<F>(&self, future: F)
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        self.sender().spawn(future).unwrap();
    }

   pub fn spawn<F>(&self, future: F) -> Result<(), SpawnError>
    where
        F: Future<Item = (), Error = ()> + Send + 'static,
    {
        let mut s = self;
        tokio_executor::Executor::spawn(&mut s, Box::new(future))
    }



impl<E: Executor + ?Sized> Executor for Box<E> {
    fn spawn(
        &mut self,
        future: Box<dyn Future<Item = (), Error = ()> + Send>,
    ) -> Result<(), SpawnError> {
        (**self).spawn(future)
    }

    fn status(&self) -> Result<(), SpawnError> {
        (**self).status()
    }
}


  fn spawn(
        &mut self,
        future: Box<dyn Future<Item = (), Error = ()> + Send>,
    ) -> Result<(), SpawnError> {
        self.prepare_for_spawn()?;

        // At this point, the pool has accepted the future, so schedule it for
        // execution.

        // Create a new task for the future
        let task = Arc::new(Task::new(future));

        // Call `submit_external()` in order to place the task into the global
        // queue. This way all workers have equal chance of running this task,
        // which means IO handles will be assigned to reactors more evenly.
        self.pool.submit_external(task, &self.pool);

        Ok(())
    }

    pub fn submit_external(&self, task: Arc<Task>, pool: &Arc<Pool>) {
        debug_assert_eq!(*self, **pool);

        trace!("    -> submit external");

        self.queue.push(task);
        self.signal_work(pool);
    }
   pub fn signal_work(&self, pool: &Arc<Pool>) {
        debug_assert_eq!(*self, **pool);

        use crate::worker::Lifecycle::Signaled;

        if let Some((idx, worker_state)) = self.sleep_stack.pop(&self.workers, Signaled, false) {
            let entry = &self.workers[idx];

            debug_assert!(
                worker_state.lifecycle() != Signaled,
                "actual={:?}",
                worker_state.lifecycle(),
            );

            trace!("signal_work -- notify; idx={}", idx);

            if !entry.notify(worker_state) {
                trace!("signal_work -- spawn; idx={}", idx);
                self.spawn_thread(WorkerId(idx), pool);
            }
        }
    }

    pub fn spawn_thread(&self, id: WorkerId, pool: &Arc<Pool>) {
...
let res = th.spawn(move || {
...
 loop {
...
        let worker = Worker::new(worker_id, backup_id, pool.clone(), trigger.clone());

                // Run the worker. If the worker transitioned to a "blocking"
                // state, then `is_blocking` will be true.
                if !worker.do_run() {
                    // The worker shutdown, so exit the thread.
                    break;
                }
...
}
...
}

pub(crate) fn do_run(&self) -> bool {
...
       CURRENT_WORKER.with(|c| {
            c.set(self as *const _);

            let pool = self.pool.clone();
            let mut sender = Sender { pool };

            // Enter an execution context
            let mut enter = tokio_executor::enter().unwrap();

            tokio_executor::with_default(&mut sender, &mut enter, |enter| {
                if let Some(ref callback) = self.pool.config.around_worker {
                    callback.call(self, enter);
                } else {
                    self.run();
                }
            });
        });
...
}

pub fn run(&self) {
...
     if !self.sleep() {
                return;
            }
...
}
    fn sleep(&self) -> bool {
...
        self.sleep_light();

...
}

 fn sleep_light(&self) {
        self.entry().park_timeout(Duration::from_millis(0));

        use crossbeam_deque::Steal;
        loop {
            match self.pool.queue.steal_batch(&self.entry().worker) {
                Steal::Success(()) => {
                    self.pool.signal_work(&self.pool);
                    break;
                }
                Steal::Empty => break,
                Steal::Retry => {}
            }
        }
    }

可以看到其实是吧future包装成task,然后提交到线程池中,然后通知他工作,如果通知失败,就开启新的线程

接着看runtime.shutdown_on_idle()

 pub fn shutdown_on_idle(mut self) -> Shutdown {
        let inner = self.inner.take().unwrap();
        let inner = inner.pool.shutdown_on_idle();
        Shutdown { inner }
    }




impl Future for Shutdown {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        try_ready!(self.inner.poll());
        Ok(().into())
    }
}

pool shutdown

  pub fn shutdown_on_idle(mut self) -> Shutdown {
        let inner = self.inner.take().unwrap();
        inner.sender.pool.shutdown(false, false);
        Shutdown::new(&inner.trigger)
    }

impl Shutdown {
    pub(crate) fn new(trigger: &ShutdownTrigger) -> Shutdown {
        Shutdown {
            inner: trigger.inner.clone(),
        }
    }
}
impl Future for Shutdown {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> Poll<(), ()> {
        let inner = self.inner.lock().unwrap();

        if !inner.completed {
            inner.task.register();
            Ok(Async::NotReady)
        } else {
            Ok(().into())
        }
    }
}

接下来就是futures的代码了

 pub fn register(&self) {
        self.register_task(super::current());
    }
pub fn current() -> Task {
    with(|borrowed| {
        let unpark = borrowed.unpark.to_owned();
        let events = borrowed.events.to_owned();

        Task {
            id: borrowed.id,
            unpark: unpark,
            events: events,
        }
    })
}

fn with<F: FnOnce(&BorrowedTask) -> R, R>(f: F) -> R {
    unsafe {
        let task = get_ptr().expect("no Task is currently running");
        assert!(!task.is_null(), "no Task is currently running");
        f(&*(task as *const BorrowedTask))
    }
}

pub fn get_ptr() -> Option<*mut u8> {
    // Since this condition will always return true when TLS task storage is
    // used (the default), the branch predictor will be able to optimize the
    // branching and a dynamic dispatch will be avoided, which makes the
    // compiler happier.
    if core::is_get_ptr(0x1) {
        Some(CURRENT_TASK.with(|c| c.get()))
    } else {
        core::get_ptr()
    }
}

接着看entered.block_on

pub fn block_on<F: Future>(&mut self, f: F) -> Result<F::Item, F::Error> {
        futures::executor::spawn(f).wait_future()
    }

接下来就是进入futures的代码了


pub fn spawn<T>(obj: T) -> Spawn<T> {
    Spawn {
        id: fresh_task_id(),
        obj: obj,
        data: local_map(),
    }
}

    pub fn wait_future(&mut self) -> Result<F::Item, F::Error> {
        ThreadNotify::with_current(|notify| {

            loop {
                match self.poll_future_notify(notify, 0)? {
                    Async::NotReady => notify.park(),
                    Async::Ready(e) => return Ok(e),
                }
            }
        })
    }

    pub fn poll_future_notify<N>(&mut self,
                                 notify: &N,
                                 id: usize) -> Poll<T::Item, T::Error>
        where N: Clone + Into<NotifyHandle>,
              T: Future,
    {
        self.poll_fn_notify(notify, id, |f| f.poll())
    }

    pub fn poll_fn_notify<N, F, R>(&mut self,
                             notify: &N,
                             id: usize,
                             f: F) -> R
        where F: FnOnce(&mut T) -> R,
              N: Clone + Into<NotifyHandle>,
    {
        let mk = || notify.clone().into();
        self.enter(BorrowedUnpark::new(&mk, id), f)
    }

    fn enter<F, R>(&mut self, unpark: BorrowedUnpark, f: F) -> R
        where F: FnOnce(&mut T) -> R
    {
        let borrowed = BorrowedTask {
            id: self.id,
            unpark: unpark,
            events: BorrowedEvents::new(),
            map: &self.data,
        };
        let obj = &mut self.obj;
        set(&borrowed, || f(obj))
    }

pub fn set<'a, F, R>(task: &BorrowedTask<'a>, f: F) -> R
    where F: FnOnce() -> R
{
    // Lazily initialize the get / set ptrs
    //
    // Note that we won't actually use these functions ever, we'll instead be
    // testing the pointer's value elsewhere and calling our own functions.
    INIT.call_once(|| unsafe {
        let get = mem::transmute::<usize, _>(0x1);
        let set = mem::transmute::<usize, _>(0x2);
        init(get, set);
    });

    // Same as above.
    if core::is_get_ptr(0x1) {
        struct Reset(*const Cell<*mut u8>, *mut u8);

        impl Drop for Reset {
            #[inline]
            fn drop(&mut self) {
                unsafe {
                    (*self.0).set(self.1);
                }
            }
        }

        unsafe {
            let slot = tls_slot();
            let _reset = Reset(slot, (*slot).get());
            (*slot).set(task as *const _ as *mut u8);
            f()
        }
    } else {
        core::set(task, f)
    }
}
pub fn set<'a, F, R>(task: &BorrowedTask<'a>, f: F) -> R
    where F: FnOnce() -> R
{
    let set = match SET.load(Relaxed) {
        0 => panic!("not initialized"),
        n => unsafe { mem::transmute::<usize, fn(*mut u8)>(n) },
    };

    struct Reset(fn(*mut u8), *mut u8);

    impl Drop for Reset {
        #[inline]
        fn drop(&mut self) {
            (self.0)(self.1);
        }
    }

    let _reset = Reset(set, get_ptr().unwrap());
    set(task as *const _ as *mut u8);
    f()
}

从这已经可以看到,就是由futures的executor来poll传入的future,而这个future来做shutdown的清理工作

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 线程 操作系统线程理论 线程概念的引入背景 进程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有...
    go以恒阅读 1,596评论 0 6
  • 一、wait--notify--sleep Object obj = new Object(); obj.wait...
    fe0180bd6eaf阅读 313评论 0 1
  • 个人笔记,方便自己查阅使用 Contents Java LangAssignment, ReferenceData...
    freenik阅读 1,314评论 0 6
  • 在最开始学习 Rust futures 的时候,executor 和 task 是两个让我比较困惑的概念,这两个东...
    siddontang阅读 5,527评论 1 8
  • 变成奶爸怎么破. 苏可若和顾十一两个疯子一样的在车上一边听音乐一边晃啊晃。 成功设置了一场伪车震。 两人没看到,左...
    MT_苏南城阅读 203评论 2 4