Compare commits

...

22 Commits

Author SHA1 Message Date
Curt Brune
fb5f596757 Use epoll_create() directly instead of probing for epoll_create1()
At runtime mio tries to figure out whether to use syscall
epoll_create1() or epoll_create().  However, the Android 4.4 loader
crashes while looking up a symbol that does not
exist (epoll_create1).

More details covered here:
https://users.rust-lang.org/t/libc-dlsym-crashing-on-i686-linux-android/14788

This problem is resolved upstream by commit 479ae7c5e2 ("Cleanup of
sys::unix (#1005)"), but is not yet released officially.

Signed-off-by: Curt Brune <curt@signal.org>
2020-09-02 16:18:47 -07:00
Patrick Mooney
a053539bd8 Release v0.6.22 2020-05-01 19:19:59 +00:00
Patrick Mooney
6df2fb1abb Update cargo package excludes/includes 2020-05-01 19:19:59 +00:00
Patrick Mooney
c0d8804d87 Update repository URLs 2020-05-01 19:19:59 +00:00
Patrick Mooney
0aed20ae81 Add support for illumos target 2020-04-21 08:59:36 +00:00
Kevin Leimkuhler
a169a606f8
Update FreeBSD version in CI (#1300)
Signed-off-by: Kevin Leimkuhler <kevin@kleimkuhler.com>
2020-04-20 17:06:16 -07:00
Carl Lerche
cd0423c472
chore: prepare v0.6.21 release (#1173)
This removes the `=` dependency on an older `cfg-if` version.
2019-11-27 12:49:38 -08:00
est31
fa23f6ea1a Remove = requirement and use cargo update --precise instead 2019-11-27 11:03:27 +00:00
Carl Lerche
ba26c767b9
prepare v0.6.20 (#1163) 2019-11-21 22:33:15 -08:00
Carl Lerche
56831e931d
windows: use the default IOCP concurrency value. (#1161)
IOCP is initialized with the max number of threads that can be
"associated" with the IOCP handle. A thread becomes associated with IOCP
when it is woken after selecting. It remains associated with IOCP until
the thread sleeps for any reason (blocking I/O, yield, page fault,
sleep, ...).

Currently, the value is set to 1. However, this was picked mostly
because I had no idea what it meant to be "associated" with IOCP and 1
sounded good. It turns out that 0 is lets windows pick a default value
(number of cores). This makes much more sense than 1.

ref: https://docs.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports#threads-and-concurrency
2019-11-21 22:08:00 -08:00
Carl Lerche
a1de661e22
chore: bump CI nightly (#1162) 2019-11-21 21:53:40 -08:00
Thomas de Zeeuw
aa90e8ee2b Ensure pipe is closed when fcntl calls fail 2019-10-17 20:56:30 +00:00
Thomas de Zeeuw
c68cde44c5 Fix setting FD_CLOEXEC in pipe
O_CLOEXEC is not accepted in fnctl using F_SETFD.
2019-10-17 20:56:30 +00:00
Kevin Leimkuhler
1bb1749ddb Lock cfg-if in Cargo.toml
Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>
2019-10-04 15:17:09 +00:00
Kevin Leimkuhler
92db541d17 Lock cfg-if to 1.9
Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>
2019-10-04 15:17:09 +00:00
Kevin Leimkuhler
1251c288ab Set default toolchain differently
Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>
2019-10-04 15:17:09 +00:00
Kevin Leimkuhler
f2c88b8750 Add small pipeline changes
Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>
2019-10-04 15:17:09 +00:00
Kevin Leimkuhler
42611b1c06 Bump nightly again
Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>
2019-10-04 15:17:09 +00:00
Kevin Leimkuhler
62b8ed700c Bump pinned nightly version because it may no longer be nightly
Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>
2019-10-04 15:17:09 +00:00
Kevin Leimkuhler
453dc1e331 Remove HUP checks on kqueue platforms
Triggering HUP events was removed in a previous commit

Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>
2019-10-04 15:17:09 +00:00
Kevin Leimkuhler
82468508a8 Test: Silence warnings on different Rust versions
Signed-off-by: Kevin Leimkuhler <kleimkuhler@icloud.com>
2019-10-04 15:17:09 +00:00
Carl Lerche
f1b580c7f4
Add more HUP tests (#944) 2019-06-07 12:03:37 -07:00
15 changed files with 342 additions and 97 deletions

View File

@ -1,8 +1,8 @@
freebsd_instance:
image: freebsd-11-2-release-amd64
image_family: freebsd-11-3-snap
task:
name: FreeBSD 11.2 amd64
name: FreeBSD
setup_script:
- pkg install -y curl
- curl https://sh.rustup.rs -sSf --output rustup.sh
@ -15,7 +15,7 @@ task:
- cargo build --no-default-features
test_script:
- . $HOME/.cargo/env
- cargo test
- RUST_BACKTRACE=1 cargo test
- cargo test --no-default-features
before_cache_script:
- rm -rf $HOME/.cargo/registry/index

View File

@ -1,3 +1,19 @@
# 0.6.22 (May 01, 2020)
### Added
- Add support for illumos target (#1294)
# 0.6.21 (November 27, 2019)
### Fixed
- remove `=` dependency on `cfg-if`.
# 0.6.20 (November 21, 2019)
### Fixed
- Use default IOCP concurrency value (#1161).
- setting FD_CLOEXEC in pipe (#1095).
# 0.6.19 (May 28, 2018)
### Fixed

View File

@ -6,20 +6,22 @@ name = "mio"
# - Update CHANGELOG.md.
# - Update doc URL.
# - Create git tag
version = "0.6.19"
version = "0.6.22"
license = "MIT"
authors = ["Carl Lerche <me@carllerche.com>"]
description = "Lightweight non-blocking IO"
documentation = "https://docs.rs/mio/0.6.19/mio/"
homepage = "https://github.com/carllerche/mio"
repository = "https://github.com/carllerche/mio"
documentation = "https://docs.rs/mio/0.6.22/mio/"
homepage = "https://github.com/tokio-rs/mio"
repository = "https://github.com/tokio-rs/mio"
readme = "README.md"
keywords = ["io", "async", "non-blocking"]
categories = ["asynchronous"]
exclude = [
".gitignore",
".travis.yml",
"deploy.sh",
include = [
"Cargo.toml",
"LICENSE",
"README.md",
"CHANGELOG.md",
"src/**/*.rs"
]
[features]
@ -31,13 +33,14 @@ log = "0.4"
slab = "0.4.0"
net2 = "0.2.29"
iovec = "0.1.1"
cfg-if = "0.1.9"
[target.'cfg(target_os = "fuchsia")'.dependencies]
fuchsia-zircon = "0.3.2"
fuchsia-zircon-sys = "0.3.2"
[target.'cfg(unix)'.dependencies]
libc = "0.2.42"
libc = "0.2.54"
[target.'cfg(windows)'.dependencies]
winapi = "0.2.6"

View File

@ -20,7 +20,7 @@ jobs:
name: nightly
displayName: Nightly
# Pin nightly to avoid being impacted by breakage
rust_version: nightly-2019-04-22
rust_version: nightly-2019-11-17
benches: true
# This represents the minimum Rust version supported by

View File

@ -1,4 +1,5 @@
#![feature(test)]
#![allow(deprecated)]
extern crate mio;
extern crate test;

View File

@ -7,7 +7,7 @@ jobs:
strategy:
matrix:
iOS:
vmImage: macOS-10.13
vmImage: macOS-10.14
target: x86_64-apple-ios
Android:

View File

@ -2,7 +2,10 @@ steps:
# Linux and macOS.
- script: |
set -e
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none
export PATH=$PATH:$HOME/.cargo/bin
rustup toolchain install $RUSTUP_TOOLCHAIN
rustup default $RUSTUP_TOOLCHAIN
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
env:
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
@ -12,8 +15,10 @@ steps:
# Windows.
- script: |
curl -sSf -o rustup-init.exe https://win.rustup.rs
rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
rustup-init.exe -y --default-toolchain none
set PATH=%PATH%;%USERPROFILE%\.cargo\bin
rustup toolchain install %RUSTUP_TOOLCHAIN%
rustup default %RUSTUP_TOOLCHAIN%
echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
env:
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}

View File

@ -12,7 +12,7 @@ jobs:
${{ if parameters.cross }}:
MacOS:
vmImage: macOS-10.13
vmImage: macOS-10.14
Windows:
vmImage: vs2017-win2016
pool:
@ -23,7 +23,10 @@ jobs:
parameters:
rust_version: ${{ parameters.rust_version }}
- script: cargo ${{ parameters.cmd }}
- script: |
cargo update
cargo update -p cfg-if --precise 0.1.9
cargo ${{ parameters.cmd }}
displayName: cargo ${{ parameters.cmd }}
env:
CI: 'True'

View File

@ -1,7 +1,7 @@
#![doc(html_root_url = "https://docs.rs/mio/0.6.19")]
#![doc(html_root_url = "https://docs.rs/mio/0.6.22")]
// Mio targets old versions of the Rust compiler. In order to do this, uses
// deprecated APIs.
#![allow(deprecated)]
#![allow(bare_trait_objects, deprecated, unknown_lints)]
#![deny(missing_docs, missing_debug_implementations)]
#![cfg_attr(test, deny(warnings))]

View File

@ -30,21 +30,9 @@ pub struct Selector {
impl Selector {
pub fn new() -> io::Result<Selector> {
let epfd = unsafe {
// Emulate `epoll_create` by using `epoll_create1` if it's available
// and otherwise falling back to `epoll_create` followed by a call to
// set the CLOEXEC flag.
dlsym!(fn epoll_create1(c_int) -> c_int);
match epoll_create1.get() {
Some(epoll_create1_fn) => {
cvt(epoll_create1_fn(libc::EPOLL_CLOEXEC))?
}
None => {
let fd = cvt(libc::epoll_create(1024))?;
drop(set_cloexec(fd));
fd
}
}
let fd = cvt(libc::epoll_create(1024))?;
drop(set_cloexec(fd));
fd
};
// offset by 1 to avoid choosing 0 as the id of a selector

View File

@ -3,10 +3,20 @@ use libc::{self, c_int};
#[macro_use]
pub mod dlsym;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "solaris"
))]
mod epoll;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "solaris"
))]
pub use self::epoll::{Events, Selector};
#[cfg(any(target_os = "bitrig", target_os = "dragonfly",
@ -50,23 +60,27 @@ pub fn pipe() -> ::io::Result<(Io, Io)> {
dlsym!(fn pipe2(*mut c_int, c_int) -> c_int);
let mut pipes = [0; 2];
let flags = libc::O_NONBLOCK | libc::O_CLOEXEC;
unsafe {
match pipe2.get() {
Some(pipe2_fn) => {
let flags = libc::O_NONBLOCK | libc::O_CLOEXEC;
cvt(pipe2_fn(pipes.as_mut_ptr(), flags))?;
Ok((Io::from_raw_fd(pipes[0]), Io::from_raw_fd(pipes[1])))
}
None => {
cvt(libc::pipe(pipes.as_mut_ptr()))?;
libc::fcntl(pipes[0], libc::F_SETFL, flags);
libc::fcntl(pipes[1], libc::F_SETFL, flags);
// Ensure the pipe are closed if any of the system calls below
// fail.
let r = Io::from_raw_fd(pipes[0]);
let w = Io::from_raw_fd(pipes[1]);
cvt(libc::fcntl(pipes[0], libc::F_SETFD, libc::FD_CLOEXEC))?;
cvt(libc::fcntl(pipes[1], libc::F_SETFD, libc::FD_CLOEXEC))?;
cvt(libc::fcntl(pipes[0], libc::F_SETFL, libc::O_NONBLOCK))?;
cvt(libc::fcntl(pipes[1], libc::F_SETFL, libc::O_NONBLOCK))?;
Ok((r, w))
}
}
}
unsafe {
Ok((Io::from_raw_fd(pipes[0]), Io::from_raw_fd(pipes[1])))
}
}
trait IsMinusOne {

View File

@ -109,10 +109,20 @@ const LIO: usize = 0b10_0000;
#[cfg(not(any(target_os = "freebsd")))]
const LIO: usize = 0b00_0000;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "solaris"
))]
const PRI: usize = 0b100_0000;
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "solaris")))]
#[cfg(not(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "solaris"
)))]
const PRI: usize = 0;
// Export to support `Ready::all`
@ -129,7 +139,12 @@ fn test_ready_all() {
);
// Issue #896.
#[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "solaris"
))]
assert!(!Ready::from(UnixReady::priority()).is_writable());
}
@ -258,8 +273,12 @@ impl UnixReady {
///
/// [`Poll`]: struct.Poll.html
#[inline]
#[cfg(any(target_os = "linux",
target_os = "android", target_os = "solaris"))]
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "solaris"
))]
pub fn priority() -> UnixReady {
UnixReady(ready_from_usize(PRI))
}
@ -385,8 +404,12 @@ impl UnixReady {
///
/// [`Poll`]: struct.Poll.html
#[inline]
#[cfg(any(target_os = "linux",
target_os = "android", target_os = "solaris"))]
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "solaris"
))]
pub fn is_priority(&self) -> bool {
self.contains(ready_from_usize(PRI))
}
@ -476,8 +499,12 @@ impl fmt::Debug for UnixReady {
(UnixReady::hup(), "Hup"),
#[allow(deprecated)]
(UnixReady::aio(), "Aio"),
#[cfg(any(target_os = "linux",
target_os = "android", target_os = "solaris"))]
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "solaris"
))]
(UnixReady::priority(), "Priority"),
];

View File

@ -55,7 +55,7 @@ impl Selector {
// offset by 1 to avoid choosing 0 as the id of a selector
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1;
CompletionPort::new(1).map(|cp| {
CompletionPort::new(0).map(|cp| {
Selector {
inner: Arc::new(SelectorInner {
id: id,

View File

@ -181,7 +181,6 @@ mod ports {
pub fn sleep_ms(ms: u64) {
use std::thread;
use std::time::Duration;
thread::sleep(Duration::from_millis(ms));
}

View File

@ -1,73 +1,262 @@
use std::net::Shutdown;
use std::time::Duration;
use std::collections::HashMap;
use std::net::{self, Shutdown};
use std::time::{Duration, Instant};
use mio::{Token, Ready, PollOpt, Poll, Events};
use mio::event::{Evented, Event};
use mio::net::TcpStream;
macro_rules! wait {
($poll:ident, $ready:ident) => {{
use std::time::Instant;
struct TestPoll {
poll: Poll,
events: Events,
buf: HashMap<Token, Ready>,
}
impl TestPoll {
fn new() -> TestPoll {
TestPoll {
poll: Poll::new().unwrap(),
events: Events::with_capacity(1024),
buf: HashMap::new(),
}
}
fn register<E: ?Sized>(&self, handle: &E, token: Token, interest: Ready, opts: PollOpt)
where E: Evented
{
self.poll.register(handle, token, interest, opts).unwrap();
}
fn wait_for(&mut self, token: Token, ready: Ready) -> Result<(), &'static str> {
let now = Instant::now();
let mut events = Events::with_capacity(16);
let mut found = false;
while !found {
if now.elapsed() > Duration::from_secs(5) {
panic!("not ready");
loop {
if now.elapsed() > Duration::from_secs(1) {
return Err("not ready");
}
$poll.poll(&mut events, Some(Duration::from_secs(1))).unwrap();
for event in &events {
#[cfg(unix)]
{
use mio::unix::UnixReady;
assert!(!UnixReady::from(event.readiness()).is_hup());
}
if event.token() == Token(0) && event.readiness().$ready() {
found = true;
if let Some(curr) = self.buf.get(&token) {
if curr.contains(ready) {
break;
}
}
self.poll.poll(&mut self.events, Some(Duration::from_millis(250))).unwrap();
for event in &self.events {
let curr = self.buf.entry(event.token())
.or_insert(Ready::empty());
*curr |= event.readiness();
}
}
*self.buf.get_mut(&token).unwrap() -= ready;
Ok(())
}
fn check_idle(&mut self) -> Result<(), Event> {
self.poll.poll(&mut self.events, Some(Duration::from_millis(100))).unwrap();
if let Some(e) = self.events.iter().next() {
Err(e)
} else {
Ok(())
}
}
}
macro_rules! assert_ready {
($poll:expr, $token:expr, $ready:expr) => {{
match $poll.wait_for($token, $ready) {
Ok(_) => {}
Err(_) => panic!("not ready; token = {:?}; interest = {:?}", $token, $ready),
}
}}
}
macro_rules! assert_not_ready {
($poll:expr, $token:expr, $ready:expr) => {{
match $poll.wait_for($token, $ready) {
Ok(_) => panic!("is ready; token = {:?}; interest = {:?}", $token, $ready),
Err(_) => {}
}
}}
}
macro_rules! assert_hup_ready {
($poll:expr) => {
#[cfg(unix)]
{
use mio::unix::UnixReady;
assert_ready!($poll, Token(0), Ready::from(UnixReady::hup()))
}
}
}
macro_rules! assert_not_hup_ready {
($poll:expr) => {
#[cfg(unix)]
{
use mio::unix::UnixReady;
assert_not_ready!($poll, Token(0), Ready::from(UnixReady::hup()))
}
}
}
macro_rules! assert_idle {
($poll:expr) => {
match $poll.check_idle() {
Ok(()) => {}
Err(e) => panic!("not idle; event = {:?}", e),
}
}
}
// TODO: replace w/ assertive
// https://github.com/carllerche/assertive
macro_rules! assert_ok {
($e:expr) => {
assert_ok!($e,)
};
($e:expr,) => {{
use std::result::Result::*;
match $e {
Ok(v) => v,
Err(e) => panic!("assertion failed: error = {:?}", e),
}
}};
($e:expr, $($arg:tt)+) => {{
use std::result::Result::*;
match $e {
Ok(v) => v,
Err(e) => panic!("assertion failed: error = {:?}: {}", e, format_args!($($arg)+)),
}
}};
}
#[test]
fn test_write_shutdown() {
let poll = Poll::new().unwrap();
use std::io::prelude::*;
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let mut poll = TestPoll::new();
let mut buf = [0; 1024];
let mut ready = Ready::readable() | Ready::writable();
let listener = assert_ok!(net::TcpListener::bind("127.0.0.1:0"));
let addr = assert_ok!(listener.local_addr());
#[cfg(unix)]
{
ready |= mio::unix::UnixReady::hup();
}
let client = TcpStream::connect(&addr).unwrap();
let mut client = assert_ok!(TcpStream::connect(&addr));
poll.register(&client,
Token(0),
ready,
PollOpt::edge()).unwrap();
Ready::readable() | Ready::writable(),
PollOpt::edge());
let (socket, _) = listener.accept().unwrap();
let (socket, _) = assert_ok!(listener.accept());
wait!(poll, is_writable);
let mut events = Events::with_capacity(16);
assert_ready!(poll, Token(0), Ready::writable());
// Polling should not have any events
poll.poll(&mut events, Some(Duration::from_millis(100))).unwrap();
assert!(events.iter().next().is_none());
assert_idle!(poll);
println!("SHUTTING DOWN");
// Now, shutdown the write half of the socket.
socket.shutdown(Shutdown::Write).unwrap();
assert_ok!(socket.shutdown(Shutdown::Write));
wait!(poll, is_readable);
assert_ready!(poll, Token(0), Ready::readable());
assert_not_hup_ready!(poll);
let n = assert_ok!(client.read(&mut buf));
assert_eq!(n, 0);
}
#[test]
fn test_graceful_shutdown() {
use std::io::prelude::*;
let mut poll = TestPoll::new();
let mut buf = [0; 1024];
let listener = assert_ok!(net::TcpListener::bind("127.0.0.1:0"));
let addr = assert_ok!(listener.local_addr());
let mut client = assert_ok!(TcpStream::connect(&addr));
poll.register(&client,
Token(0),
Ready::readable() | Ready::writable(),
PollOpt::edge());
let (mut socket, _) = assert_ok!(listener.accept());
assert_ready!(poll, Token(0), Ready::writable());
// Polling should not have any events
assert_idle!(poll);
// Now, shutdown the write half of the socket.
assert_ok!(client.shutdown(Shutdown::Write));
let n = assert_ok!(socket.read(&mut buf));
assert_eq!(0, n);
drop(socket);
assert_ready!(poll, Token(0), Ready::readable());
#[cfg(not(any(
target_os = "bitrig",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)))]
assert_hup_ready!(poll);
let mut buf = [0; 1024];
let n = assert_ok!(client.read(&mut buf));
assert_eq!(n, 0);
}
#[test]
fn test_abrupt_shutdown() {
use net2::TcpStreamExt;
use std::io::Read;
let mut poll = TestPoll::new();
let mut buf = [0; 1024];
let listener = assert_ok!(net::TcpListener::bind("127.0.0.1:0"));
let addr = assert_ok!(listener.local_addr());
let mut client = assert_ok!(TcpStream::connect(&addr));
poll.register(&client,
Token(0),
Ready::readable() | Ready::writable(),
PollOpt::edge());
let (socket, _) = assert_ok!(listener.accept());
assert_ok!(socket.set_linger(Some(Duration::from_millis(0))));
// assert_ok!(socket.set_linger(None));
// Wait to be connected
assert_ready!(poll, Token(0), Ready::writable());
drop(socket);
#[cfg(not(any(
target_os = "bitrig",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
)))]
assert_hup_ready!(poll);
assert_ready!(poll, Token(0), Ready::writable());
assert_ready!(poll, Token(0), Ready::readable());
let res = client.read(&mut buf);
assert!(res.is_err(), "not err = {:?}", res);
}