pubtraitHandler: Sized {
typeTimeout;
typeMessage;
/// Invoked when the socket represented by `token` is ready to be operated/// on. `events` indicates the specific operations that are/// ready to be performed./// This function will only be invoked a single time per socket per event/// loop tick.fnready(&mutself, event_loop: &mut EventLoop<Self>, token: Token, events: Ready) {
} //【1】/// Invoked when a message has been received via the event loop's channel.fnnotify(&mutself, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
} //【2】/// Invoked when a timeout has completed.fntimeout(&mutself, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
} //【3】/// Invoked when `EventLoop` has been interrupted by a signal interrupt.fninterrupted(&mutself, event_loop: &mut EventLoop<Self>) {
} //【4】/// Invoked at the end of an event loop tick.fntick(&mutself, event_loop: &mut EventLoop<Self>) {
} //【5】
}
impl<H: Handler> EventLoop<H> {
/// Constructs a new `EventLoop` using the default configuration values./// The `EventLoop` will not be running.pubfnnew() -> io::Result<EventLoop<H>> {
EventLoop::configured(Config::default())
}
fnconfigured(config: Config) -> io::Result<EventLoop<H>> {
// Create the IO pollerlet poll = Poll::new()?; //Linux内部调用epoll_create()let timer = timer::Builder::default()
.tick_duration(config.timer_tick)
.num_slots(config.timer_wheel_size)
.capacity(config.timer_capacity)
.build();
// Create cross thread notification queuelet (tx, rx) = channel::sync_channel(config.notify_capacity); //这里创建的是同步管道,可配置同步管道内部的buffer queue bound size.// Register the notification wakeup FD with the IO poller
poll.register(&rx, NOTIFY, Ready::readable(), PollOpt::edge() | PollOpt::oneshot())?; //NOTIFY和TIMER由mio实现
poll.register(&timer, TIMER, Ready::readable(), PollOpt::edge())?;
Ok(EventLoop {
run: true,
poll: poll,
timer: timer,
notify_tx: tx,
notify_rx: rx,
config: config,
events: Events::with_capacity(1024),
})
}
/// Keep spinning the event loop indefinitely, and notify the handler whenever/// any of the registered handles are ready.pubfnrun(&mutself, handler: &mut H) -> io::Result<()> {
self.run = true;
whileself.run {
// Execute ticks as long as the event loop is runningself.run_once(handler, None)?; //Linux下调用epoll_wait()
}
Ok(())
}
pubfnrun_once(&mutself, handler: &mut H, timeout: Option<Duration>) -> io::Result<()> {
trace!("event loop tick");
// Check the registered IO handles for any new events. Each poll// is for one second, so a shutdown request can last as long as// one second before it takes effect.let events = matchself.io_poll(timeout) {
Ok(e) => e,
Err(err) => {
if err.kind() == io::ErrorKind::Interrupted {
handler.interrupted(self); //调用Handler::interrupted() 【4】0
} else {
returnErr(err);
}
}
};
self.io_process(handler, events); //处理就绪的事件,handler为如何处理各种事件的实例
handler.tick(self); //一轮事件处理后,最后调用Handler::tick() 调用【5】Ok(())
}
#[inline]fnio_poll(&mutself, timeout: Option<Duration>) -> io::Result<usize> {
self.poll.poll(&mutself.events, timeout)
}
// Process IO events that have been previously polledfnio_process(&mutself, handler: &mut H, cnt: usize) {
letmut i = 0;
trace!("io_process(..); cnt={}; len={}", cnt, self.events.len());
// Iterate over the notifications. Each event provides the token// it was registered with (which usually represents, at least, the// handle that the event is about) as well as information about// what kind of event occurred (readable, writable, signal, etc.)while i < cnt { //遍历所有就绪的事件,进行处理let evt = self.events.get(i).unwrap();
trace!("event={:?}; idx={:?}", evt, i);
// mio在epoll之上,增加了NOTIFY和TIMERmatch evt.token() {
NOTIFY => self.notify(handler), //channel处理 ,这个epoll中是没有的,mio实现
TIMER => self.timer_process(handler), //Timer处理, 这个epoll中也是没有的,mio实现
_ => self.io_event(handler, evt) //IO事件的处理, 这个epoll有
}
i += 1;
}
}
fnio_event(&mutself, handler: &mut H, evt: Event) {
handler.ready(self, evt.token(), evt.readiness()); //调用Handler::ready() 【1】
}
fnnotify(&mutself, handler: &mut H) {
for _ in0..self.config.messages_per_tick {
matchself.notify_rx.try_recv() { //从channel中接收数据,内部实现是std::sync::mpsc::sync_channel()Ok(msg) => handler.notify(self, msg), //调用Handler::notify() 【2】
_ => break,
}
}
// Re-registerlet _ = self.poll.reregister(&self.notify_rx, NOTIFY, Ready::readable(), PollOpt::edge() | PollOpt::oneshot()); //PollOpt::oneshot(),必须重新reregister.
}
fntimer_process(&mutself, handler: &mut H) {
whileletSome(t) = self.timer.poll() {
handler.timeout(self, t); //调用Handler::timeout() 【3】
}
}
/// Registers an IO handle with the event loop.pubfnregister<E: ?Sized>(&mutself, io: &E, token: Token, interest: Ready, opt: PollOpt) -> io::Result<()>
where E: Evented
{
self.poll.register(io, token, interest, opt)
}
/// Re-Registers an IO handle with the event loop.pubfnreregister<E: ?Sized>(&mutself, io: &E, token: Token, interest: Ready, opt: PollOpt) -> io::Result<()>
where E: Evented
{
self.poll.reregister(io, token, interest, opt)
}
/// Deregisters an IO handle with the event loop.pubfnderegister<E: ?Sized>(&mutself, io: &E) -> io::Result<()> where E: Evented {
self.poll.deregister(io)
}
/// Returns a sender that allows sending messages to the event loop in a/// thread-safe way, waking up the event loop if needed.pubfnchannel(&self) -> Sender<H::Message> {
Sender::new(self.notify_tx.clone())
}
/// Schedules a timeout after the requested time interval. When the/// duration has been reached,pubfntimeout(&mutself, token: H::Timeout, delay: Duration) -> timer::Result<Timeout> {
self.timer.set_timeout(delay, token)
}
/// If the supplied timeout has not been triggered, cancel it such that it/// will not be triggered in the future.pubfnclear_timeout(&mutself, timeout: &Timeout) -> bool {
self.timer.cancel_timeout(&timeout).is_some()
}
/// Tells the event loop to exit after it is done handling all events in the current iteration.pubfnshutdown(&mutself) {
self.run = false;
}
/// Indicates whether the event loop is currently running. If it's not it has either/// stopped or is scheduled to stop on the next tick.pubfnis_running(&self) -> bool {
self.run
}
}
pubstructPoll {
// Platform specific IO selector
selector: sys::Selector,
// Custom readiness queue// The second readiness queue is implemented in user space by `ReadinessQueue`. It provides a way to implement purely user space `Evented` types.
readiness_queue: ReadinessQueue, //区别于系统就绪队列(sys::Selector),这是上层自己实现的就绪队列// Use an atomic to first check if a full lock will be required. This is a// fast-path check for single threaded cases avoiding the extra syscall
lock_state: AtomicUsize,
// Sequences concurrent calls to `Poll::poll`
lock: Mutex<()>,
// Wakeup the next waiter
condvar: Condvar,
}
impl Poll {
/// Return a new `Poll` handle.pubfnnew() -> io::Result<Poll> {
is_send::<Poll>();
is_sync::<Poll>();
let poll = Poll {
selector: sys::Selector::new()?,
readiness_queue: ReadinessQueue::new()?,
lock_state: AtomicUsize::new(0),
lock: Mutex::new(()),
condvar: Condvar::new(),
};
// Register the notification wakeup FD with the IO poller
poll.readiness_queue.inner.awakener.register(&poll, AWAKEN, Ready::readable(), PollOpt::edge())?;
Ok(poll)
}
/// Wait for readiness events////// Blocks the current thread and waits for readiness events for any of the/// `Evented` handles that have been registered with this `Poll` instance./// The function will block until either at least one readiness event has/// been received or `timeout` has elapsed. A `timeout` of `None` means that/// `poll` will block until a readiness event has been received.pubfnpoll(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<usize> {
self.poll1(events, timeout, false) //Poll::poll()非常最重要的一个方法, poll()-->poll1()-->poll2()
}
fnpoll1(&self, events: &mut Events, mut timeout: Option<Duration>, interruptible: bool) -> io::Result<usize> {
let zero = Some(Duration::from_millis(0));
letmut curr = self.lock_state.compare_and_swap(0, 1, SeqCst);
if0 != curr { ... } //{ ... }代表中间有很多代码被省略掉了.let ret = self.poll2(events, timeout, interruptible);
// Release the lockif1 != self.lock_state.fetch_and(!1, Release) { ... } //{ ... }代表中间有很多代码被省略掉了.
ret
}
#[inline]fnpoll2(&self, events: &mut Events, mut timeout: Option<Duration>, interruptible: bool) -> io::Result<usize> {
// Compute the timeout value passed to the system selector. If the// readiness queue has pending nodes, we still want to poll the system// selector for new events, but we don't want to block the thread to// wait for new events.if timeout == Some(Duration::from_millis(0)) {
// If blocking is not requested, then there is no need to prepare// the queue for sleep//// The sleep_marker should be removed by readiness_queue.poll().
} elseifself.readiness_queue.prepare_for_sleep() {
// The readiness queue is empty. The call to `prepare_for_sleep`// inserts `sleep_marker` into the queue. This signals to any// threads setting readiness that the `Poll::poll` is going to// sleep, so the awakener should be used.
} else {
// The readiness queue is not empty, so do not block the thread.
timeout = Some(Duration::from_millis(0));
}
//poll系统就绪队列loop {
let now = Instant::now();
// First get selector eventslet res = self.selector.select(&mut events.inner, AWAKEN, timeout); //Linux下调用epoll_wait(),就绪事件放入events中match res {
Ok(true) => {
// Some awakeners require reading from a FD.self.readiness_queue.inner.awakener.cleanup();
break;
}
Ok(false) => break,
Err(ref e) if e.kind() == io::ErrorKind::Interrupted && !interruptible => {
// Interrupted by a signal; update timeout if necessary and retryifletSome(to) = timeout {
let elapsed = now.elapsed();
if elapsed >= to {
break;
} else {
timeout = Some(to - elapsed);
}
}
}
Err(e) => returnErr(e),
}
}
// Poll custom event queueself.readiness_queue.poll(&mut events.inner); //Poll用户就绪队列// Return number of polled eventsOk(events.inner.len())
}
/// Register an `Evented` handle with the `Poll` instance.pubfnregister<E: ?Sized>(&self, handle: &E, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()>
where E: Evented {
validate_args(token)?;
// Register interests for this socket
handle.register(self, token, interest, opts)?;
Ok(())
}
/// Re-register an `Evented` handle with the `Poll` instance.pubfnreregister<E: ?Sized>(&self, handle: &E, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()>
where E: Evented {
validate_args(token)?;
// Register interests for this socket
handle.reregister(self, token, interest, opts)?;
Ok(())
}
/// Deregister an `Evented` handle with the `Poll` instance.pubfnderegister<E: ?Sized>(&self, handle: &E) -> io::Result<()>
where E: Evented {
// Deregister interests for this socket
handle.deregister(self)?;
Ok(())
}
}
pubstructSelector {
id: usize,
epfd: RawFd,
}
impl Selector {
pubfnnew() -> io::Result<Selector> {
let epfd = unsafe {
// Emulate `epoll_create` by using `epoll_create1` if it's available// and otherwise falling back to `epoll_create` followed by a call to// set the CLOEXEC flag.
dlsym!(fnepoll_create1(c_int) -> c_int);
match epoll_create1.get() {
Some(epoll_create1_fn) => {
cvt(epoll_create1_fn(libc::EPOLL_CLOEXEC))?
}
None => {
let fd = cvt(libc::epoll_create(1024))?;
drop(set_cloexec(fd));
fd
}
}
};
// offset by 1 to avoid choosing 0 as the id of a selectorlet id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1;
Ok(Selector {
id: id,
epfd: epfd,
})
}
pubfnid(&self) -> usize {
self.id
}
/// Wait for events from the OSpubfnselect(&self, evts: &mut Events, awakener: Token, timeout: Option<Duration>) -> io::Result<bool> {
let timeout_ms = timeout
.map(|to| cmp::min(millis(to), i32::MAX asu64) asi32)
.unwrap_or(-1);
// Wait for epoll events for at most timeout_ms milliseconds
evts.clear();
unsafe {
let cnt = cvt(libc::epoll_wait(self.epfd,
evts.events.as_mut_ptr(),
evts.events.capacity() asi32,
timeout_ms))?;
let cnt = cnt asusize;
evts.events.set_len(cnt);
for i in0..cnt {
if evts.events[i].u64asusize == awakener.into() {
evts.events.remove(i);
returnOk(true);
}
}
}
Ok(false)
}
/// Register event interests for the given IO handle with the OSpubfnregister(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
letmut info = libc::epoll_event {
events: ioevent_to_epoll(interests, opts),
u64: usize::from(token) asu64
};
unsafe {
cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, fd, &mut info))?;
Ok(())
}
}
/// Register event interests for the given IO handle with the OSpubfnreregister(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
letmut info = libc::epoll_event {
events: ioevent_to_epoll(interests, opts),
u64: usize::from(token) asu64
};
unsafe {
cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_MOD, fd, &mut info))?;
Ok(())
}
}
/// Deregister event interests for the given IO handle with the OSpubfnderegister(&self, fd: RawFd) -> io::Result<()> {
// The &info argument should be ignored by the system,// but linux < 2.6.9 required it to be not null.// For compatibility, we provide a dummy EpollEvent.letmut info = libc::epoll_event {
events: 0,
u64: 0,
};
unsafe {
cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_DEL, fd, &mut info))?;
Ok(())
}
}
}
【4】Notify channel代码分析
这个涉及的代码比较多,比较杂,也较为难以理解。
// `ReadinessQueue` is backed by a MPSC queue that supports reuse of linked// list nodes. This significantly reduces the number of required allocations.// Each `Registration` / `SetReadiness` pair allocates a single readiness node// that is used for the lifetime of the registration.//// The readiness node also includes a single atomic variable, `state` that// tracks most of the state associated with the registration. This includes the// current readiness, interest, poll options, and internal state. When the node// state is mutated, it is queued in the MPSC channel. A call to// `ReadinessQueue::poll` will dequeue and process nodes. The node state can// still be mutated while it is queued in the channel for processing.// Intermediate state values do not matter as long as the final state is// included in the call to `poll`. This is the eventually consistent nature of// the readiness queue.//// The readiness node is ref counted using the `ref_count` field. On creation,// the ref_count is initialized to 3: one `Registration` handle, one// `SetReadiness` handle, and one for the readiness queue. Since the readiness queue// doesn't *always* hold a handle to the node, we don't use the Arc type for// managing ref counts (this is to avoid constantly incrementing and// decrementing the ref count when pushing & popping from the queue). When the// `Registration` handle is dropped, the `dropped` flag is set on the node, then// the node is pushed into the registration queue. When Poll::poll pops the// node, it sees the drop flag is set, and decrements it's ref count.//// The MPSC queue is a modified version of the intrusive MPSC node based queue// described by 1024cores [1].#[derive(Clone)]structReadinessQueue {
inner: Arc<ReadinessQueueInner>,
}
structReadinessQueueInner {
// Used to wake up `Poll` when readiness is set in another thread.
awakener: sys::Awakener,
// Head of the MPSC queue used to signal readiness to `Poll::poll`.
head_readiness: AtomicPtr<ReadinessNode>,
// Tail of the readiness queue.//// Only accessed by Poll::poll. Coordination will be handled by the poll fn
tail_readiness: UnsafeCell<*mut ReadinessNode>,
// Fake readiness node used to punctuate the end of the readiness queue.// Before attempting to read from the queue, this node is inserted in order// to partition the queue between nodes that are "owned" by the dequeue end// and nodes that will be pushed on by producers.
end_marker: Box<ReadinessNode>,
// Similar to `end_marker`, but this node signals to producers that `Poll`// has gone to sleep and must be woken up.
sleep_marker: Box<ReadinessNode>,
// Similar to `end_marker`, but the node signals that the queue is closed.// This happens when `ReadyQueue` is dropped and signals to producers that// the nodes should no longer be pushed into the queue.
closed_marker: Box<ReadinessNode>,
}
/// Node shared by a `Registration` / `SetReadiness` pair as well as the node/// queued into the MPSC channel.structReadinessNode {
// Node state, see struct docs for `ReadinessState`//// This variable is the primary point of coordination between all the// various threads concurrently accessing the node.
state: AtomicState,
// The registration token cannot fit into the `state` variable, so it is// broken out here. In order to atomically update both the state and token// we have to jump through a few hoops.//// First, `state` includes `token_read_pos` and `token_write_pos`. These can// either be 0, 1, or 2 which represent a token slot. `token_write_pos` is// the token slot that contains the most up to date registration token.// `token_read_pos` is the token slot that `poll` is currently reading from.//// When a call to `update` includes a different token than the one currently// associated with the registration (token_write_pos), first an unused token// slot is found. The unused slot is the one not represented by// `token_read_pos` OR `token_write_pos`. The new token is written to this// slot, then `state` is updated with the new `token_write_pos` value. This// requires that there is only a *single* concurrent call to `update`.//// When `poll` reads a node state, it checks that `token_read_pos` matches// `token_write_pos`. If they do not match, then it atomically updates// `state` such that `token_read_pos` is set to `token_write_pos`. It will// then read the token at the newly updated `token_read_pos`.
token_0: UnsafeCell<Token>,
token_1: UnsafeCell<Token>,
token_2: UnsafeCell<Token>,
// Used when the node is queued in the readiness linked list. Accessing// this field requires winning the "queue" lock
next_readiness: AtomicPtr<ReadinessNode>,
// Ensures that there is only one concurrent call to `update`.//// Each call to `update` will attempt to swap `update_lock` from `false` to// `true`. If the CAS succeeds, the thread has obtained the update lock. If// the CAS fails, then the `update` call returns immediately and the update// is discarded.
update_lock: AtomicBool,
// Pointer to Arc<ReadinessQueueInner>
readiness_queue: AtomicPtr<()>,
// Tracks the number of `ReadyRef` pointers
ref_count: AtomicUsize,
}
/// Handle to a user space `Poll` registration.////// `Registration` allows implementing [`Evented`] for types that cannot work/// with the [system selector]. A `Registration` is always paired with a/// `SetReadiness`, which allows updating the registration's readiness state./// When [`set_readiness`] is called and the `Registration` is associated with a/// [`Poll`] instance, a readiness event will be created and eventually returned/// by [`poll`].pubstructRegistration {
inner: RegistrationInner,
}
/// Updates the readiness state of the associated `Registration`.#[derive(Clone)]pubstructSetReadiness {
inner: RegistrationInner,
}
pubstructTimer<T> {
// Size of each tick in milliseconds
tick_ms: u64,
// Slab of timeout entries
entries: Slab<Entry<T>>,
// Timeout wheel. Each tick, the timer will look at the next slot for// timeouts that match the current tick.
wheel: Vec<WheelEntry>,
// Tick 0's time instant
start: Instant,
// The current tick
tick: Tick,
// The next entry to possibly timeout
next: Token,
// Masks the target tick to get the slot
mask: u64,
// Set on registration with Poll
inner: LazyCell<Inner>,
}
#[macro_use]externcrate log;
externcrate simple_logger;
externcrate mio;
use mio::*;
use mio::tcp::{TcpListener, TcpStream};
use std::io::{Read,Write};
fnmain() {
simple_logger::init().unwrap();
// Setup some tokens to allow us to identify which event is for which socket.const SERVER: Token = Token(0);
const CLIENT: Token = Token(1);
let addr = "127.0.0.1:12345".parse().unwrap();
// Setup the server socketlet server = TcpListener::bind(&addr).unwrap();
// Create a poll instancelet poll = Poll::new().unwrap();
// Start listening for incoming connections
poll.register(&server, SERVER, Ready::readable(), PollOpt::edge()).unwrap();
// Setup the client socketlet sock = TcpStream::connect(&addr).unwrap();
// Register the socket
poll.register(&sock, CLIENT, Ready::readable(), PollOpt::edge()).unwrap();
// Create storage for eventsletmut events = Events::with_capacity(1024);
loop {
poll.poll(&mut events, None).unwrap();
for event in events.iter() {
match event.token() {
SERVER => {
// Accept and drop the socket immediately, this will close// the socket and notify the client of the EOF.let (stream,addr) = server.accept().unwrap();
info!("Listener accept {:?}",addr);
},
CLIENT => {
// The server just shuts down the socket, let's just exit// from our event loop.
info!("client response.");
return;
},
_ => unreachable!(),
}
}
}
}
#[macro_use]externcrate log;
externcrate simple_logger;
externcrate mio;
use mio::*;
use mio::timer::{Timeout};
use mio::deprecated::{EventLoop, Handler, Sender, EventLoopBuilder};
use std::thread;
use std::time::Duration;
fnmain() {
simple_logger::init().unwrap();
letmut event_loop=EventLoop::new().unwrap();
let channel_sender=event_loop.channel();
thread::spawn(move ||{
channel_sender.send(IoMessage::Notify);
thread::sleep_ms(5*1000);
channel_sender.send(IoMessage::End);
});
let timeout = event_loop.timeout(Token(123), Duration::from_millis(3000)).unwrap();
letmut handler=MioHandler::new();
let _ = event_loop.run(&mut handler).unwrap();
}
pubenumIoMessage{
Notify,
End,
}
pubstructMioHandler{
}
impl MioHandler{
pubfnnew()->Self{
MioHandler{}
}
}
impl Handler for MioHandler {
typeTimeout = Token;
typeMessage = IoMessage;
/// Invoked when the socket represented by `token` is ready to be operated on.fnready(&mutself, event_loop: &mut EventLoop<Self>, token: Token, events: Ready) {
}
/// Invoked when a message has been received via the event loop's channel.fnnotify(&mutself, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
match msg {
IoMessage::Notify=>info!("channel notify"),
IoMessage::End=>{
info!("shutdown eventloop.");
event_loop.shutdown();
}
}
}
/// Invoked when a timeout has completed.fntimeout(&mutself, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
match timeout{
Token(123)=>info!("time out."),
Token(_)=>{},
}
}
/// Invoked when `EventLoop` has been interrupted by a signal interrupt.fninterrupted(&mutself, event_loop: &mut EventLoop<Self>) {
}
/// Invoked at the end of an event loop tick.fntick(&mutself, event_loop: &mut EventLoop<Self>) {
}
}