├── examples ├── next.rs ├── timeout.rs └── tcp.rs ├── .gitignore ├── .travis.yml ├── src ├── util.rs ├── mutscoped.rs ├── error.rs ├── handler.rs ├── lib.rs └── ioloop.rs ├── Cargo.toml └── README.md /examples/next.rs: -------------------------------------------------------------------------------- 1 | extern crate event; 2 | 3 | fn main() { 4 | event::next(|| { println!("Hello World!"); }).unwrap(); 5 | event::run().unwrap(); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.dylib 8 | *.dSYM 9 | *.dll 10 | *.rlib 11 | *.dummy 12 | *.exe 13 | *-test 14 | /doc/ 15 | /target/ 16 | /examples/* 17 | !/examples/*.rs 18 | Cargo.lock 19 | 20 | -------------------------------------------------------------------------------- /examples/timeout.rs: -------------------------------------------------------------------------------- 1 | #![feature(std_misc)] 2 | extern crate event; 3 | 4 | use std::time::duration::Duration; 5 | 6 | fn main() { 7 | event::interval(|| { println!("Hello World!"); }, Duration::milliseconds(500)); 8 | event::run().unwrap(); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | env: 3 | - LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib/rustlib/x86_64-unknown-linux-gnu/lib/ 4 | install: 5 | - curl https://static.rust-lang.org/rustup.sh | sudo bash 6 | script: 7 | - cargo build -v 8 | - cargo test -v 9 | - cargo doc -v 10 | os: 11 | - linux 12 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | use mio::{IoDesc, IoHandle}; 2 | 3 | pub struct Desc<'a> { 4 | desc: &'a IoDesc 5 | } 6 | 7 | impl<'a> Desc<'a> { 8 | pub fn new(desc: &'a IoDesc) -> Desc<'a> { 9 | Desc { desc: desc } 10 | } 11 | } 12 | 13 | impl<'a> IoHandle for Desc<'a> { 14 | fn desc(&self) -> &IoDesc { self.desc } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "event" 4 | version = "0.2.2" 5 | authors = ["Jonathan Reem "] 6 | description = "A mulit-threaded event loop for Rust" 7 | repository = "https://github.com/reem/rust-event" 8 | license = "MIT" 9 | 10 | [dependencies.log] 11 | version = "*" 12 | 13 | [dependencies.mio] 14 | git = "https://github.com/carllerche/mio" 15 | 16 | -------------------------------------------------------------------------------- /src/mutscoped.rs: -------------------------------------------------------------------------------- 1 | use core::nonzero::NonZero; 2 | use std::cell::RefCell; 3 | 4 | pub struct MutScoped { 5 | inner: RefCell> 6 | } 7 | 8 | impl MutScoped { 9 | pub fn new(reff: &mut T) -> MutScoped { 10 | MutScoped { inner: RefCell::new(unsafe { NonZero::new(reff as *mut _) }) } 11 | } 12 | 13 | pub unsafe fn borrow_mut R, R>(&self, cb: F) -> R { 14 | cb(&mut ***self.inner.borrow_mut()) 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use mio::{MioError, TimerError}; 2 | use std::error::FromError; 3 | 4 | pub type EventResult = Result; 5 | 6 | #[derive(Debug)] 7 | pub enum EventError { 8 | MioError(MioError), 9 | TimerError(TimerError) 10 | } 11 | 12 | impl FromError for EventError { 13 | fn from_error(err: MioError) -> EventError { 14 | EventError::MioError(err) 15 | } 16 | } 17 | 18 | impl FromError for EventError { 19 | fn from_error(err: TimerError) -> EventError { 20 | EventError::TimerError(err) 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/handler.rs: -------------------------------------------------------------------------------- 1 | use mio::{IoDesc, IoHandle, ReadHint, Interest, PollOpt}; 2 | 3 | pub trait Handler: 'static { 4 | fn readable(&mut self, hint: ReadHint) -> bool; 5 | fn writable(&mut self) -> bool; 6 | fn desc(&self) -> &IoDesc; 7 | 8 | fn interest(&self) -> Option { None } 9 | fn opt(&self) -> Option { None } 10 | } 11 | 12 | pub struct ClosureHandler { 13 | pub io: I, 14 | pub read: R, 15 | pub write: W, 16 | pub interest: Option, 17 | pub opt: Option 18 | } 19 | 20 | impl Handler for ClosureHandler 21 | where I: Send + IoHandle, 22 | R: Send + FnMut(&mut I, ReadHint) -> bool, 23 | W: Send + FnMut(&mut I) -> bool { 24 | fn readable(&mut self, hint: ReadHint) -> bool { 25 | (self.read)(&mut self.io, hint) 26 | } 27 | 28 | fn writable(&mut self) -> bool { 29 | (self.write)(&mut self.io) 30 | } 31 | 32 | fn desc(&self) -> &IoDesc { 33 | self.io.desc() 34 | } 35 | 36 | fn interest(&self) -> Option { 37 | self.interest 38 | } 39 | 40 | fn opt(&self) -> Option { 41 | self.opt 42 | } 43 | } 44 | 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Event [![Build Status](https://travis-ci.org/reem/rust-event.svg?branch=master)](https://travis-ci.org/reem/rust-event) 2 | 3 | > A fast, thread-local event loop. 4 | 5 | ## Overview 6 | 7 | This is a fully-featured event loop built on top of `mio` that provides 8 | IO, timeout, and "next tick" listeners. It is meant to expose nearly all 9 | of the flexibility and speed of mio while making huge ergonomic gains by 10 | internally managing handler registration. 11 | 12 | ## Examples 13 | 14 | There are examples in the `examples` folder that show the functionality of 15 | `event`. 16 | 17 | ## Benchmarks 18 | 19 | A simple wrk benchmark of the tcp example\*: 20 | 21 | ```sh 22 | $ ./target/release/examples/tcp& 23 | $ wrk -t12 -c100 -d10s http://localhost:3000/ 24 | Running 10s test @ http://localhost:3000/ 25 | 12 threads and 100 connections 26 | Thread Stats Avg Stdev Max +/- Stdev 27 | Latency 0.00us 0.00us 0.00us 100.00% 28 | Req/Sec 0.94M 73.98k 1.03M 90.09% 29 | 8913835 requests in 10.01s, 450.55MB read 30 | Socket errors: connect 0, read 0, write 0, timeout 397 31 | Requests/sec: 890692.14 32 | Transfer/sec: 45.02MB 33 | ``` 34 | 35 | You did read that correctly. 36 | 37 | ## License 38 | 39 | MIT 40 | 41 | -------------------------------------------------------------------------------- /examples/tcp.rs: -------------------------------------------------------------------------------- 1 | #![feature(unboxed_closures)] 2 | 3 | extern crate event; 4 | extern crate mio; 5 | 6 | #[macro_use] extern crate log; 7 | 8 | use event::{run, register, ClosureHandler}; 9 | 10 | use mio::net::SockAddr; 11 | use mio::net::tcp::{TcpSocket, TcpAcceptor}; 12 | use mio::{IoWriter, IoAcceptor, PollOpt, Interest, ReadHint}; 13 | 14 | const RESPONSE: &'static str = "HTTP/1.1 200 OK\r 15 | Content-Length: 14\r 16 | \r 17 | Hello World\r 18 | \r"; 19 | 20 | fn accept(acceptor: &mut TcpAcceptor, _: ReadHint) -> bool { 21 | let sock = acceptor.accept().unwrap(); 22 | 23 | if !sock.would_block() { 24 | register(ClosureHandler { 25 | io: sock.unwrap(), 26 | read: |_: &mut _, _| true, 27 | write: |sock: &mut TcpSocket| { 28 | loop { 29 | match sock.write_slice(RESPONSE.as_bytes()) { 30 | Ok(..) => {}, 31 | Err(..) => return false 32 | } 33 | } 34 | }, 35 | interest: Some(Interest::writable()), 36 | opt: Some(PollOpt::edge() | PollOpt::oneshot()) 37 | }).unwrap(); 38 | } 39 | 40 | true 41 | } 42 | 43 | fn main() { 44 | let addr = SockAddr::parse("127.0.0.1:3000").ok() 45 | .expect("could not parse InetAddr"); 46 | 47 | // Open socket 48 | let srv = TcpSocket::v4().unwrap() 49 | .bind(&addr).unwrap() 50 | .listen(256).unwrap(); 51 | 52 | register(ClosureHandler { 53 | io: srv, 54 | read: accept, 55 | write: move |_: &mut TcpAcceptor| true, 56 | interest: Some(Interest::readable()), 57 | opt: Some(PollOpt::edge() | PollOpt::oneshot()) 58 | }).unwrap(); 59 | 60 | run().unwrap(); 61 | } 62 | 63 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(unboxed_closures, core, std_misc, box_syntax)] 2 | #![cfg_attr(test, deny(warnings))] 3 | // #![deny(missing_docs)] 4 | 5 | //! An Event-Loop for Rust. 6 | 7 | extern crate mio; 8 | extern crate core; 9 | 10 | #[macro_use] 11 | extern crate log; 12 | 13 | use std::cell::RefCell; 14 | use std::time::duration::Duration; 15 | 16 | use ioloop::{IoLoop, IoHandler}; 17 | use mutscoped::MutScoped; 18 | 19 | pub use mio::Timeout; 20 | pub use handler::{Handler, ClosureHandler}; 21 | pub use error::{EventResult, EventError}; 22 | 23 | mod ioloop; 24 | mod error; 25 | mod handler; 26 | mod util; 27 | mod mutscoped; 28 | 29 | thread_local! { 30 | static EVENT_LOOP_LOCKED: RefCell> = 31 | RefCell::new(box IoLoop::new()) 32 | } 33 | 34 | scoped_thread_local!(static EVENT_LOOP_FAST: MutScoped); 35 | scoped_thread_local!(static HANDLER: MutScoped); 36 | 37 | /// Register interest in an io descriptor. 38 | pub fn register(handler: H) -> EventResult<()> { 39 | apply_events(move |events| events.register(handler)) 40 | } 41 | 42 | /// Set a callback to be called soon after a certain timeout. 43 | pub fn timeout(callback: F, timeout: Duration) -> EventResult 44 | where F: FnOnce() + 'static { 45 | apply_events(move |events| events.timeout(callback, timeout)) 46 | } 47 | 48 | /// File a callback to be called on the next run of the event loop. 49 | pub fn next(callback: F) -> Result<(), ()> { 50 | apply_events(move |events| events.next(callback)) 51 | } 52 | 53 | pub fn interval(callback: F, delay: Duration) { 54 | struct Interval { 55 | callback: F, 56 | delay: Duration 57 | } 58 | 59 | impl FnOnce<()> for Interval { 60 | type Output = (); 61 | 62 | extern "rust-call" fn call_once(mut self, _: ()) { 63 | (self.callback)(); 64 | let delay = self.delay.clone(); 65 | timeout(self, delay).unwrap(); 66 | } 67 | } 68 | 69 | timeout(Interval { callback: callback, delay: delay.clone() }, delay).unwrap(); 70 | } 71 | 72 | /// Starts the event loop on this thread. 73 | /// 74 | /// ## Panics 75 | /// 76 | /// Panics if the event loop is already running on this thread. It should 77 | /// not be called recursively (from within anything running off the event loop). 78 | pub fn run() -> Result<(), EventError> { 79 | if EVENT_LOOP_FAST.is_set() { 80 | warn!("Tried to start and event loop when the event loop for that \ 81 | thread was already running. Nooping."); 82 | Ok(()) 83 | } else { 84 | EVENT_LOOP_LOCKED.with(move |event| { 85 | let mut event_ref = event.borrow_mut(); 86 | EVENT_LOOP_FAST.set( 87 | &MutScoped::new(&mut *event_ref), 88 | || { event_ref.run() } 89 | ) 90 | }) 91 | } 92 | } 93 | 94 | /// Shutdown the event loop on this thread, if it's running. 95 | /// 96 | /// ## Warns 97 | /// 98 | /// Warns if the event loop is not currently running on this thread. 99 | pub fn shutdown() { 100 | if EVENT_LOOP_FAST.is_set() { 101 | EVENT_LOOP_FAST.with(|event| { 102 | debug!("Shutting down the event loop."); 103 | unsafe { event.borrow_mut(|event| event.shutdown()) } 104 | }) 105 | } else { 106 | warn!("Tried to shutdown the event loop when it wasn't running.") 107 | } 108 | } 109 | 110 | fn apply_events(action: A) -> R 111 | where A: FnOnce(&mut IoLoop) -> R { 112 | if EVENT_LOOP_FAST.is_set() { 113 | EVENT_LOOP_FAST.with(move |event| unsafe { event.borrow_mut(action) }) 114 | } else { 115 | EVENT_LOOP_LOCKED.with(move |event| action(&mut event.borrow_mut())) 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /src/ioloop.rs: -------------------------------------------------------------------------------- 1 | use std::thunk::Invoke; 2 | use std::time::duration::Duration; 3 | 4 | use mio::util::Slab; 5 | use mio::{MioError, EventLoop, Token, IoDesc, Timeout, ReadHint, PollOpt, Interest}; 6 | use mio::Handler as MioHandler; 7 | use util::Desc; 8 | 9 | use mutscoped::MutScoped; 10 | 11 | use {EventResult, EventError, Handler, HANDLER}; 12 | 13 | type MioEventLoop = EventLoop; 14 | 15 | pub type Callback = IsSend>; 16 | 17 | struct IsSend(T); 18 | 19 | unsafe impl Send for IsSend {} 20 | 21 | const MAX_LISTENERS: usize = 64 * 1024; 22 | 23 | pub struct IoLoop { 24 | events: MioEventLoop, 25 | handler: Option 26 | } 27 | 28 | impl IoLoop { 29 | pub fn new() -> IoLoop { 30 | IoLoop { 31 | events: EventLoop::new().ok().expect("Failed to create mio event loop."), 32 | handler: Some(IoHandler::new()) 33 | } 34 | } 35 | 36 | pub fn run(&mut self) -> EventResult<()> { 37 | match self.events.run(&mut self.handler.take().unwrap()) { 38 | Ok(..) => Ok(()), 39 | Err(err) => { 40 | Err(EventError::MioError(err)) 41 | } 42 | } 43 | } 44 | 45 | pub fn register(&mut self, handler: H) -> Result<(), EventError> { 46 | match self.handler { 47 | Some(ref mut iohandler) => { 48 | iohandler.register(&mut self.events, Box::new(handler)) 49 | }, 50 | 51 | None => HANDLER.with(move |iohandler| { 52 | unsafe { 53 | iohandler.borrow_mut(move |iohandler| { 54 | iohandler.register(&mut self.events, Box::new(handler)) 55 | }) 56 | } 57 | }) 58 | } 59 | } 60 | 61 | 62 | pub fn timeout(&mut self, callback: F, 63 | timeout: Duration) -> EventResult 64 | where F: FnOnce() + 'static { 65 | Ok(try!(self.events.timeout(IsSend(Box::new(move |()| callback())), timeout))) 66 | } 67 | 68 | pub fn next(&mut self, callback: F) -> Result<(), ()> 69 | where F: FnOnce() + 'static { 70 | self.events.channel() 71 | .send(IsSend(Box::new(move |()| callback()))) 72 | .map_err(|_| ()) 73 | } 74 | 75 | pub fn shutdown(&mut self) { 76 | self.events.shutdown() 77 | } 78 | } 79 | 80 | pub struct IoHandler { 81 | slab: Slab>, 82 | } 83 | 84 | impl IoHandler { 85 | fn new() -> IoHandler { 86 | IoHandler { 87 | slab: Slab::new(MAX_LISTENERS) 88 | } 89 | } 90 | 91 | fn register(&mut self, events: &mut MioEventLoop, 92 | handler: Box) -> Result<(), EventError> { 93 | let token = self.slab.insert(handler) 94 | .ok().expect("More than MAX_LISTENERS events registered."); 95 | Ok(try!(register(events, &mut *self.slab[token], token))) 96 | } 97 | 98 | fn reregister(&mut self, events: &mut MioEventLoop, token: Token) { 99 | reregister(events, &mut *self.slab[token], token); 100 | } 101 | 102 | fn deregister(&mut self, events: &mut MioEventLoop, token: Token) { 103 | deregister(events, self.slab[token].desc()); 104 | self.slab.remove(token); 105 | } 106 | } 107 | 108 | impl MioHandler for IoHandler { 109 | fn readable(&mut self, events: &mut MioEventLoop, token: Token, 110 | hint: ReadHint) { 111 | HANDLER.set(&MutScoped::new(self), || { 112 | if !self.slab.contains(token) { return } 113 | 114 | if self.slab[token].readable(hint) { 115 | self.reregister(events, token); 116 | } else { 117 | self.deregister(events, token); 118 | } 119 | }) 120 | } 121 | 122 | fn writable(&mut self, events: &mut MioEventLoop, token: Token) { 123 | HANDLER.set(&MutScoped::new(self), || { 124 | if !self.slab.contains(token) { return } 125 | 126 | if self.slab[token].writable() { 127 | self.reregister(events, token); 128 | } else { 129 | self.deregister(events, token); 130 | } 131 | }) 132 | } 133 | 134 | fn notify(&mut self, _: &mut MioEventLoop, thunk: IsSend>) { 135 | thunk.0.invoke(()) 136 | } 137 | 138 | fn timeout(&mut self, _: &mut MioEventLoop, thunk: IsSend>) { 139 | thunk.0.invoke(()) 140 | } 141 | } 142 | 143 | fn register(events: &mut MioEventLoop, 144 | handler: &mut Handler, token: Token) -> Result<(), MioError> { 145 | events.register_opt( 146 | &Desc::new(handler.desc()), 147 | token, 148 | handler.interest().unwrap_or(Interest::readable()), 149 | handler.opt().unwrap_or(PollOpt::level()) 150 | ) 151 | } 152 | 153 | fn reregister(events: &mut MioEventLoop, handler: &mut Handler, token: Token) { 154 | let _ = events.reregister( 155 | &Desc::new(handler.desc()), 156 | token, 157 | handler.interest().unwrap_or(Interest::readable()), 158 | handler.opt().unwrap_or(PollOpt::level()) 159 | ); 160 | } 161 | 162 | fn deregister(events: &mut MioEventLoop, desc: &IoDesc) { 163 | let _ = events.deregister(&Desc::new(desc)).unwrap(); 164 | } 165 | 166 | --------------------------------------------------------------------------------