Make mio optional in ethcore-io (#8537)

* Make mio optional in ethcore-io

* Add some annotations, plus a check for features

* Increase timer for test
This commit is contained in:
Pierre Krieger
2018-05-10 12:34:36 +02:00
committed by Afri Schoedon
parent 6e2e08628a
commit 1b8f299df2
9 changed files with 510 additions and 60 deletions

View File

@@ -54,30 +54,59 @@
//! // Drop the service
//! }
//! ```
//!
//! # Mio vs non-mio
//!
//! This library has two modes: mio and not mio. The `mio` feature can be activated or deactivated
//! when compiling or depending on the library.
//!
//! Without mio, only timers and message-passing are available. With mio, you can also use
//! low-level sockets provided by mio.
//!
//! The non-mio mode exists because the `mio` library doesn't compile on platforms such as
//! emscripten.
//TODO: use Poll from mio
#![allow(deprecated)]
#[cfg(feature = "mio")]
extern crate mio;
#[macro_use]
extern crate log as rlog;
extern crate slab;
extern crate crossbeam;
extern crate parking_lot;
extern crate num_cpus;
extern crate timer;
extern crate fnv;
extern crate time;
mod service;
#[cfg(feature = "mio")]
mod service_mio;
#[cfg(not(feature = "mio"))]
mod service_non_mio;
#[cfg(feature = "mio")]
mod worker;
use std::cell::Cell;
use std::{fmt, error};
#[cfg(feature = "mio")]
use mio::deprecated::{EventLoop, NotifyError};
#[cfg(feature = "mio")]
use mio::Token;
pub use worker::LOCAL_STACK_SIZE;
thread_local! {
/// Stack size
/// Should be modified if it is changed in Rust since it is no way
/// to know or get it
pub static LOCAL_STACK_SIZE: Cell<usize> = Cell::new(::std::env::var("RUST_MIN_STACK").ok().and_then(|s| s.parse().ok()).unwrap_or(2 * 1024 * 1024));
}
#[derive(Debug)]
/// IO Error
pub enum IoError {
/// Low level error from mio crate
#[cfg(feature = "mio")]
Mio(::std::io::Error),
/// Error concerning the Rust standard library's IO subsystem.
StdIo(::std::io::Error),
@@ -88,6 +117,7 @@ impl fmt::Display for IoError {
// just defer to the std implementation for now.
// we can refine the formatting when more variants are added.
match *self {
#[cfg(feature = "mio")]
IoError::Mio(ref std_err) => std_err.fmt(f),
IoError::StdIo(ref std_err) => std_err.fmt(f),
}
@@ -106,8 +136,9 @@ impl From<::std::io::Error> for IoError {
}
}
impl<Message> From<NotifyError<service::IoMessage<Message>>> for IoError where Message: Send {
fn from(_err: NotifyError<service::IoMessage<Message>>) -> IoError {
#[cfg(feature = "mio")]
impl<Message> From<NotifyError<service_mio::IoMessage<Message>>> for IoError where Message: Send {
fn from(_err: NotifyError<service_mio::IoMessage<Message>>) -> IoError {
IoError::Mio(::std::io::Error::new(::std::io::ErrorKind::ConnectionAborted, "Network IO notification error"))
}
}
@@ -123,58 +154,120 @@ pub trait IoHandler<Message>: Send + Sync where Message: Send + Sync + 'static {
/// Called when a broadcasted message is received. The message can only be sent from a different IO handler.
fn message(&self, _io: &IoContext<Message>, _message: &Message) {}
/// Called when an IO stream gets closed
#[cfg(feature = "mio")]
fn stream_hup(&self, _io: &IoContext<Message>, _stream: StreamToken) {}
/// Called when an IO stream can be read from
#[cfg(feature = "mio")]
fn stream_readable(&self, _io: &IoContext<Message>, _stream: StreamToken) {}
/// Called when an IO stream can be written to
#[cfg(feature = "mio")]
fn stream_writable(&self, _io: &IoContext<Message>, _stream: StreamToken) {}
/// Register a new stream with the event loop
#[cfg(feature = "mio")]
fn register_stream(&self, _stream: StreamToken, _reg: Token, _event_loop: &mut EventLoop<IoManager<Message>>) {}
/// Re-register a stream with the event loop
#[cfg(feature = "mio")]
fn update_stream(&self, _stream: StreamToken, _reg: Token, _event_loop: &mut EventLoop<IoManager<Message>>) {}
/// Deregister a stream. Called whenstream is removed from event loop
#[cfg(feature = "mio")]
fn deregister_stream(&self, _stream: StreamToken, _event_loop: &mut EventLoop<IoManager<Message>>) {}
}
pub use service::TimerToken;
pub use service::StreamToken;
pub use service::IoContext;
pub use service::IoService;
pub use service::IoChannel;
pub use service::IoManager;
pub use service::TOKENS_PER_HANDLER;
#[cfg(feature = "mio")]
pub use service_mio::{TimerToken, StreamToken, IoContext, IoService, IoChannel, IoManager, TOKENS_PER_HANDLER};
#[cfg(not(feature = "mio"))]
pub use service_non_mio::{TimerToken, IoContext, IoService, IoChannel, TOKENS_PER_HANDLER};
#[cfg(test)]
mod tests {
use std::sync::Arc;
use std::sync::atomic;
use std::thread;
use std::time::Duration;
use super::*;
struct MyHandler;
#[test]
fn send_message_to_handler() {
struct MyHandler(atomic::AtomicBool);
#[derive(Clone)]
struct MyMessage {
data: u32
}
impl IoHandler<MyMessage> for MyHandler {
fn initialize(&self, io: &IoContext<MyMessage>) {
io.register_timer(0, Duration::from_secs(1)).unwrap();
#[derive(Clone)]
struct MyMessage {
data: u32
}
fn timeout(&self, _io: &IoContext<MyMessage>, timer: TimerToken) {
println!("Timeout {}", timer);
impl IoHandler<MyMessage> for MyHandler {
fn message(&self, _io: &IoContext<MyMessage>, message: &MyMessage) {
assert_eq!(message.data, 5);
self.0.store(true, atomic::Ordering::SeqCst);
}
}
fn message(&self, _io: &IoContext<MyMessage>, message: &MyMessage) {
println!("Message {}", message.data);
}
let handler = Arc::new(MyHandler(atomic::AtomicBool::new(false)));
let service = IoService::<MyMessage>::start().expect("Error creating network service");
service.register_handler(handler.clone()).unwrap();
service.send_message(MyMessage { data: 5 }).unwrap();
thread::sleep(Duration::from_secs(5));
assert!(handler.0.load(atomic::Ordering::SeqCst));
}
#[test]
fn test_service_register_handler () {
fn timeout_working() {
struct MyHandler(atomic::AtomicBool);
#[derive(Clone)]
struct MyMessage {
data: u32
}
impl IoHandler<MyMessage> for MyHandler {
fn initialize(&self, io: &IoContext<MyMessage>) {
io.register_timer_once(1234, Duration::from_millis(500)).unwrap();
}
fn timeout(&self, _io: &IoContext<MyMessage>, timer: TimerToken) {
assert_eq!(timer, 1234);
assert!(!self.0.swap(true, atomic::Ordering::SeqCst));
}
}
let handler = Arc::new(MyHandler(atomic::AtomicBool::new(false)));
let service = IoService::<MyMessage>::start().expect("Error creating network service");
service.register_handler(Arc::new(MyHandler)).unwrap();
service.register_handler(handler.clone()).unwrap();
thread::sleep(Duration::from_secs(2));
assert!(handler.0.load(atomic::Ordering::SeqCst));
}
#[test]
fn multi_timeout_working() {
struct MyHandler(atomic::AtomicUsize);
#[derive(Clone)]
struct MyMessage {
data: u32
}
impl IoHandler<MyMessage> for MyHandler {
fn initialize(&self, io: &IoContext<MyMessage>) {
io.register_timer(1234, Duration::from_millis(500)).unwrap();
}
fn timeout(&self, _io: &IoContext<MyMessage>, timer: TimerToken) {
assert_eq!(timer, 1234);
self.0.fetch_add(1, atomic::Ordering::SeqCst);
}
}
let handler = Arc::new(MyHandler(atomic::AtomicUsize::new(0)));
let service = IoService::<MyMessage>::start().expect("Error creating network service");
service.register_handler(handler.clone()).unwrap();
thread::sleep(Duration::from_secs(2));
assert!(handler.0.load(atomic::Ordering::SeqCst) >= 2);
}
}