├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md ├── examples ├── http-echo-client.rs ├── http-echo-server.rs ├── simple.rs ├── tcp-echo-server.rs └── udp-echo-server.rs └── src ├── coroutine.rs ├── lib.rs ├── net ├── http │ ├── client.rs │ ├── conn.rs │ ├── mod.rs │ ├── server.rs │ ├── stream.rs │ └── utils.rs ├── mod.rs ├── tcp.rs └── udp.rs ├── options.rs ├── processor.rs ├── scheduler.rs └── sync ├── mod.rs └── mutex.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: nightly 3 | 4 | script: 5 | - cargo build -v 6 | - cargo test -v 7 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simplesched" 3 | version = "0.1.1" 4 | authors = ["Y. T. Chung "] 5 | description = "A naive coroutine based scheduler with asynchronous I/O support" 6 | repository = "https://github.com/zonyitoo/simplesched" 7 | license = "MIT" 8 | keywords = ["coroutine", "green", "net", "fiber"] 9 | 10 | [lib] 11 | name = "simplesched" 12 | 13 | [features] 14 | default = [ 15 | "openssl" 16 | ] 17 | 18 | [dependencies] 19 | log = "0.3.1" 20 | lazy_static = "0.1.14" 21 | url = "0.2" 22 | context = "^0.1.1" 23 | mio = "^0.4.1" 24 | bytes = "^0.2.10" 25 | hyper = "^0.6.8" 26 | 27 | [dev-dependencies] 28 | env_logger = "*" 29 | clap = "*" 30 | rand = "*" 31 | openssl = "*" 32 | num_cpus = "*" 33 | 34 | [dependencies.openssl] 35 | version = "^0.6.4" 36 | optional = true 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simple Sched 2 | 3 | The most naive scheduler for Coroutines in Rust. 4 | 5 | [![Build Status](https://img.shields.io/travis/zonyitoo/simplesched.svg)](https://travis-ci.org/zonyitoo/simplesched) 6 | [![Crates.io]( https://img.shields.io/crates/l/simplesched.svg)]( https://crates.io/crates/simplesched) 7 | [![Crates.io]( https://img.shields.io/crates/v/simplesched.svg)](https://crates.io/crates/simplesched) 8 | 9 | ## Usage 10 | 11 | ```toml 12 | [dependencies] 13 | simplesched = "*" 14 | ``` 15 | 16 | ### Basic 17 | 18 | ```rust 19 | extern crate simplesched; 20 | 21 | use simplesched::Scheduler; 22 | 23 | fn main() { 24 | Scheduler::spawn(|| { 25 | for _ in 0..10 { 26 | println!("Heil Hydra"); 27 | } 28 | }); 29 | 30 | Scheduler::run(1); 31 | } 32 | ``` 33 | 34 | ### TCP Echo Server 35 | 36 | ```rust 37 | extern crate simplesched; 38 | 39 | use std::io::{Read, Write}; 40 | 41 | use simplesched::net::TcpListener; 42 | use simplesched::Scheduler; 43 | 44 | fn main() { 45 | // Spawn a coroutine for accepting new connections 46 | Scheduler::spawn(move|| { 47 | let acceptor = TcpListener::bind("127.0.0.1:8080").unwrap(); 48 | println!("Waiting for connection ..."); 49 | 50 | for stream in acceptor.incoming() { 51 | let mut stream = stream.unwrap(); 52 | 53 | println!("Got connection from {:?}", stream.peer_addr().unwrap()); 54 | 55 | // Spawn a new coroutine to handle the connection 56 | Scheduler::spawn(move|| { 57 | let mut buf = [0; 1024]; 58 | 59 | loop { 60 | match stream.read(&mut buf) { 61 | Ok(0) => { 62 | println!("EOF"); 63 | break; 64 | }, 65 | Ok(len) => { 66 | println!("Read {} bytes, echo back", len); 67 | stream.write_all(&buf[0..len]).unwrap(); 68 | }, 69 | Err(err) => { 70 | println!("Error occurs: {:?}", err); 71 | break; 72 | } 73 | } 74 | } 75 | 76 | println!("Client closed"); 77 | }); 78 | } 79 | }); 80 | 81 | // Schedule with 4 threads 82 | Scheduler::run(4); 83 | } 84 | ``` 85 | 86 | More examples could be found in `examples`. 87 | 88 | ## Basic Benchmark 89 | 90 | ### Test environment 91 | * OS X 10.10.5 Beta 92 | * MacBook Pro Late 2013 93 | * 2.4GHz Intel Core i5 94 | * 8 GB 1600 MHz DDR3 95 | 96 | Release build. Run the `examples/http-echo-server.rs` with 4 threads, test it with `wrk`: 97 | 98 | ``` 99 | $ wrk -c 400 -t 2 http://127.0.0.1:8000/ 100 | Running 10s test @ http://127.0.0.1:8000/ 101 | 2 threads and 400 connections 102 | Thread Stats Avg Stdev Max +/- Stdev 103 | Latency 7.07ms 4.58ms 78.12ms 83.27% 104 | Req/Sec 29.07k 3.80k 39.00k 76.00% 105 | 578941 requests in 10.04s, 51.90MB read 106 | Socket errors: connect 0, read 101, write 0, timeout 0 107 | Requests/sec: 57667.33 108 | Transfer/sec: 5.17MB 109 | ``` 110 | 111 | Go 1.4.2 example HTTP echo server, with `GOMAXPROCS=4`: 112 | 113 | ``` 114 | $ wrk -c 400 -t 2 http://127.0.0.1:8000/ 115 | Running 10s test @ http://127.0.0.1:8000/ 116 | 2 threads and 400 connections 117 | Thread Stats Avg Stdev Max +/- Stdev 118 | Latency 6.01ms 3.68ms 96.42ms 84.52% 119 | Req/Sec 29.32k 6.53k 51.77k 71.21% 120 | 583573 requests in 10.05s, 75.13MB read 121 | Socket errors: connect 0, read 35, write 0, timeout 0 122 | Requests/sec: 58084.36 123 | Transfer/sec: 7.48MB 124 | ``` 125 | -------------------------------------------------------------------------------- /examples/http-echo-client.rs: -------------------------------------------------------------------------------- 1 | extern crate simplesched; 2 | extern crate clap; 3 | #[macro_use] extern crate log; 4 | extern crate env_logger; 5 | extern crate hyper; 6 | 7 | use std::io; 8 | 9 | use clap::{Arg, App}; 10 | 11 | use simplesched::scheduler::Scheduler; 12 | use simplesched::net::http::Client; 13 | 14 | use hyper::header::Connection; 15 | 16 | fn main() { 17 | env_logger::init().unwrap(); 18 | 19 | let matches = App::new("http-client") 20 | .version(env!("CARGO_PKG_VERSION")) 21 | .author("Y. T. Chung ") 22 | .arg(Arg::with_name("ADDR").short("a").long("addr").takes_value(true).required(true) 23 | .help("Address to connect")) 24 | .get_matches(); 25 | 26 | 27 | let addr = matches.value_of("ADDR").unwrap().to_owned(); 28 | 29 | Scheduler::spawn(move|| { 30 | let client = Client::new(); 31 | 32 | let mut res = client.get(&addr).header(Connection::close()).send().unwrap(); 33 | 34 | println!("Response: {}", res.status); 35 | println!("Headers:\n{}", res.headers); 36 | io::copy(&mut res, &mut io::stdout()).unwrap(); 37 | }); 38 | 39 | Scheduler::run(1); 40 | } 41 | -------------------------------------------------------------------------------- /examples/http-echo-server.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | #[macro_use] extern crate log; 3 | extern crate env_logger; 4 | extern crate hyper; 5 | 6 | extern crate simplesched; 7 | 8 | use std::io; 9 | 10 | use clap::{Arg, App}; 11 | 12 | use simplesched::Scheduler; 13 | use simplesched::net::http::Server; 14 | 15 | use hyper::{Get, Post}; 16 | use hyper::server::{Request, Response}; 17 | use hyper::uri::RequestUri::AbsolutePath; 18 | 19 | macro_rules! try_return( 20 | ($e:expr) => {{ 21 | match $e { 22 | Ok(v) => v, 23 | Err(e) => { println!("Error: {}", e); return; } 24 | } 25 | }} 26 | ); 27 | 28 | fn echo(mut req: Request, mut res: Response) { 29 | match req.uri { 30 | AbsolutePath(ref path) => match (&req.method, &path[..]) { 31 | (&Get, "/") | (&Get, "/echo") => { 32 | try_return!(res.send(b"Try POST /echo")); 33 | return; 34 | }, 35 | (&Post, "/echo") => (), // fall through, fighting mutable borrows 36 | _ => { 37 | *res.status_mut() = hyper::NotFound; 38 | return; 39 | } 40 | }, 41 | _ => { 42 | return; 43 | } 44 | }; 45 | 46 | let mut res = try_return!(res.start()); 47 | try_return!(io::copy(&mut req, &mut res)); 48 | } 49 | 50 | fn main() { 51 | env_logger::init().unwrap(); 52 | 53 | let matches = App::new("http-echo") 54 | .version(env!("CARGO_PKG_VERSION")) 55 | .author("Y. T. Chung ") 56 | .arg(Arg::with_name("BIND").short("b").long("bind").takes_value(true).required(true) 57 | .help("Listening on this address")) 58 | .arg(Arg::with_name("THREADS").short("t").long("threads").takes_value(true) 59 | .help("Number of threads")) 60 | .arg(Arg::with_name("KEEPALIVE").short("c").long("keep-alive").takes_value(false) 61 | .help("Keep alive")) 62 | .get_matches(); 63 | 64 | let bind_addr = matches.value_of("BIND").unwrap().to_owned(); 65 | 66 | let server = Server::http(&bind_addr[..]).unwrap(); 67 | server.listen(echo).unwrap(); 68 | 69 | Scheduler::run(matches.value_of("THREADS").unwrap_or("1").parse().unwrap()); 70 | } 71 | -------------------------------------------------------------------------------- /examples/simple.rs: -------------------------------------------------------------------------------- 1 | extern crate simplesched; 2 | 3 | use simplesched::Scheduler; 4 | 5 | fn main() { 6 | Scheduler::spawn(|| { 7 | for _ in 0..10 { 8 | println!("Heil Hydra"); 9 | } 10 | }); 11 | 12 | Scheduler::run(1); 13 | } 14 | -------------------------------------------------------------------------------- /examples/tcp-echo-server.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | #[macro_use] extern crate log; 3 | extern crate env_logger; 4 | extern crate mio; 5 | 6 | extern crate simplesched; 7 | 8 | use std::net::SocketAddr; 9 | 10 | use clap::{Arg, App}; 11 | 12 | use simplesched::Scheduler; 13 | use simplesched::net::tcp::TcpSocket; 14 | 15 | fn main() { 16 | env_logger::init().unwrap(); 17 | 18 | let matches = App::new("tcp-echo") 19 | .version(env!("CARGO_PKG_VERSION")) 20 | .author("Y. T. Chung ") 21 | .arg(Arg::with_name("BIND").short("b").long("bind").takes_value(true).required(true) 22 | .help("Listening on this address")) 23 | .arg(Arg::with_name("THREADS").short("t").long("threads").takes_value(true) 24 | .help("Number of threads")) 25 | .get_matches(); 26 | 27 | let bind_addr = matches.value_of("BIND").unwrap().to_owned(); 28 | 29 | Scheduler::spawn(move|| { 30 | let addr = bind_addr.parse().unwrap(); 31 | let server = match &addr { 32 | &SocketAddr::V4(..) => TcpSocket::v4(), 33 | &SocketAddr::V6(..) => TcpSocket::v6(), 34 | }.unwrap(); 35 | server.set_reuseaddr(true).unwrap(); 36 | server.bind(&addr).unwrap(); 37 | let server = server.listen(64).unwrap(); 38 | 39 | info!("Listening on {:?}", server.local_addr().unwrap()); 40 | 41 | for stream in server.incoming() { 42 | use std::io::{Read, Write}; 43 | 44 | let mut stream = stream.unwrap(); 45 | info!("Accept connection: {:?}", stream.peer_addr().unwrap()); 46 | 47 | Scheduler::spawn(move|| { 48 | let mut buf = [0; 10240]; 49 | 50 | loop { 51 | debug!("Trying to Read..."); 52 | match stream.read(&mut buf) { 53 | Ok(0) => { 54 | debug!("EOF received, going to close"); 55 | break; 56 | }, 57 | Ok(len) => { 58 | info!("Read {} bytes, echo back!", len); 59 | stream.write_all(&buf[0..len]).unwrap(); 60 | }, 61 | Err(err) => { 62 | panic!("Error occurs: {:?}", err); 63 | } 64 | } 65 | } 66 | 67 | info!("{:?} closed", stream.peer_addr().unwrap()); 68 | }); 69 | } 70 | }); 71 | 72 | Scheduler::run(matches.value_of("THREADS").unwrap_or("1").parse().unwrap()); 73 | } 74 | -------------------------------------------------------------------------------- /examples/udp-echo-server.rs: -------------------------------------------------------------------------------- 1 | extern crate clap; 2 | #[macro_use] extern crate log; 3 | extern crate env_logger; 4 | extern crate mio; 5 | 6 | extern crate simplesched; 7 | 8 | use std::net::SocketAddr; 9 | 10 | use clap::{Arg, App}; 11 | 12 | use simplesched::Scheduler; 13 | use simplesched::net::udp::UdpSocket; 14 | 15 | fn main() { 16 | env_logger::init().unwrap(); 17 | 18 | let matches = App::new("udp-echo") 19 | .version(env!("CARGO_PKG_VERSION")) 20 | .author("Y. T. Chung ") 21 | .arg(Arg::with_name("BIND").short("b").long("bind").takes_value(true).required(true) 22 | .help("Listening on this address")) 23 | .arg(Arg::with_name("THREADS").short("t").long("threads").takes_value(true) 24 | .help("Number of threads")) 25 | .get_matches(); 26 | 27 | let bind_addr = matches.value_of("BIND").unwrap().to_owned(); 28 | 29 | Scheduler::spawn(move|| { 30 | let addr: SocketAddr = bind_addr.parse().unwrap(); 31 | let server = UdpSocket::bind(&addr).unwrap(); 32 | 33 | info!("Listening on {:?}", server.local_addr().unwrap()); 34 | 35 | let mut buf = [0u8; 1024]; 36 | 37 | loop { 38 | let (len, peer_addr) = server.recv_from(&mut buf).unwrap(); 39 | info!("Accept connection: {:?}", peer_addr); 40 | 41 | server.send_to(&mut buf[..len], &peer_addr).unwrap(); 42 | } 43 | }); 44 | 45 | Scheduler::run(matches.value_of("THREADS").unwrap_or("1").parse().unwrap()); 46 | } 47 | -------------------------------------------------------------------------------- /src/coroutine.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc Developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | use std::rt; 23 | use std::cell::UnsafeCell; 24 | use std::mem; 25 | use std::any::Any; 26 | 27 | use context::{Context, Stack}; 28 | use context::stack::StackPool; 29 | use context::thunk::Thunk; 30 | 31 | use processor::Processor; 32 | use options::Options; 33 | 34 | thread_local!(static STACK_POOL: UnsafeCell = UnsafeCell::new(StackPool::new())); 35 | 36 | /// Initialization function for make context 37 | extern "C" fn coroutine_initialize(_: usize, f: *mut ()) -> ! { 38 | let ret = unsafe { 39 | let func: Box = mem::transmute(f); 40 | rt::unwind::try(move|| func.invoke(())) 41 | }; 42 | 43 | let processor = Processor::current(); 44 | 45 | let st = match ret { 46 | Ok(..) => { 47 | processor.yield_with(Ok(State::Finished)); 48 | State::Finished 49 | }, 50 | Err(err) => { 51 | processor.yield_with(Err(Error::Panicking(err))); 52 | State::Panicked 53 | } 54 | }; 55 | 56 | loop { 57 | processor.yield_with(Ok(st)); 58 | } 59 | } 60 | 61 | pub type Handle = Box; 62 | 63 | /// Coroutine is nothing more than a context and a stack 64 | pub struct Coroutine { 65 | context: Context, 66 | stack: Option, 67 | } 68 | 69 | impl Coroutine { 70 | pub unsafe fn empty() -> Handle { 71 | Box::new(Coroutine { 72 | context: Context::empty(), 73 | stack: None, 74 | }) 75 | } 76 | 77 | pub fn spawn_opts(f: F, opts: Options) -> Handle 78 | where F: FnOnce() + Send + 'static 79 | { 80 | let mut stack = STACK_POOL.with(|pool| unsafe { 81 | (&mut *pool.get()).take_stack(opts.stack_size) 82 | }); 83 | 84 | let ctx = Context::new(coroutine_initialize, 0, f, &mut stack); 85 | Box::new(Coroutine { 86 | context: ctx, 87 | stack: Some(stack), 88 | }) 89 | } 90 | 91 | pub fn yield_to(&mut self, target: &Coroutine) { 92 | Context::swap(&mut self.context, &target.context); 93 | } 94 | } 95 | 96 | impl Drop for Coroutine { 97 | fn drop(&mut self) { 98 | match self.stack.take() { 99 | None => {}, 100 | Some(st) => { 101 | STACK_POOL.with(|pool| unsafe { 102 | let pool: &mut StackPool = mem::transmute(pool.get()); 103 | pool.give_stack(st); 104 | }) 105 | } 106 | } 107 | } 108 | } 109 | 110 | #[derive(Debug, Copy, Clone)] 111 | pub enum State { 112 | Suspended, 113 | Blocked, 114 | Panicked, 115 | Finished, 116 | } 117 | 118 | #[derive(Debug)] 119 | pub enum Error { 120 | Panicking(Box), 121 | } 122 | 123 | pub type Result = ::std::result::Result; 124 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc Developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! The most naive coroutine scheduler with asynchronous I/O support. 23 | //! 24 | //! The scheduler will hold a global lock-free queue for tasks, all worker threads 25 | //! will take tasks from the global queue. 26 | 27 | #![feature(libc, rt, box_raw, reflect_marker)] 28 | 29 | extern crate context; 30 | #[macro_use] extern crate log; 31 | extern crate mio; 32 | extern crate libc; 33 | #[macro_use] extern crate lazy_static; 34 | extern crate hyper; 35 | extern crate url; 36 | #[cfg(feature = "openssl")] 37 | extern crate openssl; 38 | extern crate bytes; 39 | 40 | pub use scheduler::Scheduler; 41 | pub use options::Options; 42 | 43 | pub mod scheduler; 44 | pub mod net; 45 | pub mod processor; 46 | pub mod options; 47 | pub mod sync; 48 | mod coroutine; 49 | 50 | /// Spawn a new Coroutine 51 | pub fn spawn(f: F) 52 | where F: FnOnce() + Send + 'static 53 | { 54 | Scheduler::spawn(f) 55 | } 56 | 57 | /// Spawn a new Coroutine with options 58 | pub fn spawn_opts(f: F, opts: Options) 59 | where F: FnOnce() + Send + 'static 60 | { 61 | Scheduler::spawn_opts(f, opts) 62 | } 63 | 64 | /// Giveup the CPU 65 | pub fn sched() { 66 | Scheduler::sched() 67 | } 68 | 69 | pub struct Builder { 70 | opts: Options 71 | } 72 | 73 | impl Builder { 74 | pub fn new() -> Builder { 75 | Builder { 76 | opts: Options::new() 77 | } 78 | } 79 | 80 | pub fn stack_size(mut self, stack_size: usize) -> Builder { 81 | self.opts.stack_size = stack_size; 82 | self 83 | } 84 | 85 | pub fn name(mut self, name: Option) -> Builder { 86 | self.opts.name = name; 87 | self 88 | } 89 | 90 | pub fn spawn(self, f: F) 91 | where F: FnOnce() + Send + 'static 92 | { 93 | Scheduler::spawn_opts(f, self.opts) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/net/http/client.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | use std::ops::{Deref, DerefMut}; 24 | 25 | use hyper::client; 26 | use hyper::net::{NetworkConnector, NetworkStream}; 27 | use hyper::http::Protocol; 28 | use hyper::http::h1::Http11Protocol; 29 | 30 | use net::http::conn::DefaultConnector; 31 | 32 | pub struct Client(client::Client); 33 | 34 | impl Client { 35 | pub fn new() -> Client { 36 | Client::with_connector(DefaultConnector::default()) 37 | } 38 | 39 | /// Create a new client with a specific connector. 40 | pub fn with_connector(connector: C) -> Client 41 | where C: NetworkConnector + Send + Sync + 'static, 42 | S: NetworkStream + Send 43 | { 44 | Client::with_protocol(Http11Protocol::with_connector(connector)) 45 | } 46 | 47 | /// Create a new client with a specific `Protocol`. 48 | pub fn with_protocol(protocol: P) -> Client { 49 | Client(client::Client::with_protocol(protocol)) 50 | } 51 | } 52 | 53 | impl Deref for Client { 54 | type Target = client::Client; 55 | 56 | fn deref(&self) -> &client::Client { 57 | &self.0 58 | } 59 | } 60 | 61 | impl DerefMut for Client { 62 | fn deref_mut(&mut self) -> &mut client::Client { 63 | &mut self.0 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/net/http/conn.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | #![allow(dead_code)] 24 | 25 | use std::io::{self, Read, Write}; 26 | use std::net::{ToSocketAddrs, SocketAddr, Shutdown}; 27 | use std::fmt; 28 | use std::convert::From; 29 | 30 | use hyper; 31 | use hyper::net::{NetworkListener, NetworkStream, NetworkConnector}; 32 | 33 | use net::tcp::{TcpStream, TcpListener}; 34 | use net; 35 | 36 | pub struct HttpListener(TcpListener); 37 | 38 | impl Clone for HttpListener { 39 | fn clone(&self) -> HttpListener { 40 | HttpListener(self.0.try_clone().unwrap()) 41 | } 42 | } 43 | 44 | impl HttpListener { 45 | 46 | /// Start listening to an address over HTTP. 47 | pub fn new(addr: To) -> io::Result { 48 | Ok(HttpListener(try!(TcpListener::bind(addr)))) 49 | } 50 | 51 | } 52 | 53 | impl NetworkListener for HttpListener { 54 | type Stream = HttpStream; 55 | 56 | #[inline] 57 | fn accept(&mut self) -> hyper::Result { 58 | Ok(HttpStream(try!(self.0.accept()))) 59 | } 60 | 61 | #[inline] 62 | fn local_addr(&mut self) -> io::Result { 63 | self.0.local_addr() 64 | } 65 | } 66 | 67 | pub struct HttpStream(TcpStream); 68 | 69 | impl Clone for HttpStream { 70 | fn clone(&self) -> HttpStream { 71 | HttpStream(self.0.try_clone().unwrap()) 72 | } 73 | } 74 | 75 | impl Read for HttpStream { 76 | #[inline] 77 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 78 | self.0.read(buf) 79 | } 80 | } 81 | 82 | impl Write for HttpStream { 83 | #[inline] 84 | fn write(&mut self, buf: &[u8]) -> io::Result { 85 | self.0.write(buf) 86 | } 87 | 88 | #[inline] 89 | fn flush(&mut self) -> io::Result<()> { 90 | self.0.flush() 91 | } 92 | } 93 | 94 | impl fmt::Debug for HttpStream { 95 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 96 | write!(f, "HttpStream(_)") 97 | } 98 | } 99 | 100 | impl NetworkStream for HttpStream { 101 | #[inline] 102 | fn peer_addr(&mut self) -> io::Result { 103 | self.0.peer_addr() 104 | } 105 | 106 | #[inline] 107 | fn close(&mut self, how: Shutdown) -> io::Result<()> { 108 | let how = match how { 109 | Shutdown::Read => net::Shutdown::Read, 110 | Shutdown::Write => net::Shutdown::Write, 111 | Shutdown::Both => net::Shutdown::Both, 112 | }; 113 | 114 | self.0.shutdown(how) 115 | } 116 | } 117 | 118 | /// A connector that will produce HttpStreams. 119 | #[derive(Debug, Clone, Default)] 120 | pub struct HttpConnector; 121 | 122 | impl NetworkConnector for HttpConnector { 123 | type Stream = HttpStream; 124 | 125 | fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result { 126 | let addr = &(host, port); 127 | Ok(try!(match scheme { 128 | "http" => { 129 | debug!("http scheme"); 130 | Ok(HttpStream(try!(TcpStream::connect(addr)))) 131 | }, 132 | _ => { 133 | Err(io::Error::new(io::ErrorKind::InvalidInput, 134 | "Invalid scheme for Http")) 135 | } 136 | })) 137 | } 138 | } 139 | 140 | /// A stream over the HTTP protocol, possibly protected by SSL. 141 | #[derive(Debug, Clone)] 142 | pub enum HttpsStream { 143 | /// A plain text stream. 144 | Http(HttpStream), 145 | /// A stream protected by SSL. 146 | Https(S) 147 | } 148 | 149 | impl Read for HttpsStream { 150 | #[inline] 151 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 152 | match *self { 153 | HttpsStream::Http(ref mut s) => s.read(buf), 154 | HttpsStream::Https(ref mut s) => s.read(buf) 155 | } 156 | } 157 | } 158 | 159 | impl Write for HttpsStream { 160 | #[inline] 161 | fn write(&mut self, msg: &[u8]) -> io::Result { 162 | match *self { 163 | HttpsStream::Http(ref mut s) => s.write(msg), 164 | HttpsStream::Https(ref mut s) => s.write(msg) 165 | } 166 | } 167 | 168 | #[inline] 169 | fn flush(&mut self) -> io::Result<()> { 170 | match *self { 171 | HttpsStream::Http(ref mut s) => s.flush(), 172 | HttpsStream::Https(ref mut s) => s.flush() 173 | } 174 | } 175 | } 176 | 177 | impl NetworkStream for HttpsStream { 178 | #[inline] 179 | fn peer_addr(&mut self) -> io::Result { 180 | match *self { 181 | HttpsStream::Http(ref mut s) => s.peer_addr(), 182 | HttpsStream::Https(ref mut s) => s.peer_addr() 183 | } 184 | } 185 | 186 | #[inline] 187 | fn close(&mut self, how: Shutdown) -> io::Result<()> { 188 | match *self { 189 | HttpsStream::Http(ref mut s) => s.close(how), 190 | HttpsStream::Https(ref mut s) => s.close(how) 191 | } 192 | } 193 | } 194 | 195 | /// A Http Listener over SSL. 196 | #[derive(Clone)] 197 | pub struct HttpsListener { 198 | listener: HttpListener, 199 | ssl: S, 200 | } 201 | 202 | impl HttpsListener { 203 | 204 | /// Start listening to an address over HTTPS. 205 | pub fn new(addr: To, ssl: S) -> hyper::Result> { 206 | HttpListener::new(addr).map(|l| HttpsListener { 207 | listener: l, 208 | ssl: ssl 209 | }).map_err(From::from) 210 | } 211 | 212 | } 213 | 214 | impl NetworkListener for HttpsListener { 215 | type Stream = S::Stream; 216 | 217 | #[inline] 218 | fn accept(&mut self) -> hyper::Result { 219 | self.listener.accept().and_then(|s| self.ssl.wrap_server(s)) 220 | } 221 | 222 | #[inline] 223 | fn local_addr(&mut self) -> io::Result { 224 | self.listener.local_addr() 225 | } 226 | } 227 | 228 | /// A connector that can protect HTTP streams using SSL. 229 | #[derive(Debug, Default)] 230 | pub struct HttpsConnector { 231 | ssl: S 232 | } 233 | 234 | impl HttpsConnector { 235 | /// Create a new connector using the provided SSL implementation. 236 | pub fn new(s: S) -> HttpsConnector { 237 | HttpsConnector { ssl: s } 238 | } 239 | } 240 | 241 | impl NetworkConnector for HttpsConnector { 242 | type Stream = HttpsStream; 243 | 244 | fn connect(&self, host: &str, port: u16, scheme: &str) -> hyper::Result { 245 | let addr = &(host, port); 246 | if scheme == "https" { 247 | debug!("https scheme"); 248 | let stream = HttpStream(try!(TcpStream::connect(addr))); 249 | self.ssl.wrap_client(stream, host).map(HttpsStream::Https) 250 | } else { 251 | HttpConnector.connect(host, port, scheme).map(HttpsStream::Http) 252 | } 253 | } 254 | } 255 | 256 | /// An abstraction to allow any SSL implementation to be used with HttpsStreams. 257 | pub trait Ssl { 258 | /// The protected stream. 259 | type Stream: NetworkStream + Send + Clone; 260 | /// Wrap a client stream with SSL. 261 | fn wrap_client(&self, stream: HttpStream, host: &str) -> hyper::Result; 262 | /// Wrap a server stream with SSL. 263 | fn wrap_server(&self, stream: HttpStream) -> hyper::Result; 264 | } 265 | 266 | #[cfg(not(feature = "openssl"))] 267 | #[doc(hidden)] 268 | pub type DefaultConnector = HttpConnector; 269 | 270 | #[cfg(feature = "openssl")] 271 | #[doc(hidden)] 272 | pub type DefaultConnector = HttpsConnector; 273 | 274 | #[cfg(feature = "openssl")] 275 | mod openssl { 276 | use std::io; 277 | use std::path::Path; 278 | use std::sync::Arc; 279 | use openssl::ssl::{Ssl, SslContext, SslStream, SslMethod, SSL_VERIFY_NONE}; 280 | use openssl::ssl::error::StreamError as SslIoError; 281 | use openssl::ssl::error::SslError; 282 | use openssl::x509::X509FileType; 283 | use super::HttpStream; 284 | use hyper; 285 | 286 | 287 | #[derive(Debug, Clone)] 288 | pub struct Openssl { 289 | /// The `SslContext` from openssl crate. 290 | pub context: Arc 291 | } 292 | 293 | impl Default for Openssl { 294 | fn default() -> Openssl { 295 | Openssl { 296 | context: Arc::new(SslContext::new(SslMethod::Sslv23).unwrap_or_else(|e| { 297 | // if we cannot create a SslContext, that's because of a 298 | // serious problem. just crash. 299 | panic!("{}", e) 300 | })) 301 | } 302 | } 303 | } 304 | 305 | impl Openssl { 306 | /// Ease creating an `Openssl` with a certificate and key. 307 | pub fn with_cert_and_key(cert: C, key: K) -> Result 308 | where C: AsRef, K: AsRef 309 | { 310 | let mut ctx = try!(SslContext::new(SslMethod::Sslv23)); 311 | try!(ctx.set_cipher_list("DEFAULT")); 312 | try!(ctx.set_certificate_file(cert.as_ref(), X509FileType::PEM)); 313 | try!(ctx.set_private_key_file(key.as_ref(), X509FileType::PEM)); 314 | ctx.set_verify(SSL_VERIFY_NONE, None); 315 | Ok(Openssl { context: Arc::new(ctx) }) 316 | } 317 | } 318 | 319 | impl super::Ssl for Openssl { 320 | type Stream = SslStream; 321 | 322 | fn wrap_client(&self, stream: HttpStream, host: &str) -> hyper::Result { 323 | //if let Some(ref verifier) = self.verifier { 324 | // verifier(&mut context); 325 | //} 326 | let ssl = try!(Ssl::new(&self.context)); 327 | try!(ssl.set_hostname(host)); 328 | SslStream::new_from(ssl, stream).map_err(From::from) 329 | } 330 | 331 | fn wrap_server(&self, stream: HttpStream) -> hyper::Result { 332 | match SslStream::new_server(&self.context, stream) { 333 | Ok(ssl_stream) => Ok(ssl_stream), 334 | Err(SslIoError(e)) => { 335 | Err(io::Error::new(io::ErrorKind::ConnectionAborted, e).into()) 336 | }, 337 | Err(e) => Err(e.into()) 338 | } 339 | } 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /src/net/http/mod.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | pub use self::client::Client; 24 | pub use self::server::Server; 25 | 26 | pub mod client; 27 | pub mod server; 28 | 29 | pub mod conn; 30 | -------------------------------------------------------------------------------- /src/net/http/server.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | use std::net::{ToSocketAddrs, SocketAddr}; 24 | use std::convert::From; 25 | use std::io::{self, Write, BufWriter}; 26 | 27 | use hyper; 28 | use hyper::http; 29 | use hyper::buffer::BufReader; 30 | use hyper::server::{Request, Response, Handler}; 31 | use hyper::header::{Connection, Headers, Expect}; 32 | use hyper::version::HttpVersion; 33 | use hyper::net::{NetworkListener, NetworkStream}; 34 | use hyper::status::StatusCode; 35 | use hyper::error::Error; 36 | 37 | use net::http::conn::{HttpListener, HttpsListener, Ssl}; 38 | 39 | use scheduler::Scheduler; 40 | 41 | /// A server can listen on a TCP socket. 42 | /// 43 | /// Once listening, it will create a `Request`/`Response` pair for each 44 | /// incoming connection, and hand them to the provided handler. 45 | #[derive(Debug)] 46 | pub struct Server { 47 | listener: L, 48 | } 49 | 50 | impl Server { 51 | /// Creates a new server with the provided handler. 52 | #[inline] 53 | pub fn new(listener: L) -> Server { 54 | Server { 55 | listener: listener, 56 | } 57 | } 58 | } 59 | 60 | impl Server { 61 | /// Creates a new server that will handle `HttpStream`s. 62 | pub fn http(addr: To) -> hyper::Result> { 63 | HttpListener::new(addr).map(Server::new).map_err(From::from) 64 | } 65 | } 66 | 67 | impl Server> { 68 | /// Creates a new server that will handle `HttpStream`s over SSL. 69 | /// 70 | /// You can use any SSL implementation, as long as implements `hyper::net::Ssl`. 71 | pub fn https(addr: A, ssl: S) -> hyper::Result>> { 72 | HttpsListener::new(addr, ssl).map(Server::new) 73 | } 74 | } 75 | 76 | impl Server { 77 | /// Binds to a socket. 78 | pub fn listen(mut self, handler: H) -> hyper::Result { 79 | let socket = try!(self.listener.local_addr()); 80 | 81 | Scheduler::spawn(move|| { 82 | use std::sync::Arc; 83 | 84 | let handler = Arc::new(handler); 85 | loop { 86 | let mut stream = self.listener.accept().unwrap(); 87 | 88 | let handler = handler.clone(); 89 | Scheduler::spawn(move|| Worker(&*handler).handle_connection(&mut stream)); 90 | } 91 | }); 92 | 93 | Ok(socket) 94 | } 95 | } 96 | 97 | struct Worker<'a, H: Handler + 'static>(&'a H); 98 | 99 | impl<'a, H: Handler + 'static> Worker<'a, H> { 100 | 101 | fn handle_connection(&self, mut stream: &mut S) where S: NetworkStream + Clone { 102 | debug!("Incoming stream"); 103 | let addr = match stream.peer_addr() { 104 | Ok(addr) => addr, 105 | Err(e) => { 106 | error!("Peer Name error: {:?}", e); 107 | return; 108 | } 109 | }; 110 | 111 | // FIXME: Use Type ascription 112 | let stream_clone: &mut NetworkStream = &mut stream.clone(); 113 | let rdr = BufReader::new(stream_clone); 114 | let wrt = BufWriter::new(stream); 115 | 116 | self.keep_alive_loop(rdr, wrt, addr); 117 | debug!("keep_alive loop ending for {}", addr); 118 | } 119 | 120 | fn keep_alive_loop(&self, mut rdr: BufReader<&mut NetworkStream>, mut wrt: W, addr: SocketAddr) { 121 | let mut keep_alive = true; 122 | while keep_alive { 123 | let req = match Request::new(&mut rdr, addr) { 124 | Ok(req) => req, 125 | Err(Error::Io(ref e)) if e.kind() == io::ErrorKind::ConnectionAborted => { 126 | trace!("tcp closed, cancelling keep-alive loop"); 127 | break; 128 | } 129 | Err(Error::Io(e)) => { 130 | debug!("ioerror in keepalive loop = {:?}", e); 131 | break; 132 | } 133 | Err(e) => { 134 | //TODO: send a 400 response 135 | error!("request error = {:?}", e); 136 | break; 137 | } 138 | }; 139 | 140 | 141 | if !self.handle_expect(&req, &mut wrt) { 142 | break; 143 | } 144 | 145 | keep_alive = http::should_keep_alive(req.version, &req.headers); 146 | let version = req.version; 147 | let mut res_headers = Headers::new(); 148 | if !keep_alive { 149 | res_headers.set(Connection::close()); 150 | } 151 | { 152 | let mut res = Response::new(&mut wrt, &mut res_headers); 153 | res.version = version; 154 | self.0.handle(req, res); 155 | } 156 | 157 | // if the request was keep-alive, we need to check that the server agrees 158 | // if it wasn't, then the server cannot force it to be true anyways 159 | if keep_alive { 160 | keep_alive = http::should_keep_alive(version, &res_headers); 161 | } 162 | 163 | debug!("keep_alive = {:?} for {}", keep_alive, addr); 164 | } 165 | 166 | } 167 | 168 | fn handle_expect(&self, req: &Request, wrt: &mut W) -> bool { 169 | if req.version == HttpVersion::Http11 && req.headers.get() == Some(&Expect::Continue) { 170 | let status = self.0.check_continue((&req.method, &req.uri, &req.headers)); 171 | match write!(wrt, "{} {}\r\n\r\n", HttpVersion::Http11, status) { 172 | Ok(..) => (), 173 | Err(e) => { 174 | error!("error writing 100-continue: {:?}", e); 175 | return false; 176 | } 177 | } 178 | 179 | if status != StatusCode::Continue { 180 | debug!("non-100 status ({}) for Expect 100 request", status); 181 | return false; 182 | } 183 | } 184 | 185 | true 186 | } 187 | } 188 | 189 | macro_rules! try_return( 190 | ($e:expr) => {{ 191 | match $e { 192 | Ok(v) => v, 193 | Err(e) => { println!("Error: {}", e); return; } 194 | } 195 | }} 196 | ); 197 | -------------------------------------------------------------------------------- /src/net/http/stream.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | use std::io::{self, Read, Write}; 24 | use std::net::{SocketAddr, Shutdown}; 25 | 26 | use hyper::net::NetworkStream; 27 | 28 | use net::{self, TcpStream}; 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/net/http/utils.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zonyitoo/simplesched/8d20de49835d13b2a39229774feaf7fd1e419078/src/net/http/utils.rs -------------------------------------------------------------------------------- /src/net/mod.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc Developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Asynchronous network library 23 | 24 | pub use self::tcp::{TcpListener, TcpStream, TcpSocket, Shutdown}; 25 | pub use self::udp::UdpSocket; 26 | 27 | use std::io; 28 | use std::net::{ToSocketAddrs, SocketAddr}; 29 | 30 | pub mod tcp; 31 | pub mod udp; 32 | pub mod http; 33 | 34 | fn each_addr(addr: A, mut f: F) -> io::Result 35 | where F: FnMut(&SocketAddr) -> io::Result 36 | { 37 | let mut last_err = None; 38 | for addr in try!(addr.to_socket_addrs()) { 39 | match f(&addr) { 40 | Ok(l) => return Ok(l), 41 | Err(e) => last_err = Some(e), 42 | } 43 | } 44 | Err(last_err.unwrap_or_else(|| { 45 | io::Error::new(io::ErrorKind::InvalidInput, 46 | "could not resolve to any addresses") 47 | })) 48 | } 49 | -------------------------------------------------------------------------------- /src/net/tcp.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc Developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! TCP 23 | 24 | use std::io; 25 | use std::net::{ToSocketAddrs, SocketAddr}; 26 | use std::ops::{Deref, DerefMut}; 27 | use std::convert::From; 28 | use std::iter::Iterator; 29 | 30 | use mio::{self, EventSet}; 31 | 32 | use processor::Processor; 33 | 34 | #[derive(Debug)] 35 | pub struct TcpSocket(::mio::tcp::TcpSocket); 36 | 37 | impl TcpSocket { 38 | /// Returns a new, unbound, non-blocking, IPv4 socket 39 | pub fn v4() -> io::Result { 40 | Ok(TcpSocket(try!(::mio::tcp::TcpSocket::v4()))) 41 | } 42 | 43 | /// Returns a new, unbound, non-blocking, IPv6 socket 44 | pub fn v6() -> io::Result { 45 | Ok(TcpSocket(try!(::mio::tcp::TcpSocket::v6()))) 46 | } 47 | 48 | pub fn connect(addr: A) -> io::Result<(TcpStream, bool)> { 49 | super::each_addr(addr, |a| { 50 | match a { 51 | &SocketAddr::V4(..) => try!(TcpSocket::v4()).0.connect(a), 52 | &SocketAddr::V6(..) => try!(TcpSocket::v6()).0.connect(a), 53 | } 54 | }).map(|(stream, complete)| (TcpStream(stream), complete)) 55 | } 56 | 57 | pub fn listen(self, backlog: usize) -> io::Result { 58 | Ok(TcpListener(try!(self.0.listen(backlog)))) 59 | } 60 | } 61 | 62 | impl Deref for TcpSocket { 63 | type Target = ::mio::tcp::TcpSocket; 64 | 65 | fn deref(&self) -> &::mio::tcp::TcpSocket { 66 | &self.0 67 | } 68 | } 69 | 70 | impl DerefMut for TcpSocket { 71 | fn deref_mut(&mut self) -> &mut ::mio::tcp::TcpSocket { 72 | &mut self.0 73 | } 74 | } 75 | 76 | #[derive(Debug)] 77 | pub struct TcpListener(::mio::tcp::TcpListener); 78 | 79 | impl TcpListener { 80 | pub fn bind(addr: A) -> io::Result { 81 | super::each_addr(addr, ::mio::tcp::TcpListener::bind).map(TcpListener) 82 | } 83 | 84 | pub fn accept(&self) -> io::Result { 85 | match self.0.accept() { 86 | Ok(None) => { 87 | debug!("accept WouldBlock; going to register into eventloop"); 88 | }, 89 | Ok(Some(stream)) => { 90 | return Ok(TcpStream(stream)); 91 | }, 92 | Err(err) => { 93 | return Err(err); 94 | } 95 | } 96 | 97 | loop { 98 | try!(Processor::current().wait_event(&self.0, EventSet::readable())); 99 | 100 | match self.0.accept() { 101 | Ok(None) => { 102 | warn!("accept WouldBlock; Coroutine was awaked by readable event"); 103 | }, 104 | Ok(Some(stream)) => { 105 | return Ok(TcpStream(stream)); 106 | }, 107 | Err(err) => { 108 | return Err(err); 109 | } 110 | } 111 | } 112 | } 113 | 114 | pub fn try_clone(&self) -> io::Result { 115 | Ok(TcpListener(try!(self.0.try_clone()))) 116 | } 117 | 118 | pub fn incoming<'a>(&'a self) -> Incoming<'a> { 119 | Incoming(self) 120 | } 121 | } 122 | 123 | impl Deref for TcpListener { 124 | type Target = ::mio::tcp::TcpListener; 125 | 126 | fn deref(&self) -> &::mio::tcp::TcpListener { 127 | &self.0 128 | } 129 | } 130 | 131 | impl DerefMut for TcpListener { 132 | fn deref_mut(&mut self) -> &mut ::mio::tcp::TcpListener { 133 | &mut self.0 134 | } 135 | } 136 | 137 | pub struct Incoming<'a>(&'a TcpListener); 138 | 139 | impl<'a> Iterator for Incoming<'a> { 140 | type Item = io::Result; 141 | 142 | fn next(&mut self) -> Option> { 143 | Some(self.0.accept()) 144 | } 145 | } 146 | 147 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 148 | pub enum Shutdown { 149 | /// Further receptions will be disallowed. 150 | Read, 151 | /// Further transmissions will be disallowed. 152 | Write, 153 | /// Further receptions and transmissions will be disallowed. 154 | Both, 155 | } 156 | 157 | impl From for mio::tcp::Shutdown { 158 | fn from(shutdown: Shutdown) -> mio::tcp::Shutdown { 159 | match shutdown { 160 | Shutdown::Read => mio::tcp::Shutdown::Read, 161 | Shutdown::Write => mio::tcp::Shutdown::Write, 162 | Shutdown::Both => mio::tcp::Shutdown::Both, 163 | } 164 | } 165 | } 166 | 167 | #[derive(Debug)] 168 | pub struct TcpStream(mio::tcp::TcpStream); 169 | 170 | impl TcpStream { 171 | pub fn connect(addr: A) -> io::Result { 172 | match TcpSocket::connect(addr) { 173 | Ok((stream, completed)) => { 174 | if !completed { 175 | try!(Processor::current().wait_event(&stream.0, EventSet::writable())); 176 | try!(stream.take_socket_error()); 177 | } 178 | 179 | Ok(stream) 180 | }, 181 | Err(err) => Err(err) 182 | } 183 | } 184 | 185 | pub fn peer_addr(&self) -> io::Result { 186 | self.0.peer_addr() 187 | } 188 | 189 | pub fn local_addr(&self) -> io::Result { 190 | self.0.local_addr() 191 | } 192 | 193 | pub fn try_clone(&self) -> io::Result { 194 | let stream = try!(self.0.try_clone()); 195 | 196 | Ok(TcpStream(stream)) 197 | } 198 | 199 | pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { 200 | self.0.shutdown(From::from(how)) 201 | } 202 | 203 | pub fn take_socket_error(&self) -> io::Result<()> { 204 | self.0.take_socket_error() 205 | } 206 | } 207 | 208 | impl io::Read for TcpStream { 209 | fn read(&mut self, buf: &mut [u8]) -> io::Result { 210 | use mio::TryRead; 211 | 212 | match self.0.try_read(buf) { 213 | Ok(None) => { 214 | debug!("TcpStream read WouldBlock"); 215 | }, 216 | Ok(Some(len)) => { 217 | debug!("TcpStream read {} bytes", len); 218 | return Ok(len); 219 | }, 220 | Err(err) => { 221 | return Err(err); 222 | } 223 | } 224 | 225 | loop { 226 | debug!("Read: Going to register event"); 227 | try!(Processor::current().wait_event(&self.0, EventSet::readable())); 228 | debug!("Read: Got read event"); 229 | 230 | match self.0.try_read(buf) { 231 | Ok(None) => { 232 | debug!("TcpStream read WouldBlock"); 233 | }, 234 | Ok(Some(len)) => { 235 | debug!("TcpStream read {} bytes", len); 236 | return Ok(len); 237 | }, 238 | Err(err) => { 239 | return Err(err); 240 | } 241 | } 242 | } 243 | } 244 | } 245 | 246 | impl io::Write for TcpStream { 247 | fn write(&mut self, buf: &[u8]) -> io::Result { 248 | use mio::TryWrite; 249 | 250 | match self.0.try_write(buf) { 251 | Ok(None) => { 252 | debug!("TcpStream write WouldBlock"); 253 | }, 254 | Ok(Some(len)) => { 255 | debug!("TcpStream written {} bytes", len); 256 | return Ok(len); 257 | }, 258 | Err(err) => { 259 | return Err(err) 260 | } 261 | } 262 | 263 | loop { 264 | debug!("Write: Going to register event"); 265 | try!(Processor::current().wait_event(&self.0, EventSet::writable())); 266 | debug!("Write: Got write event"); 267 | 268 | match self.0.try_write(buf) { 269 | Ok(None) => { 270 | debug!("TcpStream write WouldBlock"); 271 | }, 272 | Ok(Some(len)) => { 273 | debug!("TcpStream written {} bytes", len); 274 | return Ok(len); 275 | }, 276 | Err(err) => { 277 | return Err(err) 278 | } 279 | } 280 | } 281 | } 282 | 283 | fn flush(&mut self) -> io::Result<()> { 284 | Ok(()) 285 | } 286 | } 287 | 288 | impl Deref for TcpStream { 289 | type Target = ::mio::tcp::TcpStream; 290 | 291 | fn deref(&self) -> &::mio::tcp::TcpStream { 292 | &self.0 293 | } 294 | } 295 | 296 | impl DerefMut for TcpStream { 297 | fn deref_mut(&mut self) -> &mut ::mio::tcp::TcpStream { 298 | &mut self.0 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/net/udp.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc Developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! UDP 23 | 24 | use std::ops::{Deref, DerefMut}; 25 | use std::io; 26 | use std::net::{ToSocketAddrs, SocketAddr}; 27 | 28 | use mio::EventSet; 29 | 30 | use bytes::{Buf, MutBuf, SliceBuf, MutSliceBuf}; 31 | 32 | use processor::Processor; 33 | 34 | pub struct UdpSocket(::mio::udp::UdpSocket); 35 | 36 | impl UdpSocket { 37 | /// Returns a new, unbound, non-blocking, IPv4 UDP socket 38 | pub fn v4() -> io::Result { 39 | Ok(UdpSocket(try!(::mio::udp::UdpSocket::v4()))) 40 | } 41 | 42 | /// Returns a new, unbound, non-blocking, IPv6 UDP socket 43 | pub fn v6() -> io::Result { 44 | Ok(UdpSocket(try!(::mio::udp::UdpSocket::v6()))) 45 | } 46 | 47 | pub fn bind(addr: A) -> io::Result { 48 | super::each_addr(addr, |a| { 49 | ::mio::udp::UdpSocket::bound(&a) 50 | }).map(UdpSocket) 51 | } 52 | 53 | pub fn try_clone(&self) -> io::Result { 54 | Ok(UdpSocket(try!(self.0.try_clone()))) 55 | } 56 | 57 | pub fn send_to(&self, slice_buf: &[u8], target: A) -> io::Result { 58 | let mut buf = SliceBuf::wrap(slice_buf); 59 | 60 | let mut last_err = Ok(0); 61 | for addr in try!(target.to_socket_addrs()) { 62 | match self.0.send_to(&mut buf, &addr) { 63 | Ok(None) => { 64 | debug!("UdpSocket send_to WOULDBLOCK"); 65 | 66 | loop { 67 | try!(Processor::current().wait_event(&self.0, EventSet::writable())); 68 | 69 | match self.0.send_to(&mut buf, &addr) { 70 | Ok(None) => { 71 | warn!("UdpSocket send_to WOULDBLOCK"); 72 | }, 73 | Ok(Some(..)) => { 74 | return Ok(slice_buf.len() - buf.remaining()); 75 | }, 76 | Err(err) => { 77 | return Err(err); 78 | } 79 | } 80 | } 81 | }, 82 | Ok(Some(..)) => { 83 | return Ok(slice_buf.len() - buf.remaining()); 84 | }, 85 | Err(err) => last_err = Err(err), 86 | } 87 | } 88 | 89 | last_err 90 | } 91 | 92 | pub fn recv_from(&self, slice_buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { 93 | let total_len = slice_buf.len(); 94 | let mut buf = MutSliceBuf::wrap(slice_buf); 95 | 96 | match try!(self.0.recv_from(&mut buf)) { 97 | None => { 98 | debug!("UdpSocket recv_from WOULDBLOCK"); 99 | }, 100 | Some(addr) => { 101 | return Ok((total_len - buf.remaining(), addr)); 102 | } 103 | } 104 | 105 | loop { 106 | try!(Processor::current().wait_event(&self.0, EventSet::readable())); 107 | 108 | match try!(self.0.recv_from(&mut buf)) { 109 | None => { 110 | warn!("UdpSocket recv_from WOULDBLOCK"); 111 | }, 112 | Some(addr) => { 113 | return Ok((total_len - buf.remaining(), addr)); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | 120 | impl Deref for UdpSocket { 121 | type Target = ::mio::udp::UdpSocket; 122 | 123 | fn deref(&self) -> &::mio::udp::UdpSocket { 124 | return &self.0 125 | } 126 | } 127 | 128 | impl DerefMut for UdpSocket { 129 | fn deref_mut(&mut self) -> &mut ::mio::udp::UdpSocket { 130 | return &mut self.0 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/options.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Coroutine options 23 | 24 | use std::rt; 25 | use std::default::Default; 26 | 27 | /// Coroutine options 28 | pub struct Options { 29 | pub stack_size: usize, 30 | pub name: Option, 31 | } 32 | 33 | impl Options { 34 | pub fn new() -> Options { 35 | Options { 36 | stack_size: rt::min_stack(), 37 | name: None, 38 | } 39 | } 40 | 41 | pub fn stack_size(mut self, size: usize) -> Options { 42 | self.stack_size = size; 43 | self 44 | } 45 | 46 | pub fn name(mut self, name: Option) -> Options { 47 | self.name = name; 48 | self 49 | } 50 | } 51 | 52 | impl Default for Options { 53 | fn default() -> Options { 54 | Options::new() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/processor.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Processing unit of a thread 23 | 24 | use std::cell::UnsafeCell; 25 | use std::io; 26 | #[cfg(target_os = "linux")] 27 | use std::os::unix::io::AsRawFd; 28 | #[cfg(target_os = "linux")] 29 | use std::convert::From; 30 | use std::sync::Arc; 31 | use std::thread; 32 | use std::mem; 33 | 34 | use mio::{EventLoop, Evented, Handler, Token, EventSet, PollOpt}; 35 | use mio::util::Slab; 36 | #[cfg(target_os = "linux")] 37 | use mio::Io; 38 | 39 | use mio::util::BoundedQueue; 40 | 41 | use scheduler::{Scheduler, CoroutineRefMut}; 42 | use coroutine::{self, Coroutine, State, Handle}; 43 | use options::Options; 44 | 45 | thread_local!(static PROCESSOR: UnsafeCell = UnsafeCell::new(Processor::new())); 46 | 47 | /// Processing unit of a thread 48 | pub struct Processor { 49 | event_loop: EventLoop, 50 | work_queue: Arc>, 51 | handler: IoHandler, 52 | main_coro: Handle, 53 | cur_running: Option, 54 | last_result: Option>, 55 | new_spawned: Option, 56 | } 57 | 58 | impl Processor { 59 | #[doc(hidden)] 60 | pub fn new() -> Processor { 61 | let main_coro = unsafe { 62 | Coroutine::empty() 63 | }; 64 | 65 | Processor { 66 | event_loop: EventLoop::new().unwrap(), 67 | work_queue: Scheduler::get().get_queue(), 68 | handler: IoHandler::new(), 69 | main_coro: main_coro, 70 | cur_running: None, 71 | last_result: None, 72 | new_spawned: None, 73 | } 74 | } 75 | 76 | #[doc(hidden)] 77 | pub fn running(&mut self) -> Option { 78 | self.cur_running 79 | } 80 | 81 | /// Get the thread local processor 82 | pub fn current() -> &'static mut Processor { 83 | PROCESSOR.with(|p| unsafe { &mut *p.get() }) 84 | } 85 | 86 | /// Spawn a new coroutine and run it in this processor immediately 87 | pub fn spawn_opts(&mut self, f: F, opts: Options) 88 | where F: FnOnce() + Send + 'static 89 | { 90 | let coro = Coroutine::spawn_opts(f, opts); 91 | let coro = CoroutineRefMut::new(unsafe { mem::transmute(coro) }); 92 | self.new_spawned = Some(coro); 93 | self.sched(); 94 | } 95 | 96 | #[doc(hidden)] 97 | pub fn set_last_result(&mut self, r: coroutine::Result) { 98 | self.last_result = Some(r); 99 | } 100 | 101 | fn run_task(&mut self, hdl: CoroutineRefMut) { 102 | match self.resume(hdl) { 103 | Ok(State::Suspended) => { 104 | Scheduler::ready(hdl); 105 | }, 106 | Ok(State::Finished) | Ok(State::Panicked) => { 107 | Scheduler::finished(hdl); 108 | }, 109 | Ok(State::Blocked) => (), 110 | Err(err) => { 111 | error!("Coroutine resume failed, {:?}", err); 112 | Scheduler::finished(hdl); 113 | } 114 | } 115 | } 116 | 117 | #[doc(hidden)] 118 | pub fn schedule(&mut self) -> io::Result<()> { 119 | loop { 120 | match self.work_queue.pop() { 121 | Some(hdl) => { 122 | self.run_task(hdl) 123 | }, 124 | None => { 125 | if self.handler.slabs.count() != 0 { 126 | try!(self.event_loop.run_once(&mut self.handler)); 127 | } else if Scheduler::get().work_count() == 0 { 128 | break; 129 | } else { 130 | thread::sleep_ms(100); 131 | } 132 | } 133 | } 134 | 135 | while let Some(hdl) = self.new_spawned.take() { 136 | self.run_task(hdl); 137 | } 138 | } 139 | 140 | Ok(()) 141 | } 142 | 143 | #[doc(hidden)] 144 | pub fn resume(&mut self, coro_ref: CoroutineRefMut) -> coroutine::Result { 145 | self.cur_running = Some(coro_ref); 146 | unsafe { 147 | self.main_coro.yield_to(&mut *coro_ref.coro_ptr); 148 | } 149 | 150 | match self.last_result.take() { 151 | None => Ok(State::Suspended), 152 | Some(r) => r, 153 | } 154 | } 155 | 156 | /// Suspended the current running coroutine, equivalent to `Scheduler::sched` 157 | pub fn sched(&mut self) { 158 | match self.cur_running.take() { 159 | None => {}, 160 | Some(coro_ref) => unsafe { 161 | self.set_last_result(Ok(State::Suspended)); 162 | (&mut *coro_ref.coro_ptr).yield_to(&*self.main_coro) 163 | } 164 | } 165 | } 166 | 167 | /// Block the current running coroutine, equivalent to `Scheduler::block` 168 | pub fn block(&mut self) { 169 | match self.cur_running.take() { 170 | None => {}, 171 | Some(coro_ref) => unsafe { 172 | self.set_last_result(Ok(State::Blocked)); 173 | (&mut *coro_ref.coro_ptr).yield_to(&*self.main_coro) 174 | } 175 | } 176 | } 177 | 178 | /// Yield the current running coroutine with specified result 179 | pub fn yield_with(&mut self, r: coroutine::Result) { 180 | match self.cur_running.take() { 181 | None => {}, 182 | Some(coro_ref) => unsafe { 183 | self.set_last_result(r); 184 | (&mut *coro_ref.coro_ptr).yield_to(&*self.main_coro) 185 | } 186 | } 187 | } 188 | } 189 | 190 | const MAX_TOKEN_NUM: usize = 102400; 191 | impl IoHandler { 192 | fn new() -> IoHandler { 193 | IoHandler { 194 | slabs: Slab::new(MAX_TOKEN_NUM), 195 | } 196 | } 197 | } 198 | 199 | #[cfg(any(target_os = "linux", 200 | target_os = "android"))] 201 | impl Processor { 202 | /// Register and wait I/O 203 | pub fn wait_event(&mut self, fd: &E, interest: EventSet) -> io::Result<()> { 204 | let token = self.handler.slabs.insert((Processor::current().running().unwrap(), 205 | From::from(fd.as_raw_fd()))).unwrap(); 206 | try!(self.event_loop.register_opt(fd, token, interest, 207 | PollOpt::edge()|PollOpt::oneshot())); 208 | 209 | debug!("wait_event: Blocked current Coroutine ...; token={:?}", token); 210 | Scheduler::block(); 211 | debug!("wait_event: Waked up; token={:?}", token); 212 | 213 | Ok(()) 214 | } 215 | } 216 | 217 | #[cfg(any(target_os = "linux", 218 | target_os = "android"))] 219 | struct IoHandler { 220 | slabs: Slab<(CoroutineRefMut, Io)>, 221 | } 222 | 223 | #[cfg(any(target_os = "linux", 224 | target_os = "android"))] 225 | impl Handler for IoHandler { 226 | type Timeout = (); 227 | type Message = (); 228 | 229 | fn ready(&mut self, event_loop: &mut EventLoop, token: Token, events: EventSet) { 230 | debug!("Got {:?} for {:?}", events, token); 231 | 232 | match self.slabs.remove(token) { 233 | Some((hdl, fd)) => { 234 | // Linux EPoll needs to explicit EPOLL_CTL_DEL the fd 235 | event_loop.deregister(&fd).unwrap(); 236 | mem::forget(fd); 237 | Scheduler::ready(hdl); 238 | }, 239 | None => { 240 | warn!("No coroutine is waiting on readable {:?}", token); 241 | } 242 | } 243 | } 244 | } 245 | 246 | #[cfg(any(target_os = "macos", 247 | target_os = "freebsd", 248 | target_os = "dragonfly", 249 | target_os = "ios", 250 | target_os = "bitrig", 251 | target_os = "openbsd"))] 252 | impl Processor { 253 | /// Register and wait I/O 254 | pub fn wait_event(&mut self, fd: &E, interest: EventSet) -> io::Result<()> { 255 | let token = self.handler.slabs.insert(Processor::current().running().unwrap()).unwrap(); 256 | try!(self.event_loop.register_opt(fd, token, interest, 257 | PollOpt::edge()|PollOpt::oneshot())); 258 | 259 | debug!("wait_event: Blocked current Coroutine ...; token={:?}", token); 260 | // Coroutine::block(); 261 | Scheduler::block(); 262 | debug!("wait_event: Waked up; token={:?}", token); 263 | 264 | Ok(()) 265 | } 266 | } 267 | 268 | #[cfg(any(target_os = "macos", 269 | target_os = "freebsd", 270 | target_os = "dragonfly", 271 | target_os = "ios", 272 | target_os = "bitrig", 273 | target_os = "openbsd"))] 274 | struct IoHandler { 275 | slabs: Slab, 276 | } 277 | 278 | #[cfg(any(target_os = "macos", 279 | target_os = "freebsd", 280 | target_os = "dragonfly", 281 | target_os = "ios", 282 | target_os = "bitrig", 283 | target_os = "openbsd"))] 284 | impl Handler for IoHandler { 285 | type Timeout = (); 286 | type Message = (); 287 | 288 | fn ready(&mut self, _: &mut EventLoop, token: Token, events: EventSet) { 289 | debug!("Got {:?} for {:?}", events, token); 290 | 291 | match self.slabs.remove(token) { 292 | Some(hdl) => { 293 | Scheduler::ready(hdl); 294 | }, 295 | None => { 296 | warn!("No coroutine is waiting on readable {:?}", token); 297 | } 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/scheduler.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Rustcc Developers 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | //! Global coroutine scheduler 23 | 24 | use std::thread; 25 | use std::sync::atomic::{AtomicUsize, Ordering}; 26 | use std::sync::Arc; 27 | use std::default::Default; 28 | 29 | use mio::util::BoundedQueue; 30 | 31 | use processor::Processor; 32 | 33 | use coroutine::Coroutine; 34 | use options::Options; 35 | 36 | lazy_static! { 37 | static ref SCHEDULER: Scheduler = Scheduler::new(); 38 | } 39 | 40 | #[doc(hidden)] 41 | #[allow(raw_pointer_derive)] 42 | #[derive(Copy, Clone, Debug)] 43 | pub struct CoroutineRefMut { 44 | pub coro_ptr: *mut Coroutine, 45 | } 46 | 47 | impl CoroutineRefMut { 48 | pub fn new(coro: *mut Coroutine) -> CoroutineRefMut { 49 | CoroutineRefMut { 50 | coro_ptr: coro, 51 | } 52 | } 53 | } 54 | 55 | unsafe impl Send for CoroutineRefMut {} 56 | 57 | /// Coroutine scheduler 58 | pub struct Scheduler { 59 | global_queue: Arc>, 60 | work_counts: AtomicUsize, 61 | } 62 | 63 | unsafe impl Send for Scheduler {} 64 | unsafe impl Sync for Scheduler {} 65 | 66 | const GLOBAL_QUEUE_SIZE: usize = 0x1000; 67 | 68 | impl Scheduler { 69 | fn new() -> Scheduler { 70 | Scheduler { 71 | global_queue: Arc::new(BoundedQueue::with_capacity(GLOBAL_QUEUE_SIZE)), 72 | work_counts: AtomicUsize::new(0), 73 | } 74 | } 75 | 76 | /// Get the global Scheduler 77 | pub fn get() -> &'static Scheduler { 78 | &SCHEDULER 79 | } 80 | 81 | #[doc(hidden)] 82 | /// A coroutine is ready for schedule 83 | pub fn ready(mut coro: CoroutineRefMut) { 84 | loop { 85 | match Scheduler::get().global_queue.push(coro) { 86 | Ok(..) => return, 87 | Err(h) => coro = h, 88 | } 89 | } 90 | } 91 | 92 | #[doc(hidden)] 93 | /// Get the global work queue 94 | pub fn get_queue(&self) -> Arc> { 95 | self.global_queue.clone() 96 | } 97 | 98 | #[doc(hidden)] 99 | /// A coroutine is finished 100 | pub fn finished(coro: CoroutineRefMut) { 101 | Scheduler::get().work_counts.fetch_sub(1, Ordering::SeqCst); 102 | 103 | let boxed = unsafe { Box::from_raw(coro.coro_ptr) }; 104 | drop(boxed); 105 | } 106 | 107 | /// Total work 108 | pub fn work_count(&self) -> usize { 109 | Scheduler::get().work_counts.load(Ordering::SeqCst) 110 | } 111 | 112 | /// Spawn a new coroutine 113 | pub fn spawn(f: F) 114 | where F: FnOnce() + 'static + Send 115 | { 116 | Scheduler::spawn_opts(f, Default::default()) 117 | } 118 | 119 | /// Spawn a new coroutine with options 120 | pub fn spawn_opts(f: F, opts: Options) 121 | where F: FnOnce() + 'static + Send 122 | { 123 | Scheduler::get().work_counts.fetch_add(1, Ordering::SeqCst); 124 | Processor::current().spawn_opts(f, opts) 125 | } 126 | 127 | /// Run the scheduler with `n` threads 128 | pub fn run(n: usize) { 129 | let mut futs = Vec::new(); 130 | for _ in 1..n { 131 | let fut = thread::spawn(|| { 132 | match Processor::current().schedule() { 133 | Ok(..) => {}, 134 | Err(err) => error!("Processor schedule error: {:?}", err), 135 | } 136 | }); 137 | 138 | futs.push(fut); 139 | } 140 | 141 | Processor::current().schedule() 142 | .unwrap_or_else(|err| { 143 | error!("Processor schedule error: {:?}", err); 144 | }); 145 | 146 | for fut in futs.into_iter() { 147 | fut.join().unwrap(); 148 | } 149 | } 150 | 151 | /// Suspend the current coroutine 152 | pub fn sched() { 153 | Processor::current().sched(); 154 | } 155 | 156 | /// Block the current coroutine 157 | pub fn block() { 158 | Processor::current().block(); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/sync/mod.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | //! Coroutine synchronization 24 | 25 | pub use self::mutex::Mutex; 26 | 27 | pub mod mutex; 28 | -------------------------------------------------------------------------------- /src/sync/mutex.rs: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | 3 | // Copyright (c) 2015 Y. T. Chung 4 | 5 | // Permission is hereby granted, free of charge, to any person obtaining a 6 | // copy of this software and associated documentation files (the "Software"), 7 | // to deal in the Software without restriction, including without limitation 8 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | // and/or sell copies of the Software, and to permit persons to whom the 10 | // Software is furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | // DEALINGS IN THE SOFTWARE. 22 | 23 | use std::sync::atomic::{AtomicBool, Ordering}; 24 | use std::cell::UnsafeCell; 25 | use std::fmt; 26 | use std::error::Error; 27 | use std::marker::Reflect; 28 | use std::ops::{Deref, DerefMut}; 29 | 30 | use scheduler::Scheduler; 31 | 32 | pub type LockResult = Result>; 33 | pub type TryLockResult = Result>; 34 | 35 | const POISON_RETRY_COUNT: usize = 102400; 36 | 37 | /// A mutual exclusion primitive useful for protecting shared data 38 | pub struct Mutex { 39 | data: UnsafeCell, 40 | lock: AtomicBool, 41 | } 42 | 43 | impl Mutex { 44 | /// Creates a new mutex in an unlocked state ready for use. 45 | pub fn new(data: T) -> Mutex { 46 | Mutex { 47 | data: UnsafeCell::new(data), 48 | lock: AtomicBool::new(false), 49 | } 50 | } 51 | 52 | /// Acquires a mutex, blocking the current thread until it is able to do so. 53 | pub fn lock<'a>(&'a self) -> LockResult> { 54 | for _ in 0..POISON_RETRY_COUNT { 55 | if self.lock.compare_and_swap(false, true, Ordering::SeqCst) == false { 56 | return Ok(Guard::new(unsafe { &mut *self.data.get() }, self)); 57 | } 58 | 59 | Scheduler::sched(); 60 | } 61 | 62 | Err(PoisonError::new(Guard::new(unsafe { &mut *self.data.get() }, self))) 63 | } 64 | 65 | pub fn try_lock<'a>(&'a self) -> TryLockResult> { 66 | if !self.lock.compare_and_swap(false, true, Ordering::SeqCst) { 67 | Ok(Guard::new(unsafe { &mut *self.data.get() }, self)) 68 | } else { 69 | Err(PoisonError::new(Guard::new(unsafe { &mut *self.data.get() }, self))) 70 | } 71 | } 72 | } 73 | 74 | unsafe impl Send for Mutex {} 75 | unsafe impl Sync for Mutex {} 76 | 77 | pub struct Guard<'a, T: 'a> { 78 | data: &'a mut T, 79 | mutex: &'a Mutex, 80 | } 81 | 82 | impl<'a, T: 'a> Guard<'a, T> { 83 | fn new(data: &'a mut T, mutex: &'a Mutex) -> Guard<'a, T> { 84 | Guard { 85 | data: data, 86 | mutex: mutex, 87 | } 88 | } 89 | } 90 | 91 | impl<'a, T: 'a> Drop for Guard<'a, T> { 92 | fn drop(&mut self) { 93 | while self.mutex.lock.compare_and_swap(true, false, Ordering::SeqCst) == true {} 94 | } 95 | } 96 | 97 | impl<'a, T: 'a> Deref for Guard<'a, T> { 98 | type Target = T; 99 | fn deref(&self) -> &T { 100 | self.data 101 | } 102 | } 103 | 104 | impl<'a, T: 'a> DerefMut for Guard<'a, T> { 105 | fn deref_mut(&mut self) -> &mut T { 106 | self.data 107 | } 108 | } 109 | 110 | pub struct PoisonError { 111 | guard: T, 112 | } 113 | 114 | impl PoisonError { 115 | pub fn new(guard: T) -> PoisonError { 116 | PoisonError { 117 | guard: guard, 118 | } 119 | } 120 | 121 | pub fn into_inner(self) -> T { 122 | self.guard 123 | } 124 | 125 | pub fn get_ref(&self) -> &T { 126 | &self.guard 127 | } 128 | 129 | pub fn get_mut(&mut self) -> &mut T { 130 | &mut self.guard 131 | } 132 | } 133 | 134 | impl fmt::Debug for PoisonError { 135 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 136 | "PoisonError { inner: .. }".fmt(f) 137 | } 138 | } 139 | 140 | impl fmt::Display for PoisonError { 141 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 142 | "poisoned lock: another task failed inside".fmt(f) 143 | } 144 | } 145 | 146 | impl Error for PoisonError { 147 | fn description(&self) -> &str { 148 | "poisoned lock: another task failed inside" 149 | } 150 | } 151 | 152 | #[cfg(test)] 153 | mod test { 154 | use std::sync::Arc; 155 | 156 | use scheduler::Scheduler; 157 | 158 | use super::Mutex; 159 | 160 | #[test] 161 | fn test_mutex() { 162 | let num = Arc::new(Mutex::new(0)); 163 | 164 | let cloned_num = num.clone(); 165 | Scheduler::spawn(move|| { 166 | for _ in 0..100 { 167 | let num = cloned_num.clone(); 168 | Scheduler::spawn(move|| { 169 | for _ in 0..10 { 170 | let mut guard = num.lock().unwrap(); 171 | *guard += 1; 172 | } 173 | }); 174 | println!("????"); 175 | } 176 | }); 177 | 178 | Scheduler::run(10); 179 | 180 | assert_eq!(*num.lock().unwrap(), 1000); 181 | } 182 | } 183 | --------------------------------------------------------------------------------