简单总结:
初始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
从上面可以看到,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) -> Spawn
where F: Future- + 'static + Send
{
::tokio_executor::spawn(f);
Spawn(())
}
pub fn spawn
(future: T)
where
T: Future- + Send + 'static,
{
DefaultExecutor::current().spawn(Box::new(future)).unwrap()
}
pub fn current() -> DefaultExecutor {
DefaultExecutor { _dummy: () }
}
#[inline]
fn with_current
R, R>(f: F) -> Option {
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 + 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 = Cell::new(State::Empty)
}
从上看到这个executor是个tls变量,存储着executor,有他来spawn future
tokio::run之前就是构建一个future,接着看tokio::run
/// [mod]: ../index.html
pub fn run(future: F)
where F: Future- + 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 {
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 {
...
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(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(&self, future: F) -> &Self
where F: Future- + Send + 'static,
{
self.inner().pool.spawn(future);
self
}
pub fn spawn
(&self, future: F)
where
F: Future- + Send + 'static,
{
self.sender().spawn(future).unwrap();
}
pub fn spawn
(&self, future: F) -> Result<(), SpawnError>
where
F: Future- + Send + 'static,
{
let mut s = self;
tokio_executor::Executor::spawn(&mut s, Box::new(future))
}
impl
Executor for Box {
fn spawn(
&mut self,
future: Box + Send>,
) -> Result<(), SpawnError> {
(**self).spawn(future)
}
fn status(&self) -> Result<(), SpawnError> {
(**self).status()
}
}
fn spawn(
&mut self,
future: Box + 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, pool: &Arc) {
debug_assert_eq!(*self, **pool);
trace!(" -> submit external");
self.queue.push(task);
self.signal_work(pool);
}
pub fn signal_work(&self, pool: &Arc) {
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) {
...
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 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(&mut self, f: F) -> Result {
futures::executor::spawn(f).wait_future()
}
接下来就是进入futures的代码了
pub fn spawn(obj: T) -> Spawn {
Spawn {
id: fresh_task_id(),
obj: obj,
data: local_map(),
}
}
pub fn wait_future(&mut self) -> Result {
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(&mut self,
notify: &N,
id: usize) -> Poll
where N: Clone + Into,
T: Future,
{
self.poll_fn_notify(notify, id, |f| f.poll())
}
pub fn poll_fn_notify(&mut self,
notify: &N,
id: usize,
f: F) -> R
where F: FnOnce(&mut T) -> R,
N: Clone + Into,
{
let mk = || notify.clone().into();
self.enter(BorrowedUnpark::new(&mk, id), f)
}
fn enter(&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::(0x1);
let set = mem::transmute::(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::(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的清理工作