Compare commits

..

1 Commits

Author SHA1 Message Date
Carl Lerche
f1b580c7f4
Add more HUP tests (#944) 2019-06-07 12:03:37 -07:00
114 changed files with 18201 additions and 7077 deletions

View File

@ -1,9 +1,6 @@
freebsd_instance:
image: freebsd-11-2-release-amd64
env:
RUST_BACKTRACE: full
task:
name: FreeBSD 11.2 amd64
setup_script:
@ -16,12 +13,9 @@ task:
- . $HOME/.cargo/env
- cargo build
- cargo build --no-default-features
amd64_test_script:
test_script:
- . $HOME/.cargo/env
- cargo test
i386_test_script:
- . $HOME/.cargo/env
- rustup target add i686-unknown-freebsd
- cargo test --target i686-unknown-freebsd
- RUST_BACKTRACE=1 cargo test
- cargo test --no-default-features
before_cache_script:
- rm -rf $HOME/.cargo/registry/index

View File

@ -1,9 +1,3 @@
# Unreleased (Unreleased)
* The `join_multicast_v4` and `leave_multicast_v4` methods now take their
`Ipv4Addr` arguments by value rather than by reference.
* Fix lazycell related compilation issues.
# 0.6.19 (May 28, 2018)
### Fixed

View File

@ -1,18 +1,18 @@
[package]
edition = "2018"
name = "mio"
name = "mio"
# When releasing to crates.io:
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Update doc URL.
# - Create git tag
version = "0.7.0"
version = "0.6.19"
license = "MIT"
authors = ["Carl Lerche <me@carllerche.com>"]
description = "Lightweight non-blocking IO"
documentation = "https://docs.rs/mio/0.7.0/mio/"
homepage = "https://github.com/tokio-rs/mio"
repository = "https://github.com/tokio-rs/mio"
documentation = "https://docs.rs/mio/0.6.19/mio/"
homepage = "https://github.com/carllerche/mio"
repository = "https://github.com/carllerche/mio"
readme = "README.md"
keywords = ["io", "async", "non-blocking"]
categories = ["asynchronous"]
@ -21,27 +21,34 @@ exclude = [
".travis.yml",
"deploy.sh",
]
publish = false
[features]
with-deprecated = []
default = ["with-deprecated"]
[dependencies]
log = "0.4.6"
log = "0.4"
slab = "0.4.0"
net2 = "0.2.29"
iovec = "0.1.1"
[target.'cfg(target_os = "fuchsia")'.dependencies]
fuchsia-zircon = "0.3.2"
fuchsia-zircon-sys = "0.3.2"
[target.'cfg(unix)'.dependencies]
libc = "0.2.58"
libc = "0.2.42"
[target.'cfg(windows)'.dependencies]
miow = "0.3.3"
winapi = { version = "0.3", features = ["winsock2", "mswsock"] }
ntapi = "0.3"
lazy_static = "1.3.0"
winapi = "0.2.6"
miow = "0.2.1"
kernel32-sys = "0.2"
[dev-dependencies]
# Bytes v0.4 still depends on winapi v0.2, but Bytes v0.5 is released yet. So
# for testing we'll use the git version.
bytes = { version = "0.5.0", git = "https://github.com/tokio-rs/bytes", rev = "79e4b2847f27137faaf149d116a352cbeae47fd1" }
env_logger = { version = "0.6.1", default-features = false }
slab = "0.4.2"
tempdir = "0.3.7"
net2 = "0.2.33"
env_logger = { version = "0.4.0", default-features = false }
tempdir = "0.3.4"
bytes = "0.3.0"
[[test]]
name = "test"
path = "test/mod.rs"

View File

@ -14,8 +14,8 @@ overhead as possible over the OS abstractions.
[mit-url]: LICENSE
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.mio?branchName=master
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=2&branchName=master
[cirrus-badge]: https://api.cirrus-ci.com/github/tokio-rs/mio.svg
[cirrus-url]: https://cirrus-ci.com/github/tokio-rs/mio
[cirrus-badge]: https://api.cirrus-ci.com/github/carllerche/mio.svg
[cirrus-url]: https://cirrus-ci.com/github/carllerche/mio
**API documentation**
@ -42,10 +42,10 @@ extern crate mio;
## Features
* Non-blocking TCP, UDP
* I/O event queue backed by epoll, kqueue, and IOCP
* Non-blocking TCP, UDP.
* I/O event notification queue backed by epoll, kqueue, and IOCP.
* Zero allocations at runtime
* Platform specific extensions
* Platform specific extensions.
## Non-goals
@ -60,27 +60,22 @@ or higher-level libraries.
Currently supported platforms:
* Android
* Bitrig
* DragonFly BSD
* FreeBSD
* Linux
* NetBSD
* OpenBSD
* Solaris
* OS X
* Windows
* FreeBSD
* NetBSD
* Solaris
* Android
* iOS
* macOS
There are potentially others. If you find that Mio works on another
platform, submit a PR to update the list!
## Community
A group of Mio users hang out on [Gitter], this can be a good place to go for
questions.
[Gitter]: https://gitter.im/tokio-rs/mio
A group of Mio users hang out in the #mio channel on the Mozilla IRC
server (irc.mozilla.org). This can be a good place to go for questions.
## Contributing
@ -92,4 +87,4 @@ If you want to propose an API change, create an issue to start a
discussion with the community. Also, feel free to talk with us in the
IRC channel.
Finally, be kind. We support the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct).
Finally, be kind. We support the [Rust Code of Conduct](https://www.rust-lang.org/conduct.html).

View File

@ -3,9 +3,9 @@ pr: ["master", "v0.6.x"]
jobs:
# Check formatting
- template: ci/azure-rustfmt.yml
parameters:
name: rustfmt
# - template: ci/azure-rustfmt.yml
# parameters:
# name: rustfmt
# Stable
- template: ci/azure-test-stable.yml
@ -14,20 +14,13 @@ jobs:
displayName: Test
cross: true
# Stable --release
- template: ci/azure-test-stable.yml
parameters:
name: stable_release
displayName: Test --release
cmd: test --release
# Nightly
- template: ci/azure-test-stable.yml
parameters:
name: nightly
displayName: Nightly
# Pin nightly to avoid being impacted by breakage
rust_version: nightly-2019-07-16
rust_version: nightly-2019-04-22
benches: true
# This represents the minimum Rust version supported by
@ -39,18 +32,10 @@ jobs:
parameters:
name: minrust
displayName: Min Rust
rust_version: 1.36.0
rust_version: 1.18.0
cmd: check
cross: true
- template: ci/azure-minimal-versions.yml
parameters:
name: minimal_versions
- template: ci/azure-clippy.yml
parameters:
name: clippy
- template: ci/azure-cross-compile.yml
parameters:
name: cross

52
benches/bench_poll.rs Normal file
View File

@ -0,0 +1,52 @@
#![feature(test)]
extern crate mio;
extern crate test;
use mio::*;
use test::Bencher;
use std::sync::Arc;
use std::thread;
#[bench]
fn bench_poll(bench: &mut Bencher) {
const NUM: usize = 10_000;
const THREADS: usize = 4;
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let mut registrations = vec![];
let mut set_readiness = vec![];
for i in 0..NUM {
let (r, s) = Registration::new(
&poll,
Token(i),
Ready::readable(),
PollOpt::edge());
registrations.push(r);
set_readiness.push(s);
}
let set_readiness = Arc::new(set_readiness);
bench.iter(move || {
for mut i in 0..THREADS {
let set_readiness = set_readiness.clone();
thread::spawn(move || {
while i < NUM {
set_readiness[i].set_readiness(Ready::readable()).unwrap();
i += THREADS;
}
});
}
let mut n = 0;
while n < NUM {
n += poll.poll(&mut events, None).unwrap();
}
})
}

View File

@ -1,17 +0,0 @@
jobs:
- job: ${{ parameters.name }}
displayName: Clippy
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: stable
- script: rustup component add clippy
displayName: "Add component"
- script: cargo clippy --all-targets -- -D warnings -A clippy::cognitive-complexity
displayName: "Run Clippy"

View File

@ -6,30 +6,18 @@ jobs:
displayName: Cross
strategy:
matrix:
iOS_64:
iOS:
vmImage: macOS-10.13
target: x86_64-apple-ios
iOS_32:
vmImage: macOS-10.13
target: i386-apple-ios
iOS_ARM:
vmImage: macOS-10.13
target: armv7s-apple-ios
Android:
vmImage: ubuntu-16.04
target: arm-linux-androideabi
Android_ARM64:
Android_64:
vmImage: ubuntu-16.04
target: aarch64-linux-android
Android_32:
vmImage: ubuntu-16.04
target: i686-unknown-linux-gnu
NetBSD:
vmImage: ubuntu-16.04
target: x86_64-unknown-netbsd

View File

@ -2,10 +2,7 @@ steps:
# Linux and macOS.
- script: |
set -e
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
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
env:
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
@ -15,10 +12,8 @@ steps:
# Windows.
- script: |
curl -sSf -o rustup-init.exe https://win.rustup.rs
rustup-init.exe -y --default-toolchain none
rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
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}}
@ -27,7 +22,6 @@ steps:
# All platforms.
- script: |
rustup toolchain list
rustc -Vv
cargo -V
displayName: Query rust and cargo versions

View File

@ -1,37 +0,0 @@
parameters:
rust_version: nightly
jobs:
- job: ${{ parameters.name }}
displayName: Minimal versions
strategy:
matrix:
Linux:
vmImage: ubuntu-16.04
Windows:
vmImage: vs2017-win2016
pool:
vmImage: $(vmImage)
variables:
RUST_BACKTRACE: full
steps:
- template: azure-install-rust.yml
parameters:
rust_version: ${{ parameters.rust_version }}
- script: cargo update -Zminimal-versions
displayName: cargo update -Zminimal-versions
env:
CI: 'True'
- script: cargo test
displayName: cargo test
env:
CI: 'True'
- script: cargo test --no-default-features
displayName: cargo test --no-default-features
env:
CI: 'True'

View File

@ -1,16 +0,0 @@
jobs:
# Check formatting
- job: ${{ parameters.name }}
displayName: Check rustfmt
pool:
vmImage: ubuntu-16.04
steps:
- template: azure-install-rust.yml
parameters:
rust_version: stable
- script: |
rustup component add rustfmt
displayName: Install rustfmt
- script: |
cargo fmt --all -- --check
displayName: Check formatting

View File

@ -18,9 +18,6 @@ jobs:
pool:
vmImage: $(vmImage)
variables:
RUST_BACKTRACE: full
steps:
- template: azure-install-rust.yml
parameters:

390
src/channel.rs Normal file
View File

@ -0,0 +1,390 @@
//! Thread safe communication channel implementing `Evented`
#![allow(unused_imports, deprecated, missing_debug_implementations)]
use {io, Ready, Poll, PollOpt, Registration, SetReadiness, Token};
use event::Evented;
use lazycell::{LazyCell, AtomicLazyCell};
use std::any::Any;
use std::fmt;
use std::error;
use std::sync::{mpsc, Arc};
use std::sync::atomic::{AtomicUsize, Ordering};
/// Creates a new asynchronous channel, where the `Receiver` can be registered
/// with `Poll`.
pub fn channel<T>() -> (Sender<T>, Receiver<T>) {
let (tx_ctl, rx_ctl) = ctl_pair();
let (tx, rx) = mpsc::channel();
let tx = Sender {
tx,
ctl: tx_ctl,
};
let rx = Receiver {
rx,
ctl: rx_ctl,
};
(tx, rx)
}
/// Creates a new synchronous, bounded channel where the `Receiver` can be
/// registered with `Poll`.
pub fn sync_channel<T>(bound: usize) -> (SyncSender<T>, Receiver<T>) {
let (tx_ctl, rx_ctl) = ctl_pair();
let (tx, rx) = mpsc::sync_channel(bound);
let tx = SyncSender {
tx,
ctl: tx_ctl,
};
let rx = Receiver {
rx,
ctl: rx_ctl,
};
(tx, rx)
}
pub fn ctl_pair() -> (SenderCtl, ReceiverCtl) {
let inner = Arc::new(Inner {
pending: AtomicUsize::new(0),
senders: AtomicUsize::new(1),
set_readiness: AtomicLazyCell::new(),
});
let tx = SenderCtl {
inner: inner.clone(),
};
let rx = ReceiverCtl {
registration: LazyCell::new(),
inner,
};
(tx, rx)
}
/// Tracks messages sent on a channel in order to update readiness.
pub struct SenderCtl {
inner: Arc<Inner>,
}
/// Tracks messages received on a channel in order to track readiness.
pub struct ReceiverCtl {
registration: LazyCell<Registration>,
inner: Arc<Inner>,
}
pub struct Sender<T> {
tx: mpsc::Sender<T>,
ctl: SenderCtl,
}
pub struct SyncSender<T> {
tx: mpsc::SyncSender<T>,
ctl: SenderCtl,
}
pub struct Receiver<T> {
rx: mpsc::Receiver<T>,
ctl: ReceiverCtl,
}
pub enum SendError<T> {
Io(io::Error),
Disconnected(T),
}
pub enum TrySendError<T> {
Io(io::Error),
Full(T),
Disconnected(T),
}
struct Inner {
// The number of outstanding messages for the receiver to read
pending: AtomicUsize,
// The number of sender handles
senders: AtomicUsize,
// The set readiness handle
set_readiness: AtomicLazyCell<SetReadiness>,
}
impl<T> Sender<T> {
pub fn send(&self, t: T) -> Result<(), SendError<T>> {
self.tx.send(t)
.map_err(SendError::from)
.and_then(|_| {
self.ctl.inc()?;
Ok(())
})
}
}
impl<T> Clone for Sender<T> {
fn clone(&self) -> Sender<T> {
Sender {
tx: self.tx.clone(),
ctl: self.ctl.clone(),
}
}
}
impl<T> SyncSender<T> {
pub fn send(&self, t: T) -> Result<(), SendError<T>> {
self.tx.send(t)
.map_err(From::from)
.and_then(|_| {
self.ctl.inc()?;
Ok(())
})
}
pub fn try_send(&self, t: T) -> Result<(), TrySendError<T>> {
self.tx.try_send(t)
.map_err(From::from)
.and_then(|_| {
self.ctl.inc()?;
Ok(())
})
}
}
impl<T> Clone for SyncSender<T> {
fn clone(&self) -> SyncSender<T> {
SyncSender {
tx: self.tx.clone(),
ctl: self.ctl.clone(),
}
}
}
impl<T> Receiver<T> {
pub fn try_recv(&self) -> Result<T, mpsc::TryRecvError> {
self.rx.try_recv().and_then(|res| {
let _ = self.ctl.dec();
Ok(res)
})
}
}
impl<T> Evented for Receiver<T> {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.ctl.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.ctl.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.ctl.deregister(poll)
}
}
/*
*
* ===== SenderCtl / ReceiverCtl =====
*
*/
impl SenderCtl {
/// Call to track that a message has been sent
pub fn inc(&self) -> io::Result<()> {
let cnt = self.inner.pending.fetch_add(1, Ordering::Acquire);
if 0 == cnt {
// Toggle readiness to readable
if let Some(set_readiness) = self.inner.set_readiness.borrow() {
set_readiness.set_readiness(Ready::readable())?;
}
}
Ok(())
}
}
impl Clone for SenderCtl {
fn clone(&self) -> SenderCtl {
self.inner.senders.fetch_add(1, Ordering::Relaxed);
SenderCtl { inner: self.inner.clone() }
}
}
impl Drop for SenderCtl {
fn drop(&mut self) {
if self.inner.senders.fetch_sub(1, Ordering::Release) == 1 {
let _ = self.inc();
}
}
}
impl ReceiverCtl {
pub fn dec(&self) -> io::Result<()> {
let first = self.inner.pending.load(Ordering::Acquire);
if first == 1 {
// Unset readiness
if let Some(set_readiness) = self.inner.set_readiness.borrow() {
set_readiness.set_readiness(Ready::empty())?;
}
}
// Decrement
let second = self.inner.pending.fetch_sub(1, Ordering::AcqRel);
if first == 1 && second > 1 {
// There are still pending messages. Since readiness was
// previously unset, it must be reset here
if let Some(set_readiness) = self.inner.set_readiness.borrow() {
set_readiness.set_readiness(Ready::readable())?;
}
}
Ok(())
}
}
impl Evented for ReceiverCtl {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
if self.registration.borrow().is_some() {
return Err(io::Error::new(io::ErrorKind::Other, "receiver already registered"));
}
let (registration, set_readiness) = Registration::new(poll, token, interest, opts);
if self.inner.pending.load(Ordering::Relaxed) > 0 {
// TODO: Don't drop readiness
let _ = set_readiness.set_readiness(Ready::readable());
}
self.registration.fill(registration).expect("unexpected state encountered");
self.inner.set_readiness.fill(set_readiness).expect("unexpected state encountered");
Ok(())
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
match self.registration.borrow() {
Some(registration) => registration.update(poll, token, interest, opts),
None => Err(io::Error::new(io::ErrorKind::Other, "receiver not registered")),
}
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
match self.registration.borrow() {
Some(registration) => registration.deregister(poll),
None => Err(io::Error::new(io::ErrorKind::Other, "receiver not registered")),
}
}
}
/*
*
* ===== Error conversions =====
*
*/
impl<T> From<mpsc::SendError<T>> for SendError<T> {
fn from(src: mpsc::SendError<T>) -> SendError<T> {
SendError::Disconnected(src.0)
}
}
impl<T> From<io::Error> for SendError<T> {
fn from(src: io::Error) -> SendError<T> {
SendError::Io(src)
}
}
impl<T> From<mpsc::TrySendError<T>> for TrySendError<T> {
fn from(src: mpsc::TrySendError<T>) -> TrySendError<T> {
match src {
mpsc::TrySendError::Full(v) => TrySendError::Full(v),
mpsc::TrySendError::Disconnected(v) => TrySendError::Disconnected(v),
}
}
}
impl<T> From<mpsc::SendError<T>> for TrySendError<T> {
fn from(src: mpsc::SendError<T>) -> TrySendError<T> {
TrySendError::Disconnected(src.0)
}
}
impl<T> From<io::Error> for TrySendError<T> {
fn from(src: io::Error) -> TrySendError<T> {
TrySendError::Io(src)
}
}
/*
*
* ===== Implement Error, Debug and Display for Errors =====
*
*/
impl<T: Any> error::Error for SendError<T> {
fn description(&self) -> &str {
match *self {
SendError::Io(ref io_err) => io_err.description(),
SendError::Disconnected(..) => "Disconnected",
}
}
}
impl<T: Any> error::Error for TrySendError<T> {
fn description(&self) -> &str {
match *self {
TrySendError::Io(ref io_err) => io_err.description(),
TrySendError::Full(..) => "Full",
TrySendError::Disconnected(..) => "Disconnected",
}
}
}
impl<T> fmt::Debug for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format_send_error(self, f)
}
}
impl<T> fmt::Display for SendError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format_send_error(self, f)
}
}
impl<T> fmt::Debug for TrySendError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format_try_send_error(self, f)
}
}
impl<T> fmt::Display for TrySendError<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
format_try_send_error(self, f)
}
}
#[inline]
fn format_send_error<T>(e: &SendError<T>, f: &mut fmt::Formatter) -> fmt::Result {
match *e {
SendError::Io(ref io_err) => write!(f, "{}", io_err),
SendError::Disconnected(..) => write!(f, "Disconnected"),
}
}
#[inline]
fn format_try_send_error<T>(e: &TrySendError<T>, f: &mut fmt::Formatter) -> fmt::Result {
match *e {
TrySendError::Io(ref io_err) => write!(f, "{}", io_err),
TrySendError::Full(..) => write!(f, "Full"),
TrySendError::Disconnected(..) => write!(f, "Disconnected"),
}
}

View File

@ -0,0 +1,346 @@
use {channel, Poll, Events, Token};
use event::Evented;
use deprecated::{Handler, NotifyError};
use event_imp::{Event, Ready, PollOpt};
use timer::{self, Timer, Timeout};
use std::{io, fmt, usize};
use std::default::Default;
use std::time::Duration;
#[derive(Debug, Default, Clone)]
pub struct EventLoopBuilder {
config: Config,
}
/// `EventLoop` configuration details
#[derive(Clone, Debug)]
struct Config {
// == Notifications ==
notify_capacity: usize,
messages_per_tick: usize,
// == Timer ==
timer_tick: Duration,
timer_wheel_size: usize,
timer_capacity: usize,
}
impl Default for Config {
fn default() -> Config {
// Default EventLoop configuration values
Config {
notify_capacity: 4_096,
messages_per_tick: 256,
timer_tick: Duration::from_millis(100),
timer_wheel_size: 1_024,
timer_capacity: 65_536,
}
}
}
impl EventLoopBuilder {
/// Construct a new `EventLoopBuilder` with the default configuration
/// values.
pub fn new() -> EventLoopBuilder {
EventLoopBuilder::default()
}
/// Sets the maximum number of messages that can be buffered on the event
/// loop's notification channel before a send will fail.
///
/// The default value for this is 4096.
pub fn notify_capacity(&mut self, capacity: usize) -> &mut Self {
self.config.notify_capacity = capacity;
self
}
/// Sets the maximum number of messages that can be processed on any tick of
/// the event loop.
///
/// The default value for this is 256.
pub fn messages_per_tick(&mut self, messages: usize) -> &mut Self {
self.config.messages_per_tick = messages;
self
}
pub fn timer_tick(&mut self, val: Duration) -> &mut Self {
self.config.timer_tick = val;
self
}
pub fn timer_wheel_size(&mut self, size: usize) -> &mut Self {
self.config.timer_wheel_size = size;
self
}
pub fn timer_capacity(&mut self, cap: usize) -> &mut Self {
self.config.timer_capacity = cap;
self
}
/// Constructs a new `EventLoop` using the configured values. The
/// `EventLoop` will not be running.
pub fn build<H: Handler>(self) -> io::Result<EventLoop<H>> {
EventLoop::configured(self.config)
}
}
/// Single threaded IO event loop.
pub struct EventLoop<H: Handler> {
run: bool,
poll: Poll,
events: Events,
timer: Timer<H::Timeout>,
notify_tx: channel::SyncSender<H::Message>,
notify_rx: channel::Receiver<H::Message>,
config: Config,
}
// Token used to represent notifications
const NOTIFY: Token = Token(usize::MAX - 1);
const TIMER: Token = Token(usize::MAX - 2);
impl<H: Handler> EventLoop<H> {
/// Constructs a new `EventLoop` using the default configuration values.
/// The `EventLoop` will not be running.
pub fn new() -> io::Result<EventLoop<H>> {
EventLoop::configured(Config::default())
}
fn configured(config: Config) -> io::Result<EventLoop<H>> {
// Create the IO poller
let poll = Poll::new()?;
let timer = timer::Builder::default()
.tick_duration(config.timer_tick)
.num_slots(config.timer_wheel_size)
.capacity(config.timer_capacity)
.build();
// Create cross thread notification queue
let (tx, rx) = channel::sync_channel(config.notify_capacity);
// Register the notification wakeup FD with the IO poller
poll.register(&rx, NOTIFY, Ready::readable(), PollOpt::edge() | PollOpt::oneshot())?;
poll.register(&timer, TIMER, Ready::readable(), PollOpt::edge())?;
Ok(EventLoop {
run: true,
poll,
timer,
notify_tx: tx,
notify_rx: rx,
config,
events: Events::with_capacity(1024),
})
}
/// Returns a sender that allows sending messages to the event loop in a
/// thread-safe way, waking up the event loop if needed.
///
/// # Implementation Details
///
/// Each [EventLoop](#) contains a lock-free queue with a pre-allocated
/// buffer size. The size can be changed by modifying
/// [EventLoopConfig.notify_capacity](struct.EventLoopConfig.html#method.notify_capacity).
/// When a message is sent to the EventLoop, it is first pushed on to the
/// queue. Then, if the EventLoop is currently running, an atomic flag is
/// set to indicate that the next loop iteration should be started without
/// waiting.
///
/// If the loop is blocked waiting for IO events, then it is woken up. The
/// strategy for waking up the event loop is platform dependent. For
/// example, on a modern Linux OS, eventfd is used. On older OSes, a pipe
/// is used.
///
/// The strategy of setting an atomic flag if the event loop is not already
/// sleeping allows avoiding an expensive wakeup operation if at all possible.
pub fn channel(&self) -> Sender<H::Message> {
Sender::new(self.notify_tx.clone())
}
/// Schedules a timeout after the requested time interval. When the
/// duration has been reached,
/// [Handler::timeout](trait.Handler.html#method.timeout) will be invoked
/// passing in the supplied token.
///
/// Returns a handle to the timeout that can be used to cancel the timeout
/// using [#clear_timeout](#method.clear_timeout).
pub fn timeout(&mut self, token: H::Timeout, delay: Duration) -> timer::Result<Timeout> {
self.timer.set_timeout(delay, token)
}
/// If the supplied timeout has not been triggered, cancel it such that it
/// will not be triggered in the future.
pub fn clear_timeout(&mut self, timeout: &Timeout) -> bool {
self.timer.cancel_timeout(&timeout).is_some()
}
/// Tells the event loop to exit after it is done handling all events in the
/// current iteration.
pub fn shutdown(&mut self) {
self.run = false;
}
/// Indicates whether the event loop is currently running. If it's not it has either
/// stopped or is scheduled to stop on the next tick.
pub fn is_running(&self) -> bool {
self.run
}
/// Registers an IO handle with the event loop.
pub fn register<E: ?Sized>(&mut self, io: &E, token: Token, interest: Ready, opt: PollOpt) -> io::Result<()>
where E: Evented
{
self.poll.register(io, token, interest, opt)
}
/// Re-Registers an IO handle with the event loop.
pub fn reregister<E: ?Sized>(&mut self, io: &E, token: Token, interest: Ready, opt: PollOpt) -> io::Result<()>
where E: Evented
{
self.poll.reregister(io, token, interest, opt)
}
/// Keep spinning the event loop indefinitely, and notify the handler whenever
/// any of the registered handles are ready.
pub fn run(&mut self, handler: &mut H) -> io::Result<()> {
self.run = true;
while self.run {
// Execute ticks as long as the event loop is running
self.run_once(handler, None)?;
}
Ok(())
}
/// Deregisters an IO handle with the event loop.
///
/// Both kqueue and epoll will automatically clear any pending events when closing a
/// file descriptor (socket). In that case, this method does not need to be called
/// prior to dropping a connection from the slab.
///
/// Warning: kqueue effectively builds in deregister when using edge-triggered mode with
/// oneshot. Calling `deregister()` on the socket will cause a TcpStream error.
pub fn deregister<E: ?Sized>(&mut self, io: &E) -> io::Result<()> where E: Evented {
self.poll.deregister(io)
}
/// Spin the event loop once, with a given timeout (forever if `None`),
/// and notify the handler if any of the registered handles become ready
/// during that time.
pub fn run_once(&mut self, handler: &mut H, timeout: Option<Duration>) -> io::Result<()> {
trace!("event loop tick");
// Check the registered IO handles for any new events. Each poll
// is for one second, so a shutdown request can last as long as
// one second before it takes effect.
let events = match self.io_poll(timeout) {
Ok(e) => e,
Err(err) => {
if err.kind() == io::ErrorKind::Interrupted {
handler.interrupted(self);
0
} else {
return Err(err);
}
}
};
self.io_process(handler, events);
handler.tick(self);
Ok(())
}
#[inline]
fn io_poll(&mut self, timeout: Option<Duration>) -> io::Result<usize> {
self.poll.poll(&mut self.events, timeout)
}
// Process IO events that have been previously polled
fn io_process(&mut self, handler: &mut H, cnt: usize) {
let mut i = 0;
trace!("io_process(..); cnt={}; len={}", cnt, self.events.len());
// Iterate over the notifications. Each event provides the token
// it was registered with (which usually represents, at least, the
// handle that the event is about) as well as information about
// what kind of event occurred (readable, writable, signal, etc.)
while i < cnt {
let evt = self.events.get(i).unwrap();
trace!("event={:?}; idx={:?}", evt, i);
match evt.token() {
NOTIFY => self.notify(handler),
TIMER => self.timer_process(handler),
_ => self.io_event(handler, evt)
}
i += 1;
}
}
fn io_event(&mut self, handler: &mut H, evt: Event) {
handler.ready(self, evt.token(), evt.readiness());
}
fn notify(&mut self, handler: &mut H) {
for _ in 0..self.config.messages_per_tick {
match self.notify_rx.try_recv() {
Ok(msg) => handler.notify(self, msg),
_ => break,
}
}
// Re-register
let _ = self.poll.reregister(&self.notify_rx, NOTIFY, Ready::readable(), PollOpt::edge() | PollOpt::oneshot());
}
fn timer_process(&mut self, handler: &mut H) {
while let Some(t) = self.timer.poll() {
handler.timeout(self, t);
}
}
}
impl<H: Handler> fmt::Debug for EventLoop<H> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("EventLoop")
.field("run", &self.run)
.field("poll", &self.poll)
.field("config", &self.config)
.finish()
}
}
/// Sends messages to the EventLoop from other threads.
pub struct Sender<M> {
tx: channel::SyncSender<M>
}
impl<M> fmt::Debug for Sender<M> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "Sender<?> {{ ... }}")
}
}
impl<M> Clone for Sender <M> {
fn clone(&self) -> Sender<M> {
Sender { tx: self.tx.clone() }
}
}
impl<M> Sender<M> {
fn new(tx: channel::SyncSender<M>) -> Sender<M> {
Sender { tx }
}
pub fn send(&self, msg: M) -> Result<(), NotifyError<M>> {
self.tx.try_send(msg)?;
Ok(())
}
}

37
src/deprecated/handler.rs Normal file
View File

@ -0,0 +1,37 @@
use {Ready, Token};
use deprecated::{EventLoop};
#[allow(unused_variables)]
pub trait Handler: Sized {
type Timeout;
type Message;
/// Invoked when the socket represented by `token` is ready to be operated
/// on. `events` indicates the specific operations that are
/// ready to be performed.
///
/// For example, when a TCP socket is ready to be read from, `events` will
/// have `readable` set. When the socket is ready to be written to,
/// `events` will have `writable` set.
///
/// This function will only be invoked a single time per socket per event
/// loop tick.
fn ready(&mut self, event_loop: &mut EventLoop<Self>, token: Token, events: Ready) {
}
/// Invoked when a message has been received via the event loop's channel.
fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: Self::Message) {
}
/// Invoked when a timeout has completed.
fn timeout(&mut self, event_loop: &mut EventLoop<Self>, timeout: Self::Timeout) {
}
/// Invoked when `EventLoop` has been interrupted by a signal interrupt.
fn interrupted(&mut self, event_loop: &mut EventLoop<Self>) {
}
/// Invoked at the end of an event loop tick.
fn tick(&mut self, event_loop: &mut EventLoop<Self>) {
}
}

28
src/deprecated/io.rs Normal file
View File

@ -0,0 +1,28 @@
use ::io::MapNonBlock;
use std::io::{self, Read, Write};
pub trait TryRead {
fn try_read(&mut self, buf: &mut [u8]) -> io::Result<Option<usize>>;
}
pub trait TryWrite {
fn try_write(&mut self, buf: &[u8]) -> io::Result<Option<usize>>;
}
impl<T: Read> TryRead for T {
fn try_read(&mut self, dst: &mut [u8]) -> io::Result<Option<usize>> {
self.read(dst).map_non_block()
}
}
impl<T: Write> TryWrite for T {
fn try_write(&mut self, src: &[u8]) -> io::Result<Option<usize>> {
self.write(src).map_non_block()
}
}
pub trait TryAccept {
type Output;
fn accept(&self) -> io::Result<Option<Self::Output>>;
}

36
src/deprecated/mod.rs Normal file
View File

@ -0,0 +1,36 @@
#![allow(deprecated)]
mod event_loop;
mod io;
mod handler;
mod notify;
#[cfg(all(unix, not(target_os = "fuchsia")))]
pub mod unix;
pub use self::event_loop::{
EventLoop,
EventLoopBuilder,
Sender,
};
pub use self::io::{
TryAccept,
TryRead,
TryWrite,
};
pub use self::handler::{
Handler,
};
pub use self::notify::{
NotifyError,
};
#[cfg(all(unix, not(target_os = "fuchsia")))]
pub use self::unix::{
pipe,
PipeReader,
PipeWriter,
UnixListener,
UnixSocket,
UnixStream,
Shutdown,
};

63
src/deprecated/notify.rs Normal file
View File

@ -0,0 +1,63 @@
use {channel};
use std::{fmt, io, error, any};
pub enum NotifyError<T> {
Io(io::Error),
Full(T),
Closed(Option<T>),
}
impl<M> fmt::Debug for NotifyError<M> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
NotifyError::Io(ref e) => {
write!(fmt, "NotifyError::Io({:?})", e)
}
NotifyError::Full(..) => {
write!(fmt, "NotifyError::Full(..)")
}
NotifyError::Closed(..) => {
write!(fmt, "NotifyError::Closed(..)")
}
}
}
}
impl<M> fmt::Display for NotifyError<M> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
NotifyError::Io(ref e) => {
write!(fmt, "IO error: {}", e)
}
NotifyError::Full(..) => write!(fmt, "Full"),
NotifyError::Closed(..) => write!(fmt, "Closed")
}
}
}
impl<M: any::Any> error::Error for NotifyError<M> {
fn description(&self) -> &str {
match *self {
NotifyError::Io(ref err) => err.description(),
NotifyError::Closed(..) => "The receiving end has hung up",
NotifyError::Full(..) => "Queue is full"
}
}
fn cause(&self) -> Option<&error::Error> {
match *self {
NotifyError::Io(ref err) => Some(err),
_ => None
}
}
}
impl<M> From<channel::TrySendError<M>> for NotifyError<M> {
fn from(src: channel::TrySendError<M>) -> NotifyError<M> {
match src {
channel::TrySendError::Io(e) => NotifyError::Io(e),
channel::TrySendError::Full(v) => NotifyError::Full(v),
channel::TrySendError::Disconnected(v) => NotifyError::Closed(Some(v)),
}
}
}

420
src/deprecated/unix.rs Normal file
View File

@ -0,0 +1,420 @@
use {io, sys, Ready, Poll, PollOpt, Token};
use event::Evented;
use deprecated::TryAccept;
use io::MapNonBlock;
use std::io::{Read, Write};
use std::path::Path;
pub use std::net::Shutdown;
use std::process;
pub use sys::Io;
#[derive(Debug)]
pub struct UnixSocket {
sys: sys::UnixSocket,
}
impl UnixSocket {
/// Returns a new, unbound, non-blocking Unix domain socket
pub fn stream() -> io::Result<UnixSocket> {
sys::UnixSocket::stream()
.map(From::from)
}
/// Connect the socket to the specified address
pub fn connect<P: AsRef<Path> + ?Sized>(self, addr: &P) -> io::Result<(UnixStream, bool)> {
let complete = match self.sys.connect(addr) {
Ok(()) => true,
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => false,
Err(e) => return Err(e),
};
Ok((From::from(self.sys), complete))
}
/// Bind the socket to the specified address
pub fn bind<P: AsRef<Path> + ?Sized>(&self, addr: &P) -> io::Result<()> {
self.sys.bind(addr)
}
/// Listen for incoming requests
pub fn listen(self, backlog: usize) -> io::Result<UnixListener> {
self.sys.listen(backlog)?;
Ok(From::from(self.sys))
}
pub fn try_clone(&self) -> io::Result<UnixSocket> {
self.sys.try_clone()
.map(From::from)
}
}
impl Evented for UnixSocket {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.sys.deregister(poll)
}
}
impl From<sys::UnixSocket> for UnixSocket {
fn from(sys: sys::UnixSocket) -> UnixSocket {
UnixSocket { sys }
}
}
/*
*
* ===== UnixStream =====
*
*/
#[derive(Debug)]
pub struct UnixStream {
sys: sys::UnixSocket,
}
impl UnixStream {
pub fn connect<P: AsRef<Path> + ?Sized>(path: &P) -> io::Result<UnixStream> {
UnixSocket::stream()
.and_then(|sock| sock.connect(path))
.map(|(sock, _)| sock)
}
pub fn try_clone(&self) -> io::Result<UnixStream> {
self.sys.try_clone()
.map(From::from)
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<usize> {
self.sys.shutdown(how).map(|_| 0)
}
pub fn read_recv_fd(&mut self, buf: &mut [u8]) -> io::Result<(usize, Option<RawFd>)> {
self.sys.read_recv_fd(buf)
}
pub fn try_read_recv_fd(&mut self, buf: &mut [u8]) -> io::Result<Option<(usize, Option<RawFd>)>> {
self.read_recv_fd(buf).map_non_block()
}
pub fn write_send_fd(&mut self, buf: &[u8], fd: RawFd) -> io::Result<usize> {
self.sys.write_send_fd(buf, fd)
}
pub fn try_write_send_fd(&mut self, buf: &[u8], fd: RawFd) -> io::Result<Option<usize>> {
self.write_send_fd(buf, fd).map_non_block()
}
}
impl Read for UnixStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.sys.read(buf)
}
}
impl Write for UnixStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.sys.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.sys.flush()
}
}
impl Evented for UnixStream {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.sys.deregister(poll)
}
}
impl From<sys::UnixSocket> for UnixStream {
fn from(sys: sys::UnixSocket) -> UnixStream {
UnixStream { sys }
}
}
/*
*
* ===== UnixListener =====
*
*/
#[derive(Debug)]
pub struct UnixListener {
sys: sys::UnixSocket,
}
impl UnixListener {
pub fn bind<P: AsRef<Path> + ?Sized>(addr: &P) -> io::Result<UnixListener> {
UnixSocket::stream().and_then(|sock| {
sock.bind(addr)?;
sock.listen(256)
})
}
pub fn accept(&self) -> io::Result<UnixStream> {
self.sys.accept().map(From::from)
}
pub fn try_clone(&self) -> io::Result<UnixListener> {
self.sys.try_clone().map(From::from)
}
}
impl Evented for UnixListener {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.sys.deregister(poll)
}
}
impl TryAccept for UnixListener {
type Output = UnixStream;
fn accept(&self) -> io::Result<Option<UnixStream>> {
UnixListener::accept(self).map_non_block()
}
}
impl From<sys::UnixSocket> for UnixListener {
fn from(sys: sys::UnixSocket) -> UnixListener {
UnixListener { sys }
}
}
/*
*
* ===== Pipe =====
*
*/
pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> {
let (rd, wr) = sys::pipe()?;
Ok((From::from(rd), From::from(wr)))
}
#[derive(Debug)]
pub struct PipeReader {
io: Io,
}
impl PipeReader {
pub fn from_stdout(stdout: process::ChildStdout) -> io::Result<Self> {
if let Err(e) = sys::set_nonblock(stdout.as_raw_fd()) {
return Err(e);
}
Ok(PipeReader::from(unsafe { Io::from_raw_fd(stdout.into_raw_fd()) }))
}
pub fn from_stderr(stderr: process::ChildStderr) -> io::Result<Self> {
if let Err(e) = sys::set_nonblock(stderr.as_raw_fd()) {
return Err(e);
}
Ok(PipeReader::from(unsafe { Io::from_raw_fd(stderr.into_raw_fd()) }))
}
}
impl Read for PipeReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.io.read(buf)
}
}
impl<'a> Read for &'a PipeReader {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&self.io).read(buf)
}
}
impl Evented for PipeReader {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.io.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.io.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.io.deregister(poll)
}
}
impl From<Io> for PipeReader {
fn from(io: Io) -> PipeReader {
PipeReader { io }
}
}
#[derive(Debug)]
pub struct PipeWriter {
io: Io,
}
impl PipeWriter {
pub fn from_stdin(stdin: process::ChildStdin) -> io::Result<Self> {
if let Err(e) = sys::set_nonblock(stdin.as_raw_fd()) {
return Err(e);
}
Ok(PipeWriter::from(unsafe { Io::from_raw_fd(stdin.into_raw_fd()) }))
}
}
impl Write for PipeWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.io.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.io.flush()
}
}
impl<'a> Write for &'a PipeWriter {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&self.io).write(buf)
}
fn flush(&mut self) -> io::Result<()> {
(&self.io).flush()
}
}
impl Evented for PipeWriter {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.io.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.io.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.io.deregister(poll)
}
}
impl From<Io> for PipeWriter {
fn from(io: Io) -> PipeWriter {
PipeWriter { io }
}
}
/*
*
* ===== Conversions =====
*
*/
use std::os::unix::io::{RawFd, IntoRawFd, AsRawFd, FromRawFd};
impl IntoRawFd for UnixSocket {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
impl AsRawFd for UnixSocket {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
impl FromRawFd for UnixSocket {
unsafe fn from_raw_fd(fd: RawFd) -> UnixSocket {
UnixSocket { sys: FromRawFd::from_raw_fd(fd) }
}
}
impl IntoRawFd for UnixStream {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
impl AsRawFd for UnixStream {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
impl FromRawFd for UnixStream {
unsafe fn from_raw_fd(fd: RawFd) -> UnixStream {
UnixStream { sys: FromRawFd::from_raw_fd(fd) }
}
}
impl IntoRawFd for UnixListener {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
impl AsRawFd for UnixListener {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
impl FromRawFd for UnixListener {
unsafe fn from_raw_fd(fd: RawFd) -> UnixListener {
UnixListener { sys: FromRawFd::from_raw_fd(fd) }
}
}
impl IntoRawFd for PipeReader {
fn into_raw_fd(self) -> RawFd {
self.io.into_raw_fd()
}
}
impl AsRawFd for PipeReader {
fn as_raw_fd(&self) -> RawFd {
self.io.as_raw_fd()
}
}
impl FromRawFd for PipeReader {
unsafe fn from_raw_fd(fd: RawFd) -> PipeReader {
PipeReader { io: FromRawFd::from_raw_fd(fd) }
}
}
impl IntoRawFd for PipeWriter {
fn into_raw_fd(self) -> RawFd {
self.io.into_raw_fd()
}
}
impl AsRawFd for PipeWriter {
fn as_raw_fd(&self) -> RawFd {
self.io.as_raw_fd()
}
}
impl FromRawFd for PipeWriter {
unsafe fn from_raw_fd(fd: RawFd) -> PipeWriter {
PipeWriter { io: FromRawFd::from_raw_fd(fd) }
}
}

View File

@ -1,200 +0,0 @@
use crate::{sys, Token};
use std::fmt;
/// A readiness event.
///
/// `Event` is a readiness state paired with a [`Token`]. It is returned by
/// [`Poll::poll`].
///
/// For more documentation on polling and events, see [`Poll`].
///
/// [`Poll::poll`]: crate::Poll::poll
/// [`Poll`]: crate::Poll
/// [`Token`]: crate::Token
#[repr(transparent)]
pub struct Event {
inner: sys::Event,
}
impl Event {
/// Returns the event's token.
#[inline]
pub fn token(&self) -> Token {
sys::event::token(&self.inner)
}
/// Returns true if the event contains readable readiness.
#[inline]
pub fn is_readable(&self) -> bool {
sys::event::is_readable(&self.inner)
}
/// Returns true if the event contains writable readiness.
#[inline]
pub fn is_writable(&self) -> bool {
sys::event::is_writable(&self.inner)
}
/// Returns true if the event contains error readiness.
///
/// Error events occur when the socket enters an error state. In this case,
/// the socket will also receive a readable or writable event. Reading or
/// writing to the socket will result in an error.
///
/// # Notes
///
/// Method is available on all platforms, but not all platforms trigger the
/// error event.
///
/// The table below shows what flags are checked on what OS.
///
/// | [OS selector] | Flag(s) checked |
/// |---------------|-----------------|
/// | [epoll] | `EPOLLERR` |
/// | [kqueue] | `EV_ERROR` and `EV_EOF` with `fflags` set to `0`. |
///
/// [OS selector]: ../struct.Poll.html#implementation-notes
/// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
/// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
#[inline]
pub fn is_error(&self) -> bool {
sys::event::is_error(&self.inner)
}
/// Returns true if the event contains HUP readiness.
///
/// # Notes
///
/// Method is available on all platforms, but not all platforms trigger the
/// HUP event.
///
/// Because of the above be cautions when using this in cross-platform
/// applications, Mio makes no attempt at normalising this indicator and
/// only provides a convenience method to read it. Refer to the selector
/// documentation (below) when using this indicator.
///
/// The table below shows what flags are checked on what OS.
///
/// | [OS selector] | Flag(s) checked |
/// |---------------|-----------------|
/// | [epoll] | `EPOLLHUP` |
/// | [kqueue] | Not supported |
///
/// [OS selector]: ../struct.Poll.html#implementation-notes
/// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
/// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
#[inline]
pub fn is_hup(&self) -> bool {
sys::event::is_hup(&self.inner)
}
/// Returns true if the event contains read HUP readiness.
///
/// # Notes
///
/// Method is available on all platforms, but not all platforms trigger the
/// read HUP event.
///
/// Because of the above be cautions when using this in cross-platform
/// applications, Mio makes no attempt at normalising this indicator and
/// only provides a convenience method to read it. We advice looking at the
/// documentation provided for the selectors (see below) when using this
/// indicator.
///
/// The table below shows what flags are checked on what OS.
///
/// | [OS selector] | Flag(s) checked |
/// |---------------|-----------------|
/// | [epoll] | `EPOLLRDHUP` |
/// | [kqueue] | `EV_EOF` |
///
/// [OS selector]: ../struct.Poll.html#implementation-notes
/// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
/// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
#[inline]
pub fn is_read_hup(&self) -> bool {
sys::event::is_read_hup(&self.inner)
}
/// Returns true if the event contains priority readiness.
///
/// # Notes
///
/// Method is available on all platforms, but not all platforms trigger the
/// priority event.
///
/// The table below shows what flags are checked on what OS.
///
/// | [OS selector] | Flag(s) checked |
/// |---------------|-----------------|
/// | [epoll] | `EPOLLPRI` |
/// | [kqueue] | *Not supported* |
///
/// [OS selector]: ../struct.Poll.html#implementation-notes
/// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
/// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
#[inline]
pub fn is_priority(&self) -> bool {
sys::event::is_priority(&self.inner)
}
/// Returns true if the event contains AIO readiness.
///
/// # Notes
///
/// Method is available on all platforms, but not all platforms support AIO.
///
/// The table below shows what flags are checked on what OS.
///
/// | [OS selector] | Flag(s) checked |
/// |---------------|-----------------|
/// | [epoll] | *Not supported* |
/// | [kqueue]<sup>1</sup> | `EVFILT_AIO` |
///
/// 1: Only supported on DragonFly BSD, FreeBSD, iOS and macOS.
///
/// [OS selector]: ../struct.Poll.html#implementation-notes
/// [epoll]: http://man7.org/linux/man-pages/man7/epoll.7.html
/// [kqueue]: https://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2
#[inline]
pub fn is_aio(&self) -> bool {
sys::event::is_aio(&self.inner)
}
/// Returns true if the event contains LIO readiness.
///
/// # Notes
///
/// Method is available on all platforms, but only FreeBSD supports LIO. On
/// FreeBSD this method checks the `EVFILT_LIO` flag.
#[inline]
pub fn is_lio(&self) -> bool {
sys::event::is_lio(&self.inner)
}
/// Create a reference to an `Event` from a platform specific event.
pub(crate) fn from_sys_event_ref(sys_event: &sys::Event) -> &Event {
unsafe {
// This is safe because the memory layout of `Event` is
// the same as `sys::Event` due to the `repr(transparent)` attribute.
&*(sys_event as *const sys::Event as *const Event)
}
}
}
impl fmt::Debug for Event {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Event")
.field("token", &self.token())
.field("readable", &self.is_readable())
.field("writable", &self.is_writable())
.field("error", &self.is_error())
.field("hup", &self.is_hup())
.field("read_hup", &self.is_read_hup())
.field("priority", &self.is_priority())
.field("aio", &self.is_aio())
.field("lio", &self.is_lio())
.finish()
}
}

View File

@ -1,218 +0,0 @@
use crate::event::Event;
use crate::sys;
use std::fmt;
/// A collection of readiness events.
///
/// `Events` is passed as an argument to [`Poll::poll`] and will be used to
/// receive any new readiness events received since the last poll. Usually, a
/// single `Events` instance is created at the same time as a [`Poll`] and
/// reused on each call to [`Poll::poll`].
///
/// See [`Poll`] for more documentation on polling.
///
/// [`Poll::poll`]: crate::Poll::poll
/// [`Poll`]: crate::Poll
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::{Events, Poll};
/// use std::time::Duration;
///
/// let mut events = Events::with_capacity(1024);
/// let mut poll = Poll::new()?;
///
/// assert_eq!(0, events.iter().count());
///
/// // Register `event::Source`s with `poll`.
///
/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
///
/// for event in &events {
/// println!("event={:?}", event);
/// }
/// # Ok(())
/// # }
/// ```
pub struct Events {
inner: sys::Events,
}
/// [`Events`] iterator.
///
/// This struct is created by the [`iter`] method on [`Events`].
///
/// [`Events`]: crate::event::Events
/// [`iter`]: crate::event::Events::iter
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::{Events, Poll};
/// use std::time::Duration;
///
/// let mut events = Events::with_capacity(1024);
/// let mut poll = Poll::new()?;
///
/// // Register handles with `poll`
///
/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
///
/// for event in events.iter() {
/// println!("event={:?}", event);
/// }
/// # Ok(())
/// # }
/// ```
#[derive(Debug, Clone)]
pub struct Iter<'a> {
inner: &'a Events,
pos: usize,
}
impl Events {
/// Return a new `Events` capable of holding up to `capacity` events.
///
/// # Examples
///
/// ```
/// use mio::Events;
///
/// let events = Events::with_capacity(1024);
///
/// assert_eq!(1024, events.capacity());
/// ```
pub fn with_capacity(capacity: usize) -> Events {
Events {
inner: sys::Events::with_capacity(capacity),
}
}
/// Returns the number of `Event` values that `self` can hold.
///
/// ```
/// use mio::Events;
///
/// let events = Events::with_capacity(1024);
///
/// assert_eq!(1024, events.capacity());
/// ```
pub fn capacity(&self) -> usize {
self.inner.capacity()
}
/// Returns `true` if `self` contains no `Event` values.
///
/// # Examples
///
/// ```
/// use mio::Events;
///
/// let events = Events::with_capacity(1024);
///
/// assert!(events.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
/// Returns an iterator over the `Event` values.
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::{Events, Poll};
/// use std::time::Duration;
///
/// let mut events = Events::with_capacity(1024);
/// let mut poll = Poll::new()?;
///
/// // Register handles with `poll`
///
/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
///
/// for event in events.iter() {
/// println!("event={:?}", event);
/// }
/// # Ok(())
/// # }
/// ```
pub fn iter(&self) -> Iter<'_> {
Iter {
inner: self,
pos: 0,
}
}
/// Clearing all `Event` values from container explicitly.
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::{Events, Poll};
/// use std::time::Duration;
///
/// let mut events = Events::with_capacity(1024);
/// let mut poll = Poll::new()?;
///
/// // Register handles with `poll`
/// for _ in 0..2 {
/// events.clear();
/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
///
/// for event in events.iter() {
/// println!("event={:?}", event);
/// }
/// }
/// # Ok(())
/// # }
/// ```
pub fn clear(&mut self) {
self.inner.clear();
}
pub(crate) fn sys(&mut self) -> &mut sys::Events {
&mut self.inner
}
}
impl<'a> IntoIterator for &'a Events {
type Item = &'a Event;
type IntoIter = Iter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a Event;
fn next(&mut self) -> Option<Self::Item> {
let ret = self
.inner
.inner
.get(self.pos)
.map(Event::from_sys_event_ref);
self.pos += 1;
ret
}
}
impl fmt::Debug for Events {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Events")
.field("capacity", &self.capacity())
.finish()
}
}

View File

@ -1,10 +0,0 @@
//! Readiness event types and utilities.
#[allow(clippy::module_inception)]
mod event;
mod events;
mod source;
pub use self::event::Event;
pub use self::events::{Events, Iter};
pub use self::source::Source;

View File

@ -1,126 +0,0 @@
use crate::{Interests, Registry, Token};
use std::io;
use std::ops::Deref;
/// An event source that may be registered with [`Registry`].
///
/// Types that implement `event::Source` can be registered with
/// `Registry`. Users of Mio **should not** use the `event::Source` trait
/// functions directly. Instead, the equivalent functions on `Registry` should
/// be used.
///
/// See [`Registry`] for more details.
///
/// [`Registry`]: crate::Registry
///
/// # Implementing `event::Source`
///
/// Event sources are always backed by system handles, such as sockets or other
/// system handles. These `event::Source`s will be monitored by the system
/// selector. An implementation of `Source` will almost always delegates to a
/// lower level handle. Examples of this are [`TcpStream`]s, or the *unix only*
/// [`SourceFd`].
///
/// [`TcpStream`]: crate::net::TcpStream
/// [`SourceFd`]: crate::unix::SourceFd
///
/// # Dropping `event::Source`s
///
/// All `event::Source`s, unless otherwise specified, need to be [deregistered]
/// before being dropped for them to not leak resources. This goes against the
/// normal drop behaviour of types in Rust which cleanup after themselves, e.g.
/// a `File` will close itself. However since deregistering needs access to
/// [`Registry`] this cannot be done while being dropped.
///
/// [deregistered]: crate::Registry::deregister
///
/// # Examples
///
/// Implementing `Source` on a struct containing a socket:
///
/// ```
/// use mio::{Interests, Registry, Token};
/// use mio::event::Source;
/// use mio::net::TcpStream;
///
/// use std::io;
///
/// # #[allow(dead_code)]
/// pub struct MySource {
/// socket: TcpStream,
/// }
///
/// impl Source for MySource {
/// fn register(&self, registry: &Registry, token: Token, interests: Interests)
/// -> io::Result<()>
/// {
/// // Delegate the `register` call to `socket`
/// self.socket.register(registry, token, interests)
/// }
///
/// fn reregister(&self, registry: &Registry, token: Token, interests: Interests)
/// -> io::Result<()>
/// {
/// // Delegate the `reregister` call to `socket`
/// self.socket.reregister(registry, token, interests)
/// }
///
/// fn deregister(&self, registry: &Registry) -> io::Result<()> {
/// // Delegate the `deregister` call to `socket`
/// self.socket.deregister(registry)
/// }
/// }
/// ```
pub trait Source {
/// Register `self` with the given `Registry` instance.
///
/// This function should not be called directly. Use [`Registry::register`]
/// instead. Implementors should handle registration by delegating the call
/// to another `Source` type.
///
/// [`Registry::register`]: crate::Registry::register
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()>;
/// Re-register `self` with the given `Registry` instance.
///
/// This function should not be called directly. Use
/// [`Registry::reregister`] instead. Implementors should handle
/// re-registration by either delegating the call to another `Source` type.
///
/// [`Registry::reregister`]: crate::Registry::reregister
fn reregister(&self, registry: &Registry, token: Token, interests: Interests)
-> io::Result<()>;
/// Deregister `self` from the given `Registry` instance.
///
/// This function should not be called directly. Use
/// [`Registry::deregister`] instead. Implementors should handle
/// deregistration by delegating the call to another `Source` type.
///
/// [`Registry::deregister`]: crate::Registry::deregister
fn deregister(&self, registry: &Registry) -> io::Result<()>;
}
impl<T, S> Source for T
where
T: Deref<Target = S>,
S: Source + ?Sized,
{
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
self.deref().register(registry, token, interests)
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
self.deref().reregister(registry, token, interests)
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
self.deref().deregister(registry)
}
}

1164
src/event_imp.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,165 +0,0 @@
use std::num::NonZeroU8;
use std::{fmt, ops};
/// Interests used in registering.
///
/// Interests are used in [registering] [`event::Source`]s with [`Poll`], they
/// indicate what readiness should be monitored for. For example if a socket is
/// registered with [readable] interests and the socket becomes writable, no
/// event will be returned from a call to [`poll`].
///
/// The size of `Option<Interests>` should be identical to itself.
///
/// ```
/// use std::mem::size_of;
/// use mio::Interests;
///
/// assert_eq!(size_of::<Option<Interests>>(), size_of::<Interests>());
/// ```
///
/// [registering]: crate::Registry::register
/// [`event::Source`]: crate::event::Source
/// [`Poll`]: crate::Poll
/// [readable]: Interests::READABLE
/// [`poll`]: crate::Poll::poll
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Interests(NonZeroU8);
// These must be unique.
const READABLE: u8 = 0b0_001;
const WRITABLE: u8 = 0b0_010;
// The following are not available on all platforms.
#[cfg_attr(
not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"
)),
allow(dead_code)
)]
const AIO: u8 = 0b0_100;
#[cfg_attr(not(target_os = "freebsd"), allow(dead_code))]
const LIO: u8 = 0b1_000;
impl Interests {
/// Returns a `Interests` set representing readable interests.
pub const READABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(READABLE) });
/// Returns a `Interests` set representing writable interests.
pub const WRITABLE: Interests = Interests(unsafe { NonZeroU8::new_unchecked(WRITABLE) });
/// Returns a `Interests` set representing AIO completion interests.
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"
))]
pub const AIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(AIO) });
/// Returns a `Interests` set representing LIO completion interests.
#[cfg(target_os = "freebsd")]
pub const LIO: Interests = Interests(unsafe { NonZeroU8::new_unchecked(LIO) });
/// Add together two `Interests`.
///
/// This does the same thing as the `BitOr` implementation, but is a
/// constant function.
///
/// ```
/// use mio::Interests;
///
/// const INTERESTS: Interests = Interests::READABLE.add(Interests::WRITABLE);
/// # fn silent_dead_code_warning(_: Interests) { }
/// # silent_dead_code_warning(INTERESTS)
/// ```
#[allow(clippy::should_implement_trait)]
pub const fn add(self, other: Interests) -> Interests {
Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
}
/// Returns true if the value includes readable readiness.
pub const fn is_readable(self) -> bool {
(self.0.get() & READABLE) != 0
}
/// Returns true if the value includes writable readiness.
pub const fn is_writable(self) -> bool {
(self.0.get() & WRITABLE) != 0
}
/// Returns true if `Interests` contains AIO readiness
pub const fn is_aio(self) -> bool {
(self.0.get() & AIO) != 0
}
/// Returns true if `Interests` contains LIO readiness
pub const fn is_lio(self) -> bool {
(self.0.get() & LIO) != 0
}
}
impl ops::BitOr for Interests {
type Output = Self;
#[inline]
fn bitor(self, other: Self) -> Self {
Interests(unsafe { NonZeroU8::new_unchecked(self.0.get() | other.0.get()) })
}
}
impl ops::BitOrAssign for Interests {
#[inline]
fn bitor_assign(&mut self, other: Self) {
self.0 = (*self | other).0;
}
}
impl fmt::Debug for Interests {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut one = false;
if self.is_readable() {
if one {
write!(fmt, " | ")?
}
write!(fmt, "READABLE")?;
one = true
}
if self.is_writable() {
if one {
write!(fmt, " | ")?
}
write!(fmt, "WRITABLE")?;
one = true
}
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"
))]
{
if self.is_aio() {
if one {
write!(fmt, " | ")?
}
write!(fmt, "AIO")?;
one = true
}
}
#[cfg(any(target_os = "freebsd"))]
{
if self.is_lio() {
if one {
write!(fmt, " | ")?
}
write!(fmt, "LIO")?;
one = true
}
}
debug_assert!(one, "printing empty interests");
Ok(())
}
}

35
src/io.rs Normal file
View File

@ -0,0 +1,35 @@
// Re-export the io::Result / Error types for convenience
pub use std::io::{Read, Write, Result, Error, ErrorKind};
// TODO: Delete this
/// A helper trait to provide the map_non_block function on Results.
pub trait MapNonBlock<T> {
/// Maps a `Result<T>` to a `Result<Option<T>>` by converting
/// operation-would-block errors into `Ok(None)`.
fn map_non_block(self) -> Result<Option<T>>;
}
impl<T> MapNonBlock<T> for Result<T> {
fn map_non_block(self) -> Result<Option<T>> {
use std::io::ErrorKind::WouldBlock;
match self {
Ok(value) => Ok(Some(value)),
Err(err) => {
if let WouldBlock = err.kind() {
Ok(None)
} else {
Err(err)
}
}
}
}
}
#[cfg(feature = "with-deprecated")]
pub mod deprecated {
/// Returns a std `WouldBlock` error without allocating
pub fn would_block() -> ::std::io::Error {
::std::io::ErrorKind::WouldBlock.into()
}
}

554
src/lazycell.rs Normal file
View File

@ -0,0 +1,554 @@
// Original work Copyright (c) 2014 The Rust Project Developers
// Modified work Copyright (c) 2016-2018 Nikita Pekin and the lazycell contributors
// See the README.md file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(missing_docs)]
#![allow(unused)]
//! This crate provides a `LazyCell` struct which acts as a lazily filled
//! `Cell`.
//!
//! With a `RefCell`, the inner contents cannot be borrowed for the lifetime of
//! the entire object, but only of the borrows returned. A `LazyCell` is a
//! variation on `RefCell` which allows borrows to be tied to the lifetime of
//! the outer object.
//!
//! `AtomicLazyCell` is a variant that uses an atomic variable to manage
//! coordination in a thread-safe fashion. The limitation of an `AtomicLazyCell`
//! is that after it is initialized, it can't be modified.
use std::cell::UnsafeCell;
use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering};
/// A lazily filled `Cell`, with mutable contents.
///
/// A `LazyCell` is completely frozen once filled, **unless** you have `&mut`
/// access to it, in which case `LazyCell::borrow_mut` may be used to mutate the
/// contents.
#[derive(Debug, Default)]
pub struct LazyCell<T> {
inner: UnsafeCell<Option<T>>,
}
impl<T> LazyCell<T> {
/// Creates a new, empty, `LazyCell`.
pub fn new() -> LazyCell<T> {
LazyCell { inner: UnsafeCell::new(None) }
}
/// Put a value into this cell.
///
/// This function will return `Err(value)` is the cell is already full.
pub fn fill(&self, value: T) -> Result<(), T> {
let slot = unsafe { &mut *self.inner.get() };
if slot.is_some() {
return Err(value);
}
*slot = Some(value);
Ok(())
}
/// Put a value into this cell.
///
/// Note that this function is infallible but requires `&mut self`. By
/// requiring `&mut self` we're guaranteed that no active borrows to this
/// cell can exist so we can always fill in the value. This may not always
/// be usable, however, as `&mut self` may not be possible to borrow.
///
/// # Return value
///
/// This function returns the previous value, if any.
pub fn replace(&mut self, value: T) -> Option<T> {
mem::replace(unsafe { &mut *self.inner.get() }, Some(value))
}
/// Test whether this cell has been previously filled.
pub fn filled(&self) -> bool {
self.borrow().is_some()
}
/// Borrows the contents of this lazy cell for the duration of the cell
/// itself.
///
/// This function will return `Some` if the cell has been previously
/// initialized, and `None` if it has not yet been initialized.
pub fn borrow(&self) -> Option<&T> {
unsafe { &*self.inner.get() }.as_ref()
}
/// Borrows the contents of this lazy cell mutably for the duration of the cell
/// itself.
///
/// This function will return `Some` if the cell has been previously
/// initialized, and `None` if it has not yet been initialized.
pub fn borrow_mut(&mut self) -> Option<&mut T> {
unsafe { &mut *self.inner.get() }.as_mut()
}
/// Borrows the contents of this lazy cell for the duration of the cell
/// itself.
///
/// If the cell has not yet been filled, the cell is first filled using the
/// function provided.
///
/// # Panics
///
/// Panics if the cell becomes filled as a side effect of `f`.
pub fn borrow_with<F: FnOnce() -> T>(&self, f: F) -> &T {
if let Some(value) = self.borrow() {
return value;
}
let value = f();
if self.fill(value).is_err() {
panic!("borrow_with: cell was filled by closure")
}
self.borrow().unwrap()
}
/// Borrows the contents of this `LazyCell` mutably for the duration of the
/// cell itself.
///
/// If the cell has not yet been filled, the cell is first filled using the
/// function provided.
///
/// # Panics
///
/// Panics if the cell becomes filled as a side effect of `f`.
pub fn borrow_mut_with<F: FnOnce() -> T>(&mut self, f: F) -> &mut T {
if !self.filled() {
let value = f();
if self.fill(value).is_err() {
panic!("borrow_mut_with: cell was filled by closure")
}
}
self.borrow_mut().unwrap()
}
/// Same as `borrow_with`, but allows the initializing function to fail.
///
/// # Panics
///
/// Panics if the cell becomes filled as a side effect of `f`.
pub fn try_borrow_with<E, F>(&self, f: F) -> Result<&T, E>
where F: FnOnce() -> Result<T, E>
{
if let Some(value) = self.borrow() {
return Ok(value);
}
let value = f()?;
if self.fill(value).is_err() {
panic!("try_borrow_with: cell was filled by closure")
}
Ok(self.borrow().unwrap())
}
/// Same as `borrow_mut_with`, but allows the initializing function to fail.
///
/// # Panics
///
/// Panics if the cell becomes filled as a side effect of `f`.
pub fn try_borrow_mut_with<E, F>(&mut self, f: F) -> Result<&mut T, E>
where F: FnOnce() -> Result<T, E>
{
if self.filled() {
return Ok(self.borrow_mut().unwrap());
}
let value = f()?;
if self.fill(value).is_err() {
panic!("try_borrow_mut_with: cell was filled by closure")
}
Ok(self.borrow_mut().unwrap())
}
/// Consumes this `LazyCell`, returning the underlying value.
pub fn into_inner(self) -> Option<T> {
// Rust 1.25 changed UnsafeCell::into_inner() from unsafe to safe
// function. This unsafe can be removed when supporting Rust older than
// 1.25 is not needed.
#[allow(unused_unsafe)]
unsafe { self.inner.into_inner() }
}
}
impl<T: Copy> LazyCell<T> {
/// Returns a copy of the contents of the lazy cell.
///
/// This function will return `Some` if the cell has been previously initialized,
/// and `None` if it has not yet been initialized.
pub fn get(&self) -> Option<T> {
unsafe { *self.inner.get() }
}
}
// Tracks the AtomicLazyCell inner state
const NONE: usize = 0;
const LOCK: usize = 1;
const SOME: usize = 2;
/// A lazily filled and thread-safe `Cell`, with frozen contents.
#[derive(Debug, Default)]
pub struct AtomicLazyCell<T> {
inner: UnsafeCell<Option<T>>,
state: AtomicUsize,
}
impl<T> AtomicLazyCell<T> {
/// Creates a new, empty, `AtomicLazyCell`.
pub fn new() -> AtomicLazyCell<T> {
Self {
inner: UnsafeCell::new(None),
state: AtomicUsize::new(NONE),
}
}
/// Put a value into this cell.
///
/// This function will return `Err(value)` is the cell is already full.
pub fn fill(&self, t: T) -> Result<(), T> {
if NONE != self.state.compare_and_swap(NONE, LOCK, Ordering::Acquire) {
return Err(t);
}
unsafe { *self.inner.get() = Some(t) };
if LOCK != self.state.compare_and_swap(LOCK, SOME, Ordering::Release) {
panic!("unable to release lock");
}
Ok(())
}
/// Put a value into this cell.
///
/// Note that this function is infallible but requires `&mut self`. By
/// requiring `&mut self` we're guaranteed that no active borrows to this
/// cell can exist so we can always fill in the value. This may not always
/// be usable, however, as `&mut self` may not be possible to borrow.
///
/// # Return value
///
/// This function returns the previous value, if any.
pub fn replace(&mut self, value: T) -> Option<T> {
match mem::replace(self.state.get_mut(), SOME) {
NONE | SOME => {}
_ => panic!("cell in inconsistent state"),
}
mem::replace(unsafe { &mut *self.inner.get() }, Some(value))
}
/// Test whether this cell has been previously filled.
pub fn filled(&self) -> bool {
self.state.load(Ordering::Acquire) == SOME
}
/// Borrows the contents of this lazy cell for the duration of the cell
/// itself.
///
/// This function will return `Some` if the cell has been previously
/// initialized, and `None` if it has not yet been initialized.
pub fn borrow(&self) -> Option<&T> {
match self.state.load(Ordering::Acquire) {
SOME => unsafe { &*self.inner.get() }.as_ref(),
_ => None,
}
}
/// Consumes this `LazyCell`, returning the underlying value.
pub fn into_inner(self) -> Option<T> {
// Rust 1.25 changed UnsafeCell::into_inner() from unsafe to safe
// function. This unsafe can be removed when supporting Rust older than
// 1.25 is not needed.
#[allow(unused_unsafe)]
unsafe { self.inner.into_inner() }
}
}
impl<T: Copy> AtomicLazyCell<T> {
/// Returns a copy of the contents of the lazy cell.
///
/// This function will return `Some` if the cell has been previously initialized,
/// and `None` if it has not yet been initialized.
pub fn get(&self) -> Option<T> {
match self.state.load(Ordering::Acquire) {
SOME => unsafe { *self.inner.get() },
_ => None,
}
}
}
unsafe impl<T: Sync + Send> Sync for AtomicLazyCell<T> {}
unsafe impl<T: Send> Send for AtomicLazyCell<T> {}
#[cfg(test)]
mod tests {
use super::{AtomicLazyCell, LazyCell};
#[test]
fn test_borrow_from_empty() {
let lazycell: LazyCell<usize> = LazyCell::new();
let value = lazycell.borrow();
assert_eq!(value, None);
let value = lazycell.get();
assert_eq!(value, None);
}
#[test]
fn test_fill_and_borrow() {
let lazycell = LazyCell::new();
assert!(!lazycell.filled());
lazycell.fill(1).unwrap();
assert!(lazycell.filled());
let value = lazycell.borrow();
assert_eq!(value, Some(&1));
let value = lazycell.get();
assert_eq!(value, Some(1));
}
#[test]
fn test_borrow_mut() {
let mut lazycell = LazyCell::new();
assert!(lazycell.borrow_mut().is_none());
lazycell.fill(1).unwrap();
assert_eq!(lazycell.borrow_mut(), Some(&mut 1));
*lazycell.borrow_mut().unwrap() = 2;
assert_eq!(lazycell.borrow_mut(), Some(&mut 2));
// official way to reset the cell
lazycell = LazyCell::new();
assert!(lazycell.borrow_mut().is_none());
}
#[test]
fn test_already_filled_error() {
let lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
assert_eq!(lazycell.fill(1), Err(1));
}
#[test]
fn test_borrow_with() {
let lazycell = LazyCell::new();
let value = lazycell.borrow_with(|| 1);
assert_eq!(&1, value);
}
#[test]
fn test_borrow_with_already_filled() {
let lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
let value = lazycell.borrow_with(|| 1);
assert_eq!(&1, value);
}
#[test]
fn test_borrow_with_not_called_when_filled() {
let lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
let value = lazycell.borrow_with(|| 2);
assert_eq!(&1, value);
}
#[test]
#[should_panic]
fn test_borrow_with_sound_with_reentrancy() {
// Kudos to dbaupp for discovering this issue
// https://www.reddit.com/r/rust/comments/5vs9rt/lazycell_a_rust_library_providing_a_lazilyfilled/de527xm/
let lazycell: LazyCell<Box<i32>> = LazyCell::new();
let mut reference: Option<&i32> = None;
lazycell.borrow_with(|| {
let _ = lazycell.fill(Box::new(1));
reference = lazycell.borrow().map(|r| &**r);
Box::new(2)
});
}
#[test]
fn test_borrow_mut_with() {
let mut lazycell = LazyCell::new();
{
let value = lazycell.borrow_mut_with(|| 1);
assert_eq!(&mut 1, value);
*value = 2;
}
assert_eq!(&2, lazycell.borrow().unwrap());
}
#[test]
fn test_borrow_mut_with_already_filled() {
let mut lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
let value = lazycell.borrow_mut_with(|| 1);
assert_eq!(&1, value);
}
#[test]
fn test_borrow_mut_with_not_called_when_filled() {
let mut lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
let value = lazycell.borrow_mut_with(|| 2);
assert_eq!(&1, value);
}
#[test]
fn test_try_borrow_with_ok() {
let lazycell = LazyCell::new();
let result = lazycell.try_borrow_with::<(), _>(|| Ok(1));
assert_eq!(result, Ok(&1));
}
#[test]
fn test_try_borrow_with_err() {
let lazycell = LazyCell::<()>::new();
let result = lazycell.try_borrow_with(|| Err(1));
assert_eq!(result, Err(1));
}
#[test]
fn test_try_borrow_with_already_filled() {
let lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
let result = lazycell.try_borrow_with::<(), _>(|| unreachable!());
assert_eq!(result, Ok(&1));
}
#[test]
#[should_panic]
fn test_try_borrow_with_sound_with_reentrancy() {
let lazycell: LazyCell<Box<i32>> = LazyCell::new();
let mut reference: Option<&i32> = None;
let _ = lazycell.try_borrow_with::<(), _>(|| {
let _ = lazycell.fill(Box::new(1));
reference = lazycell.borrow().map(|r| &**r);
Ok(Box::new(2))
});
}
#[test]
fn test_try_borrow_mut_with_ok() {
let mut lazycell = LazyCell::new();
{
let result = lazycell.try_borrow_mut_with::<(), _>(|| Ok(1));
assert_eq!(result, Ok(&mut 1));
*result.unwrap() = 2;
}
assert_eq!(&mut 2, lazycell.borrow().unwrap());
}
#[test]
fn test_try_borrow_mut_with_err() {
let mut lazycell = LazyCell::<()>::new();
let result = lazycell.try_borrow_mut_with(|| Err(1));
assert_eq!(result, Err(1));
}
#[test]
fn test_try_borrow_mut_with_already_filled() {
let mut lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
let result = lazycell.try_borrow_mut_with::<(), _>(|| unreachable!());
assert_eq!(result, Ok(&mut 1));
}
#[test]
fn test_into_inner() {
let lazycell = LazyCell::new();
lazycell.fill(1).unwrap();
let value = lazycell.into_inner();
assert_eq!(value, Some(1));
}
#[test]
fn test_atomic_borrow_from_empty() {
let lazycell: AtomicLazyCell<usize> = AtomicLazyCell::new();
let value = lazycell.borrow();
assert_eq!(value, None);
let value = lazycell.get();
assert_eq!(value, None);
}
#[test]
fn test_atomic_fill_and_borrow() {
let lazycell = AtomicLazyCell::new();
assert!(!lazycell.filled());
lazycell.fill(1).unwrap();
assert!(lazycell.filled());
let value = lazycell.borrow();
assert_eq!(value, Some(&1));
let value = lazycell.get();
assert_eq!(value, Some(1));
}
#[test]
fn test_atomic_already_filled_panic() {
let lazycell = AtomicLazyCell::new();
lazycell.fill(1).unwrap();
assert_eq!(1, lazycell.fill(1).unwrap_err());
}
#[test]
fn test_atomic_into_inner() {
let lazycell = AtomicLazyCell::new();
lazycell.fill(1).unwrap();
let value = lazycell.into_inner();
assert_eq!(value, Some(1));
}
#[test]
fn normal_replace() {
let mut cell = LazyCell::new();
assert_eq!(cell.fill(1), Ok(()));
assert_eq!(cell.replace(2), Some(1));
assert_eq!(cell.replace(3), Some(2));
assert_eq!(cell.borrow(), Some(&3));
let mut cell = LazyCell::new();
assert_eq!(cell.replace(2), None);
}
#[test]
fn atomic_replace() {
let mut cell = AtomicLazyCell::new();
assert_eq!(cell.fill(1), Ok(()));
assert_eq!(cell.replace(2), Some(1));
assert_eq!(cell.replace(3), Some(2));
assert_eq!(cell.borrow(), Some(&3));
}
}

View File

@ -1,9 +1,13 @@
#![doc(html_root_url = "https://docs.rs/mio/0.7.0")]
#![deny(missing_docs, missing_debug_implementations, rust_2018_idioms)]
// Disallow warnings when running tests.
#![doc(html_root_url = "https://docs.rs/mio/0.6.19")]
// Mio targets old versions of the Rust compiler. In order to do this, uses
// deprecated APIs.
#![allow(deprecated)]
#![deny(missing_docs, missing_debug_implementations)]
#![cfg_attr(test, deny(warnings))]
// Disallow warnings in examples.
#![doc(test(attr(deny(warnings))))]
// Many of mio's public methods violate this lint, but they can't be fixed
// without a breaking change.
#![cfg_attr(feature = "cargo-clippy", allow(clippy::trivially_copy_pass_by_ref))]
//! A fast, low-level IO library for Rust focusing on non-blocking APIs, event
//! notification, and other useful utilities for building high performance IO
@ -12,14 +16,13 @@
//! # Features
//!
//! * Non-blocking TCP, UDP
//! * I/O event queue backed by epoll, kqueue, and IOCP
//! * I/O event notification queue backed by epoll, kqueue, and IOCP
//! * Zero allocations at runtime
//! * Platform specific extensions
//!
//! # Non-goals
//!
//! The following are specifically omitted from Mio and are left to the user or
//! higher-level libraries.
//! The following are specifically omitted from Mio and are left to the user or higher-level libraries.
//!
//! * File operations
//! * Thread pools / multi-threaded event loop
@ -29,26 +32,21 @@
//!
//! Currently supported platforms:
//!
//! * Android
//! * Bitrig
//! * DragonFly BSD
//! * FreeBSD
//! * Linux
//! * NetBSD
//! * OpenBSD
//! * Solaris
//! * OS X
//! * Windows
//! * FreeBSD
//! * NetBSD
//! * Android
//! * iOS
//! * macOS
//!
//! Mio can handle interfacing with each of the event systems of the
//! aforementioned platforms. The details of their implementation are further
//! discussed in [`Poll`].
//! mio can handle interfacing with each of the event notification systems of the aforementioned platforms. The details of
//! their implementation are further discussed in [`Poll`].
//!
//! # Usage
//!
//! Using Mio starts by creating a [`Poll`], which reads events from the OS and
//! puts them into [`Events`]. You can handle IO events from the OS with it.
//! Using mio starts by creating a [`Poll`], which reads events from the OS and
//! put them into [`Events`]. You can handle IO events from the OS with it.
//!
//! For more detail, see [`Poll`].
//!
@ -69,19 +67,21 @@
//! let addr = "127.0.0.1:13265".parse().unwrap();
//!
//! // Setup the server socket
//! let server = TcpListener::bind(addr).unwrap();
//! let server = TcpListener::bind(&addr).unwrap();
//!
//! // Create a poll instance
//! let mut poll = Poll::new().unwrap();
//! let poll = Poll::new().unwrap();
//!
//! // Start listening for incoming connections
//! poll.registry().register(&server, SERVER, Interests::READABLE).unwrap();
//! poll.register(&server, SERVER, Ready::readable(),
//! PollOpt::edge()).unwrap();
//!
//! // Setup the client socket
//! let sock = TcpStream::connect(addr).unwrap();
//! let sock = TcpStream::connect(&addr).unwrap();
//!
//! // Register the socket
//! poll.registry().register(&sock, CLIENT, Interests::READABLE).unwrap();
//! poll.register(&sock, CLIENT, Ready::readable(),
//! PollOpt::edge()).unwrap();
//!
//! // Create storage for events
//! let mut events = Events::with_capacity(1024);
@ -108,24 +108,204 @@
//!
//! ```
mod interests;
extern crate net2;
extern crate iovec;
extern crate slab;
#[cfg(target_os = "fuchsia")]
extern crate fuchsia_zircon as zircon;
#[cfg(target_os = "fuchsia")]
extern crate fuchsia_zircon_sys as zircon_sys;
#[cfg(unix)]
extern crate libc;
#[cfg(windows)]
extern crate miow;
#[cfg(windows)]
extern crate winapi;
#[cfg(windows)]
extern crate kernel32;
#[macro_use]
extern crate log;
mod event_imp;
mod io;
mod poll;
mod sys;
mod token;
mod waker;
mod lazycell;
pub mod event;
pub mod net;
pub use event::Events;
pub use interests::Interests;
pub use poll::{Poll, Registry};
pub use token::Token;
pub use waker::Waker;
#[deprecated(since = "0.6.5", note = "use mio-extras instead")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub mod channel;
#[cfg(unix)]
pub mod unix {
//! Unix only extensions.
#[deprecated(since = "0.6.5", note = "use mio-extras instead")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub mod timer;
pub use crate::sys::SourceFd;
#[deprecated(since = "0.6.5", note = "update to use `Poll`")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub mod deprecated;
#[deprecated(since = "0.6.5", note = "use iovec crate directly")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub use iovec::IoVec;
#[deprecated(since = "0.6.6", note = "use net module instead")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub mod tcp {
pub use net::{TcpListener, TcpStream};
pub use std::net::Shutdown;
}
#[deprecated(since = "0.6.6", note = "use net module instead")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub mod udp;
pub use poll::{
Poll,
Registration,
SetReadiness,
};
pub use event_imp::{
PollOpt,
Ready,
};
pub use token::Token;
pub mod event {
//! Readiness event types and utilities.
pub use super::poll::{Events, Iter};
pub use super::event_imp::{Event, Evented};
}
pub use event::{
Events,
};
#[deprecated(since = "0.6.5", note = "use events:: instead")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub use event::{Event, Evented};
#[deprecated(since = "0.6.5", note = "use events::Iter instead")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub use poll::Iter as EventsIter;
#[deprecated(since = "0.6.5", note = "std::io::Error can avoid the allocation now")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub use io::deprecated::would_block;
#[cfg(all(unix, not(target_os = "fuchsia")))]
pub mod unix {
//! Unix only extensions
pub use sys::{
EventedFd,
};
pub use sys::unix::UnixReady;
}
#[cfg(target_os = "fuchsia")]
pub mod fuchsia {
//! Fuchsia-only extensions
//!
//! # Stability
//!
//! This module depends on the [magenta-sys crate](https://crates.io/crates/magenta-sys)
//! and so might introduce breaking changes, even on minor releases,
//! so long as that crate remains unstable.
pub use sys::{
EventedHandle,
};
pub use sys::fuchsia::{FuchsiaReady, zx_signals_t};
}
/// Windows-only extensions to the mio crate.
///
/// Mio on windows is currently implemented with IOCP for a high-performance
/// implementation of asynchronous I/O. Mio then provides TCP and UDP as sample
/// bindings for the system to connect networking types to asynchronous I/O. On
/// Unix this scheme is then also extensible to all other file descriptors with
/// the `EventedFd` type, but on Windows no such analog is available. The
/// purpose of this module, however, is to similarly provide a mechanism for
/// foreign I/O types to get hooked up into the IOCP event loop.
///
/// This module provides two types for interfacing with a custom IOCP handle:
///
/// * `Binding` - this type is intended to govern binding with mio's `Poll`
/// type. Each I/O object should contain an instance of `Binding` that's
/// interfaced with for the implementation of the `Evented` trait. The
/// `register`, `reregister`, and `deregister` methods for the `Evented` trait
/// all have rough analogs with `Binding`.
///
/// Note that this type **does not handle readiness**. That is, this type does
/// not handle whether sockets are readable/writable/etc. It's intended that
/// IOCP types will internally manage this state with a `SetReadiness` type
/// from the `poll` module. The `SetReadiness` is typically lazily created on
/// the first time that `Evented::register` is called and then stored in the
/// I/O object.
///
/// Also note that for types which represent streams of bytes the mio
/// interface of *readiness* doesn't map directly to the Windows model of
/// *completion*. This means that types will have to perform internal
/// buffering to ensure that a readiness interface can be provided. For a
/// sample implementation see the TCP/UDP modules in mio itself.
///
/// * `Overlapped` - this type is intended to be used as the concrete instances
/// of the `OVERLAPPED` type that most win32 methods expect. It's crucial, for
/// safety, that all asynchronous operations are initiated with an instance of
/// `Overlapped` and not another instantiation of `OVERLAPPED`.
///
/// Mio's `Overlapped` type is created with a function pointer that receives
/// a `OVERLAPPED_ENTRY` type when called. This `OVERLAPPED_ENTRY` type is
/// defined in the `winapi` crate. Whenever a completion is posted to an IOCP
/// object the `OVERLAPPED` that was signaled will be interpreted as
/// `Overlapped` in the mio crate and this function pointer will be invoked.
/// Through this function pointer, and through the `OVERLAPPED` pointer,
/// implementations can handle management of I/O events.
///
/// When put together these two types enable custom Windows handles to be
/// registered with mio's event loops. The `Binding` type is used to associate
/// handles and the `Overlapped` type is used to execute I/O operations. When
/// the I/O operations are completed a custom function pointer is called which
/// typically modifies a `SetReadiness` set by `Evented` methods which will get
/// later hooked into the mio event loop.
#[cfg(windows)]
pub mod windows {
pub use sys::{Overlapped, Binding};
}
#[cfg(feature = "with-deprecated")]
mod convert {
use std::time::Duration;
const NANOS_PER_MILLI: u32 = 1_000_000;
const MILLIS_PER_SEC: u64 = 1_000;
/// Convert a `Duration` to milliseconds, rounding up and saturating at
/// `u64::MAX`.
///
/// The saturating is fine because `u64::MAX` milliseconds are still many
/// million years.
pub fn millis(duration: Duration) -> u64 {
// Round up.
let millis = (duration.subsec_nanos() + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI;
duration.as_secs().saturating_mul(MILLIS_PER_SEC).saturating_add(u64::from(millis))
}
}

View File

@ -7,11 +7,8 @@
//!
//! [portability guidelines]: ../struct.Poll.html#portability
mod tcp_listener;
pub use self::tcp_listener::TcpListener;
mod tcp_stream;
pub use self::tcp_stream::TcpStream;
mod tcp;
mod udp;
pub use self::tcp::{TcpListener, TcpStream};
pub use self::udp::UdpSocket;

737
src/net/tcp.rs Normal file
View File

@ -0,0 +1,737 @@
//! Primitives for working with TCP
//!
//! The types provided in this module are non-blocking by default and are
//! designed to be portable across all supported Mio platforms. As long as the
//! [portability guidelines] are followed, the behavior should be identical no
//! matter the target platform.
//!
/// [portability guidelines]: ../struct.Poll.html#portability
use std::fmt;
use std::io::{Read, Write};
use std::net::{self, SocketAddr, SocketAddrV4, SocketAddrV6, Ipv4Addr, Ipv6Addr};
use std::time::Duration;
use net2::TcpBuilder;
use iovec::IoVec;
use {io, sys, Ready, Poll, PollOpt, Token};
use event::Evented;
use poll::SelectorId;
/*
*
* ===== TcpStream =====
*
*/
/// A non-blocking TCP stream between a local socket and a remote socket.
///
/// The socket will be closed when the value is dropped.
///
/// # Examples
///
/// ```
/// # use std::net::TcpListener;
/// # use std::error::Error;
/// #
/// # fn try_main() -> Result<(), Box<Error>> {
/// # let _listener = TcpListener::bind("127.0.0.1:34254")?;
/// use mio::{Events, Ready, Poll, PollOpt, Token};
/// use mio::net::TcpStream;
/// use std::time::Duration;
///
/// let stream = TcpStream::connect(&"127.0.0.1:34254".parse()?)?;
///
/// let poll = Poll::new()?;
/// let mut events = Events::with_capacity(128);
///
/// // Register the socket with `Poll`
/// poll.register(&stream, Token(0), Ready::writable(),
/// PollOpt::edge())?;
///
/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
///
/// // The socket might be ready at this point
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub struct TcpStream {
sys: sys::TcpStream,
selector_id: SelectorId,
}
use std::net::Shutdown;
// TODO: remove when fuchsia's set_nonblocking is fixed in libstd
#[cfg(target_os = "fuchsia")]
fn set_nonblocking(stream: &net::TcpStream) -> io::Result<()> {
sys::set_nonblock(
::std::os::unix::io::AsRawFd::as_raw_fd(stream))
}
#[cfg(not(target_os = "fuchsia"))]
fn set_nonblocking(stream: &net::TcpStream) -> io::Result<()> {
stream.set_nonblocking(true)
}
impl TcpStream {
/// Create a new TCP stream and issue a non-blocking connect to the
/// specified address.
///
/// This convenience method is available and uses the system's default
/// options when creating a socket which is then connected. If fine-grained
/// control over the creation of the socket is desired, you can use
/// `net2::TcpBuilder` to configure a socket and then pass its socket to
/// `TcpStream::connect_stream` to transfer ownership into mio and schedule
/// the connect operation.
pub fn connect(addr: &SocketAddr) -> io::Result<TcpStream> {
let sock = match *addr {
SocketAddr::V4(..) => TcpBuilder::new_v4(),
SocketAddr::V6(..) => TcpBuilder::new_v6(),
}?;
// Required on Windows for a future `connect_overlapped` operation to be
// executed successfully.
if cfg!(windows) {
sock.bind(&inaddr_any(addr))?;
}
TcpStream::connect_stream(sock.to_tcp_stream()?, addr)
}
/// Creates a new `TcpStream` from the pending socket inside the given
/// `std::net::TcpBuilder`, connecting it to the address specified.
///
/// This constructor allows configuring the socket before it's actually
/// connected, and this function will transfer ownership to the returned
/// `TcpStream` if successful. An unconnected `TcpStream` can be created
/// with the `net2::TcpBuilder` type (and also configured via that route).
///
/// The platform specific behavior of this function looks like:
///
/// * On Unix, the socket is placed into nonblocking mode and then a
/// `connect` call is issued.
///
/// * On Windows, the address is stored internally and the connect operation
/// is issued when the returned `TcpStream` is registered with an event
/// loop. Note that on Windows you must `bind` a socket before it can be
/// connected, so if a custom `TcpBuilder` is used it should be bound
/// (perhaps to `INADDR_ANY`) before this method is called.
pub fn connect_stream(stream: net::TcpStream,
addr: &SocketAddr) -> io::Result<TcpStream> {
Ok(TcpStream {
sys: sys::TcpStream::connect(stream, addr)?,
selector_id: SelectorId::new(),
})
}
/// Creates a new `TcpStream` from a standard `net::TcpStream`.
///
/// This function is intended to be used to wrap a TCP stream from the
/// standard library in the mio equivalent. The conversion here will
/// automatically set `stream` to nonblocking and the returned object should
/// be ready to get associated with an event loop.
///
/// Note that the TCP stream here will not have `connect` called on it, so
/// it should already be connected via some other means (be it manually, the
/// net2 crate, or the standard library).
pub fn from_stream(stream: net::TcpStream) -> io::Result<TcpStream> {
set_nonblocking(&stream)?;
Ok(TcpStream {
sys: sys::TcpStream::from_stream(stream),
selector_id: SelectorId::new(),
})
}
/// Returns the socket address of the remote peer of this TCP connection.
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.sys.peer_addr()
}
/// Returns the socket address of the local half of this TCP connection.
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.sys.local_addr()
}
/// Creates a new independently owned handle to the underlying socket.
///
/// The returned `TcpStream` is a reference to the same stream that this
/// object references. Both handles will read and write the same stream of
/// data, and options set on one stream will be propagated to the other
/// stream.
pub fn try_clone(&self) -> io::Result<TcpStream> {
self.sys.try_clone().map(|s| {
TcpStream {
sys: s,
selector_id: self.selector_id.clone(),
}
})
}
/// Shuts down the read, write, or both halves of this connection.
///
/// This function will cause all pending and future I/O on the specified
/// portions to return immediately with an appropriate value (see the
/// documentation of `Shutdown`).
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.sys.shutdown(how)
}
/// Sets the value of the `TCP_NODELAY` option on this socket.
///
/// If set, this option disables the Nagle algorithm. This means that
/// segments are always sent as soon as possible, even if there is only a
/// small amount of data. When not set, data is buffered until there is a
/// sufficient amount to send out, thereby avoiding the frequent sending of
/// small packets.
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.sys.set_nodelay(nodelay)
}
/// Gets the value of the `TCP_NODELAY` option on this socket.
///
/// For more information about this option, see [`set_nodelay`][link].
///
/// [link]: #method.set_nodelay
pub fn nodelay(&self) -> io::Result<bool> {
self.sys.nodelay()
}
/// Sets the value of the `SO_RCVBUF` option on this socket.
///
/// Changes the size of the operating system's receive buffer associated
/// with the socket.
pub fn set_recv_buffer_size(&self, size: usize) -> io::Result<()> {
self.sys.set_recv_buffer_size(size)
}
/// Gets the value of the `SO_RCVBUF` option on this socket.
///
/// For more information about this option, see
/// [`set_recv_buffer_size`][link].
///
/// [link]: #method.set_recv_buffer_size
pub fn recv_buffer_size(&self) -> io::Result<usize> {
self.sys.recv_buffer_size()
}
/// Sets the value of the `SO_SNDBUF` option on this socket.
///
/// Changes the size of the operating system's send buffer associated with
/// the socket.
pub fn set_send_buffer_size(&self, size: usize) -> io::Result<()> {
self.sys.set_send_buffer_size(size)
}
/// Gets the value of the `SO_SNDBUF` option on this socket.
///
/// For more information about this option, see
/// [`set_send_buffer_size`][link].
///
/// [link]: #method.set_send_buffer_size
pub fn send_buffer_size(&self) -> io::Result<usize> {
self.sys.send_buffer_size()
}
/// Sets whether keepalive messages are enabled to be sent on this socket.
///
/// On Unix, this option will set the `SO_KEEPALIVE` as well as the
/// `TCP_KEEPALIVE` or `TCP_KEEPIDLE` option (depending on your platform).
/// On Windows, this will set the `SIO_KEEPALIVE_VALS` option.
///
/// If `None` is specified then keepalive messages are disabled, otherwise
/// the duration specified will be the time to remain idle before sending a
/// TCP keepalive probe.
///
/// Some platforms specify this value in seconds, so sub-second
/// specifications may be omitted.
pub fn set_keepalive(&self, keepalive: Option<Duration>) -> io::Result<()> {
self.sys.set_keepalive(keepalive)
}
/// Returns whether keepalive messages are enabled on this socket, and if so
/// the duration of time between them.
///
/// For more information about this option, see [`set_keepalive`][link].
///
/// [link]: #method.set_keepalive
pub fn keepalive(&self) -> io::Result<Option<Duration>> {
self.sys.keepalive()
}
/// Sets the value for the `IP_TTL` option on this socket.
///
/// This value sets the time-to-live field that is used in every packet sent
/// from this socket.
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.sys.set_ttl(ttl)
}
/// Gets the value of the `IP_TTL` option for this socket.
///
/// For more information about this option, see [`set_ttl`][link].
///
/// [link]: #method.set_ttl
pub fn ttl(&self) -> io::Result<u32> {
self.sys.ttl()
}
/// Sets the value for the `IPV6_V6ONLY` option on this socket.
///
/// If this is set to `true` then the socket is restricted to sending and
/// receiving IPv6 packets only. In this case two IPv4 and IPv6 applications
/// can bind the same port at the same time.
///
/// If this is set to `false` then the socket can be used to send and
/// receive packets from an IPv4-mapped IPv6 address.
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.sys.set_only_v6(only_v6)
}
/// Gets the value of the `IPV6_V6ONLY` option for this socket.
///
/// For more information about this option, see [`set_only_v6`][link].
///
/// [link]: #method.set_only_v6
pub fn only_v6(&self) -> io::Result<bool> {
self.sys.only_v6()
}
/// Sets the value for the `SO_LINGER` option on this socket.
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
self.sys.set_linger(dur)
}
/// Gets the value of the `SO_LINGER` option on this socket.
///
/// For more information about this option, see [`set_linger`][link].
///
/// [link]: #method.set_linger
pub fn linger(&self) -> io::Result<Option<Duration>> {
self.sys.linger()
}
#[deprecated(since = "0.6.9", note = "use set_keepalive")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub fn set_keepalive_ms(&self, keepalive: Option<u32>) -> io::Result<()> {
self.set_keepalive(keepalive.map(|v| {
Duration::from_millis(u64::from(v))
}))
}
#[deprecated(since = "0.6.9", note = "use keepalive")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub fn keepalive_ms(&self) -> io::Result<Option<u32>> {
self.keepalive().map(|v| {
v.map(|v| {
::convert::millis(v) as u32
})
})
}
/// Get the value of the `SO_ERROR` option on this socket.
///
/// This will retrieve the stored error in the underlying socket, clearing
/// the field in the process. This can be useful for checking errors between
/// calls.
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.sys.take_error()
}
/// Receives data on the socket from the remote address to which it is
/// connected, without removing that data from the queue. On success,
/// returns the number of bytes peeked.
///
/// Successive calls return the same data. This is accomplished by passing
/// `MSG_PEEK` as a flag to the underlying recv system call.
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.sys.peek(buf)
}
/// Read in a list of buffers all at once.
///
/// This operation will attempt to read bytes from this socket and place
/// them into the list of buffers provided. Note that each buffer is an
/// `IoVec` which can be created from a byte slice.
///
/// The buffers provided will be filled in sequentially. A buffer will be
/// entirely filled up before the next is written to.
///
/// The number of bytes read is returned, if successful, or an error is
/// returned otherwise. If no bytes are available to be read yet then
/// a "would block" error is returned. This operation does not block.
///
/// On Unix this corresponds to the `readv` syscall.
pub fn read_bufs(&self, bufs: &mut [&mut IoVec]) -> io::Result<usize> {
self.sys.readv(bufs)
}
/// Write a list of buffers all at once.
///
/// This operation will attempt to write a list of byte buffers to this
/// socket. Note that each buffer is an `IoVec` which can be created from a
/// byte slice.
///
/// The buffers provided will be written sequentially. A buffer will be
/// entirely written before the next is written.
///
/// The number of bytes written is returned, if successful, or an error is
/// returned otherwise. If the socket is not currently writable then a
/// "would block" error is returned. This operation does not block.
///
/// On Unix this corresponds to the `writev` syscall.
pub fn write_bufs(&self, bufs: &[&IoVec]) -> io::Result<usize> {
self.sys.writev(bufs)
}
}
fn inaddr_any(other: &SocketAddr) -> SocketAddr {
match *other {
SocketAddr::V4(..) => {
let any = Ipv4Addr::new(0, 0, 0, 0);
let addr = SocketAddrV4::new(any, 0);
SocketAddr::V4(addr)
}
SocketAddr::V6(..) => {
let any = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
let addr = SocketAddrV6::new(any, 0, 0, 0);
SocketAddr::V6(addr)
}
}
}
impl Read for TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&self.sys).read(buf)
}
}
impl<'a> Read for &'a TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&self.sys).read(buf)
}
}
impl Write for TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&self.sys).write(buf)
}
fn flush(&mut self) -> io::Result<()> {
(&self.sys).flush()
}
}
impl<'a> Write for &'a TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&self.sys).write(buf)
}
fn flush(&mut self) -> io::Result<()> {
(&self.sys).flush()
}
}
impl Evented for TcpStream {
fn register(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
self.selector_id.associate_selector(poll)?;
self.sys.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.sys.deregister(poll)
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.sys, f)
}
}
/*
*
* ===== TcpListener =====
*
*/
/// A structure representing a socket server
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::{Events, Ready, Poll, PollOpt, Token};
/// use mio::net::TcpListener;
/// use std::time::Duration;
///
/// let listener = TcpListener::bind(&"127.0.0.1:34255".parse()?)?;
///
/// let poll = Poll::new()?;
/// let mut events = Events::with_capacity(128);
///
/// // Register the socket with `Poll`
/// poll.register(&listener, Token(0), Ready::readable(),
/// PollOpt::edge())?;
///
/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
///
/// // There may be a socket ready to be accepted
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub struct TcpListener {
sys: sys::TcpListener,
selector_id: SelectorId,
}
impl TcpListener {
/// Convenience method to bind a new TCP listener to the specified address
/// to receive new connections.
///
/// This function will take the following steps:
///
/// 1. Create a new TCP socket.
/// 2. Set the `SO_REUSEADDR` option on the socket.
/// 3. Bind the socket to the specified address.
/// 4. Call `listen` on the socket to prepare it to receive new connections.
///
/// If fine-grained control over the binding and listening process for a
/// socket is desired then the `net2::TcpBuilder` methods can be used in
/// combination with the `TcpListener::from_listener` method to transfer
/// ownership into mio.
pub fn bind(addr: &SocketAddr) -> io::Result<TcpListener> {
// Create the socket
let sock = match *addr {
SocketAddr::V4(..) => TcpBuilder::new_v4(),
SocketAddr::V6(..) => TcpBuilder::new_v6(),
}?;
// Set SO_REUSEADDR, but only on Unix (mirrors what libstd does)
if cfg!(unix) {
sock.reuse_address(true)?;
}
// Bind the socket
sock.bind(addr)?;
// listen
let listener = sock.listen(1024)?;
Ok(TcpListener {
sys: sys::TcpListener::new(listener)?,
selector_id: SelectorId::new(),
})
}
#[deprecated(since = "0.6.13", note = "use from_std instead")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
pub fn from_listener(listener: net::TcpListener, _: &SocketAddr)
-> io::Result<TcpListener> {
TcpListener::from_std(listener)
}
/// Creates a new `TcpListener` from an instance of a
/// `std::net::TcpListener` type.
///
/// This function will set the `listener` provided into nonblocking mode on
/// Unix, and otherwise the stream will just be wrapped up in an mio stream
/// ready to accept new connections and become associated with an event
/// loop.
///
/// The address provided must be the address that the listener is bound to.
pub fn from_std(listener: net::TcpListener) -> io::Result<TcpListener> {
sys::TcpListener::new(listener).map(|s| {
TcpListener {
sys: s,
selector_id: SelectorId::new(),
}
})
}
/// Accepts a new `TcpStream`.
///
/// This may return an `Err(e)` where `e.kind()` is
/// `io::ErrorKind::WouldBlock`. This means a stream may be ready at a later
/// point and one should wait for a notification before calling `accept`
/// again.
///
/// If an accepted stream is returned, the remote address of the peer is
/// returned along with it.
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
let (s, a) = try!(self.accept_std());
Ok((TcpStream::from_stream(s)?, a))
}
/// Accepts a new `std::net::TcpStream`.
///
/// This method is the same as `accept`, except that it returns a TCP socket
/// *in blocking mode* which isn't bound to `mio`. This can be later then
/// converted to a `mio` type, if necessary.
pub fn accept_std(&self) -> io::Result<(net::TcpStream, SocketAddr)> {
self.sys.accept()
}
/// Returns the local socket address of this listener.
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.sys.local_addr()
}
/// Creates a new independently owned handle to the underlying socket.
///
/// The returned `TcpListener` is a reference to the same socket that this
/// object references. Both handles can be used to accept incoming
/// connections and options set on one listener will affect the other.
pub fn try_clone(&self) -> io::Result<TcpListener> {
self.sys.try_clone().map(|s| {
TcpListener {
sys: s,
selector_id: self.selector_id.clone(),
}
})
}
/// Sets the value for the `IP_TTL` option on this socket.
///
/// This value sets the time-to-live field that is used in every packet sent
/// from this socket.
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.sys.set_ttl(ttl)
}
/// Gets the value of the `IP_TTL` option for this socket.
///
/// For more information about this option, see [`set_ttl`][link].
///
/// [link]: #method.set_ttl
pub fn ttl(&self) -> io::Result<u32> {
self.sys.ttl()
}
/// Sets the value for the `IPV6_V6ONLY` option on this socket.
///
/// If this is set to `true` then the socket is restricted to sending and
/// receiving IPv6 packets only. In this case two IPv4 and IPv6 applications
/// can bind the same port at the same time.
///
/// If this is set to `false` then the socket can be used to send and
/// receive packets from an IPv4-mapped IPv6 address.
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.sys.set_only_v6(only_v6)
}
/// Gets the value of the `IPV6_V6ONLY` option for this socket.
///
/// For more information about this option, see [`set_only_v6`][link].
///
/// [link]: #method.set_only_v6
pub fn only_v6(&self) -> io::Result<bool> {
self.sys.only_v6()
}
/// Get the value of the `SO_ERROR` option on this socket.
///
/// This will retrieve the stored error in the underlying socket, clearing
/// the field in the process. This can be useful for checking errors between
/// calls.
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.sys.take_error()
}
}
impl Evented for TcpListener {
fn register(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
self.selector_id.associate_selector(poll)?;
self.sys.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.sys.deregister(poll)
}
}
impl fmt::Debug for TcpListener {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.sys, f)
}
}
/*
*
* ===== UNIX ext =====
*
*/
#[cfg(all(unix, not(target_os = "fuchsia")))]
use std::os::unix::io::{IntoRawFd, AsRawFd, FromRawFd, RawFd};
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl FromRawFd for TcpStream {
unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
TcpStream {
sys: FromRawFd::from_raw_fd(fd),
selector_id: SelectorId::new(),
}
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl FromRawFd for TcpListener {
unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
TcpListener {
sys: FromRawFd::from_raw_fd(fd),
selector_id: SelectorId::new(),
}
}
}

View File

@ -1,170 +0,0 @@
use super::TcpStream;
#[cfg(debug_assertions)]
use crate::poll::SelectorId;
use crate::{event, sys, Interests, Registry, Token};
use std::fmt;
use std::io;
use std::net::SocketAddr;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
/// A structure representing a socket server
///
/// # Examples
///
/// ```
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::{Events, Interests, Poll, Token};
/// use mio::net::TcpListener;
/// use std::time::Duration;
///
/// let listener = TcpListener::bind("127.0.0.1:34255".parse()?)?;
///
/// let mut poll = Poll::new()?;
/// let mut events = Events::with_capacity(128);
///
/// // Register the socket with `Poll`
/// poll.registry().register(&listener, Token(0), Interests::READABLE)?;
///
/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
///
/// // There may be a socket ready to be accepted
/// # Ok(())
/// # }
/// ```
pub struct TcpListener {
sys: sys::TcpListener,
#[cfg(debug_assertions)]
selector_id: SelectorId,
}
impl TcpListener {
/// Convenience method to bind a new TCP listener to the specified address
/// to receive new connections.
///
/// This function will take the following steps:
///
/// 1. Create a new TCP socket.
/// 2. Set the `SO_REUSEADDR` option on the socket on Unix.
/// 3. Bind the socket to the specified address.
/// 4. Calls `listen` on the socket to prepare it to receive new connections.
pub fn bind(addr: SocketAddr) -> io::Result<TcpListener> {
sys::TcpListener::bind(addr).map(|sys| TcpListener {
sys,
#[cfg(debug_assertions)]
selector_id: SelectorId::new(),
})
}
/// Accepts a new `TcpStream`.
///
/// This may return an `Err(e)` where `e.kind()` is
/// `io::ErrorKind::WouldBlock`. This means a stream may be ready at a later
/// point and one should wait for an event before calling `accept` again.
///
/// If an accepted stream is returned, the remote address of the peer is
/// returned along with it.
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
self.sys
.accept()
.map(|(sys, addr)| (TcpStream::new(sys), addr))
}
/// Returns the local socket address of this listener.
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.sys.local_addr()
}
/// Creates a new independently owned handle to the underlying socket.
///
/// The returned `TcpListener` is a reference to the same socket that this
/// object references. Both handles can be used to accept incoming
/// connections and options set on one listener will affect the other.
pub fn try_clone(&self) -> io::Result<TcpListener> {
self.sys.try_clone().map(|s| TcpListener {
sys: s,
#[cfg(debug_assertions)]
selector_id: self.selector_id.clone(),
})
}
/// Sets the value for the `IP_TTL` option on this socket.
///
/// This value sets the time-to-live field that is used in every packet sent
/// from this socket.
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.sys.set_ttl(ttl)
}
/// Gets the value of the `IP_TTL` option for this socket.
///
/// For more information about this option, see [`set_ttl`][link].
///
/// [link]: #method.set_ttl
pub fn ttl(&self) -> io::Result<u32> {
self.sys.ttl()
}
/// Get the value of the `SO_ERROR` option on this socket.
///
/// This will retrieve the stored error in the underlying socket, clearing
/// the field in the process. This can be useful for checking errors between
/// calls.
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.sys.take_error()
}
}
impl event::Source for TcpListener {
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.associate_selector(registry)?;
self.sys.register(registry, token, interests)
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
self.sys.reregister(registry, token, interests)
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
self.sys.deregister(registry)
}
}
impl fmt::Debug for TcpListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.sys, f)
}
}
#[cfg(unix)]
impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
#[cfg(unix)]
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
#[cfg(unix)]
impl FromRawFd for TcpListener {
unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
TcpListener {
sys: FromRawFd::from_raw_fd(fd),
#[cfg(debug_assertions)]
selector_id: SelectorId::new(),
}
}
}

View File

@ -1,256 +0,0 @@
use std::fmt;
use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::net::SocketAddr;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(debug_assertions)]
use crate::poll::SelectorId;
use crate::{event, sys, Interests, Registry, Token};
/// A non-blocking TCP stream between a local socket and a remote socket.
///
/// The socket will be closed when the value is dropped.
///
/// # Examples
///
/// ```
/// # use std::net::TcpListener;
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # let _listener = TcpListener::bind("127.0.0.1:34254")?;
/// use mio::{Events, Interests, Poll, Token};
/// use mio::net::TcpStream;
/// use std::time::Duration;
///
/// let stream = TcpStream::connect("127.0.0.1:34254".parse()?)?;
///
/// let mut poll = Poll::new()?;
/// let mut events = Events::with_capacity(128);
///
/// // Register the socket with `Poll`
/// poll.registry().register(&stream, Token(0), Interests::WRITABLE)?;
///
/// poll.poll(&mut events, Some(Duration::from_millis(100)))?;
///
/// // The socket might be ready at this point
/// # Ok(())
/// # }
/// ```
pub struct TcpStream {
sys: sys::TcpStream,
#[cfg(debug_assertions)]
selector_id: SelectorId,
}
use std::net::Shutdown;
impl TcpStream {
pub(crate) fn new(sys: sys::TcpStream) -> TcpStream {
TcpStream {
sys,
#[cfg(debug_assertions)]
selector_id: SelectorId::new(),
}
}
/// Create a new TCP stream and issue a non-blocking connect to the
/// specified address.
pub fn connect(addr: SocketAddr) -> io::Result<TcpStream> {
sys::TcpStream::connect(addr).map(|sys| TcpStream {
sys,
#[cfg(debug_assertions)]
selector_id: SelectorId::new(),
})
}
/// Returns the socket address of the remote peer of this TCP connection.
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.sys.peer_addr()
}
/// Returns the socket address of the local half of this TCP connection.
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.sys.local_addr()
}
/// Creates a new independently owned handle to the underlying socket.
///
/// The returned `TcpStream` is a reference to the same stream that this
/// object references. Both handles will read and write the same stream of
/// data, and options set on one stream will be propagated to the other
/// stream.
pub fn try_clone(&self) -> io::Result<TcpStream> {
self.sys.try_clone().map(|s| TcpStream {
sys: s,
#[cfg(debug_assertions)]
selector_id: self.selector_id.clone(),
})
}
/// Shuts down the read, write, or both halves of this connection.
///
/// This function will cause all pending and future I/O on the specified
/// portions to return immediately with an appropriate value (see the
/// documentation of `Shutdown`).
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
self.sys.shutdown(how)
}
/// Sets the value of the `TCP_NODELAY` option on this socket.
///
/// If set, this option disables the Nagle algorithm. This means that
/// segments are always sent as soon as possible, even if there is only a
/// small amount of data. When not set, data is buffered until there is a
/// sufficient amount to send out, thereby avoiding the frequent sending of
/// small packets.
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.sys.set_nodelay(nodelay)
}
/// Gets the value of the `TCP_NODELAY` option on this socket.
///
/// For more information about this option, see [`set_nodelay`][link].
///
/// [link]: #method.set_nodelay
pub fn nodelay(&self) -> io::Result<bool> {
self.sys.nodelay()
}
/// Sets the value for the `IP_TTL` option on this socket.
///
/// This value sets the time-to-live field that is used in every packet sent
/// from this socket.
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.sys.set_ttl(ttl)
}
/// Gets the value of the `IP_TTL` option for this socket.
///
/// For more information about this option, see [`set_ttl`][link].
///
/// [link]: #method.set_ttl
pub fn ttl(&self) -> io::Result<u32> {
self.sys.ttl()
}
/// Get the value of the `SO_ERROR` option on this socket.
///
/// This will retrieve the stored error in the underlying socket, clearing
/// the field in the process. This can be useful for checking errors between
/// calls.
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.sys.take_error()
}
/// Receives data on the socket from the remote address to which it is
/// connected, without removing that data from the queue. On success,
/// returns the number of bytes peeked.
///
/// Successive calls return the same data. This is accomplished by passing
/// `MSG_PEEK` as a flag to the underlying recv system call.
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.sys.peek(buf)
}
}
impl Read for TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&self.sys).read(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
(&self.sys).read_vectored(bufs)
}
}
impl<'a> Read for &'a TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&self.sys).read(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
(&self.sys).read_vectored(bufs)
}
}
impl Write for TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&self.sys).write(buf)
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
(&self.sys).write_vectored(bufs)
}
fn flush(&mut self) -> io::Result<()> {
(&self.sys).flush()
}
}
impl<'a> Write for &'a TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&self.sys).write(buf)
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
(&self.sys).write_vectored(bufs)
}
fn flush(&mut self) -> io::Result<()> {
(&self.sys).flush()
}
}
impl event::Source for TcpStream {
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.associate_selector(registry)?;
self.sys.register(registry, token, interests)
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
self.sys.reregister(registry, token, interests)
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
self.sys.deregister(registry)
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.sys, f)
}
}
#[cfg(unix)]
impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
#[cfg(unix)]
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
#[cfg(unix)]
impl FromRawFd for TcpStream {
unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
TcpStream {
sys: FromRawFd::from_raw_fd(fd),
#[cfg(debug_assertions)]
selector_id: SelectorId::new(),
}
}
}

View File

@ -1,21 +1,20 @@
//! Primitives for working with UDP.
//! Primitives for working with UDP
//!
//! The types provided in this module are non-blocking by default and are
//! designed to be portable across all supported Mio platforms. As long as the
//! [portability guidelines] are followed, the behavior should be identical no
//! matter the target platform.
//!
//! [portability guidelines]: ../struct.Poll.html#portability
#[cfg(debug_assertions)]
use crate::poll::SelectorId;
use crate::{event, sys, Interests, Registry, Token};
/// [portability guidelines]: ../struct.Poll.html#portability
use {io, sys, Ready, Poll, PollOpt, Token};
use event::Evented;
use poll::SelectorId;
use std::fmt;
use std::io;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
#[cfg(all(unix, not(target_os = "fuchsia")))]
use iovec::IoVec;
/// A User Datagram Protocol socket.
///
@ -28,13 +27,13 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// // An Echo program:
/// // SENDER -> sends a message.
/// // ECHOER -> listens and prints the message received.
///
/// use mio::net::UdpSocket;
/// use mio::{Events, Interests, Poll, Token};
/// use mio::{Events, Ready, Poll, PollOpt, Token};
/// use std::time::Duration;
///
/// const SENDER: Token = Token(0);
@ -42,8 +41,8 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
///
/// // This operation will fail if the address is in use, so we select different ports for each
/// // socket.
/// let sender_socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let echoer_socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let sender_socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
/// let echoer_socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
///
/// // If we do not use connect here, SENDER and ECHOER would need to call send_to and recv_from
/// // respectively.
@ -51,11 +50,11 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
///
/// // We need a Poll to check if SENDER is ready to be written into, and if ECHOER is ready to be
/// // read from.
/// let mut poll = Poll::new()?;
/// let poll = Poll::new()?;
///
/// // We register our sockets here so that we can check if they are ready to be written/read.
/// poll.registry().register(&sender_socket, SENDER, Interests::WRITABLE)?;
/// poll.registry().register(&echoer_socket, ECHOER, Interests::READABLE)?;
/// poll.register(&sender_socket, SENDER, Ready::writable(), PollOpt::edge())?;
/// poll.register(&echoer_socket, ECHOER, Ready::readable(), PollOpt::edge())?;
///
/// let msg_to_send = [9; 9];
/// let mut buffer = [0; 9];
@ -76,18 +75,22 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
/// let num_recv = echoer_socket.recv(&mut buffer)?;
/// println!("echo {:?} -> {:?}", buffer, num_recv);
/// buffer = [0; 9];
/// # drop(buffer); // Silence unused assignment warning.
/// # return Ok(());
/// }
/// _ => unreachable!()
/// }
/// }
/// }
/// #
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub struct UdpSocket {
sys: sys::UdpSocket,
#[cfg(debug_assertions)]
selector_id: SelectorId,
}
@ -99,11 +102,11 @@ impl UdpSocket {
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// // We must bind it to an open address.
/// let socket = match UdpSocket::bind("127.0.0.1:0".parse()?) {
/// let socket = match UdpSocket::bind(&"127.0.0.1:0".parse()?) {
/// Ok(new_socket) => new_socket,
/// Err(fail) => {
/// // We panic! here, but you could try to bind it again on another address.
@ -112,14 +115,31 @@ impl UdpSocket {
/// };
///
/// // Our socket was created, but we should not use it before checking it's readiness.
/// # drop(socket); // Silence unused variable warning.
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn bind(addr: SocketAddr) -> io::Result<UdpSocket> {
sys::UdpSocket::bind(addr).map(|sys| UdpSocket {
sys,
#[cfg(debug_assertions)]
pub fn bind(addr: &SocketAddr) -> io::Result<UdpSocket> {
let socket = net::UdpSocket::bind(addr)?;
UdpSocket::from_socket(socket)
}
/// Creates a new mio-wrapped socket from an underlying and bound std
/// socket.
///
/// This function requires that `socket` has previously been bound to an
/// address to work correctly, and returns an I/O object which can be used
/// with mio to send/receive UDP messages.
///
/// This can be used in conjunction with net2's `UdpBuilder` interface to
/// configure a socket before it's handed off to mio, such as setting
/// options like `reuse_address` or binding to multiple addresses.
pub fn from_socket(socket: net::UdpSocket) -> io::Result<UdpSocket> {
Ok(UdpSocket {
sys: sys::UdpSocket::new(socket)?,
selector_id: SelectorId::new(),
})
}
@ -135,14 +155,17 @@ impl UdpSocket {
#[cfg_attr(target_os = "freebsd", doc = " ```no_run")]
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// let addr = "127.0.0.1:0".parse()?;
/// let socket = UdpSocket::bind(addr)?;
/// assert_eq!(socket.local_addr()?.ip(), addr.ip());
/// let socket = UdpSocket::bind(&addr)?;
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.sys.local_addr()
@ -159,24 +182,30 @@ impl UdpSocket {
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// // We must bind it to an open address.
/// let socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
/// let cloned_socket = socket.try_clone()?;
///
/// assert_eq!(socket.local_addr()?, cloned_socket.local_addr()?);
///
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn try_clone(&self) -> io::Result<UdpSocket> {
self.sys.try_clone().map(|s| UdpSocket {
sys: s,
#[cfg(debug_assertions)]
selector_id: self.selector_id.clone(),
})
self.sys.try_clone()
.map(|s| {
UdpSocket {
sys: s,
selector_id: self.selector_id.clone(),
}
})
}
/// Sends data on the socket to the given address. On success, returns the
@ -189,21 +218,25 @@ impl UdpSocket {
///
/// ```no_run
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// let socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
///
/// // We must check if the socket is writable before calling send_to,
/// // or we could run into a WouldBlock error.
///
/// let bytes_sent = socket.send_to(&[9; 9], "127.0.0.1:11100".parse()?)?;
/// let bytes_sent = socket.send_to(&[9; 9], &"127.0.0.1:11100".parse()?)?;
/// assert_eq!(bytes_sent, 9);
/// #
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn send_to(&self, buf: &[u8], target: SocketAddr) -> io::Result<usize> {
pub fn send_to(&self, buf: &[u8], target: &SocketAddr) -> io::Result<usize> {
self.sys.send_to(buf, target)
}
@ -215,10 +248,10 @@ impl UdpSocket {
/// ```no_run
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// let socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
///
/// // We must check if the socket is readable before calling recv_from,
/// // or we could run into a WouldBlock error.
@ -229,41 +262,15 @@ impl UdpSocket {
/// #
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.sys.recv_from(buf)
}
/// Receives data from the socket, without removing it from the input queue.
/// On success, returns the number of bytes read and the address from whence
/// the data came.
/// Receives data from the socket. On success, returns the number of bytes
/// read and the address from whence the data came.
///
/// # Examples
///
/// ```no_run
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::net::UdpSocket;
///
/// let socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
///
/// // We must check if the socket is readable before calling recv_from,
/// // or we could run into a WouldBlock error.
///
/// let mut buf = [0; 9];
/// let (num_recv, from_addr) = socket.peek_from(&mut buf)?;
/// println!("Received {:?} -> {:?} bytes from {:?}", buf, num_recv, from_addr);
/// #
/// # Ok(())
/// # }
/// ```
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.sys.peek_from(buf)
}
/// Sends data on the socket to the address previously bound via connect(). On success,
/// returns the number of bytes written.
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
@ -276,12 +283,6 @@ impl UdpSocket {
self.sys.recv(buf)
}
/// Receives data from the socket, without removing it from the input queue.
/// On success, returns the number of bytes read.
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.sys.peek(buf)
}
/// Connects the UDP socket setting the default destination for `send()`
/// and limiting packets that are read via `recv` from the address specified
/// in `addr`.
@ -299,10 +300,10 @@ impl UdpSocket {
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// let broadcast_socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let broadcast_socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
/// if broadcast_socket.broadcast()? == false {
/// broadcast_socket.set_broadcast(true)?;
/// }
@ -311,6 +312,10 @@ impl UdpSocket {
/// #
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn set_broadcast(&self, on: bool) -> io::Result<()> {
self.sys.set_broadcast(on)
@ -328,14 +333,18 @@ impl UdpSocket {
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// let broadcast_socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let broadcast_socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
/// assert_eq!(broadcast_socket.broadcast()?, false);
/// #
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn broadcast(&self) -> io::Result<bool> {
self.sys.broadcast()
@ -408,10 +417,10 @@ impl UdpSocket {
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// let socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
/// if socket.ttl()? < 255 {
/// socket.set_ttl(255)?;
/// }
@ -420,6 +429,10 @@ impl UdpSocket {
/// #
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.sys.set_ttl(ttl)
@ -436,16 +449,20 @@ impl UdpSocket {
/// ```
/// # use std::error::Error;
/// #
/// # fn main() -> Result<(), Box<dyn Error>> {
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::net::UdpSocket;
///
/// let socket = UdpSocket::bind("127.0.0.1:0".parse()?)?;
/// let socket = UdpSocket::bind(&"127.0.0.1:0".parse()?)?;
/// socket.set_ttl(255)?;
///
/// assert_eq!(socket.ttl()?, 255);
/// #
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
pub fn ttl(&self) -> io::Result<u32> {
self.sys.ttl()
@ -458,7 +475,9 @@ impl UdpSocket {
/// address of the local interface with which the system should join the
/// multicast group. If it's equal to `INADDR_ANY` then an appropriate
/// interface is chosen by the system.
pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> {
pub fn join_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.sys.join_multicast_v4(multiaddr, interface)
}
@ -467,7 +486,9 @@ impl UdpSocket {
/// This function specifies a new multicast group for this socket to join.
/// The address must be a valid multicast address, and `interface` is the
/// index of the interface to join/leave (or 0 to indicate any interface).
pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
pub fn join_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.sys.join_multicast_v6(multiaddr, interface)
}
@ -477,7 +498,9 @@ impl UdpSocket {
/// [`join_multicast_v4`][link].
///
/// [link]: #method.join_multicast_v4
pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> {
pub fn leave_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.sys.leave_multicast_v4(multiaddr, interface)
}
@ -487,10 +510,33 @@ impl UdpSocket {
/// [`join_multicast_v6`][link].
///
/// [link]: #method.join_multicast_v6
pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
pub fn leave_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.sys.leave_multicast_v6(multiaddr, interface)
}
/// Sets the value for the `IPV6_V6ONLY` option on this socket.
///
/// If this is set to `true` then the socket is restricted to sending and
/// receiving IPv6 packets only. In this case two IPv4 and IPv6 applications
/// can bind the same port at the same time.
///
/// If this is set to `false` then the socket can be used to send and
/// receive packets from an IPv4-mapped IPv6 address.
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.sys.set_only_v6(only_v6)
}
/// Gets the value of the `IPV6_V6ONLY` option for this socket.
///
/// For more information about this option, see [`set_only_v6`][link].
///
/// [link]: #method.set_only_v6
pub fn only_v6(&self) -> io::Result<bool> {
self.sys.only_v6()
}
/// Get the value of the `SO_ERROR` option on this socket.
///
/// This will retrieve the stored error in the underlying socket, clearing
@ -499,31 +545,67 @@ impl UdpSocket {
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.sys.take_error()
}
/// Receives a single datagram message socket previously bound with connect.
///
/// This operation will attempt to read bytes from this socket and place
/// them into the list of buffers provided. Note that each buffer is an
/// `IoVec` which can be created from a byte slice.
///
/// The buffers provided will be filled sequentially. A buffer will be
/// entirely filled up before the next is written to.
///
/// The number of bytes read is returned, if successful, or an error is
/// returned otherwise. If no bytes are available to be read yet then
/// a [`WouldBlock`][link] error is returned. This operation does not block.
///
/// On Unix this corresponds to the `readv` syscall.
///
/// [link]: https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.WouldBlock
#[cfg(all(unix, not(target_os = "fuchsia")))]
pub fn recv_bufs(&self, bufs: &mut [&mut IoVec]) -> io::Result<usize> {
self.sys.readv(bufs)
}
/// Sends data on the socket to the address previously bound via connect.
///
/// This operation will attempt to send a list of byte buffers to this
/// socket in a single datagram. Note that each buffer is an `IoVec`
/// which can be created from a byte slice.
///
/// The buffers provided will be written sequentially. A buffer will be
/// entirely written before the next is written.
///
/// The number of bytes written is returned, if successful, or an error is
/// returned otherwise. If the socket is not currently writable then a
/// [`WouldBlock`][link] error is returned. This operation does not block.
///
/// On Unix this corresponds to the `writev` syscall.
///
/// [link]: https://doc.rust-lang.org/nightly/std/io/enum.ErrorKind.html#variant.WouldBlock
#[cfg(all(unix, not(target_os = "fuchsia")))]
pub fn send_bufs(&self, bufs: &[&IoVec]) -> io::Result<usize> {
self.sys.writev(bufs)
}
}
impl event::Source for UdpSocket {
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.associate_selector(registry)?;
self.sys.register(registry, token, interests)
impl Evented for UdpSocket {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.selector_id.associate_selector(poll)?;
self.sys.register(poll, token, interest, opts)
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
self.sys.reregister(registry, token, interests)
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.reregister(poll, token, interest, opts)
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
self.sys.deregister(registry)
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.sys.deregister(poll)
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.sys, f)
}
}
@ -534,27 +616,30 @@ impl fmt::Debug for UdpSocket {
*
*/
#[cfg(unix)]
#[cfg(all(unix, not(target_os = "fuchsia")))]
use std::os::unix::io::{IntoRawFd, AsRawFd, FromRawFd, RawFd};
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl IntoRawFd for UdpSocket {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
#[cfg(unix)]
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl AsRawFd for UdpSocket {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
#[cfg(unix)]
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl FromRawFd for UdpSocket {
unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket {
UdpSocket {
sys: FromRawFd::from_raw_fd(fd),
#[cfg(debug_assertions)]
selector_id: SelectorId::new(),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,73 @@
use {io, poll, Evented, Ready, Poll, PollOpt, Token};
use zircon;
use std::sync::{Arc, Mutex, Weak};
pub struct Awakener {
/// Token and weak reference to the port on which Awakener was registered.
///
/// When `Awakener::wakeup` is called, these are used to send a wakeup message to the port.
inner: Mutex<Option<(Token, Weak<zircon::Port>)>>,
}
impl Awakener {
/// Create a new `Awakener`.
pub fn new() -> io::Result<Awakener> {
Ok(Awakener {
inner: Mutex::new(None)
})
}
/// Send a wakeup signal to the `Selector` on which the `Awakener` was registered.
pub fn wakeup(&self) -> io::Result<()> {
let inner_locked = self.inner.lock().unwrap();
let &(token, ref weak_port) =
inner_locked.as_ref().expect("Called wakeup on unregistered awakener.");
let port = weak_port.upgrade().expect("Tried to wakeup a closed port.");
let status = 0; // arbitrary
let packet = zircon::Packet::from_user_packet(
token.0 as u64, status, zircon::UserPacket::from_u8_array([0; 32]));
Ok(port.queue(&packet)?)
}
pub fn cleanup(&self) {}
}
impl Evented for Awakener {
fn register(&self,
poll: &Poll,
token: Token,
_events: Ready,
_opts: PollOpt) -> io::Result<()>
{
let mut inner_locked = self.inner.lock().unwrap();
if inner_locked.is_some() {
panic!("Called register on already-registered Awakener.");
}
*inner_locked = Some((token, Arc::downgrade(poll::selector(poll).port())));
Ok(())
}
fn reregister(&self,
poll: &Poll,
token: Token,
_events: Ready,
_opts: PollOpt) -> io::Result<()>
{
let mut inner_locked = self.inner.lock().unwrap();
*inner_locked = Some((token, Arc::downgrade(poll::selector(poll).port())));
Ok(())
}
fn deregister(&self, _poll: &Poll) -> io::Result<()>
{
let mut inner_locked = self.inner.lock().unwrap();
*inner_locked = None;
Ok(())
}
}

View File

@ -0,0 +1,263 @@
use {io, poll, Evented, Ready, Poll, PollOpt, Token};
use libc;
use zircon;
use zircon::AsHandleRef;
use sys::fuchsia::{DontDrop, poll_opts_to_wait_async, sys};
use std::mem;
use std::os::unix::io::RawFd;
use std::sync::{Arc, Mutex};
/// Properties of an `EventedFd`'s current registration
#[derive(Debug)]
pub struct EventedFdRegistration {
token: Token,
handle: DontDrop<zircon::Handle>,
rereg_signals: Option<(zircon::Signals, zircon::WaitAsyncOpts)>,
}
impl EventedFdRegistration {
unsafe fn new(token: Token,
raw_handle: sys::zx_handle_t,
rereg_signals: Option<(zircon::Signals, zircon::WaitAsyncOpts)>,
) -> Self
{
EventedFdRegistration {
token: token,
handle: DontDrop::new(zircon::Handle::from_raw(raw_handle)),
rereg_signals: rereg_signals
}
}
pub fn rereg_signals(&self) -> Option<(zircon::Signals, zircon::WaitAsyncOpts)> {
self.rereg_signals
}
}
/// An event-ed file descriptor. The file descriptor is owned by this structure.
#[derive(Debug)]
pub struct EventedFdInner {
/// Properties of the current registration.
registration: Mutex<Option<EventedFdRegistration>>,
/// Owned file descriptor.
///
/// `fd` is closed on `Drop`, so modifying `fd` is a memory-unsafe operation.
fd: RawFd,
/// Owned `fdio_t` pointer.
fdio: *const sys::fdio_t,
}
impl EventedFdInner {
pub fn rereg_for_level(&self, port: &zircon::Port) {
let registration_opt = self.registration.lock().unwrap();
if let Some(ref registration) = *registration_opt {
if let Some((rereg_signals, rereg_opts)) = registration.rereg_signals {
let _res =
registration
.handle.inner_ref()
.wait_async_handle(
port,
registration.token.0 as u64,
rereg_signals,
rereg_opts);
}
}
}
pub fn registration(&self) -> &Mutex<Option<EventedFdRegistration>> {
&self.registration
}
pub fn fdio(&self) -> &sys::fdio_t {
unsafe { &*self.fdio }
}
}
impl Drop for EventedFdInner {
fn drop(&mut self) {
unsafe {
sys::__fdio_release(self.fdio);
let _ = libc::close(self.fd);
}
}
}
// `EventedInner` must be manually declared `Send + Sync` because it contains a `RawFd` and a
// `*const sys::fdio_t`. These are only used to make thread-safe system calls, so accessing
// them is entirely thread-safe.
//
// Note: one minor exception to this are the calls to `libc::close` and `__fdio_release`, which
// happen on `Drop`. These accesses are safe because `drop` can only be called at most once from
// a single thread, and after it is called no other functions can be called on the `EventedFdInner`.
unsafe impl Sync for EventedFdInner {}
unsafe impl Send for EventedFdInner {}
#[derive(Clone, Debug)]
pub struct EventedFd {
pub inner: Arc<EventedFdInner>
}
impl EventedFd {
pub unsafe fn new(fd: RawFd) -> Self {
let fdio = sys::__fdio_fd_to_io(fd);
assert!(fdio != ::std::ptr::null(), "FileDescriptor given to EventedFd must be valid.");
EventedFd {
inner: Arc::new(EventedFdInner {
registration: Mutex::new(None),
fd: fd,
fdio: fdio,
})
}
}
fn handle_and_signals_for_events(&self, interest: Ready, opts: PollOpt)
-> (sys::zx_handle_t, zircon::Signals)
{
let epoll_events = ioevent_to_epoll(interest, opts);
unsafe {
let mut raw_handle: sys::zx_handle_t = mem::uninitialized();
let mut signals: sys::zx_signals_t = mem::uninitialized();
sys::__fdio_wait_begin(self.inner.fdio, epoll_events, &mut raw_handle, &mut signals);
(raw_handle, signals)
}
}
fn register_with_lock(
&self,
registration: &mut Option<EventedFdRegistration>,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
if registration.is_some() {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"Called register on an already registered file descriptor."));
}
let (raw_handle, signals) = self.handle_and_signals_for_events(interest, opts);
let needs_rereg = opts.is_level() && !opts.is_oneshot();
// If we need to reregister, then each registration should be `oneshot`
let opts = opts | if needs_rereg { PollOpt::oneshot() } else { PollOpt::empty() };
let rereg_signals = if needs_rereg {
Some((signals, poll_opts_to_wait_async(opts)))
} else {
None
};
*registration = Some(
unsafe { EventedFdRegistration::new(token, raw_handle, rereg_signals) }
);
// We don't have ownership of the handle, so we can't drop it
let handle = DontDrop::new(unsafe { zircon::Handle::from_raw(raw_handle) });
let registered = poll::selector(poll)
.register_fd(handle.inner_ref(), self, token, signals, opts);
if registered.is_err() {
*registration = None;
}
registered
}
fn deregister_with_lock(
&self,
registration: &mut Option<EventedFdRegistration>,
poll: &Poll) -> io::Result<()>
{
let old_registration = if let Some(old_reg) = registration.take() {
old_reg
} else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"Called rereregister on an unregistered file descriptor."))
};
poll::selector(poll)
.deregister_fd(old_registration.handle.inner_ref(), old_registration.token)
}
}
impl Evented for EventedFd {
fn register(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
self.register_with_lock(
&mut *self.inner.registration.lock().unwrap(),
poll,
token,
interest,
opts)
}
fn reregister(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
// Take out the registration lock
let mut registration_lock = self.inner.registration.lock().unwrap();
// Deregister
self.deregister_with_lock(&mut *registration_lock, poll)?;
self.register_with_lock(
&mut *registration_lock,
poll,
token,
interest,
opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
let mut registration_lock = self.inner.registration.lock().unwrap();
self.deregister_with_lock(&mut *registration_lock, poll)
}
}
fn ioevent_to_epoll(interest: Ready, opts: PollOpt) -> u32 {
use event_imp::ready_from_usize;
const HUP: usize = 0b01000;
let mut kind = 0;
if interest.is_readable() {
kind |= libc::EPOLLIN;
}
if interest.is_writable() {
kind |= libc::EPOLLOUT;
}
if interest.contains(ready_from_usize(HUP)) {
kind |= libc::EPOLLRDHUP;
}
if opts.is_edge() {
kind |= libc::EPOLLET;
}
if opts.is_oneshot() {
kind |= libc::EPOLLONESHOT;
}
if opts.is_level() {
kind &= !libc::EPOLLET;
}
kind as u32
}

View File

@ -0,0 +1,78 @@
use {io, poll, Evented, Ready, Poll, PollOpt, Token};
use zircon_sys::zx_handle_t;
use std::sync::Mutex;
/// Wrapper for registering a `HandleBase` type with mio.
#[derive(Debug)]
pub struct EventedHandle {
/// The handle to be registered.
handle: zx_handle_t,
/// The current `Token` with which the handle is registered with mio.
token: Mutex<Option<Token>>,
}
impl EventedHandle {
/// Create a new `EventedHandle` which can be registered with mio
/// in order to receive event notifications.
///
/// The underlying handle must not be dropped while the
/// `EventedHandle` still exists.
pub unsafe fn new(handle: zx_handle_t) -> Self {
EventedHandle {
handle: handle,
token: Mutex::new(None),
}
}
/// Get the underlying handle being registered.
pub fn get_handle(&self) -> zx_handle_t {
self.handle
}
}
impl Evented for EventedHandle {
fn register(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
let mut this_token = self.token.lock().unwrap();
{
poll::selector(poll).register_handle(self.handle, token, interest, opts)?;
*this_token = Some(token);
}
Ok(())
}
fn reregister(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
let mut this_token = self.token.lock().unwrap();
{
poll::selector(poll).deregister_handle(self.handle, token)?;
*this_token = None;
poll::selector(poll).register_handle(self.handle, token, interest, opts)?;
*this_token = Some(token);
}
Ok(())
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
let mut this_token = self.token.lock().unwrap();
let token = if let Some(token) = *this_token { token } else {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"Attempted to deregister an unregistered handle."))
};
{
poll::selector(poll).deregister_handle(self.handle, token)?;
*this_token = None;
}
Ok(())
}
}

177
src/sys/fuchsia/mod.rs Normal file
View File

@ -0,0 +1,177 @@
use {io, Ready, PollOpt};
use libc;
use zircon;
use std::mem;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::ops::{Deref, DerefMut};
use std::os::unix::io::RawFd;
mod awakener;
mod handles;
mod eventedfd;
mod net;
mod ready;
mod selector;
use self::eventedfd::{EventedFd, EventedFdInner};
use self::ready::assert_fuchsia_ready_repr;
pub use self::awakener::Awakener;
pub use self::handles::EventedHandle;
pub use self::net::{TcpListener, TcpStream, UdpSocket};
pub use self::selector::{Events, Selector};
pub use self::ready::{FuchsiaReady, zx_signals_t};
// Set non-blocking (workaround since the std version doesn't work in fuchsia)
// TODO: fix the std version and replace this
pub fn set_nonblock(fd: RawFd) -> io::Result<()> {
cvt(unsafe { libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) }).map(|_| ())
}
/// Workaround until fuchsia's recv_from is fixed
unsafe fn recv_from(fd: RawFd, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
let flags = 0;
let n = cvt(
libc::recv(fd,
buf.as_mut_ptr() as *mut libc::c_void,
buf.len(),
flags)
)?;
// random address-- we don't use it
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8080);
Ok((n as usize, addr))
}
mod sys {
#![allow(non_camel_case_types)]
use std::os::unix::io::RawFd;
pub use zircon_sys::{zx_handle_t, zx_signals_t};
// 17 fn pointers we don't need for mio :)
pub type fdio_ops_t = [usize; 17];
pub type atomic_int_fast32_t = usize; // TODO: https://github.com/rust-lang/libc/issues/631
#[repr(C)]
pub struct fdio_t {
pub ops: *const fdio_ops_t,
pub magic: u32,
pub refcount: atomic_int_fast32_t,
pub dupcount: u32,
pub flags: u32,
}
#[link(name="fdio")]
extern {
pub fn __fdio_fd_to_io(fd: RawFd) -> *const fdio_t;
pub fn __fdio_release(io: *const fdio_t);
pub fn __fdio_wait_begin(
io: *const fdio_t,
events: u32,
handle_out: &mut zx_handle_t,
signals_out: &mut zx_signals_t,
);
pub fn __fdio_wait_end(
io: *const fdio_t,
signals: zx_signals_t,
events_out: &mut u32,
);
}
}
fn epoll_event_to_ready(epoll: u32) -> Ready {
let epoll = epoll as i32; // casts the bits directly
let mut kind = Ready::empty();
if (epoll & libc::EPOLLIN) != 0 || (epoll & libc::EPOLLPRI) != 0 {
kind = kind | Ready::readable();
}
if (epoll & libc::EPOLLOUT) != 0 {
kind = kind | Ready::writable();
}
kind
/* TODO: support?
// EPOLLHUP - Usually means a socket error happened
if (epoll & libc::EPOLLERR) != 0 {
kind = kind | UnixReady::error();
}
if (epoll & libc::EPOLLRDHUP) != 0 || (epoll & libc::EPOLLHUP) != 0 {
kind = kind | UnixReady::hup();
}
*/
}
fn poll_opts_to_wait_async(poll_opts: PollOpt) -> zircon::WaitAsyncOpts {
if poll_opts.is_oneshot() {
zircon::WaitAsyncOpts::Once
} else {
zircon::WaitAsyncOpts::Repeating
}
}
trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
impl IsMinusOne for i32 {
fn is_minus_one(&self) -> bool { *self == -1 }
}
impl IsMinusOne for isize {
fn is_minus_one(&self) -> bool { *self == -1 }
}
fn cvt<T: IsMinusOne>(t: T) -> ::io::Result<T> {
use std::io;
if t.is_minus_one() {
Err(io::Error::last_os_error())
} else {
Ok(t)
}
}
/// Utility type to prevent the type inside of it from being dropped.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
struct DontDrop<T>(Option<T>);
impl<T> DontDrop<T> {
fn new(t: T) -> DontDrop<T> {
DontDrop(Some(t))
}
fn inner_ref(&self) -> &T {
self.0.as_ref().unwrap()
}
fn inner_mut(&mut self) -> &mut T {
self.0.as_mut().unwrap()
}
}
impl<T> Deref for DontDrop<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.inner_ref()
}
}
impl<T> DerefMut for DontDrop<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner_mut()
}
}
impl<T> Drop for DontDrop<T> {
fn drop(&mut self) {
let inner = self.0.take();
mem::forget(inner);
}
}

444
src/sys/fuchsia/net.rs Normal file
View File

@ -0,0 +1,444 @@
use {io, Evented, Ready, Poll, PollOpt, Token};
use iovec::IoVec;
use iovec::unix as iovec;
use libc;
use net2::TcpStreamExt;
#[allow(unused_imports)] // only here for Rust 1.8
use net2::UdpSocketExt;
use sys::fuchsia::{recv_from, set_nonblock, EventedFd, DontDrop};
use std::cmp;
use std::io::{Read, Write};
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::os::unix::io::AsRawFd;
use std::time::Duration;
#[derive(Debug)]
pub struct TcpStream {
io: DontDrop<net::TcpStream>,
evented_fd: EventedFd,
}
impl TcpStream {
pub fn connect(stream: net::TcpStream, addr: &SocketAddr) -> io::Result<TcpStream> {
try!(set_nonblock(stream.as_raw_fd()));
let connected = stream.connect(addr);
match connected {
Ok(..) => {}
Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
Err(e) => return Err(e),
}
let evented_fd = unsafe { EventedFd::new(stream.as_raw_fd()) };
return Ok(TcpStream {
io: DontDrop::new(stream),
evented_fd: evented_fd,
})
}
pub fn from_stream(stream: net::TcpStream) -> TcpStream {
let evented_fd = unsafe { EventedFd::new(stream.as_raw_fd()) };
TcpStream {
io: DontDrop::new(stream),
evented_fd: evented_fd,
}
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.io.peer_addr()
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.local_addr()
}
pub fn try_clone(&self) -> io::Result<TcpStream> {
self.io.try_clone().map(|s| {
let evented_fd = unsafe { EventedFd::new(s.as_raw_fd()) };
TcpStream {
io: DontDrop::new(s),
evented_fd: evented_fd,
}
})
}
pub fn shutdown(&self, how: net::Shutdown) -> io::Result<()> {
self.io.shutdown(how)
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.io.set_nodelay(nodelay)
}
pub fn nodelay(&self) -> io::Result<bool> {
self.io.nodelay()
}
pub fn set_recv_buffer_size(&self, size: usize) -> io::Result<()> {
self.io.set_recv_buffer_size(size)
}
pub fn recv_buffer_size(&self) -> io::Result<usize> {
self.io.recv_buffer_size()
}
pub fn set_send_buffer_size(&self, size: usize) -> io::Result<()> {
self.io.set_send_buffer_size(size)
}
pub fn send_buffer_size(&self) -> io::Result<usize> {
self.io.send_buffer_size()
}
pub fn set_keepalive(&self, keepalive: Option<Duration>) -> io::Result<()> {
self.io.set_keepalive(keepalive)
}
pub fn keepalive(&self) -> io::Result<Option<Duration>> {
self.io.keepalive()
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.io.set_ttl(ttl)
}
pub fn ttl(&self) -> io::Result<u32> {
self.io.ttl()
}
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.io.set_only_v6(only_v6)
}
pub fn only_v6(&self) -> io::Result<bool> {
self.io.only_v6()
}
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
self.io.set_linger(dur)
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
self.io.linger()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.io.take_error()
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.io.peek(buf)
}
pub fn readv(&self, bufs: &mut [&mut IoVec]) -> io::Result<usize> {
unsafe {
let slice = iovec::as_os_slice_mut(bufs);
let len = cmp::min(<libc::c_int>::max_value() as usize, slice.len());
let rc = libc::readv(self.io.as_raw_fd(),
slice.as_ptr(),
len as libc::c_int);
if rc < 0 {
Err(io::Error::last_os_error())
} else {
Ok(rc as usize)
}
}
}
pub fn writev(&self, bufs: &[&IoVec]) -> io::Result<usize> {
unsafe {
let slice = iovec::as_os_slice(bufs);
let len = cmp::min(<libc::c_int>::max_value() as usize, slice.len());
let rc = libc::writev(self.io.as_raw_fd(),
slice.as_ptr(),
len as libc::c_int);
if rc < 0 {
Err(io::Error::last_os_error())
} else {
Ok(rc as usize)
}
}
}
}
impl<'a> Read for &'a TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.io.inner_ref().read(buf)
}
}
impl<'a> Write for &'a TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.io.inner_ref().write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.io.inner_ref().flush()
}
}
impl Evented for TcpStream {
fn register(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
self.evented_fd.register(poll, token, interest, opts)
}
fn reregister(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
self.evented_fd.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.evented_fd.deregister(poll)
}
}
#[derive(Debug)]
pub struct TcpListener {
io: DontDrop<net::TcpListener>,
evented_fd: EventedFd,
}
impl TcpListener {
pub fn new(inner: net::TcpListener) -> io::Result<TcpListener> {
set_nonblock(inner.as_raw_fd())?;
let evented_fd = unsafe { EventedFd::new(inner.as_raw_fd()) };
Ok(TcpListener {
io: DontDrop::new(inner),
evented_fd: evented_fd,
})
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.local_addr()
}
pub fn try_clone(&self) -> io::Result<TcpListener> {
self.io.try_clone().map(|io| {
let evented_fd = unsafe { EventedFd::new(io.as_raw_fd()) };
TcpListener {
io: DontDrop::new(io),
evented_fd: evented_fd,
}
})
}
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
self.io.accept().and_then(|(s, a)| {
set_nonblock(s.as_raw_fd())?;
let evented_fd = unsafe { EventedFd::new(s.as_raw_fd()) };
return Ok((TcpStream {
io: DontDrop::new(s),
evented_fd: evented_fd,
}, a))
})
}
#[allow(deprecated)]
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.io.set_only_v6(only_v6)
}
#[allow(deprecated)]
pub fn only_v6(&self) -> io::Result<bool> {
self.io.only_v6()
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.io.set_ttl(ttl)
}
pub fn ttl(&self) -> io::Result<u32> {
self.io.ttl()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.io.take_error()
}
}
impl Evented for TcpListener {
fn register(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
self.evented_fd.register(poll, token, interest, opts)
}
fn reregister(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
self.evented_fd.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.evented_fd.deregister(poll)
}
}
#[derive(Debug)]
pub struct UdpSocket {
io: DontDrop<net::UdpSocket>,
evented_fd: EventedFd,
}
impl UdpSocket {
pub fn new(socket: net::UdpSocket) -> io::Result<UdpSocket> {
set_nonblock(socket.as_raw_fd())?;
let evented_fd = unsafe { EventedFd::new(socket.as_raw_fd()) };
Ok(UdpSocket {
io: DontDrop::new(socket),
evented_fd: evented_fd,
})
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.local_addr()
}
pub fn try_clone(&self) -> io::Result<UdpSocket> {
self.io.try_clone().and_then(|io| {
UdpSocket::new(io)
})
}
pub fn send_to(&self, buf: &[u8], target: &SocketAddr) -> io::Result<usize> {
self.io.send_to(buf, target)
}
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
unsafe { recv_from(self.io.as_raw_fd(), buf) }
}
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
self.io.send(buf)
}
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
self.io.recv(buf)
}
pub fn connect(&self, addr: SocketAddr)
-> io::Result<()> {
self.io.connect(addr)
}
pub fn broadcast(&self) -> io::Result<bool> {
self.io.broadcast()
}
pub fn set_broadcast(&self, on: bool) -> io::Result<()> {
self.io.set_broadcast(on)
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
self.io.multicast_loop_v4()
}
pub fn set_multicast_loop_v4(&self, on: bool) -> io::Result<()> {
self.io.set_multicast_loop_v4(on)
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
self.io.multicast_ttl_v4()
}
pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> {
self.io.set_multicast_ttl_v4(ttl)
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
self.io.multicast_loop_v6()
}
pub fn set_multicast_loop_v6(&self, on: bool) -> io::Result<()> {
self.io.set_multicast_loop_v6(on)
}
pub fn ttl(&self) -> io::Result<u32> {
self.io.ttl()
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.io.set_ttl(ttl)
}
pub fn join_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.io.join_multicast_v4(multiaddr, interface)
}
pub fn join_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.io.join_multicast_v6(multiaddr, interface)
}
pub fn leave_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.io.leave_multicast_v4(multiaddr, interface)
}
pub fn leave_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.io.leave_multicast_v6(multiaddr, interface)
}
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.io.set_only_v6(only_v6)
}
pub fn only_v6(&self) -> io::Result<bool> {
self.io.only_v6()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.io.take_error()
}
}
impl Evented for UdpSocket {
fn register(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
self.evented_fd.register(poll, token, interest, opts)
}
fn reregister(&self,
poll: &Poll,
token: Token,
interest: Ready,
opts: PollOpt) -> io::Result<()>
{
self.evented_fd.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.evented_fd.deregister(poll)
}
}

181
src/sys/fuchsia/ready.rs Normal file
View File

@ -0,0 +1,181 @@
use event_imp::{Ready, ready_as_usize, ready_from_usize};
pub use zircon_sys::{
zx_signals_t,
ZX_OBJECT_READABLE,
ZX_OBJECT_WRITABLE,
};
use std::ops;
// The following impls are valid because Fuchsia and mio both represent
// "readable" as `1 << 0` and "writable" as `1 << 2`.
// We define this assertion here and call it from `Selector::new`,
// since `Selector:;new` is guaranteed to be called during a standard mio runtime,
// unlike the functions in this file.
#[inline]
pub fn assert_fuchsia_ready_repr() {
debug_assert!(
ZX_OBJECT_READABLE.bits() as usize == ready_as_usize(Ready::readable()),
"Zircon ZX_OBJECT_READABLE should have the same repr as Ready::readable()"
);
debug_assert!(
ZX_OBJECT_WRITABLE.bits() as usize == ready_as_usize(Ready::writable()),
"Zircon ZX_OBJECT_WRITABLE should have the same repr as Ready::writable()"
);
}
/// Fuchsia specific extensions to `Ready`
///
/// Provides additional readiness event kinds that are available on Fuchsia.
///
/// Conversion traits are implemented between `Ready` and `FuchsiaReady`.
///
/// For high level documentation on polling and readiness, see [`Poll`].
///
/// [`Poll`]: struct.Poll.html
#[derive(Debug, Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct FuchsiaReady(Ready);
impl FuchsiaReady {
/// Returns the `FuchsiaReady` as raw zircon signals.
/// This function is just a more explicit, non-generic version of
/// `FuchsiaReady::into`.
#[inline]
pub fn into_zx_signals(self) -> zx_signals_t {
zx_signals_t::from_bits_truncate(ready_as_usize(self.0) as u32)
}
}
impl Into<zx_signals_t> for FuchsiaReady {
#[inline]
fn into(self) -> zx_signals_t {
self.into_zx_signals()
}
}
impl From<zx_signals_t> for FuchsiaReady {
#[inline]
fn from(src: zx_signals_t) -> Self {
FuchsiaReady(src.into())
}
}
impl From<zx_signals_t> for Ready {
#[inline]
fn from(src: zx_signals_t) -> Self {
ready_from_usize(src.bits() as usize)
}
}
impl From<Ready> for FuchsiaReady {
#[inline]
fn from(src: Ready) -> FuchsiaReady {
FuchsiaReady(src)
}
}
impl From<FuchsiaReady> for Ready {
#[inline]
fn from(src: FuchsiaReady) -> Ready {
src.0
}
}
impl ops::Deref for FuchsiaReady {
type Target = Ready;
#[inline]
fn deref(&self) -> &Ready {
&self.0
}
}
impl ops::DerefMut for FuchsiaReady {
#[inline]
fn deref_mut(&mut self) -> &mut Ready {
&mut self.0
}
}
impl ops::BitOr for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn bitor(self, other: FuchsiaReady) -> FuchsiaReady {
(self.0 | other.0).into()
}
}
impl ops::BitXor for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn bitxor(self, other: FuchsiaReady) -> FuchsiaReady {
(self.0 ^ other.0).into()
}
}
impl ops::BitAnd for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn bitand(self, other: FuchsiaReady) -> FuchsiaReady {
(self.0 & other.0).into()
}
}
impl ops::Sub for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn sub(self, other: FuchsiaReady) -> FuchsiaReady {
(self.0 & !other.0).into()
}
}
#[deprecated(since = "0.6.10", note = "removed")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
impl ops::Not for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn not(self) -> FuchsiaReady {
(!self.0).into()
}
}
impl ops::BitOr<zx_signals_t> for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn bitor(self, other: zx_signals_t) -> FuchsiaReady {
self | FuchsiaReady::from(other)
}
}
impl ops::BitXor<zx_signals_t> for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn bitxor(self, other: zx_signals_t) -> FuchsiaReady {
self ^ FuchsiaReady::from(other)
}
}
impl ops::BitAnd<zx_signals_t> for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn bitand(self, other: zx_signals_t) -> FuchsiaReady {
self & FuchsiaReady::from(other)
}
}
impl ops::Sub<zx_signals_t> for FuchsiaReady {
type Output = FuchsiaReady;
#[inline]
fn sub(self, other: zx_signals_t) -> FuchsiaReady {
self - FuchsiaReady::from(other)
}
}

353
src/sys/fuchsia/selector.rs Normal file
View File

@ -0,0 +1,353 @@
use {io, Event, PollOpt, Ready, Token};
use sys::fuchsia::{
assert_fuchsia_ready_repr,
epoll_event_to_ready,
poll_opts_to_wait_async,
EventedFd,
EventedFdInner,
FuchsiaReady,
};
use zircon;
use zircon::AsHandleRef;
use zircon_sys::zx_handle_t;
use std::collections::hash_map;
use std::fmt;
use std::mem;
use std::sync::atomic::{AtomicBool, AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use std::sync::{Arc, Mutex, Weak};
use std::time::Duration;
use sys;
/// The kind of registration-- file descriptor or handle.
///
/// The last bit of a token is set to indicate the type of the registration.
#[derive(Copy, Clone, Eq, PartialEq)]
enum RegType {
Fd,
Handle,
}
fn key_from_token_and_type(token: Token, reg_type: RegType) -> io::Result<u64> {
let key = token.0 as u64;
let msb = 1u64 << 63;
if (key & msb) != 0 {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Most-significant bit of token must remain unset."));
}
Ok(match reg_type {
RegType::Fd => key,
RegType::Handle => key | msb,
})
}
fn token_and_type_from_key(key: u64) -> (Token, RegType) {
let msb = 1u64 << 63;
(
Token((key & !msb) as usize),
if (key & msb) == 0 {
RegType::Fd
} else {
RegType::Handle
}
)
}
/// Each Selector has a globally unique(ish) ID associated with it. This ID
/// gets tracked by `TcpStream`, `TcpListener`, etc... when they are first
/// registered with the `Selector`. If a type that is previously associated with
/// a `Selector` attempts to register itself with a different `Selector`, the
/// operation will return with an error. This matches windows behavior.
static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
pub struct Selector {
id: usize,
/// Zircon object on which the handles have been registered, and on which events occur
port: Arc<zircon::Port>,
/// Whether or not `tokens_to_rereg` contains any elements. This is a best-effort attempt
/// used to prevent having to lock `tokens_to_rereg` when it is empty.
has_tokens_to_rereg: AtomicBool,
/// List of `Token`s corresponding to registrations that need to be reregistered before the
/// next `port::wait`. This is necessary to provide level-triggered behavior for
/// `Async::repeating` registrations.
///
/// When a level-triggered `Async::repeating` event is seen, its token is added to this list so
/// that it will be reregistered before the next `port::wait` call, making `port::wait` return
/// immediately if the signal was high during the reregistration.
///
/// Note: when used at the same time, the `tokens_to_rereg` lock should be taken out _before_
/// `token_to_fd`.
tokens_to_rereg: Mutex<Vec<Token>>,
/// Map from tokens to weak references to `EventedFdInner`-- a structure describing a
/// file handle, its associated `fdio` object, and its current registration.
token_to_fd: Mutex<hash_map::HashMap<Token, Weak<EventedFdInner>>>,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
// Assertion from fuchsia/ready.rs to make sure that FuchsiaReady's representation is
// compatible with Ready.
assert_fuchsia_ready_repr();
let port = Arc::new(
zircon::Port::create(zircon::PortOpts::Default)?
);
// offset by 1 to avoid choosing 0 as the id of a selector
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1;
let has_tokens_to_rereg = AtomicBool::new(false);
let tokens_to_rereg = Mutex::new(Vec::new());
let token_to_fd = Mutex::new(hash_map::HashMap::new());
Ok(Selector {
id: id,
port: port,
has_tokens_to_rereg: has_tokens_to_rereg,
tokens_to_rereg: tokens_to_rereg,
token_to_fd: token_to_fd,
})
}
pub fn id(&self) -> usize {
self.id
}
/// Returns a reference to the underlying port `Arc`.
pub fn port(&self) -> &Arc<zircon::Port> { &self.port }
/// Reregisters all registrations pointed to by the `tokens_to_rereg` list
/// if `has_tokens_to_rereg`.
fn reregister_handles(&self) -> io::Result<()> {
// We use `Ordering::Acquire` to make sure that we see all `tokens_to_rereg`
// written before the store using `Ordering::Release`.
if self.has_tokens_to_rereg.load(Ordering::Acquire) {
let mut tokens = self.tokens_to_rereg.lock().unwrap();
let token_to_fd = self.token_to_fd.lock().unwrap();
for token in tokens.drain(0..) {
if let Some(eventedfd) = token_to_fd.get(&token)
.and_then(|h| h.upgrade()) {
eventedfd.rereg_for_level(&self.port);
}
}
self.has_tokens_to_rereg.store(false, Ordering::Release);
}
Ok(())
}
pub fn select(&self,
evts: &mut Events,
_awakener: Token,
timeout: Option<Duration>) -> io::Result<bool>
{
evts.clear();
self.reregister_handles()?;
let deadline = match timeout {
Some(duration) => {
let nanos = duration.as_secs().saturating_mul(1_000_000_000)
.saturating_add(duration.subsec_nanos() as u64);
zircon::deadline_after(nanos)
}
None => zircon::ZX_TIME_INFINITE,
};
let packet = match self.port.wait(deadline) {
Ok(packet) => packet,
Err(zircon::Status::ErrTimedOut) => return Ok(false),
Err(e) => Err(e)?,
};
let observed_signals = match packet.contents() {
zircon::PacketContents::SignalOne(signal_packet) => {
signal_packet.observed()
}
zircon::PacketContents::SignalRep(signal_packet) => {
signal_packet.observed()
}
zircon::PacketContents::User(_user_packet) => {
// User packets are only ever sent by an Awakener
return Ok(true);
}
};
let key = packet.key();
let (token, reg_type) = token_and_type_from_key(key);
match reg_type {
RegType::Handle => {
// We can return immediately-- no lookup or registration necessary.
evts.events.push(Event::new(Ready::from(observed_signals), token));
Ok(false)
},
RegType::Fd => {
// Convert the signals to epoll events using __fdio_wait_end,
// and add to reregistration list if necessary.
let events: u32;
{
let handle = if let Some(handle) =
self.token_to_fd.lock().unwrap()
.get(&token)
.and_then(|h| h.upgrade()) {
handle
} else {
// This handle is apparently in the process of removal.
// It has been removed from the list, but port_cancel has not been called.
return Ok(false);
};
events = unsafe {
let mut events: u32 = mem::uninitialized();
sys::fuchsia::sys::__fdio_wait_end(handle.fdio(), observed_signals, &mut events);
events
};
// If necessary, queue to be reregistered before next port_await
let needs_to_rereg = {
let registration_lock = handle.registration().lock().unwrap();
registration_lock
.as_ref()
.and_then(|r| r.rereg_signals())
.is_some()
};
if needs_to_rereg {
let mut tokens_to_rereg_lock = self.tokens_to_rereg.lock().unwrap();
tokens_to_rereg_lock.push(token);
// We use `Ordering::Release` to make sure that we see all `tokens_to_rereg`
// written before the store.
self.has_tokens_to_rereg.store(true, Ordering::Release);
}
}
evts.events.push(Event::new(epoll_event_to_ready(events), token));
Ok(false)
},
}
}
/// Register event interests for the given IO handle with the OS
pub fn register_fd(&self,
handle: &zircon::Handle,
fd: &EventedFd,
token: Token,
signals: zircon::Signals,
poll_opts: PollOpt) -> io::Result<()>
{
{
let mut token_to_fd = self.token_to_fd.lock().unwrap();
match token_to_fd.entry(token) {
hash_map::Entry::Occupied(_) =>
return Err(io::Error::new(io::ErrorKind::AlreadyExists,
"Attempted to register a filedescriptor on an existing token.")),
hash_map::Entry::Vacant(slot) => slot.insert(Arc::downgrade(&fd.inner)),
};
}
let wait_async_opts = poll_opts_to_wait_async(poll_opts);
let wait_res = handle.wait_async_handle(&self.port, token.0 as u64, signals, wait_async_opts);
if wait_res.is_err() {
self.token_to_fd.lock().unwrap().remove(&token);
}
Ok(wait_res?)
}
/// Deregister event interests for the given IO handle with the OS
pub fn deregister_fd(&self, handle: &zircon::Handle, token: Token) -> io::Result<()> {
self.token_to_fd.lock().unwrap().remove(&token);
// We ignore NotFound errors since oneshots are automatically deregistered,
// but mio will attempt to deregister them manually.
self.port.cancel(&*handle, token.0 as u64)
.map_err(io::Error::from)
.or_else(|e| if e.kind() == io::ErrorKind::NotFound {
Ok(())
} else {
Err(e)
})
}
pub fn register_handle(&self,
handle: zx_handle_t,
token: Token,
interests: Ready,
poll_opts: PollOpt) -> io::Result<()>
{
if poll_opts.is_level() && !poll_opts.is_oneshot() {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"Repeated level-triggered events are not supported on Fuchsia handles."));
}
let temp_handle = unsafe { zircon::Handle::from_raw(handle) };
let res = temp_handle.wait_async_handle(
&self.port,
key_from_token_and_type(token, RegType::Handle)?,
FuchsiaReady::from(interests).into_zx_signals(),
poll_opts_to_wait_async(poll_opts));
mem::forget(temp_handle);
Ok(res?)
}
pub fn deregister_handle(&self, handle: zx_handle_t, token: Token) -> io::Result<()>
{
let temp_handle = unsafe { zircon::Handle::from_raw(handle) };
let res = self.port.cancel(&temp_handle, key_from_token_and_type(token, RegType::Handle)?);
mem::forget(temp_handle);
Ok(res?)
}
}
pub struct Events {
events: Vec<Event>
}
impl Events {
pub fn with_capacity(_u: usize) -> Events {
// The Fuchsia selector only handles one event at a time,
// so we ignore the default capacity and set it to one.
Events { events: Vec::with_capacity(1) }
}
pub fn len(&self) -> usize {
self.events.len()
}
pub fn capacity(&self) -> usize {
self.events.capacity()
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
pub fn get(&self, idx: usize) -> Option<Event> {
self.events.get(idx).map(|e| *e)
}
pub fn push_event(&mut self, event: Event) {
self.events.push(event)
}
pub fn clear(&mut self) {
self.events.events.drain(0..);
}
}
impl fmt::Debug for Events {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Events")
.field("len", &self.len())
.finish()
}
}

View File

@ -1,20 +1,56 @@
//! Module with system specific types.
//!
//! `Event`: a type alias for the system specific event, e.g.
//! `kevent` or `epoll_event`.
//! `event`: a module with various helper functions for `Event`, see
//! `crate::event::Event` for the required functions.
#[cfg(unix)]
#[cfg(all(unix, not(target_os = "fuchsia")))]
pub use self::unix::{
event, Event, Events, Selector, SourceFd, TcpListener, TcpStream, UdpSocket, Waker,
Awakener,
EventedFd,
Events,
Io,
Selector,
TcpStream,
TcpListener,
UdpSocket,
pipe,
set_nonblock,
};
#[cfg(unix)]
mod unix;
#[cfg(all(unix, not(target_os = "fuchsia")))]
pub use self::unix::READY_ALL;
#[cfg(all(unix, not(target_os = "fuchsia")))]
#[cfg(feature = "with-deprecated")]
pub use self::unix::UnixSocket;
#[cfg(all(unix, not(target_os = "fuchsia")))]
pub mod unix;
#[cfg(windows)]
pub use self::windows::{event, Event, Events, Selector, TcpListener, TcpStream, UdpSocket, Waker};
pub use self::windows::{
Awakener,
Events,
Selector,
TcpStream,
TcpListener,
UdpSocket,
Overlapped,
Binding,
};
#[cfg(windows)]
mod windows;
#[cfg(target_os = "fuchsia")]
pub use self::fuchsia::{
Awakener,
Events,
EventedHandle,
Selector,
TcpStream,
TcpListener,
UdpSocket,
set_nonblock,
};
#[cfg(target_os = "fuchsia")]
pub mod fuchsia;
#[cfg(not(all(unix, not(target_os = "fuchsia"))))]
pub const READY_ALL: usize = 0;

74
src/sys/unix/awakener.rs Normal file
View File

@ -0,0 +1,74 @@
pub use self::pipe::Awakener;
/// Default awakener backed by a pipe
mod pipe {
use sys::unix;
use {io, Ready, Poll, PollOpt, Token};
use event::Evented;
use std::io::{Read, Write};
/*
*
* ===== Awakener =====
*
*/
pub struct Awakener {
reader: unix::Io,
writer: unix::Io,
}
impl Awakener {
pub fn new() -> io::Result<Awakener> {
let (rd, wr) = unix::pipe()?;
Ok(Awakener {
reader: rd,
writer: wr,
})
}
pub fn wakeup(&self) -> io::Result<()> {
match (&self.writer).write(&[1]) {
Ok(_) => Ok(()),
Err(e) => {
if e.kind() == io::ErrorKind::WouldBlock {
Ok(())
} else {
Err(e)
}
}
}
}
pub fn cleanup(&self) {
let mut buf = [0; 128];
loop {
// Consume data until all bytes are purged
match (&self.reader).read(&mut buf) {
Ok(i) if i > 0 => {},
_ => return,
}
}
}
fn reader(&self) -> &unix::Io {
&self.reader
}
}
impl Evented for Awakener {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.reader().register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.reader().reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.reader().deregister(poll)
}
}
}

47
src/sys/unix/dlsym.rs Normal file
View File

@ -0,0 +1,47 @@
use std::marker;
use std::mem;
use std::sync::atomic::{AtomicUsize, Ordering};
use libc;
macro_rules! dlsym {
(fn $name:ident($($t:ty),*) -> $ret:ty) => (
#[allow(bad_style)]
static $name: ::sys::unix::dlsym::DlSym<unsafe extern fn($($t),*) -> $ret> =
::sys::unix::dlsym::DlSym {
name: concat!(stringify!($name), "\0"),
addr: ::std::sync::atomic::ATOMIC_USIZE_INIT,
_marker: ::std::marker::PhantomData,
};
)
}
pub struct DlSym<F> {
pub name: &'static str,
pub addr: AtomicUsize,
pub _marker: marker::PhantomData<F>,
}
impl<F> DlSym<F> {
pub fn get(&self) -> Option<&F> {
assert_eq!(mem::size_of::<F>(), mem::size_of::<usize>());
unsafe {
if self.addr.load(Ordering::SeqCst) == 0 {
self.addr.store(fetch(self.name), Ordering::SeqCst);
}
if self.addr.load(Ordering::SeqCst) == 1 {
None
} else {
mem::transmute::<&AtomicUsize, Option<&F>>(&self.addr)
}
}
}
}
unsafe fn fetch(name: &str) -> usize {
assert_eq!(name.as_bytes()[name.len() - 1], 0);
match libc::dlsym(libc::RTLD_DEFAULT, name.as_ptr() as *const _) as usize {
0 => 1,
n => n,
}
}

View File

@ -1,169 +1,260 @@
use crate::sys::Events;
use crate::{Interests, Token};
use libc::{EPOLLET, EPOLLIN, EPOLLOUT};
use log::error;
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering};
#![allow(deprecated)]
use std::os::unix::io::AsRawFd;
use std::os::unix::io::RawFd;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::time::Duration;
use std::{cmp, i32, io, ptr};
use std::{cmp, i32};
/// Unique id for use as `SelectorId`.
#[cfg(debug_assertions)]
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
use libc::{self, c_int};
use libc::{EPOLLERR, EPOLLHUP, EPOLLONESHOT};
use libc::{EPOLLET, EPOLLOUT, EPOLLIN, EPOLLPRI};
use {io, Ready, PollOpt, Token};
use event_imp::Event;
use sys::unix::{cvt, UnixReady};
use sys::unix::io::set_cloexec;
/// Each Selector has a globally unique(ish) ID associated with it. This ID
/// gets tracked by `TcpStream`, `TcpListener`, etc... when they are first
/// registered with the `Selector`. If a type that is previously associated with
/// a `Selector` attempts to register itself with a different `Selector`, the
/// operation will return with an error. This matches windows behavior.
static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
#[derive(Debug)]
pub struct Selector {
#[cfg(debug_assertions)]
id: usize,
ep: RawFd,
epfd: RawFd,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
// According to libuv `EPOLL_CLOEXEC` is not defined on Android API <
// 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on all platforms,
// so we use that instead.
syscall!(epoll_create1(libc::O_CLOEXEC)).map(|ep| Selector {
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
ep,
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
}
}
};
// offset by 1 to avoid choosing 0 as the id of a selector
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1;
Ok(Selector {
id: id,
epfd: epfd,
})
}
#[cfg(debug_assertions)]
pub fn id(&self) -> usize {
self.id
}
pub fn try_clone(&self) -> io::Result<Selector> {
syscall!(dup(self.ep)).map(|ep| Selector {
// It's the same selector, so we use the same id.
#[cfg(debug_assertions)]
id: self.id,
ep,
})
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
let timeout = timeout
.map(|to| cmp::min(to.as_millis(), libc::c_int::max_value() as u128) as libc::c_int)
/// Wait for events from the OS
pub fn select(&self, evts: &mut Events, awakener: Token, timeout: Option<Duration>) -> io::Result<bool> {
let timeout_ms = timeout
.map(|to| cmp::min(millis(to), i32::MAX as u64) as i32)
.unwrap_or(-1);
events.clear();
syscall!(epoll_wait(
self.ep,
events.as_mut_ptr(),
events.capacity() as i32,
timeout,
))
.map(|n_events| {
// This is safe because `epoll_wait` ensures that `n_events` are
// assigned.
unsafe { events.set_len(n_events as usize) };
})
// Wait for epoll events for at most timeout_ms milliseconds
evts.clear();
unsafe {
let cnt = cvt(libc::epoll_wait(self.epfd,
evts.events.as_mut_ptr(),
evts.events.capacity() as i32,
timeout_ms))?;
let cnt = cnt as usize;
evts.events.set_len(cnt);
for i in 0..cnt {
if evts.events[i].u64 as usize == awakener.into() {
evts.events.remove(i);
return Ok(true);
}
}
}
Ok(false)
}
pub fn register(&self, fd: RawFd, token: Token, interests: Interests) -> io::Result<()> {
let mut event = libc::epoll_event {
events: interests_to_epoll(interests),
u64: usize::from(token) as u64,
/// Register event interests for the given IO handle with the OS
pub fn register(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
let mut info = libc::epoll_event {
events: ioevent_to_epoll(interests, opts),
u64: usize::from(token) as u64
};
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_ADD, fd, &mut event)).map(|_| ())
unsafe {
cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, fd, &mut info))?;
Ok(())
}
}
pub fn reregister(&self, fd: RawFd, token: Token, interests: Interests) -> io::Result<()> {
let mut event = libc::epoll_event {
events: interests_to_epoll(interests),
u64: usize::from(token) as u64,
/// Register event interests for the given IO handle with the OS
pub fn reregister(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
let mut info = libc::epoll_event {
events: ioevent_to_epoll(interests, opts),
u64: usize::from(token) as u64
};
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_MOD, fd, &mut event)).map(|_| ())
unsafe {
cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_MOD, fd, &mut info))?;
Ok(())
}
}
/// Deregister event interests for the given IO handle with the OS
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ())
// The &info argument should be ignored by the system,
// but linux < 2.6.9 required it to be not null.
// For compatibility, we provide a dummy EpollEvent.
let mut info = libc::epoll_event {
events: 0,
u64: 0,
};
unsafe {
cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_DEL, fd, &mut info))?;
Ok(())
}
}
}
fn interests_to_epoll(interests: Interests) -> u32 {
let mut kind = EPOLLET;
fn ioevent_to_epoll(interest: Ready, opts: PollOpt) -> u32 {
let mut kind = 0;
if interests.is_readable() {
if interest.is_readable() {
kind |= EPOLLIN;
}
if interests.is_writable() {
if interest.is_writable() {
kind |= EPOLLOUT;
}
if UnixReady::from(interest).is_priority() {
kind |= EPOLLPRI;
}
if opts.is_edge() {
kind |= EPOLLET;
}
if opts.is_oneshot() {
kind |= EPOLLONESHOT;
}
if opts.is_level() {
kind &= !EPOLLET;
}
kind as u32
}
impl AsRawFd for Selector {
fn as_raw_fd(&self) -> RawFd {
self.ep
self.epfd
}
}
impl Drop for Selector {
fn drop(&mut self) {
if let Err(err) = syscall!(close(self.ep)) {
error!("error closing epoll: {}", err);
unsafe {
let _ = libc::close(self.epfd);
}
}
}
pub type Event = libc::epoll_event;
pub struct Events {
events: Vec<libc::epoll_event>,
}
pub mod event {
use crate::sys::Event;
use crate::Token;
pub fn token(event: &Event) -> Token {
Token(event.u64 as usize)
impl Events {
pub fn with_capacity(u: usize) -> Events {
Events {
events: Vec::with_capacity(u)
}
}
pub fn is_readable(event: &Event) -> bool {
(event.events as libc::c_int & libc::EPOLLIN) != 0
|| (event.events as libc::c_int & libc::EPOLLPRI) != 0
#[inline]
pub fn len(&self) -> usize {
self.events.len()
}
pub fn is_writable(event: &Event) -> bool {
(event.events as libc::c_int & libc::EPOLLOUT) != 0
#[inline]
pub fn capacity(&self) -> usize {
self.events.capacity()
}
pub fn is_error(event: &Event) -> bool {
(event.events as libc::c_int & libc::EPOLLERR) != 0
#[inline]
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
pub fn is_hup(event: &Event) -> bool {
(event.events as libc::c_int & libc::EPOLLHUP) != 0
#[inline]
pub fn get(&self, idx: usize) -> Option<Event> {
self.events.get(idx).map(|event| {
let epoll = event.events as c_int;
let mut kind = Ready::empty();
if (epoll & EPOLLIN) != 0 {
kind = kind | Ready::readable();
}
if (epoll & EPOLLPRI) != 0 {
kind = kind | Ready::readable() | UnixReady::priority();
}
if (epoll & EPOLLOUT) != 0 {
kind = kind | Ready::writable();
}
// EPOLLHUP - Usually means a socket error happened
if (epoll & EPOLLERR) != 0 {
kind = kind | UnixReady::error();
}
if (epoll & EPOLLHUP) != 0 {
kind = kind | UnixReady::hup();
}
let token = self.events[idx].u64;
Event::new(kind, Token(token as usize))
})
}
pub fn is_read_hup(event: &Event) -> bool {
(event.events as libc::c_int & libc::EPOLLRDHUP) != 0
pub fn push_event(&mut self, event: Event) {
self.events.push(libc::epoll_event {
events: ioevent_to_epoll(event.readiness(), PollOpt::empty()),
u64: usize::from(event.token()) as u64
});
}
pub fn is_priority(event: &Event) -> bool {
(event.events as libc::c_int & libc::EPOLLPRI) != 0
}
pub fn is_aio(_: &Event) -> bool {
// Not supported in the kernel, only in libc.
false
}
pub fn is_lio(_: &Event) -> bool {
// Not supported.
false
pub fn clear(&mut self) {
unsafe { self.events.set_len(0); }
}
}
#[test]
fn assert_close_on_exec_flag() {
// This assertion need to be true for Selector::new.
assert_eq!(libc::O_CLOEXEC, libc::EPOLL_CLOEXEC);
const NANOS_PER_MILLI: u32 = 1_000_000;
const MILLIS_PER_SEC: u64 = 1_000;
/// Convert a `Duration` to milliseconds, rounding up and saturating at
/// `u64::MAX`.
///
/// The saturating is fine because `u64::MAX` milliseconds are still many
/// million years.
pub fn millis(duration: Duration) -> u64 {
// Round up.
let millis = (duration.subsec_nanos() + NANOS_PER_MILLI - 1) / NANOS_PER_MILLI;
duration.as_secs().saturating_mul(MILLIS_PER_SEC).saturating_add(millis as u64)
}

107
src/sys/unix/eventedfd.rs Normal file
View File

@ -0,0 +1,107 @@
use {io, poll, Ready, Poll, PollOpt, Token};
use event::Evented;
use std::os::unix::io::RawFd;
/*
*
* ===== EventedFd =====
*
*/
#[derive(Debug)]
/// Adapter for [`RawFd`] providing an [`Evented`] implementation.
///
/// `EventedFd` enables registering any type with an FD with [`Poll`].
///
/// While only implementations for TCP and UDP are provided, Mio supports
/// registering any FD that can be registered with the underlying OS selector.
/// `EventedFd` provides the necessary bridge.
///
/// Note that `EventedFd` takes a `&RawFd`. This is because `EventedFd` **does
/// not** take ownership of the FD. Specifically, it will not manage any
/// lifecycle related operations, such as closing the FD on drop. It is expected
/// that the `EventedFd` is constructed right before a call to
/// [`Poll::register`]. See the examples for more detail.
///
/// # Examples
///
/// Basic usage
///
/// ```
/// # use std::error::Error;
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::{Ready, Poll, PollOpt, Token};
/// use mio::unix::EventedFd;
///
/// use std::os::unix::io::AsRawFd;
/// use std::net::TcpListener;
///
/// // Bind a std listener
/// let listener = TcpListener::bind("127.0.0.1:0")?;
///
/// let poll = Poll::new()?;
///
/// // Register the listener
/// poll.register(&EventedFd(&listener.as_raw_fd()),
/// Token(0), Ready::readable(), PollOpt::edge())?;
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
///
/// Implementing [`Evented`] for a custom type backed by a [`RawFd`].
///
/// ```
/// use mio::{Ready, Poll, PollOpt, Token};
/// use mio::event::Evented;
/// use mio::unix::EventedFd;
///
/// use std::os::unix::io::RawFd;
/// use std::io;
///
/// pub struct MyIo {
/// fd: RawFd,
/// }
///
/// impl Evented for MyIo {
/// fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
/// -> io::Result<()>
/// {
/// EventedFd(&self.fd).register(poll, token, interest, opts)
/// }
///
/// fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt)
/// -> io::Result<()>
/// {
/// EventedFd(&self.fd).reregister(poll, token, interest, opts)
/// }
///
/// fn deregister(&self, poll: &Poll) -> io::Result<()> {
/// EventedFd(&self.fd).deregister(poll)
/// }
/// }
/// ```
///
/// [`RawFd`]: https://doc.rust-lang.org/std/os/unix/io/type.RawFd.html
/// [`Evented`]: ../event/trait.Evented.html
/// [`Poll`]: ../struct.Poll.html
/// [`Poll::register`]: ../struct.Poll.html#method.register
pub struct EventedFd<'a>(pub &'a RawFd);
impl<'a> Evented for EventedFd<'a> {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
poll::selector(poll).register(*self.0, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
poll::selector(poll).reregister(*self.0, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
poll::selector(poll).deregister(*self.0)
}
}

107
src/sys/unix/io.rs Normal file
View File

@ -0,0 +1,107 @@
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::io::{IntoRawFd, AsRawFd, FromRawFd, RawFd};
use libc;
use {io, Ready, Poll, PollOpt, Token};
use event::Evented;
use unix::EventedFd;
use sys::unix::cvt;
pub fn set_nonblock(fd: libc::c_int) -> io::Result<()> {
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFL);
cvt(libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK)).map(|_|())
}
}
pub fn set_cloexec(fd: libc::c_int) -> io::Result<()> {
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFD);
cvt(libc::fcntl(fd, libc::F_SETFD, flags | libc::FD_CLOEXEC)).map(|_| ())
}
}
/*
*
* ===== Basic IO type =====
*
*/
/// Manages a FD
#[derive(Debug)]
pub struct Io {
fd: File,
}
impl Io {
/// Try to clone the FD
pub fn try_clone(&self) -> io::Result<Io> {
Ok(Io { fd: self.fd.try_clone()? })
}
}
impl FromRawFd for Io {
unsafe fn from_raw_fd(fd: RawFd) -> Io {
Io { fd: File::from_raw_fd(fd) }
}
}
impl IntoRawFd for Io {
fn into_raw_fd(self) -> RawFd {
self.fd.into_raw_fd()
}
}
impl AsRawFd for Io {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}
impl Evented for Io {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).deregister(poll)
}
}
impl Read for Io {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
(&self.fd).read(dst)
}
}
impl<'a> Read for &'a Io {
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
(&self.fd).read(dst)
}
}
impl Write for Io {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
(&self.fd).write(src)
}
fn flush(&mut self) -> io::Result<()> {
(&self.fd).flush()
}
}
impl<'a> Write for &'a Io {
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
(&self.fd).write(src)
}
fn flush(&mut self) -> io::Result<()> {
(&self.fd).flush()
}
}

View File

@ -1,293 +1,212 @@
use crate::sys::Events;
use crate::{Interests, Token};
use log::error;
use std::mem::MaybeUninit;
use std::os::unix::io::{AsRawFd, RawFd};
#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
use std::{cmp, io, ptr, slice};
/// Unique id for use as `SelectorId`.
#[cfg(debug_assertions)]
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
// Type of the `nchanges` and `nevents` parameters in the `kevent` function.
use std::{cmp, fmt, ptr};
#[cfg(not(target_os = "netbsd"))]
type Count = libc::c_int;
#[cfg(target_os = "netbsd")]
type Count = libc::size_t;
use std::os::raw::{c_int, c_short};
use std::os::unix::io::AsRawFd;
use std::os::unix::io::RawFd;
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
use std::time::Duration;
use libc::{self, time_t};
use {io, Ready, PollOpt, Token};
use event_imp::{self as event, Event};
use sys::unix::{cvt, UnixReady};
use sys::unix::io::set_cloexec;
/// Each Selector has a globally unique(ish) ID associated with it. This ID
/// gets tracked by `TcpStream`, `TcpListener`, etc... when they are first
/// registered with the `Selector`. If a type that is previously associated with
/// a `Selector` attempts to register itself with a different `Selector`, the
/// operation will return with an error. This matches windows behavior.
static NEXT_ID: AtomicUsize = ATOMIC_USIZE_INIT;
#[cfg(not(target_os = "netbsd"))]
type Filter = c_short;
#[cfg(not(target_os = "netbsd"))]
type UData = *mut ::libc::c_void;
#[cfg(not(target_os = "netbsd"))]
type Count = c_int;
// Type of the `filter` field in the `kevent` structure.
#[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
type Filter = libc::c_short;
#[cfg(any(target_os = "macos", target_os = "ios"))]
type Filter = i16;
#[cfg(target_os = "netbsd")]
type Filter = u32;
// Type of the `data` field in the `kevent` structure.
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"
))]
type Data = libc::intptr_t;
#[cfg(any(target_os = "bitrig", target_os = "netbsd", target_os = "openbsd",))]
type Data = i64;
// Type of the `udata` field in the `kevent` structure.
#[cfg(not(target_os = "netbsd"))]
type UData = *mut libc::c_void;
#[cfg(target_os = "netbsd")]
type UData = libc::intptr_t;
type UData = ::libc::intptr_t;
#[cfg(target_os = "netbsd")]
type Count = usize;
macro_rules! kevent {
($id: expr, $filter: expr, $flags: expr, $data: expr) => {
libc::kevent {
ident: $id as libc::uintptr_t,
ident: $id as ::libc::uintptr_t,
filter: $filter as Filter,
flags: $flags,
fflags: 0,
data: 0,
udata: $data as UData,
}
};
}
}
#[derive(Debug)]
pub struct Selector {
#[cfg(debug_assertions)]
id: usize,
kq: RawFd,
}
impl Selector {
pub fn new() -> io::Result<Selector> {
syscall!(kqueue())
.and_then(|kq| syscall!(fcntl(kq, libc::F_SETFD, libc::FD_CLOEXEC)).map(|_| kq))
.map(|kq| Selector {
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
kq,
})
}
// offset by 1 to avoid choosing 0 as the id of a selector
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1;
let kq = unsafe { cvt(libc::kqueue())? };
drop(set_cloexec(kq));
#[cfg(debug_assertions)]
pub fn id(&self) -> usize {
self.id
}
pub fn try_clone(&self) -> io::Result<Selector> {
syscall!(dup(self.kq)).map(|kq| Selector {
// It's the same selector, so we use the same id.
#[cfg(debug_assertions)]
id: self.id,
Ok(Selector {
id,
kq,
})
}
pub fn select(&self, events: &mut Events, timeout: Option<Duration>) -> io::Result<()> {
let timeout = timeout.map(|to| libc::timespec {
tv_sec: cmp::min(to.as_secs(), libc::time_t::max_value() as u64) as libc::time_t,
// `Duration::subsec_nanos` is guaranteed to be less than one
// billion (the number of nanoseconds in a second), making the
// cast to i32 safe. The cast itself is needed for platforms
// where C's long is only 32 bits.
tv_nsec: libc::c_long::from(to.subsec_nanos() as i32),
pub fn id(&self) -> usize {
self.id
}
pub fn select(&self, evts: &mut Events, awakener: Token, timeout: Option<Duration>) -> io::Result<bool> {
let timeout = timeout.map(|to| {
libc::timespec {
tv_sec: cmp::min(to.as_secs(), time_t::max_value() as u64) as time_t,
// `Duration::subsec_nanos` is guaranteed to be less than one
// billion (the number of nanoseconds in a second), making the
// cast to i32 safe. The cast itself is needed for platforms
// where C's long is only 32 bits.
tv_nsec: libc::c_long::from(to.subsec_nanos() as i32),
}
});
let timeout = timeout
.as_ref()
.map(|s| s as *const _)
.unwrap_or(ptr::null_mut());
let timeout = timeout.as_ref().map(|s| s as *const _).unwrap_or(ptr::null_mut());
events.clear();
syscall!(kevent(
self.kq,
ptr::null(),
0,
events.as_mut_ptr(),
events.capacity() as Count,
timeout,
))
.map(|n_events| {
// This is safe because `kevent` ensures that `n_events` are
// assigned.
unsafe { events.set_len(n_events as usize) };
})
evts.clear();
unsafe {
let cnt = cvt(libc::kevent(self.kq,
ptr::null(),
0,
evts.sys_events.0.as_mut_ptr(),
evts.sys_events.0.capacity() as Count,
timeout))?;
evts.sys_events.0.set_len(cnt as usize);
Ok(evts.coalesce(awakener))
}
}
pub fn register(&self, fd: RawFd, token: Token, interests: Interests) -> io::Result<()> {
let flags = libc::EV_CLEAR | libc::EV_RECEIPT | libc::EV_ADD;
// At most we need two changes, but maybe we only need 1.
let mut changes: [MaybeUninit<libc::kevent>; 2] =
[MaybeUninit::uninit(), MaybeUninit::uninit()];
let mut n_changes = 0;
pub fn register(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
trace!("registering; token={:?}; interests={:?}", token, interests);
if interests.is_writable() {
let kevent = kevent!(fd, libc::EVFILT_WRITE, flags, token.0);
changes[n_changes] = MaybeUninit::new(kevent);
n_changes += 1;
let flags = if opts.contains(PollOpt::edge()) { libc::EV_CLEAR } else { 0 } |
if opts.contains(PollOpt::oneshot()) { libc::EV_ONESHOT } else { 0 } |
libc::EV_RECEIPT;
unsafe {
let r = if interests.contains(Ready::readable()) { libc::EV_ADD } else { libc::EV_DELETE };
let w = if interests.contains(Ready::writable()) { libc::EV_ADD } else { libc::EV_DELETE };
let mut changes = [
kevent!(fd, libc::EVFILT_READ, flags | r, usize::from(token)),
kevent!(fd, libc::EVFILT_WRITE, flags | w, usize::from(token)),
];
cvt(libc::kevent(self.kq,
changes.as_ptr(),
changes.len() as Count,
changes.as_mut_ptr(),
changes.len() as Count,
::std::ptr::null()))?;
for change in changes.iter() {
debug_assert_eq!(change.flags & libc::EV_ERROR, libc::EV_ERROR);
// Test to see if an error happened
if change.data == 0 {
continue
}
// Older versions of OSX (10.11 and 10.10 have been witnessed)
// can return EPIPE when registering a pipe file descriptor
// where the other end has already disappeared. For example code
// that creates a pipe, closes a file descriptor, and then
// registers the other end will see an EPIPE returned from
// `register`.
//
// It also turns out that kevent will still report events on the
// file descriptor, telling us that it's readable/hup at least
// after we've done this registration. As a result we just
// ignore `EPIPE` here instead of propagating it.
//
// More info can be found at carllerche/mio#582
if change.data as i32 == libc::EPIPE &&
change.filter == libc::EVFILT_WRITE as Filter {
continue
}
// ignore ENOENT error for EV_DELETE
let orig_flags = if change.filter == libc::EVFILT_READ as Filter { r } else { w };
if change.data as i32 == libc::ENOENT && orig_flags & libc::EV_DELETE != 0 {
continue
}
return Err(::std::io::Error::from_raw_os_error(change.data as i32));
}
Ok(())
}
if interests.is_readable() {
let kevent = kevent!(fd, libc::EVFILT_READ, flags, token.0);
changes[n_changes] = MaybeUninit::new(kevent);
n_changes += 1;
}
// Older versions of macOS (OS X 10.11 and 10.10 have been witnessed)
// can return EPIPE when registering a pipe file descriptor where the
// other end has already disappeared. For example code that creates a
// pipe, closes a file descriptor, and then registers the other end will
// see an EPIPE returned from `register`.
//
// It also turns out that kevent will still report events on the file
// descriptor, telling us that it's readable/hup at least after we've
// done this registration. As a result we just ignore `EPIPE` here
// instead of propagating it.
//
// More info can be found at tokio-rs/mio#582.
let changes = unsafe {
// This is safe because we ensure that at least `n_changes` are in
// the array.
slice::from_raw_parts_mut(changes[0].as_mut_ptr(), n_changes)
};
kevent_register(self.kq, changes, &[libc::EPIPE as Data])
}
pub fn reregister(&self, fd: RawFd, token: Token, interests: Interests) -> io::Result<()> {
let flags = libc::EV_CLEAR | libc::EV_RECEIPT;
let write_flags = if interests.is_writable() {
flags | libc::EV_ADD
} else {
flags | libc::EV_DELETE
};
let read_flags = if interests.is_readable() {
flags | libc::EV_ADD
} else {
flags | libc::EV_DELETE
};
let mut changes: [libc::kevent; 2] = [
kevent!(fd, libc::EVFILT_WRITE, write_flags, token.0),
kevent!(fd, libc::EVFILT_READ, read_flags, token.0),
];
// Since there is no way to check with which interests the fd was
// registered we modify both readable and write, adding it when required
// and removing it otherwise, ignoring the ENOENT error when it comes
// up. The ENOENT error informs us that a filter we're trying to remove
// wasn't there in first place, but we don't really care since our goal
// is accomplished.
//
// For the explanation of ignoring `EPIPE` see `register`.
kevent_register(
self.kq,
&mut changes,
&[libc::ENOENT as Data, libc::EPIPE as Data],
)
pub fn reregister(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
// Just need to call register here since EV_ADD is a mod if already
// registered
self.register(fd, token, interests, opts)
}
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
let flags = libc::EV_DELETE | libc::EV_RECEIPT;
let mut changes: [libc::kevent; 2] = [
kevent!(fd, libc::EVFILT_WRITE, flags, 0),
kevent!(fd, libc::EVFILT_READ, flags, 0),
];
unsafe {
// EV_RECEIPT is a nice way to apply changes and get back per-event results while not
// draining the actual changes.
let filter = libc::EV_DELETE | libc::EV_RECEIPT;
#[cfg(not(target_os = "netbsd"))]
let mut changes = [
kevent!(fd, libc::EVFILT_READ, filter, ptr::null_mut()),
kevent!(fd, libc::EVFILT_WRITE, filter, ptr::null_mut()),
];
// Since there is no way to check with which interests the fd was
// registered we remove both filters (readable and writeable) and ignore
// the ENOENT error when it comes up. The ENOENT error informs us that
// the filter wasn't there in first place, but we don't really care
// about that since our goal is to remove it.
kevent_register(self.kq, &mut changes, &[libc::ENOENT as Data])
}
#[cfg(target_os = "netbsd")]
let mut changes = [
kevent!(fd, libc::EVFILT_READ, filter, 0),
kevent!(fd, libc::EVFILT_WRITE, filter, 0),
];
// Used by `Waker`.
#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
pub fn setup_waker(&self, token: Token) -> io::Result<()> {
// First attempt to accept user space notifications.
let mut kevent = kevent!(
0,
libc::EVFILT_USER,
libc::EV_ADD | libc::EV_CLEAR | libc::EV_RECEIPT,
token.0
);
cvt(libc::kevent(self.kq,
changes.as_ptr(),
changes.len() as Count,
changes.as_mut_ptr(),
changes.len() as Count,
::std::ptr::null())).map(|_| ())?;
syscall!(kevent(self.kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| {
if (kevent.flags & libc::EV_ERROR) != 0 && kevent.data != 0 {
Err(io::Error::from_raw_os_error(kevent.data as i32))
} else {
Ok(())
if changes[0].data as i32 == libc::ENOENT && changes[1].data as i32 == libc::ENOENT {
return Err(::std::io::Error::from_raw_os_error(changes[0].data as i32));
}
})
}
// Used by `Waker`.
#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
pub fn wake(&self, token: Token) -> io::Result<()> {
let mut kevent = kevent!(
0,
libc::EVFILT_USER,
libc::EV_ADD | libc::EV_RECEIPT,
token.0
);
kevent.fflags = libc::NOTE_TRIGGER;
syscall!(kevent(self.kq, &kevent, 1, &mut kevent, 1, ptr::null())).and_then(|_| {
if (kevent.flags & libc::EV_ERROR) != 0 && kevent.data != 0 {
Err(io::Error::from_raw_os_error(kevent.data as i32))
} else {
Ok(())
for change in changes.iter() {
debug_assert_eq!(libc::EV_ERROR & change.flags, libc::EV_ERROR);
if change.data != 0 && change.data as i32 != libc::ENOENT {
return Err(::std::io::Error::from_raw_os_error(changes[0].data as i32));
}
}
})
}
}
/// Register `changes` with `kq`ueue.
fn kevent_register(
kq: RawFd,
changes: &mut [libc::kevent],
ignored_errors: &[Data],
) -> io::Result<()> {
syscall!(kevent(
kq,
changes.as_ptr(),
changes.len() as Count,
changes.as_mut_ptr(),
changes.len() as Count,
ptr::null(),
))
.map(|_| ())
.or_else(|err| {
// According to the manual page of FreeBSD: "When kevent() call fails
// with EINTR error, all changes in the changelist have been applied",
// so we can safely ignore it.
if err.raw_os_error() == Some(libc::EINTR) {
Ok(())
} else {
Err(err)
}
})
.and_then(|()| check_errors(&changes, ignored_errors))
}
/// Check all events for possible errors, it returns the first error found.
fn check_errors(events: &[libc::kevent], ignored_errors: &[Data]) -> io::Result<()> {
for event in events {
// We can't use references to packed structures (in checking the ignored
// errors), so we need copy the data out before use.
let data = event.data;
// Check for the error flag, the actual error will be in the `data`
// field.
if (event.flags & libc::EV_ERROR != 0) && data != 0 && !ignored_errors.contains(&data) {
return Err(io::Error::from_raw_os_error(data as i32));
}
}
Ok(())
}
impl fmt::Debug for Selector {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Selector")
.field("id", &self.id)
.field("kq", &self.kq)
.finish()
}
}
impl AsRawFd for Selector {
@ -298,110 +217,144 @@ impl AsRawFd for Selector {
impl Drop for Selector {
fn drop(&mut self) {
if let Err(err) = syscall!(close(self.kq)) {
error!("error closing kqueue: {}", err);
unsafe {
let _ = libc::close(self.kq);
}
}
}
pub type Event = libc::kevent;
pub struct Events {
sys_events: KeventList,
events: Vec<Event>,
event_map: HashMap<Token, usize>,
}
pub mod event {
use crate::sys::Event;
use crate::Token;
struct KeventList(Vec<libc::kevent>);
pub fn token(event: &Event) -> Token {
Token(event.udata as usize)
unsafe impl Send for KeventList {}
unsafe impl Sync for KeventList {}
impl Events {
pub fn with_capacity(cap: usize) -> Events {
Events {
sys_events: KeventList(Vec::with_capacity(cap)),
events: Vec::with_capacity(cap),
event_map: HashMap::with_capacity(cap)
}
}
pub fn is_readable(event: &Event) -> bool {
event.filter == libc::EVFILT_READ || {
#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
// Used by the `Awakener`. On platforms that use `eventfd` or a unix
// pipe it will emit a readable event so we'll fake that here as
// well.
{
event.filter == libc::EVFILT_USER
#[inline]
pub fn len(&self) -> usize {
self.events.len()
}
#[inline]
pub fn capacity(&self) -> usize {
self.events.capacity()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
pub fn get(&self, idx: usize) -> Option<Event> {
self.events.get(idx).cloned()
}
fn coalesce(&mut self, awakener: Token) -> bool {
let mut ret = false;
self.events.clear();
self.event_map.clear();
for e in self.sys_events.0.iter() {
let token = Token(e.udata as usize);
let len = self.events.len();
if token == awakener {
// TODO: Should this return an error if event is an error. It
// is not critical as spurious wakeups are permitted.
ret = true;
continue;
}
#[cfg(not(any(target_os = "freebsd", target_os = "ios", target_os = "macos")))]
let idx = *self.event_map.entry(token)
.or_insert(len);
if idx == len {
// New entry, insert the default
self.events.push(Event::new(Ready::empty(), token));
}
if e.flags & libc::EV_ERROR != 0 {
event::kind_mut(&mut self.events[idx]).insert(*UnixReady::error());
}
if e.filter == libc::EVFILT_READ as Filter {
event::kind_mut(&mut self.events[idx]).insert(Ready::readable());
} else if e.filter == libc::EVFILT_WRITE as Filter {
event::kind_mut(&mut self.events[idx]).insert(Ready::writable());
}
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos"))]
{
false
if e.filter == libc::EVFILT_AIO {
event::kind_mut(&mut self.events[idx]).insert(UnixReady::aio());
}
}
#[cfg(any(target_os = "freebsd"))]
{
if e.filter == libc::EVFILT_LIO {
event::kind_mut(&mut self.events[idx]).insert(UnixReady::lio());
}
}
}
ret
}
pub fn is_writable(event: &Event) -> bool {
event.filter == libc::EVFILT_WRITE
pub fn push_event(&mut self, event: Event) {
self.events.push(event);
}
pub fn is_error(event: &Event) -> bool {
(event.flags & libc::EV_ERROR) != 0 ||
// When the read end of the socket is closed, EV_EOF is set on
// flags, and fflags contains the error if there is one.
(event.flags & libc::EV_EOF) != 0 && event.fflags != 0
pub fn clear(&mut self) {
self.sys_events.0.truncate(0);
self.events.truncate(0);
self.event_map.clear();
}
}
pub fn is_hup(_: &Event) -> bool {
// Not supported.
false
}
pub fn is_read_hup(event: &Event) -> bool {
event.filter == libc::EVFILT_READ && (event.flags & libc::EV_EOF) != 0
}
pub fn is_priority(_: &Event) -> bool {
// kqueue doesn't have priority indicators.
false
}
#[allow(unused_variables)] // `event` is not used on some platforms.
pub fn is_aio(event: &Event) -> bool {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"
))]
{
event.filter == libc::EVFILT_AIO
}
#[cfg(not(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos"
)))]
{
false
}
}
#[allow(unused_variables)] // `event` is only used on FreeBSD.
pub fn is_lio(event: &Event) -> bool {
#[cfg(target_os = "freebsd")]
{
event.filter == libc::EVFILT_LIO
}
#[cfg(not(target_os = "freebsd"))]
{
false
}
impl fmt::Debug for Events {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Events")
.field("len", &self.sys_events.0.len())
.finish()
}
}
#[test]
fn does_not_register_rw() {
use crate::unix::SourceFd;
use crate::{Poll, Token};
use {Poll, Ready, PollOpt, Token};
use unix::EventedFd;
let kq = unsafe { libc::kqueue() };
let kqf = SourceFd(&kq);
let kqf = EventedFd(&kq);
let poll = Poll::new().unwrap();
// Registering kqueue fd will fail if write is requested (On anything but
// some versions of macOS).
poll.registry()
.register(&kqf, Token(1234), Interests::READABLE)
.unwrap();
// registering kqueue fd will fail if write is requested (On anything but some versions of OS
// X)
poll.register(&kqf, Token(1234), Ready::readable(),
PollOpt::edge() | PollOpt::oneshot()).unwrap();
}
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos"))]
#[test]
fn test_coalesce_aio() {
let mut events = Events::with_capacity(1);
events.sys_events.0.push(kevent!(0x1234, libc::EVFILT_AIO, 0, 42));
events.coalesce(Token(0));
assert!(events.events[0].readiness() == UnixReady::aio().into());
assert!(events.events[0].token() == Token(42));
}

View File

@ -1,56 +1,91 @@
/// Helper macro to execute a system call that returns an `io::Result`.
//
// Macro must be defined before any modules that uses them.
macro_rules! syscall {
($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
let res = unsafe { libc::$fn($($arg, )*) };
if res == -1 {
Err(io::Error::last_os_error())
} else {
Ok(res)
}
}};
}
use libc::{self, c_int};
#[macro_use]
pub mod dlsym;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
mod epoll;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
pub use self::epoll::{event, Event, Selector};
pub use self::epoll::{Events, Selector};
#[cfg(any(
target_os = "bitrig",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
#[cfg(any(target_os = "bitrig", target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos",
target_os = "netbsd", target_os = "openbsd"))]
mod kqueue;
#[cfg(any(
target_os = "bitrig",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd"
))]
pub use self::kqueue::{event, Event, Selector};
#[cfg(any(target_os = "bitrig", target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos",
target_os = "netbsd", target_os = "openbsd"))]
pub use self::kqueue::{Events, Selector};
mod net;
mod sourcefd;
mod tcp_listener;
mod tcp_stream;
mod awakener;
mod eventedfd;
mod io;
mod ready;
mod tcp;
mod udp;
mod waker;
mod uio;
pub use self::sourcefd::SourceFd;
pub use self::tcp_listener::TcpListener;
pub use self::tcp_stream::TcpStream;
#[cfg(feature = "with-deprecated")]
mod uds;
pub use self::awakener::Awakener;
pub use self::eventedfd::EventedFd;
pub use self::io::{Io, set_nonblock};
pub use self::ready::{UnixReady, READY_ALL};
pub use self::tcp::{TcpStream, TcpListener};
pub use self::udp::UdpSocket;
pub use self::waker::Waker;
pub type Events = Vec<Event>;
#[cfg(feature = "with-deprecated")]
pub use self::uds::UnixSocket;
pub use iovec::IoVec;
use std::os::unix::io::FromRawFd;
pub fn pipe() -> ::io::Result<(Io, Io)> {
// Use pipe2 for atomically setting O_CLOEXEC if we can, but otherwise
// just fall back to using `pipe`.
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) => {
cvt(pipe2_fn(pipes.as_mut_ptr(), flags))?;
}
None => {
cvt(libc::pipe(pipes.as_mut_ptr()))?;
libc::fcntl(pipes[0], libc::F_SETFL, flags);
libc::fcntl(pipes[1], libc::F_SETFL, flags);
}
}
}
unsafe {
Ok((Io::from_raw_fd(pipes[0]), Io::from_raw_fd(pipes[1])))
}
}
trait IsMinusOne {
fn is_minus_one(&self) -> bool;
}
impl IsMinusOne for i32 {
fn is_minus_one(&self) -> bool { *self == -1 }
}
impl IsMinusOne for isize {
fn is_minus_one(&self) -> bool { *self == -1 }
}
fn cvt<T: IsMinusOne>(t: T) -> ::io::Result<T> {
use std::io;
if t.is_minus_one() {
Err(io::Error::last_os_error())
} else {
Ok(t)
}
}

View File

@ -1,55 +0,0 @@
use std::io;
use std::mem::size_of_val;
use std::net::SocketAddr;
/// Create a new non-blocking socket.
pub fn new_socket(addr: SocketAddr, socket_type: libc::c_int) -> io::Result<libc::c_int> {
let domain = match addr {
SocketAddr::V4(..) => libc::AF_INET,
SocketAddr::V6(..) => libc::AF_INET6,
};
#[cfg(any(
target_os = "android",
target_os = "bitrig",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd"
))]
let socket_type = socket_type | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC;
// Gives a warning for platforms without SOCK_NONBLOCK.
#[allow(clippy::let_and_return)]
let socket = syscall!(socket(domain, socket_type, 0));
// Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. Not sure about
// Solaris, couldn't find anything online.
#[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))]
let socket = socket.and_then(|socket| {
// For platforms that don't support flags in socket, we need to
// set the flags ourselves.
syscall!(fcntl(
socket,
libc::F_SETFL,
libc::O_NONBLOCK | libc::O_CLOEXEC
))
.map(|_| socket)
});
socket
}
pub fn socket_addr(addr: &SocketAddr) -> (*const libc::sockaddr, libc::socklen_t) {
match addr {
SocketAddr::V4(ref addr) => (
addr as *const _ as *const libc::sockaddr,
size_of_val(addr) as libc::socklen_t,
),
SocketAddr::V6(ref addr) => (
addr as *const _ as *const libc::sockaddr,
size_of_val(addr) as libc::socklen_t,
),
}
}

499
src/sys/unix/ready.rs Normal file
View File

@ -0,0 +1,499 @@
use event_imp::{Ready, ready_as_usize, ready_from_usize};
use std::ops;
use std::fmt;
/// Unix specific extensions to `Ready`
///
/// Provides additional readiness event kinds that are available on unix
/// platforms. Unix platforms are able to provide readiness events for
/// additional socket events, such as HUP and error.
///
/// HUP events occur when the remote end of a socket hangs up. In the TCP case,
/// this occurs when the remote end of a TCP socket shuts down writes.
///
/// Error events occur when the socket enters an error state. In this case, the
/// socket will also receive a readable or writable event. Reading or writing to
/// the socket will result in an error.
///
/// Conversion traits are implemented between `Ready` and `UnixReady`. See the
/// examples.
///
/// For high level documentation on polling and readiness, see [`Poll`].
///
/// # Examples
///
/// Most of the time, all that is needed is using bit operations
///
/// ```
/// use mio::Ready;
/// use mio::unix::UnixReady;
///
/// let ready = Ready::readable() | UnixReady::hup();
///
/// assert!(ready.is_readable());
/// assert!(UnixReady::from(ready).is_hup());
/// ```
///
/// Basic conversion between ready types.
///
/// ```
/// use mio::Ready;
/// use mio::unix::UnixReady;
///
/// // Start with a portable ready
/// let ready = Ready::readable();
///
/// // Convert to a unix ready, adding HUP
/// let mut unix_ready = UnixReady::from(ready) | UnixReady::hup();
///
/// unix_ready.insert(UnixReady::error());
///
/// // `unix_ready` maintains readable interest
/// assert!(unix_ready.is_readable());
/// assert!(unix_ready.is_hup());
/// assert!(unix_ready.is_error());
///
/// // Convert back to `Ready`
/// let ready = Ready::from(unix_ready);
///
/// // Readable is maintained
/// assert!(ready.is_readable());
/// ```
///
/// Registering readable and error interest on a socket
///
/// ```
/// # use std::error::Error;
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::{Ready, Poll, PollOpt, Token};
/// use mio::net::TcpStream;
/// use mio::unix::UnixReady;
///
/// let addr = "216.58.193.68:80".parse()?;
/// let socket = TcpStream::connect(&addr)?;
///
/// let poll = Poll::new()?;
///
/// poll.register(&socket,
/// Token(0),
/// Ready::readable() | UnixReady::error(),
/// PollOpt::edge())?;
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
///
/// [`Poll`]: ../struct.Poll.html
/// [readiness]: struct.Poll.html#readiness-operations
#[derive(Copy, PartialEq, Eq, Clone, PartialOrd, Ord)]
pub struct UnixReady(Ready);
const ERROR: usize = 0b00_0100;
const HUP: usize = 0b00_1000;
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos"))]
const AIO: usize = 0b01_0000;
#[cfg(not(any(target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos")))]
const AIO: usize = 0b00_0000;
#[cfg(any(target_os = "freebsd"))]
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"))]
const PRI: usize = 0b100_0000;
#[cfg(not(any(target_os = "linux", target_os = "android", target_os = "solaris")))]
const PRI: usize = 0;
// Export to support `Ready::all`
pub const READY_ALL: usize = ERROR | HUP | AIO | LIO | PRI;
#[test]
fn test_ready_all() {
let readable = Ready::readable().as_usize();
let writable = Ready::writable().as_usize();
assert_eq!(
READY_ALL | readable | writable,
ERROR + HUP + AIO + LIO + PRI + readable + writable
);
// Issue #896.
#[cfg(any(target_os = "linux", target_os = "android", target_os = "solaris"))]
assert!(!Ready::from(UnixReady::priority()).is_writable());
}
impl UnixReady {
/// Returns a `Ready` representing AIO completion readiness
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::aio();
///
/// assert!(ready.is_aio());
/// ```
///
/// [`Poll`]: ../struct.Poll.html
#[inline]
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos"))]
pub fn aio() -> UnixReady {
UnixReady(ready_from_usize(AIO))
}
#[cfg(not(any(target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos")))]
#[deprecated(since = "0.6.12", note = "this function is now platform specific")]
#[doc(hidden)]
pub fn aio() -> UnixReady {
UnixReady(Ready::empty())
}
/// Returns a `Ready` representing error readiness.
///
/// **Note that only readable and writable readiness is guaranteed to be
/// supported on all platforms**. This means that `error` readiness
/// should be treated as a hint. For more details, see [readiness] in the
/// poll documentation.
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::error();
///
/// assert!(ready.is_error());
/// ```
///
/// [`Poll`]: ../struct.Poll.html
/// [readiness]: ../struct.Poll.html#readiness-operations
#[inline]
pub fn error() -> UnixReady {
UnixReady(ready_from_usize(ERROR))
}
/// Returns a `Ready` representing HUP readiness.
///
/// A HUP (or hang-up) signifies that a stream socket **peer** closed the
/// connection, or shut down the writing half of the connection.
///
/// **Note that only readable and writable readiness is guaranteed to be
/// supported on all platforms**. This means that `hup` readiness
/// should be treated as a hint. For more details, see [readiness] in the
/// poll documentation. It is also unclear if HUP readiness will remain in 0.7. See
/// [here][issue-941].
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::hup();
///
/// assert!(ready.is_hup());
/// ```
///
/// [`Poll`]: ../struct.Poll.html
/// [readiness]: ../struct.Poll.html#readiness-operations
/// [issue-941]: https://github.com/tokio-rs/mio/issues/941
#[inline]
pub fn hup() -> UnixReady {
UnixReady(ready_from_usize(HUP))
}
/// Returns a `Ready` representing LIO completion readiness
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::lio();
///
/// assert!(ready.is_lio());
/// ```
///
/// [`Poll`]: struct.Poll.html
#[inline]
#[cfg(any(target_os = "freebsd"))]
pub fn lio() -> UnixReady {
UnixReady(ready_from_usize(LIO))
}
/// Returns a `Ready` representing priority (`EPOLLPRI`) readiness
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::priority();
///
/// assert!(ready.is_priority());
/// ```
///
/// [`Poll`]: struct.Poll.html
#[inline]
#[cfg(any(target_os = "linux",
target_os = "android", target_os = "solaris"))]
pub fn priority() -> UnixReady {
UnixReady(ready_from_usize(PRI))
}
/// Returns true if `Ready` contains AIO readiness
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::aio();
///
/// assert!(ready.is_aio());
/// ```
///
/// [`Poll`]: ../struct.Poll.html
#[inline]
#[cfg(any(target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos"))]
pub fn is_aio(&self) -> bool {
self.contains(ready_from_usize(AIO))
}
#[deprecated(since = "0.6.12", note = "this function is now platform specific")]
#[cfg(feature = "with-deprecated")]
#[cfg(not(any(target_os = "dragonfly",
target_os = "freebsd", target_os = "ios", target_os = "macos")))]
#[doc(hidden)]
pub fn is_aio(&self) -> bool {
false
}
/// Returns true if the value includes error readiness
///
/// **Note that only readable and writable readiness is guaranteed to be
/// supported on all platforms**. This means that `error` readiness should
/// be treated as a hint. For more details, see [readiness] in the poll
/// documentation.
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::error();
///
/// assert!(ready.is_error());
/// ```
///
/// [`Poll`]: ../struct.Poll.html
/// [readiness]: ../struct.Poll.html#readiness-operations
#[inline]
pub fn is_error(&self) -> bool {
self.contains(ready_from_usize(ERROR))
}
/// Returns true if the value includes HUP readiness
///
/// A HUP (or hang-up) signifies that a stream socket **peer** closed the
/// connection, or shut down the writing half of the connection.
///
/// **Note that only readable and writable readiness is guaranteed to be
/// supported on all platforms**. This means that `hup` readiness
/// should be treated as a hint. For more details, see [readiness] in the
/// poll documentation.
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::hup();
///
/// assert!(ready.is_hup());
/// ```
///
/// [`Poll`]: ../struct.Poll.html
/// [readiness]: ../struct.Poll.html#readiness-operations
#[inline]
pub fn is_hup(&self) -> bool {
self.contains(ready_from_usize(HUP))
}
/// Returns true if `Ready` contains LIO readiness
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::lio();
///
/// assert!(ready.is_lio());
/// ```
#[inline]
#[cfg(any(target_os = "freebsd"))]
pub fn is_lio(&self) -> bool {
self.contains(ready_from_usize(LIO))
}
/// Returns true if `Ready` contains priority (`EPOLLPRI`) readiness
///
/// See [`Poll`] for more documentation on polling.
///
/// # Examples
///
/// ```
/// use mio::unix::UnixReady;
///
/// let ready = UnixReady::priority();
///
/// assert!(ready.is_priority());
/// ```
///
/// [`Poll`]: struct.Poll.html
#[inline]
#[cfg(any(target_os = "linux",
target_os = "android", target_os = "solaris"))]
pub fn is_priority(&self) -> bool {
self.contains(ready_from_usize(PRI))
}
}
impl From<Ready> for UnixReady {
fn from(src: Ready) -> UnixReady {
UnixReady(src)
}
}
impl From<UnixReady> for Ready {
fn from(src: UnixReady) -> Ready {
src.0
}
}
impl ops::Deref for UnixReady {
type Target = Ready;
fn deref(&self) -> &Ready {
&self.0
}
}
impl ops::DerefMut for UnixReady {
fn deref_mut(&mut self) -> &mut Ready {
&mut self.0
}
}
impl ops::BitOr for UnixReady {
type Output = UnixReady;
#[inline]
fn bitor(self, other: UnixReady) -> UnixReady {
(self.0 | other.0).into()
}
}
impl ops::BitXor for UnixReady {
type Output = UnixReady;
#[inline]
fn bitxor(self, other: UnixReady) -> UnixReady {
(self.0 ^ other.0).into()
}
}
impl ops::BitAnd for UnixReady {
type Output = UnixReady;
#[inline]
fn bitand(self, other: UnixReady) -> UnixReady {
(self.0 & other.0).into()
}
}
impl ops::Sub for UnixReady {
type Output = UnixReady;
#[inline]
fn sub(self, other: UnixReady) -> UnixReady {
ready_from_usize(ready_as_usize(self.0) & !ready_as_usize(other.0)).into()
}
}
#[deprecated(since = "0.6.10", note = "removed")]
#[cfg(feature = "with-deprecated")]
#[doc(hidden)]
impl ops::Not for UnixReady {
type Output = UnixReady;
#[inline]
fn not(self) -> UnixReady {
(!self.0).into()
}
}
impl fmt::Debug for UnixReady {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
let mut one = false;
let flags = [
(UnixReady(Ready::readable()), "Readable"),
(UnixReady(Ready::writable()), "Writable"),
(UnixReady::error(), "Error"),
(UnixReady::hup(), "Hup"),
#[allow(deprecated)]
(UnixReady::aio(), "Aio"),
#[cfg(any(target_os = "linux",
target_os = "android", target_os = "solaris"))]
(UnixReady::priority(), "Priority"),
];
for &(flag, msg) in &flags {
if self.contains(flag) {
if one { write!(fmt, " | ")? }
write!(fmt, "{}", msg)?;
one = true
}
}
if !one {
fmt.write_str("(empty)")?;
}
Ok(())
}
}

View File

@ -1,103 +0,0 @@
use crate::{event, poll, Interests, Registry, Token};
use std::io;
use std::os::unix::io::RawFd;
/// Adapter for [`RawFd`] providing an [`event::Source`] implementation.
///
/// `SourceFd` enables registering any type with an FD with [`Poll`].
///
/// While only implementations for TCP and UDP are provided, Mio supports
/// registering any FD that can be registered with the underlying OS selector.
/// `SourceFd` provides the necessary bridge.
///
/// Note that `SourceFd` takes a `&RawFd`. This is because `SourceFd` **does
/// not** take ownership of the FD. Specifically, it will not manage any
/// lifecycle related operations, such as closing the FD on drop. It is expected
/// that the `SourceFd` is constructed right before a call to
/// [`Registry::register`]. See the examples for more detail.
///
/// [`event::Source`]: crate::event::Source
/// [`Poll`]: crate::Poll
/// [`Registry::register`]: crate::Registry::register
///
/// # Examples
///
/// Basic usage.
///
/// ```
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::{Interests, Poll, Token};
/// use mio::unix::SourceFd;
///
/// use std::os::unix::io::AsRawFd;
/// use std::net::TcpListener;
///
/// // Bind a std listener
/// let listener = TcpListener::bind("127.0.0.1:0")?;
///
/// let poll = Poll::new()?;
///
/// // Register the listener
/// poll.registry().register(
/// &SourceFd(&listener.as_raw_fd()),
/// Token(0),
/// Interests::READABLE)?;
/// # Ok(())
/// # }
/// ```
///
/// Implementing [`event::Source`] for a custom type backed by a [`RawFd`].
///
/// ```
/// use mio::{event, Interests, Registry, Token};
/// use mio::unix::SourceFd;
///
/// use std::os::unix::io::RawFd;
/// use std::io;
///
/// # #[allow(dead_code)]
/// pub struct MyIo {
/// fd: RawFd,
/// }
///
/// impl event::Source for MyIo {
/// fn register(&self, registry: &Registry, token: Token, interests: Interests)
/// -> io::Result<()>
/// {
/// SourceFd(&self.fd).register(registry, token, interests)
/// }
///
/// fn reregister(&self, registry: &Registry, token: Token, interests: Interests)
/// -> io::Result<()>
/// {
/// SourceFd(&self.fd).reregister(registry, token, interests)
/// }
///
/// fn deregister(&self, registry: &Registry) -> io::Result<()> {
/// SourceFd(&self.fd).deregister(registry)
/// }
/// }
/// ```
#[derive(Debug)]
pub struct SourceFd<'a>(pub &'a RawFd);
impl<'a> event::Source for SourceFd<'a> {
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
poll::selector(registry).register(*self.0, token, interests)
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
poll::selector(registry).reregister(*self.0, token, interests)
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
poll::selector(registry).deregister(*self.0)
}
}

286
src/sys/unix/tcp.rs Normal file
View File

@ -0,0 +1,286 @@
use std::fmt;
use std::io::{Read, Write};
use std::net::{self, SocketAddr};
use std::os::unix::io::{RawFd, FromRawFd, IntoRawFd, AsRawFd};
use std::time::Duration;
use libc;
use net2::TcpStreamExt;
use iovec::IoVec;
use {io, Ready, Poll, PollOpt, Token};
use event::Evented;
use sys::unix::eventedfd::EventedFd;
use sys::unix::io::set_nonblock;
use sys::unix::uio::VecIo;
pub struct TcpStream {
inner: net::TcpStream,
}
pub struct TcpListener {
inner: net::TcpListener,
}
impl TcpStream {
pub fn connect(stream: net::TcpStream, addr: &SocketAddr) -> io::Result<TcpStream> {
set_nonblock(stream.as_raw_fd())?;
match stream.connect(addr) {
Ok(..) => {}
Err(ref e) if e.raw_os_error() == Some(libc::EINPROGRESS) => {}
Err(e) => return Err(e),
}
Ok(TcpStream {
inner: stream,
})
}
pub fn from_stream(stream: net::TcpStream) -> TcpStream {
TcpStream {
inner: stream,
}
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.inner.peer_addr()
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
pub fn try_clone(&self) -> io::Result<TcpStream> {
self.inner.try_clone().map(|s| {
TcpStream {
inner: s,
}
})
}
pub fn shutdown(&self, how: net::Shutdown) -> io::Result<()> {
self.inner.shutdown(how)
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}
pub fn nodelay(&self) -> io::Result<bool> {
self.inner.nodelay()
}
pub fn set_recv_buffer_size(&self, size: usize) -> io::Result<()> {
self.inner.set_recv_buffer_size(size)
}
pub fn recv_buffer_size(&self) -> io::Result<usize> {
self.inner.recv_buffer_size()
}
pub fn set_send_buffer_size(&self, size: usize) -> io::Result<()> {
self.inner.set_send_buffer_size(size)
}
pub fn send_buffer_size(&self) -> io::Result<usize> {
self.inner.send_buffer_size()
}
pub fn set_keepalive(&self, keepalive: Option<Duration>) -> io::Result<()> {
self.inner.set_keepalive(keepalive)
}
pub fn keepalive(&self) -> io::Result<Option<Duration>> {
self.inner.keepalive()
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.inner.set_ttl(ttl)
}
pub fn ttl(&self) -> io::Result<u32> {
self.inner.ttl()
}
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.inner.set_only_v6(only_v6)
}
pub fn only_v6(&self) -> io::Result<bool> {
self.inner.only_v6()
}
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
self.inner.set_linger(dur)
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
self.inner.linger()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.inner.take_error()
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.peek(buf)
}
pub fn readv(&self, bufs: &mut [&mut IoVec]) -> io::Result<usize> {
self.inner.readv(bufs)
}
pub fn writev(&self, bufs: &[&IoVec]) -> io::Result<usize> {
self.inner.writev(bufs)
}
}
impl<'a> Read for &'a TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&self.inner).read(buf)
}
}
impl<'a> Write for &'a TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&self.inner).write(buf)
}
fn flush(&mut self) -> io::Result<()> {
(&self.inner).flush()
}
}
impl Evented for TcpStream {
fn register(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).deregister(poll)
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.inner, f)
}
}
impl FromRawFd for TcpStream {
unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
TcpStream {
inner: net::TcpStream::from_raw_fd(fd),
}
}
}
impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd {
self.inner.into_raw_fd()
}
}
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
impl TcpListener {
pub fn new(inner: net::TcpListener) -> io::Result<TcpListener> {
set_nonblock(inner.as_raw_fd())?;
Ok(TcpListener {
inner,
})
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
pub fn try_clone(&self) -> io::Result<TcpListener> {
self.inner.try_clone().map(|s| {
TcpListener {
inner: s,
}
})
}
pub fn accept(&self) -> io::Result<(net::TcpStream, SocketAddr)> {
self.inner.accept()
}
#[allow(deprecated)]
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.inner.set_only_v6(only_v6)
}
#[allow(deprecated)]
pub fn only_v6(&self) -> io::Result<bool> {
self.inner.only_v6()
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.inner.set_ttl(ttl)
}
pub fn ttl(&self) -> io::Result<u32> {
self.inner.ttl()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.inner.take_error()
}
}
impl Evented for TcpListener {
fn register(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).deregister(poll)
}
}
impl fmt::Debug for TcpListener {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.inner, f)
}
}
impl FromRawFd for TcpListener {
unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
TcpListener {
inner: net::TcpListener::from_raw_fd(fd),
}
}
}
impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd {
self.inner.into_raw_fd()
}
}
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}

View File

@ -1,113 +0,0 @@
use crate::sys::unix::net::{new_socket, socket_addr};
use crate::sys::unix::{SourceFd, TcpStream};
use crate::{event, Interests, Registry, Token};
use std::fmt;
use std::io;
use std::mem::size_of;
use std::net::{self, SocketAddr};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
pub struct TcpListener {
inner: net::TcpListener,
}
impl TcpListener {
pub fn bind(addr: SocketAddr) -> io::Result<TcpListener> {
new_socket(addr, libc::SOCK_STREAM).and_then(|socket| {
// Set SO_REUSEADDR (mirrors what libstd does).
syscall!(setsockopt(
socket,
libc::SOL_SOCKET,
libc::SO_REUSEADDR,
&1 as *const libc::c_int as *const libc::c_void,
size_of::<libc::c_int>() as libc::socklen_t,
))
.and_then(|_| {
let (raw_addr, raw_addr_length) = socket_addr(&addr);
syscall!(bind(socket, raw_addr, raw_addr_length))
})
.and_then(|_| syscall!(listen(socket, 1024)))
.map_err(|err| {
// Close the socket if we hit an error, ignoring the error
// from closing since we can't pass back two errors.
let _ = unsafe { libc::close(socket) };
err
})
.map(|_| TcpListener {
inner: unsafe { net::TcpListener::from_raw_fd(socket) },
})
})
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
pub fn try_clone(&self) -> io::Result<TcpListener> {
self.inner.try_clone().map(|s| TcpListener { inner: s })
}
pub fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
self.inner
.accept()
.map(|(inner, addr)| (TcpStream::new(inner), addr))
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.inner.set_ttl(ttl)
}
pub fn ttl(&self) -> io::Result<u32> {
self.inner.ttl()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.inner.take_error()
}
}
impl event::Source for TcpListener {
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).register(registry, token, interests)
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).reregister(registry, token, interests)
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).deregister(registry)
}
}
impl fmt::Debug for TcpListener {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.inner, f)
}
}
impl FromRawFd for TcpListener {
unsafe fn from_raw_fd(fd: RawFd) -> TcpListener {
TcpListener {
inner: net::TcpListener::from_raw_fd(fd),
}
}
}
impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd {
self.inner.into_raw_fd()
}
}
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}

View File

@ -1,150 +0,0 @@
use crate::sys::unix::net::{new_socket, socket_addr};
use crate::sys::unix::SourceFd;
use crate::{event, Interests, Registry, Token};
use std::fmt;
use std::io::{self, IoSlice, IoSliceMut, Read, Write};
use std::net::{self, SocketAddr};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
pub struct TcpStream {
inner: net::TcpStream,
}
impl TcpStream {
pub(crate) fn new(inner: net::TcpStream) -> TcpStream {
TcpStream { inner }
}
pub fn connect(addr: SocketAddr) -> io::Result<TcpStream> {
new_socket(addr, libc::SOCK_STREAM)
.and_then(|socket| {
let (raw_addr, raw_addr_length) = socket_addr(&addr);
syscall!(connect(socket, raw_addr, raw_addr_length))
.or_else(|err| match err {
// Connect hasn't finished, but that is fine.
ref err if err.raw_os_error() == Some(libc::EINPROGRESS) => Ok(0),
err => Err(err),
})
.map(|_| socket)
.map_err(|err| {
// Close the socket if we hit an error, ignoring the error
// from closing since we can't pass back two errors.
let _ = unsafe { libc::close(socket) };
err
})
})
.map(|socket| TcpStream {
inner: unsafe { net::TcpStream::from_raw_fd(socket) },
})
}
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
self.inner.peer_addr()
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.inner.local_addr()
}
pub fn try_clone(&self) -> io::Result<TcpStream> {
self.inner.try_clone().map(|s| TcpStream { inner: s })
}
pub fn shutdown(&self, how: net::Shutdown) -> io::Result<()> {
self.inner.shutdown(how)
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.inner.set_nodelay(nodelay)
}
pub fn nodelay(&self) -> io::Result<bool> {
self.inner.nodelay()
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.inner.set_ttl(ttl)
}
pub fn ttl(&self) -> io::Result<u32> {
self.inner.ttl()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.inner.take_error()
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.peek(buf)
}
}
impl<'a> Read for &'a TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(&self.inner).read(buf)
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
(&self.inner).read_vectored(bufs)
}
}
impl<'a> Write for &'a TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
(&self.inner).write(buf)
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
(&self.inner).write_vectored(bufs)
}
fn flush(&mut self) -> io::Result<()> {
(&self.inner).flush()
}
}
impl event::Source for TcpStream {
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).register(registry, token, interests)
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).reregister(registry, token, interests)
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).deregister(registry)
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.inner, f)
}
}
impl FromRawFd for TcpStream {
unsafe fn from_raw_fd(fd: RawFd) -> TcpStream {
TcpStream {
inner: net::TcpStream::from_raw_fd(fd),
}
}
}
impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd {
self.inner.into_raw_fd()
}
}
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}

View File

@ -1,46 +1,24 @@
use crate::sys::unix::net::{new_socket, socket_addr};
use crate::unix::SourceFd;
use crate::{event, Interests, Registry, Token};
use {io, Ready, Poll, PollOpt, Token};
use event::Evented;
use unix::EventedFd;
use sys::unix::uio::VecIo;
use std::fmt;
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::os::unix::io::{RawFd, IntoRawFd, AsRawFd, FromRawFd};
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::{fmt, io, net};
#[allow(unused_imports)] // only here for Rust 1.8
use net2::UdpSocketExt;
use iovec::IoVec;
pub struct UdpSocket {
io: net::UdpSocket,
}
impl UdpSocket {
pub fn bind(addr: SocketAddr) -> io::Result<UdpSocket> {
// Gives a warning for non Apple platforms.
#[allow(clippy::let_and_return)]
let socket = new_socket(addr, libc::SOCK_DGRAM);
// Set SO_NOSIGPIPE on iOS and macOS (mirrors what libstd does).
#[cfg(any(target_os = "ios", target_os = "macos"))]
let socket = socket.and_then(|socket| {
syscall!(setsockopt(
socket,
libc::SOL_SOCKET,
libc::SO_NOSIGPIPE,
&1 as *const libc::c_int as *const libc::c_void,
std::mem::size_of::<libc::c_int>() as libc::socklen_t,
))
.map(|_| socket)
});
socket.and_then(|socket| {
let (raw_addr, raw_addr_length) = socket_addr(&addr);
syscall!(bind(socket, raw_addr, raw_addr_length))
.map_err(|err| {
// Close the socket if we hit an error, ignoring the error
// from closing since we can't pass back two errors.
let _ = unsafe { libc::close(socket) };
err
})
.map(|_| UdpSocket {
io: unsafe { net::UdpSocket::from_raw_fd(socket) },
})
pub fn new(socket: net::UdpSocket) -> io::Result<UdpSocket> {
socket.set_nonblocking(true)?;
Ok(UdpSocket {
io: socket,
})
}
@ -49,10 +27,14 @@ impl UdpSocket {
}
pub fn try_clone(&self) -> io::Result<UdpSocket> {
self.io.try_clone().map(|io| UdpSocket { io })
self.io.try_clone().map(|io| {
UdpSocket {
io,
}
})
}
pub fn send_to(&self, buf: &[u8], target: SocketAddr) -> io::Result<usize> {
pub fn send_to(&self, buf: &[u8], target: &SocketAddr) -> io::Result<usize> {
self.io.send_to(buf, target)
}
@ -60,10 +42,6 @@ impl UdpSocket {
self.io.recv_from(buf)
}
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
self.io.peek_from(buf)
}
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
self.io.send(buf)
}
@ -72,11 +50,8 @@ impl UdpSocket {
self.io.recv(buf)
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
self.io.peek(buf)
}
pub fn connect(&self, addr: SocketAddr) -> io::Result<()> {
pub fn connect(&self, addr: SocketAddr)
-> io::Result<()> {
self.io.connect(addr)
}
@ -120,48 +95,67 @@ impl UdpSocket {
self.io.set_ttl(ttl)
}
pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> {
self.io.join_multicast_v4(&multiaddr, &interface)
pub fn join_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.io.join_multicast_v4(multiaddr, interface)
}
pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
pub fn join_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.io.join_multicast_v6(multiaddr, interface)
}
pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> {
self.io.leave_multicast_v4(&multiaddr, &interface)
pub fn leave_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.io.leave_multicast_v4(multiaddr, interface)
}
pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
pub fn leave_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.io.leave_multicast_v6(multiaddr, interface)
}
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.io.set_only_v6(only_v6)
}
pub fn only_v6(&self) -> io::Result<bool> {
self.io.only_v6()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.io.take_error()
}
pub fn readv(&self, bufs: &mut [&mut IoVec]) -> io::Result<usize> {
self.io.readv(bufs)
}
pub fn writev(&self, bufs: &[&IoVec]) -> io::Result<usize> {
self.io.writev(bufs)
}
}
impl event::Source for UdpSocket {
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).register(registry, token, interests)
impl Evented for UdpSocket {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).register(poll, token, interest, opts)
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).reregister(registry, token, interests)
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).reregister(poll, token, interest, opts)
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
SourceFd(&self.as_raw_fd()).deregister(registry)
fn deregister(&self, poll: &Poll) -> io::Result<()> {
EventedFd(&self.as_raw_fd()).deregister(poll)
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.io, f)
}
}
@ -169,7 +163,7 @@ impl fmt::Debug for UdpSocket {
impl FromRawFd for UdpSocket {
unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket {
UdpSocket {
io: std::net::UdpSocket::from_raw_fd(fd),
io: net::UdpSocket::from_raw_fd(fd),
}
}
}

262
src/sys/unix/uds.rs Normal file
View File

@ -0,0 +1,262 @@
use std::io::{Read, Write};
use std::mem;
use std::net::Shutdown;
use std::os::unix::prelude::*;
use std::path::Path;
use std::ptr;
use libc;
use {io, Ready, Poll, PollOpt, Token};
use event::Evented;
use sys::unix::{cvt, Io};
use sys::unix::io::{set_nonblock, set_cloexec};
trait MyInto<T> {
fn my_into(self) -> T;
}
impl MyInto<u32> for usize {
fn my_into(self) -> u32 { self as u32 }
}
impl MyInto<usize> for usize {
fn my_into(self) -> usize { self }
}
unsafe fn sockaddr_un(path: &Path)
-> io::Result<(libc::sockaddr_un, libc::socklen_t)> {
let mut addr: libc::sockaddr_un = mem::zeroed();
addr.sun_family = libc::AF_UNIX as libc::sa_family_t;
let bytes = path.as_os_str().as_bytes();
if bytes.len() >= addr.sun_path.len() {
return Err(io::Error::new(io::ErrorKind::InvalidInput,
"path must be shorter than SUN_LEN"))
}
for (dst, src) in addr.sun_path.iter_mut().zip(bytes.iter()) {
*dst = *src as libc::c_char;
}
// null byte for pathname addresses is already there because we zeroed the
// struct
let mut len = sun_path_offset() + bytes.len();
match bytes.get(0) {
Some(&0) | None => {}
Some(_) => len += 1,
}
Ok((addr, len as libc::socklen_t))
}
fn sun_path_offset() -> usize {
unsafe {
// Work with an actual instance of the type since using a null pointer is UB
let addr: libc::sockaddr_un = mem::uninitialized();
let base = &addr as *const _ as usize;
let path = &addr.sun_path as *const _ as usize;
path - base
}
}
#[derive(Debug)]
pub struct UnixSocket {
io: Io,
}
impl UnixSocket {
/// Returns a new, unbound, non-blocking Unix domain socket
pub fn stream() -> io::Result<UnixSocket> {
#[cfg(target_os = "linux")]
use libc::{SOCK_CLOEXEC, SOCK_NONBLOCK};
#[cfg(not(target_os = "linux"))]
const SOCK_CLOEXEC: libc::c_int = 0;
#[cfg(not(target_os = "linux"))]
const SOCK_NONBLOCK: libc::c_int = 0;
unsafe {
if cfg!(target_os = "linux") {
let flags = libc::SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK;
match cvt(libc::socket(libc::AF_UNIX, flags, 0)) {
Ok(fd) => return Ok(UnixSocket::from_raw_fd(fd)),
Err(ref e) if e.raw_os_error() == Some(libc::EINVAL) => {}
Err(e) => return Err(e),
}
}
let fd = cvt(libc::socket(libc::AF_UNIX, libc::SOCK_STREAM, 0))?;
let fd = UnixSocket::from_raw_fd(fd);
set_cloexec(fd.as_raw_fd())?;
set_nonblock(fd.as_raw_fd())?;
Ok(fd)
}
}
/// Connect the socket to the specified address
pub fn connect<P: AsRef<Path> + ?Sized>(&self, addr: &P) -> io::Result<()> {
unsafe {
let (addr, len) = sockaddr_un(addr.as_ref())?;
cvt(libc::connect(self.as_raw_fd(),
&addr as *const _ as *const _,
len))?;
Ok(())
}
}
/// Listen for incoming requests
pub fn listen(&self, backlog: usize) -> io::Result<()> {
unsafe {
cvt(libc::listen(self.as_raw_fd(), backlog as i32))?;
Ok(())
}
}
pub fn accept(&self) -> io::Result<UnixSocket> {
unsafe {
let fd = cvt(libc::accept(self.as_raw_fd(),
ptr::null_mut(),
ptr::null_mut()))?;
let fd = Io::from_raw_fd(fd);
set_cloexec(fd.as_raw_fd())?;
set_nonblock(fd.as_raw_fd())?;
Ok(UnixSocket { io: fd })
}
}
/// Bind the socket to the specified address
pub fn bind<P: AsRef<Path> + ?Sized>(&self, addr: &P) -> io::Result<()> {
unsafe {
let (addr, len) = sockaddr_un(addr.as_ref())?;
cvt(libc::bind(self.as_raw_fd(),
&addr as *const _ as *const _,
len))?;
Ok(())
}
}
pub fn try_clone(&self) -> io::Result<UnixSocket> {
Ok(UnixSocket { io: self.io.try_clone()? })
}
pub fn shutdown(&self, how: Shutdown) -> io::Result<()> {
let how = match how {
Shutdown::Read => libc::SHUT_RD,
Shutdown::Write => libc::SHUT_WR,
Shutdown::Both => libc::SHUT_RDWR,
};
unsafe {
cvt(libc::shutdown(self.as_raw_fd(), how))?;
Ok(())
}
}
pub fn read_recv_fd(&mut self, buf: &mut [u8]) -> io::Result<(usize, Option<RawFd>)> {
unsafe {
let mut iov = libc::iovec {
iov_base: buf.as_mut_ptr() as *mut _,
iov_len: buf.len(),
};
struct Cmsg {
hdr: libc::cmsghdr,
data: [libc::c_int; 1],
}
let mut cmsg: Cmsg = mem::zeroed();
let mut msg: libc::msghdr = mem::zeroed();
msg.msg_iov = &mut iov;
msg.msg_iovlen = 1;
msg.msg_control = &mut cmsg as *mut _ as *mut _;
msg.msg_controllen = mem::size_of_val(&cmsg).my_into();
let bytes = cvt(libc::recvmsg(self.as_raw_fd(), &mut msg, 0))?;
const SCM_RIGHTS: libc::c_int = 1;
let fd = if cmsg.hdr.cmsg_level == libc::SOL_SOCKET &&
cmsg.hdr.cmsg_type == SCM_RIGHTS {
Some(cmsg.data[0])
} else {
None
};
Ok((bytes as usize, fd))
}
}
pub fn write_send_fd(&mut self, buf: &[u8], fd: RawFd) -> io::Result<usize> {
unsafe {
let mut iov = libc::iovec {
iov_base: buf.as_ptr() as *mut _,
iov_len: buf.len(),
};
struct Cmsg {
hdr: libc::cmsghdr,
data: [libc::c_int; 1],
}
let mut cmsg: Cmsg = mem::zeroed();
cmsg.hdr.cmsg_len = mem::size_of_val(&cmsg).my_into();
cmsg.hdr.cmsg_level = libc::SOL_SOCKET;
cmsg.hdr.cmsg_type = 1; // SCM_RIGHTS
cmsg.data[0] = fd;
let mut msg: libc::msghdr = mem::zeroed();
msg.msg_iov = &mut iov;
msg.msg_iovlen = 1;
msg.msg_control = &mut cmsg as *mut _ as *mut _;
msg.msg_controllen = mem::size_of_val(&cmsg).my_into();
let bytes = cvt(libc::sendmsg(self.as_raw_fd(), &msg, 0))?;
Ok(bytes as usize)
}
}
}
impl Read for UnixSocket {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.io.read(buf)
}
}
impl Write for UnixSocket {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.io.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.io.flush()
}
}
impl Evented for UnixSocket {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.io.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.io.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.io.deregister(poll)
}
}
impl From<Io> for UnixSocket {
fn from(io: Io) -> UnixSocket {
UnixSocket { io }
}
}
impl FromRawFd for UnixSocket {
unsafe fn from_raw_fd(fd: RawFd) -> UnixSocket {
UnixSocket { io: Io::from_raw_fd(fd) }
}
}
impl IntoRawFd for UnixSocket {
fn into_raw_fd(self) -> RawFd {
self.io.into_raw_fd()
}
}
impl AsRawFd for UnixSocket {
fn as_raw_fd(&self) -> RawFd {
self.io.as_raw_fd()
}
}

44
src/sys/unix/uio.rs Normal file
View File

@ -0,0 +1,44 @@
use std::cmp;
use std::io;
use std::os::unix::io::AsRawFd;
use libc;
use iovec::IoVec;
use iovec::unix as iovec;
pub trait VecIo {
fn readv(&self, bufs: &mut [&mut IoVec]) -> io::Result<usize>;
fn writev(&self, bufs: &[&IoVec]) -> io::Result<usize>;
}
impl<T: AsRawFd> VecIo for T {
fn readv(&self, bufs: &mut [&mut IoVec]) -> io::Result<usize> {
unsafe {
let slice = iovec::as_os_slice_mut(bufs);
let len = cmp::min(<libc::c_int>::max_value() as usize, slice.len());
let rc = libc::readv(self.as_raw_fd(),
slice.as_ptr(),
len as libc::c_int);
if rc < 0 {
Err(io::Error::last_os_error())
} else {
Ok(rc as usize)
}
}
}
fn writev(&self, bufs: &[&IoVec]) -> io::Result<usize> {
unsafe {
let slice = iovec::as_os_slice(bufs);
let len = cmp::min(<libc::c_int>::max_value() as usize, slice.len());
let rc = libc::writev(self.as_raw_fd(),
slice.as_ptr(),
len as libc::c_int);
if rc < 0 {
Err(io::Error::last_os_error())
} else {
Ok(rc as usize)
}
}
}
}

View File

@ -1,174 +0,0 @@
#[cfg(any(target_os = "linux", target_os = "android"))]
mod eventfd {
use crate::sys::Selector;
use crate::{Interests, Token};
use std::fs::File;
use std::io::{self, Read, Write};
use std::os::unix::io::FromRawFd;
/// Waker backed by `eventfd`.
///
/// `eventfd` is effectively an 64 bit counter. All writes must be of 8
/// bytes (64 bits) and are converted (native endian) into an 64 bit
/// unsigned integer and added to the count. Reads must also be 8 bytes and
/// reset the count to 0, returning the count.
#[derive(Debug)]
pub struct Waker {
fd: File,
}
impl Waker {
pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
syscall!(eventfd(0, libc::EFD_CLOEXEC | libc::EFD_NONBLOCK)).and_then(|fd| {
// Turn the file descriptor into a file first so we're ensured
// it's closed when dropped, e.g. when register below fails.
let file = unsafe { File::from_raw_fd(fd) };
selector
.register(fd, token, Interests::READABLE)
.map(|()| Waker { fd: file })
})
}
pub fn wake(&self) -> io::Result<()> {
let buf: [u8; 8] = 1u64.to_ne_bytes();
match (&self.fd).write(&buf) {
Ok(_) => Ok(()),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
// Writing only blocks if the counter is going to overflow.
// So we'll reset the counter to 0 and wake it again.
self.reset()?;
self.wake()
}
Err(err) => Err(err),
}
}
/// Reset the eventfd object, only need to call this if `wake` fails.
fn reset(&self) -> io::Result<()> {
let mut buf: [u8; 8] = 0u64.to_ne_bytes();
match (&self.fd).read(&mut buf) {
Ok(_) => Ok(()),
// If the `Waker` hasn't been awoken yet this will return a
// `WouldBlock` error which we can safely ignore.
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()),
Err(err) => Err(err),
}
}
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub use self::eventfd::Waker;
#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
mod kqueue {
use crate::sys::Selector;
use crate::Token;
use std::io;
/// Waker backed by kqueue user space notifications (`EVFILT_USER`).
///
/// The implementation is fairly simple, first the kqueue must be setup to
/// receive waker events this done by calling `Selector.setup_waker`. Next
/// we need access to kqueue, thus we need to duplicate the file descriptor.
/// Now waking is as simple as adding an event to the kqueue.
#[derive(Debug)]
pub struct Waker {
selector: Selector,
token: Token,
}
impl Waker {
pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
selector.try_clone().and_then(|selector| {
selector
.setup_waker(token)
.map(|()| Waker { selector, token })
})
}
pub fn wake(&self) -> io::Result<()> {
self.selector.wake(self.token)
}
}
}
#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
pub use self::kqueue::Waker;
#[cfg(any(
target_os = "bitrig",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"
))]
mod pipe {
use crate::sys::unix::Selector;
use crate::{Interests, Token};
use std::fs::File;
use std::io::{self, Read, Write};
use std::os::unix::io::FromRawFd;
/// Waker backed by a unix pipe.
///
/// Waker controls both the sending and receiving ends and empties the pipe
/// if writing to it (waking) fails.
#[derive(Debug)]
pub struct Waker {
sender: File,
receiver: File,
}
impl Waker {
pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
let mut fds = [-1; 2];
syscall!(pipe2(fds.as_mut_ptr(), libc::O_NONBLOCK | libc::O_CLOEXEC))?;
// Turn the file descriptors into files first so we're ensured
// they're closed when dropped, e.g. when register below fails.
let sender = unsafe { File::from_raw_fd(fds[1]) };
let receiver = unsafe { File::from_raw_fd(fds[0]) };
selector
.register(fds[0], token, Interests::READABLE)
.map(|()| Waker { sender, receiver })
}
pub fn wake(&self) -> io::Result<()> {
match (&self.sender).write(&[1]) {
Ok(_) => Ok(()),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
// The reading end is full so we'll empty the buffer and try
// again.
self.empty();
self.wake()
}
Err(ref err) if err.kind() == io::ErrorKind::Interrupted => self.wake(),
Err(err) => Err(err),
}
}
/// Empty the pipe's buffer, only need to call this if `wake` fails.
/// This ignores any errors.
fn empty(&self) {
let mut buf = [0; 4096];
loop {
match (&self.receiver).read(&mut buf) {
Ok(n) if n > 0 => continue,
_ => return,
}
}
}
}
}
#[cfg(any(
target_os = "bitrig",
target_os = "dragonfly",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris"
))]
pub use self::pipe::Waker;

View File

@ -1,222 +0,0 @@
use lazy_static::lazy_static;
use miow::iocp::CompletionPort;
use ntapi::ntioapi::FILE_OPEN;
use ntapi::ntioapi::{IO_STATUS_BLOCK_u, IO_STATUS_BLOCK};
use ntapi::ntioapi::{NtCancelIoFileEx, NtCreateFile, NtDeviceIoControlFile};
use ntapi::ntrtl::RtlNtStatusToDosError;
use std::ffi::OsStr;
use std::fmt;
use std::fs::File;
use std::io;
use std::mem::{size_of, zeroed};
use std::os::windows::ffi::OsStrExt;
use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle};
use std::ptr::null_mut;
use std::sync::atomic::{AtomicUsize, Ordering};
use winapi::shared::ntdef::{
HANDLE, LARGE_INTEGER, NTSTATUS, OBJECT_ATTRIBUTES, PVOID, ULONG, UNICODE_STRING,
};
use winapi::shared::ntstatus::{STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS};
use winapi::um::handleapi::INVALID_HANDLE_VALUE;
use winapi::um::winbase::{SetFileCompletionNotificationModes, FILE_SKIP_SET_EVENT_ON_HANDLE};
use winapi::um::winnt::SYNCHRONIZE;
use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE};
const IOCTL_AFD_POLL: ULONG = 0x00012024;
static NEXT_TOKEN: AtomicUsize = AtomicUsize::new(0);
lazy_static! {
static ref AFD_HELPER_NAME: Vec<u16> = {
OsStr::new("\\Device\\Afd\\Mio")
.encode_wide()
.collect::<Vec<_>>()
};
}
struct UnicodeString(UNICODE_STRING);
unsafe impl Send for UnicodeString {}
unsafe impl Sync for UnicodeString {}
struct ObjectAttributes(OBJECT_ATTRIBUTES);
unsafe impl Send for ObjectAttributes {}
unsafe impl Sync for ObjectAttributes {}
lazy_static! {
static ref AFD_OBJ_NAME: UnicodeString = UnicodeString(UNICODE_STRING {
// Lengths are calced in bytes
Length: (AFD_HELPER_NAME.len() * 2) as u16,
MaximumLength: (AFD_HELPER_NAME.len() * 2) as u16,
Buffer: AFD_HELPER_NAME.as_ptr() as *mut _,
});
static ref AFD_HELPER_ATTRIBUTES: ObjectAttributes = ObjectAttributes(OBJECT_ATTRIBUTES {
Length: size_of::<OBJECT_ATTRIBUTES>() as ULONG,
RootDirectory: null_mut() as HANDLE,
ObjectName: &AFD_OBJ_NAME.0 as *const _ as *mut _,
Attributes: 0 as ULONG,
SecurityDescriptor: null_mut() as PVOID,
SecurityQualityOfService: null_mut() as PVOID,
});
}
/// Winsock2 AFD driver instance.
///
/// All operations are unsafe due to IO_STATUS_BLOCK parameter are being used by Afd driver during STATUS_PENDING before I/O Completion Port returns its result.
#[derive(Debug)]
pub struct Afd {
fd: File,
}
#[repr(C)]
#[derive(Debug)]
pub struct AfdPollHandleInfo {
pub handle: HANDLE,
pub events: ULONG,
pub status: NTSTATUS,
}
unsafe impl Send for AfdPollHandleInfo {}
#[repr(C)]
pub struct AfdPollInfo {
pub timeout: LARGE_INTEGER,
// Can have only value 1.
pub number_of_handles: ULONG,
pub exclusive: ULONG,
pub handles: [AfdPollHandleInfo; 1],
}
impl AfdPollInfo {
pub fn zeroed() -> AfdPollInfo {
unsafe { zeroed() }
}
}
impl fmt::Debug for AfdPollInfo {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AfdPollInfo").finish()
}
}
impl Afd {
/// Create new Afd instance.
pub fn new(cp: &CompletionPort) -> io::Result<Afd> {
let mut afd_helper_handle: HANDLE = INVALID_HANDLE_VALUE;
let mut iosb = IO_STATUS_BLOCK {
u: IO_STATUS_BLOCK_u { Status: 0 },
Information: 0,
};
unsafe {
let status = NtCreateFile(
&mut afd_helper_handle as *mut _,
SYNCHRONIZE,
&AFD_HELPER_ATTRIBUTES.0 as *const _ as *mut _,
&mut iosb,
null_mut(),
0 as ULONG,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OPEN,
0 as ULONG,
null_mut(),
0 as ULONG,
);
if status != STATUS_SUCCESS {
return Err(io::Error::from_raw_os_error(
RtlNtStatusToDosError(status) as i32
));
}
let fd = File::from_raw_handle(afd_helper_handle as RawHandle);
let token = NEXT_TOKEN.fetch_add(1, Ordering::Relaxed) + 1;
let afd = Afd { fd };
cp.add_handle(token, &afd.fd)?;
match SetFileCompletionNotificationModes(
afd_helper_handle,
FILE_SKIP_SET_EVENT_ON_HANDLE,
) {
0 => Err(io::Error::last_os_error()),
_ => Ok(afd),
}
}
}
/// Poll `Afd` instance with `AfdPollInfo`.
///
/// # Unsafety
///
/// This function is unsafe due to memory of `IO_STATUS_BLOCK` still being used by `Afd` instance while `Ok(false)` (`STATUS_PENDING`).
/// `iosb` needs to be untouched after the call while operation is in effective at ALL TIME except for `cancel` method.
/// So be careful not to `poll` twice while polling.
/// User should deallocate there overlapped value when error to prevent memory leak.
pub unsafe fn poll(
&self,
info: &mut AfdPollInfo,
iosb: *mut IO_STATUS_BLOCK,
overlapped: PVOID,
) -> io::Result<bool> {
let info_ptr: PVOID = info as *mut _ as PVOID;
(*iosb).u.Status = STATUS_PENDING;
let status = NtDeviceIoControlFile(
self.fd.as_raw_handle(),
null_mut(),
None,
overlapped,
iosb,
IOCTL_AFD_POLL,
info_ptr,
size_of::<AfdPollInfo>() as u32,
info_ptr,
size_of::<AfdPollInfo>() as u32,
);
match status {
STATUS_SUCCESS => Ok(true),
STATUS_PENDING => Ok(false),
_ => Err(io::Error::from_raw_os_error(
RtlNtStatusToDosError(status) as i32
)),
}
}
/// Cancel previous polled request of `Afd`.
///
/// iosb needs to be used by `poll` first for valid `cancel`.
///
/// # Unsafety
///
/// This function is unsafe due to memory of `IO_STATUS_BLOCK` still being used by `Afd` instance while `Ok(false)` (`STATUS_PENDING`).
/// Use it only with request is still being polled so that you have valid `IO_STATUS_BLOCK` to use.
/// User should NOT deallocate there overlapped value after the `cancel` to prevent double free.
pub unsafe fn cancel(&self, iosb: *mut IO_STATUS_BLOCK) -> io::Result<()> {
if (*iosb).u.Status != STATUS_PENDING {
return Ok(());
}
let mut cancel_iosb = IO_STATUS_BLOCK {
u: IO_STATUS_BLOCK_u { Status: 0 },
Information: 0,
};
let status = NtCancelIoFileEx(self.fd.as_raw_handle(), iosb, &mut cancel_iosb);
if status == STATUS_SUCCESS || status == STATUS_NOT_FOUND {
return Ok(());
}
Err(io::Error::from_raw_os_error(
RtlNtStatusToDosError(status) as i32
))
}
}
pub const AFD_POLL_RECEIVE: u32 = 0x0001;
pub const AFD_POLL_RECEIVE_EXPEDITED: u32 = 0x0002;
pub const AFD_POLL_SEND: u32 = 0x0004;
pub const AFD_POLL_DISCONNECT: u32 = 0x0008;
pub const AFD_POLL_ABORT: u32 = 0x0010;
pub const AFD_POLL_LOCAL_CLOSE: u32 = 0x0020;
pub const AFD_POLL_ACCEPT: u32 = 0x0080;
pub const AFD_POLL_CONNECT_FAIL: u32 = 0x0100;
pub const KNOWN_AFD_EVENTS: u32 = AFD_POLL_RECEIVE
| AFD_POLL_RECEIVE_EXPEDITED
| AFD_POLL_SEND
| AFD_POLL_DISCONNECT
| AFD_POLL_ABORT
| AFD_POLL_ACCEPT
| AFD_POLL_CONNECT_FAIL;

View File

@ -0,0 +1,66 @@
use std::sync::Mutex;
use miow::iocp::CompletionStatus;
use {io, poll, Ready, Poll, PollOpt, Token};
use event::Evented;
use sys::windows::Selector;
pub struct Awakener {
inner: Mutex<Option<AwakenerInner>>,
}
struct AwakenerInner {
token: Token,
selector: Selector,
}
impl Awakener {
pub fn new() -> io::Result<Awakener> {
Ok(Awakener {
inner: Mutex::new(None),
})
}
pub fn wakeup(&self) -> io::Result<()> {
// Each wakeup notification has NULL as its `OVERLAPPED` pointer to
// indicate that it's from this awakener and not part of an I/O
// operation. This is specially recognized by the selector.
//
// If we haven't been registered with an event loop yet just silently
// succeed.
if let Some(inner) = self.inner.lock().unwrap().as_ref() {
let status = CompletionStatus::new(0,
usize::from(inner.token),
0 as *mut _);
inner.selector.port().post(status)?;
}
Ok(())
}
pub fn cleanup(&self) {
// noop
}
}
impl Evented for Awakener {
fn register(&self, poll: &Poll, token: Token, events: Ready,
opts: PollOpt) -> io::Result<()> {
assert_eq!(opts, PollOpt::edge());
assert_eq!(events, Ready::readable());
*self.inner.lock().unwrap() = Some(AwakenerInner {
selector: poll::selector(poll).clone_ref(),
token: token,
});
Ok(())
}
fn reregister(&self, poll: &Poll, token: Token, events: Ready,
opts: PollOpt) -> io::Result<()> {
self.register(poll, token, events, opts)
}
fn deregister(&self, _poll: &Poll) -> io::Result<()> {
*self.inner.lock().unwrap() = None;
Ok(())
}
}

View File

@ -0,0 +1,20 @@
pub struct BufferPool {
pool: Vec<Vec<u8>>,
}
impl BufferPool {
pub fn new(cap: usize) -> BufferPool {
BufferPool { pool: Vec::with_capacity(cap) }
}
pub fn get(&mut self, default_cap: usize) -> Vec<u8> {
self.pool.pop().unwrap_or_else(|| Vec::with_capacity(default_cap))
}
pub fn put(&mut self, mut buf: Vec<u8>) {
if self.pool.len() < self.pool.capacity(){
unsafe { buf.set_len(0); }
self.pool.push(buf);
}
}
}

View File

@ -1,97 +0,0 @@
#[derive(Debug)]
pub struct Event {
pub flags: u32,
pub data: u64,
}
use miow::iocp::CompletionStatus;
use super::afd;
use crate::Token;
pub fn token(event: &Event) -> Token {
Token(event.data as usize)
}
pub fn is_readable(event: &Event) -> bool {
if is_error(event) || is_read_hup(event) {
return true;
}
event.flags & (afd::AFD_POLL_RECEIVE | afd::AFD_POLL_ACCEPT) != 0
}
pub fn is_writable(event: &Event) -> bool {
if is_error(event) {
return true;
}
event.flags & afd::AFD_POLL_SEND != 0
}
pub fn is_error(event: &Event) -> bool {
event.flags & afd::AFD_POLL_CONNECT_FAIL != 0
}
pub fn is_hup(event: &Event) -> bool {
event.flags & afd::AFD_POLL_ABORT != 0
}
pub fn is_read_hup(event: &Event) -> bool {
event.flags & afd::AFD_POLL_DISCONNECT != 0
}
pub fn is_priority(event: &Event) -> bool {
event.flags & afd::AFD_POLL_RECEIVE_EXPEDITED != 0
}
pub fn is_aio(_: &Event) -> bool {
// Not supported.
false
}
pub fn is_lio(_: &Event) -> bool {
// Not supported.
false
}
pub struct Events {
/// Raw I/O event completions are filled in here by the call to `get_many`
/// on the completion port above. These are then processed to run callbacks
/// which figure out what to do after the event is done.
pub statuses: Box<[CompletionStatus]>,
/// Literal events returned by `get` to the upwards `EventLoop`. This file
/// doesn't really modify this (except for the waker), instead almost all
/// events are filled in by the `ReadinessQueue` from the `poll` module.
pub events: Vec<Event>,
}
impl Events {
pub fn with_capacity(cap: usize) -> Events {
// Note that it's possible for the output `events` to grow beyond the
// capacity as it can also include deferred events, but that's certainly
// not the end of the world!
Events {
statuses: vec![CompletionStatus::zero(); cap].into_boxed_slice(),
events: Vec::with_capacity(cap),
}
}
pub fn is_empty(&self) -> bool {
self.events.is_empty()
}
pub fn capacity(&self) -> usize {
self.events.capacity()
}
pub fn get(&self, idx: usize) -> Option<&Event> {
self.events.get(idx)
}
pub fn clear(&mut self) {
self.events.truncate(0);
for c in 0..self.statuses.len() {
self.statuses[c] = CompletionStatus::zero();
}
}
}

View File

@ -0,0 +1,116 @@
//! A "Manual Arc" which allows manually frobbing the reference count
//!
//! This module contains a copy of the `Arc` found in the standard library,
//! stripped down to the bare bones of what we actually need. The reason this is
//! done is for the ability to concretely know the memory layout of the `Inner`
//! structure of the arc pointer itself (e.g. `ArcInner` in the standard
//! library).
//!
//! We do some unsafe casting from `*mut OVERLAPPED` to a `FromRawArc<T>` to
//! ensure that data lives for the length of an I/O operation, but this means
//! that we have to know the layouts of the structures involved. This
//! representation primarily guarantees that the data, `T` is at the front of
//! the inner pointer always.
//!
//! Note that we're missing out on some various optimizations implemented in the
//! standard library:
//!
//! * The size of `FromRawArc` is actually two words because of the drop flag
//! * The compiler doesn't understand that the pointer in `FromRawArc` is never
//! null, so Option<FromRawArc<T>> is not a nullable pointer.
use std::ops::Deref;
use std::mem;
use std::sync::atomic::{self, AtomicUsize, Ordering};
pub struct FromRawArc<T> {
_inner: *mut Inner<T>,
}
unsafe impl<T: Sync + Send> Send for FromRawArc<T> { }
unsafe impl<T: Sync + Send> Sync for FromRawArc<T> { }
#[repr(C)]
struct Inner<T> {
data: T,
cnt: AtomicUsize,
}
impl<T> FromRawArc<T> {
pub fn new(data: T) -> FromRawArc<T> {
let x = Box::new(Inner {
data: data,
cnt: AtomicUsize::new(1),
});
FromRawArc { _inner: unsafe { mem::transmute(x) } }
}
pub unsafe fn from_raw(ptr: *mut T) -> FromRawArc<T> {
// Note that if we could use `mem::transmute` here to get a libstd Arc
// (guaranteed) then we could just use std::sync::Arc, but this is the
// crucial reason this currently exists.
FromRawArc { _inner: ptr as *mut Inner<T> }
}
}
impl<T> Clone for FromRawArc<T> {
fn clone(&self) -> FromRawArc<T> {
// Atomic ordering of Relaxed lifted from libstd, but the general idea
// is that you need synchronization to communicate this increment to
// another thread, so this itself doesn't need to be synchronized.
unsafe {
(*self._inner).cnt.fetch_add(1, Ordering::Relaxed);
}
FromRawArc { _inner: self._inner }
}
}
impl<T> Deref for FromRawArc<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &(*self._inner).data }
}
}
impl<T> Drop for FromRawArc<T> {
fn drop(&mut self) {
unsafe {
// Atomic orderings lifted from the standard library
if (*self._inner).cnt.fetch_sub(1, Ordering::Release) != 1 {
return
}
atomic::fence(Ordering::Acquire);
drop(mem::transmute::<_, Box<T>>(self._inner));
}
}
}
#[cfg(test)]
mod tests {
use super::FromRawArc;
#[test]
fn smoke() {
let a = FromRawArc::new(1);
assert_eq!(*a, 1);
assert_eq!(*a.clone(), 1);
}
#[test]
fn drops() {
struct A<'a>(&'a mut bool);
impl<'a> Drop for A<'a> {
fn drop(&mut self) {
*self.0 = true;
}
}
let mut a = false;
{
let a = FromRawArc::new(A(&mut a));
let _ = a.clone();
assert!(!*a.0);
}
assert!(a);
}
}

View File

@ -1,32 +0,0 @@
use ntapi::ntioapi::{IO_STATUS_BLOCK_u, IO_STATUS_BLOCK};
use std::cell::UnsafeCell;
use std::fmt;
pub struct IoStatusBlock(UnsafeCell<IO_STATUS_BLOCK>);
// There is a pointer field in `IO_STATUS_BLOCK_u`, which we don't use that. Thus it is safe to implement Send here.
unsafe impl Send for IoStatusBlock {}
impl IoStatusBlock {
pub fn zeroed() -> IoStatusBlock {
let iosb = IO_STATUS_BLOCK {
u: IO_STATUS_BLOCK_u { Status: 0 },
Information: 0,
};
IoStatusBlock(UnsafeCell::new(iosb))
}
pub fn as_ptr(&self) -> *const IO_STATUS_BLOCK {
self.0.get()
}
pub fn as_mut_ptr(&self) -> *mut IO_STATUS_BLOCK {
self.0.get()
}
}
impl fmt::Debug for IoStatusBlock {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("IoStatusBlock").finish()
}
}

View File

@ -1,127 +1,187 @@
//! Implementation of mio for Windows using IOCP
//!
//! This module uses I/O Completion Ports (IOCP) on Windows to implement mio's
//! Unix epoll-like interface. Unfortunately these two I/O models are
//! fundamentally incompatible:
//!
//! * IOCP is a completion-based model where work is submitted to the kernel and
//! a program is notified later when the work finished.
//! * epoll is a readiness-based model where the kernel is queried as to what
//! work can be done, and afterwards the work is done.
//!
//! As a result, this implementation for Windows is much less "low level" than
//! the Unix implementation of mio. This design decision was intentional,
//! however.
//!
//! ## What is IOCP?
//!
//! The [official docs][docs] have a comprehensive explanation of what IOCP is,
//! but at a high level it requires the following operations to be executed to
//! perform some I/O:
//!
//! 1. A completion port is created
//! 2. An I/O handle and a token is registered with this completion port
//! 3. Some I/O is issued on the handle. This generally means that an API was
//! invoked with a zeroed `OVERLAPPED` structure. The API will immediately
//! return.
//! 4. After some time, the application queries the I/O port for completed
//! events. The port will returned a pointer to the `OVERLAPPED` along with
//! the token presented at registration time.
//!
//! Many I/O operations can be fired off before waiting on a port, and the port
//! will block execution of the calling thread until an I/O event has completed
//! (or a timeout has elapsed).
//!
//! Currently all of these low-level operations are housed in a separate `miow`
//! crate to provide a 0-cost abstraction over IOCP. This crate uses that to
//! implement all fiddly bits so there's very few actual Windows API calls or
//! `unsafe` blocks as a result.
//!
//! [docs]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365198%28v=vs.85%29.aspx
//!
//! ## Safety of IOCP
//!
//! Unfortunately for us, IOCP is pretty unsafe in terms of Rust lifetimes and
//! such. When an I/O operation is submitted to the kernel, it involves handing
//! the kernel a few pointers like a buffer to read/write, an `OVERLAPPED`
//! structure pointer, and perhaps some other buffers such as for socket
//! addresses. These pointers all have to remain valid **for the entire I/O
//! operation's duration**.
//!
//! There's no way to define a safe lifetime for these pointers/buffers over
//! the span of an I/O operation, so we're forced to add a layer of abstraction
//! (not 0-cost) to make these APIs safe. Currently this implementation
//! basically just boxes everything up on the heap to give it a stable address
//! and then keys off that most of the time.
//!
//! ## From completion to readiness
//!
//! Translating a completion-based model to a readiness-based model is also no
//! easy task, and a significant portion of this implementation is managing this
//! translation. The basic idea behind this implementation is to issue I/O
//! operations preemptively and then translate their completions to a "I'm
//! ready" event.
//!
//! For example, in the case of reading a `TcpSocket`, as soon as a socket is
//! connected (or registered after an accept) a read operation is executed.
//! While the read is in progress calls to `read` will return `WouldBlock`, and
//! once the read is completed we translate the completion notification into a
//! `readable` event. Once the internal buffer is drained (e.g. all data from it
//! has been read) a read operation is re-issued.
//!
//! Write operations are a little different from reads, and the current
//! implementation is to just schedule a write as soon as `write` is first
//! called. While that write operation is in progress all future calls to
//! `write` will return `WouldBlock`. Completion of the write then translates to
//! a `writable` event. Note that this will probably want to add some layer of
//! internal buffering in the future.
//!
//! ## Buffer Management
//!
//! As there's lots of I/O operations in flight at any one point in time,
//! there's lots of live buffers that need to be juggled around (e.g. this
//! implementation's own internal buffers).
//!
//! Currently all buffers are created for the I/O operation at hand and are then
//! discarded when it completes (this is listed as future work below).
//!
//! ## Callback Management
//!
//! When the main event loop receives a notification that an I/O operation has
//! completed, some work needs to be done to translate that to a set of events
//! or perhaps some more I/O needs to be scheduled. For example after a
//! `TcpStream` is connected it generates a writable event and also schedules a
//! read.
//!
//! To manage all this the `Selector` uses the `OVERLAPPED` pointer from the
//! completion status. The selector assumes that all `OVERLAPPED` pointers are
//! actually pointers to the interior of a `selector::Overlapped` which means
//! that right after the `OVERLAPPED` itself there's a function pointer. This
//! function pointer is given the completion status as well as another callback
//! to push events onto the selector.
//!
//! The callback for each I/O operation doesn't have any environment, so it
//! relies on memory layout and unsafe casting to translate an `OVERLAPPED`
//! pointer (or in this case a `selector::Overlapped` pointer) to a type of
//! `FromRawArc<T>` (see module docs for why this type exists).
//!
//! ## Thread Safety
//!
//! Currently all of the I/O primitives make liberal use of `Arc` and `Mutex`
//! as an implementation detail. The main reason for this is to ensure that the
//! types are `Send` and `Sync`, but the implementations have not been stressed
//! in multithreaded situations yet. As a result, there are bound to be
//! functional surprises in using these concurrently.
//!
//! ## Future Work
//!
//! First up, let's take a look at unimplemented portions of this module:
//!
//! * The `PollOpt::level()` option is currently entirely unimplemented.
//! * Each `EventLoop` currently owns its completion port, but this prevents an
//! I/O handle from being added to multiple event loops (something that can be
//! done on Unix). Additionally, it hinders event loops moving across threads.
//! This should be solved by likely having a global `Selector` which all
//! others then communicate with.
//! * Although Unix sockets don't exist on Windows, there are named pipes and
//! those should likely be bound here in a similar fashion to `TcpStream`.
//!
//! Next up, there are a few performance improvements and optimizations that can
//! still be implemented
//!
//! * Buffer management right now is pretty bad, they're all just allocated
//! right before an I/O operation and discarded right after. There should at
//! least be some form of buffering buffers.
//! * No calls to `write` are internally buffered before being scheduled, which
//! means that writing performance is abysmal compared to Unix. There should
//! be some level of buffering of writes probably.
use std::io;
use std::mem::size_of_val;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::sync::{Arc, Mutex, Once};
use winapi::ctypes::c_int;
use winapi::shared::ws2def::SOCKADDR;
use winapi::um::winsock2::{
ioctlsocket, socket, FIONBIO, INVALID_SOCKET, PF_INET, PF_INET6, SOCKET,
};
use std::os::windows::prelude::*;
/// Helper macro to execute a system call that returns an `io::Result`.
//
// Macro must be defined before any modules that uses them.
macro_rules! syscall {
($fn: ident ( $($arg: expr),* $(,)* ), $err_test: path, $err_value: expr) => {{
let res = unsafe { $fn($($arg, )*) };
if $err_test(&res, &$err_value) {
Err(io::Error::last_os_error())
} else {
Ok(res)
}
}};
}
use kernel32;
use winapi;
mod afd;
pub mod event;
mod io_status_block;
mod awakener;
#[macro_use]
mod selector;
mod tcp;
mod udp;
mod waker;
mod from_raw_arc;
mod buffer_pool;
pub use event::{Event, Events};
pub use selector::{Selector, SelectorInner, SockState};
pub use tcp::{TcpListener, TcpStream};
pub use udp::UdpSocket;
pub use waker::Waker;
pub use self::awakener::Awakener;
pub use self::selector::{Events, Selector, Overlapped, Binding};
pub use self::tcp::{TcpStream, TcpListener};
pub use self::udp::UdpSocket;
pub trait SocketState {
fn get_sock_state(&self) -> Option<Arc<Mutex<SockState>>>;
fn set_sock_state(&self, sock_state: Option<Arc<Mutex<SockState>>>);
#[derive(Copy, Clone)]
enum Family {
V4, V6,
}
use crate::{Interests, Token};
struct InternalState {
selector: Arc<SelectorInner>,
token: Token,
interests: Interests,
sock_state: Option<Arc<Mutex<SockState>>>,
}
impl InternalState {
fn new(selector: Arc<SelectorInner>, token: Token, interests: Interests) -> InternalState {
InternalState {
selector,
token,
interests,
sock_state: None,
}
unsafe fn cancel(socket: &AsRawSocket,
overlapped: &Overlapped) -> io::Result<()> {
let handle = socket.as_raw_socket() as winapi::HANDLE;
let ret = kernel32::CancelIoEx(handle, overlapped.as_mut_ptr());
if ret == 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
impl Drop for InternalState {
fn drop(&mut self) {
if let Some(sock_state) = self.sock_state.as_ref() {
let mut sock_state = sock_state.lock().unwrap();
sock_state.mark_delete();
}
}
}
/// Initialise the network stack for Windows.
fn init() {
static INIT: Once = Once::new();
INIT.call_once(|| {
// Let standard library call `WSAStartup` for us, we can't do it
// ourselves because otherwise using any type in `std::net` would panic
// when it tries to call `WSAStartup` a second time.
drop(std::net::UdpSocket::bind("127.0.0.1:0"));
});
}
/// Create a new non-blocking socket.
fn new_socket(addr: SocketAddr, socket_type: c_int) -> io::Result<SOCKET> {
let domain = match addr {
SocketAddr::V4(..) => PF_INET,
SocketAddr::V6(..) => PF_INET6,
};
syscall!(
socket(domain, socket_type, 0),
PartialEq::eq,
INVALID_SOCKET
)
.and_then(|socket| {
syscall!(ioctlsocket(socket, FIONBIO, &mut 1), PartialEq::ne, 0).map(|_| socket as SOCKET)
})
}
fn socket_addr(addr: &SocketAddr) -> (*const SOCKADDR, c_int) {
match addr {
SocketAddr::V4(ref addr) => (
addr as *const _ as *const SOCKADDR,
size_of_val(addr) as c_int,
),
SocketAddr::V6(ref addr) => (
addr as *const _ as *const SOCKADDR,
size_of_val(addr) as c_int,
),
}
}
fn inaddr_any(other: SocketAddr) -> SocketAddr {
match other {
SocketAddr::V4(..) => {
let any = Ipv4Addr::new(0, 0, 0, 0);
let addr = SocketAddrV4::new(any, 0);
SocketAddr::V4(addr)
}
SocketAddr::V6(..) => {
let any = Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 0);
let addr = SocketAddrV6::new(any, 0, 0, 0);
SocketAddr::V6(addr)
}
unsafe fn no_notify_on_instant_completion(handle: winapi::HANDLE) -> io::Result<()> {
// TODO: move those to winapi
const FILE_SKIP_COMPLETION_PORT_ON_SUCCESS: winapi::UCHAR = 1;
const FILE_SKIP_SET_EVENT_ON_HANDLE: winapi::UCHAR = 2;
let flags = FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE;
let r = kernel32::SetFileCompletionNotificationModes(handle, flags);
if r == winapi::TRUE {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,267 +1,413 @@
use super::selector::SockState;
use super::InternalState;
use super::{new_socket, socket_addr};
use crate::poll;
use crate::sys::windows::init;
use crate::{event, Interests, Registry, Token};
//! UDP for IOCP
//!
//! Note that most of this module is quite similar to the TCP module, so if
//! something seems odd you may also want to try the docs over there.
use std::fmt;
use std::io::prelude::*;
use std::io;
use std::mem;
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use std::os::windows::raw::SOCKET as StdSocket; // winapi uses usize, stdlib uses u32/u64.
use std::sync::{Arc, Mutex};
use std::{fmt, io};
use winapi::um::winsock2::{bind, closesocket, SOCKET_ERROR, SOCK_DGRAM};
use std::sync::{Mutex, MutexGuard};
#[allow(unused_imports)]
use net2::{UdpBuilder, UdpSocketExt};
use winapi::*;
use miow::iocp::CompletionStatus;
use miow::net::SocketAddrBuf;
use miow::net::UdpSocketExt as MiowUdpSocketExt;
use {poll, Ready, Poll, PollOpt, Token};
use event::Evented;
use sys::windows::from_raw_arc::FromRawArc;
use sys::windows::selector::{Overlapped, ReadyBinding};
pub struct UdpSocket {
internal: Arc<Mutex<Option<InternalState>>>,
io: net::UdpSocket,
imp: Imp,
registration: Mutex<Option<poll::Registration>>,
}
macro_rules! wouldblock {
($self:ident, $method:ident, $($args:expr),* ) => {{
let result = $self.io.$method($($args),*);
if let Err(ref e) = result {
if e.kind() == io::ErrorKind::WouldBlock {
let internal = $self.internal.lock().unwrap();
if internal.is_some() {
let selector = internal.as_ref().unwrap().selector.clone();
let token = internal.as_ref().unwrap().token;
let interests = internal.as_ref().unwrap().interests;
drop(internal);
selector.reregister(
$self,
token,
interests,
)?;
}
}
}
result
}};
#[derive(Clone)]
struct Imp {
inner: FromRawArc<Io>,
}
struct Io {
read: Overlapped,
write: Overlapped,
socket: net::UdpSocket,
inner: Mutex<Inner>,
}
struct Inner {
iocp: ReadyBinding,
read: State<Vec<u8>, Vec<u8>>,
write: State<Vec<u8>, (Vec<u8>, usize)>,
read_buf: SocketAddrBuf,
}
enum State<T, U> {
Empty,
Pending(T),
Ready(U),
Error(io::Error),
}
impl UdpSocket {
pub fn bind(addr: SocketAddr) -> io::Result<UdpSocket> {
init();
new_socket(addr, SOCK_DGRAM).and_then(|socket| {
let (raw_addr, raw_addr_length) = socket_addr(&addr);
syscall!(
bind(socket, raw_addr, raw_addr_length,),
PartialEq::eq,
SOCKET_ERROR
)
.map_err(|err| {
// Close the socket if we hit an error, ignoring the error
// from closing since we can't pass back two errors.
let _ = unsafe { closesocket(socket) };
err
})
.map(|_| UdpSocket {
internal: Arc::new(Mutex::new(None)),
io: unsafe { net::UdpSocket::from_raw_socket(socket as StdSocket) },
})
pub fn new(socket: net::UdpSocket) -> io::Result<UdpSocket> {
Ok(UdpSocket {
registration: Mutex::new(None),
imp: Imp {
inner: FromRawArc::new(Io {
read: Overlapped::new(recv_done),
write: Overlapped::new(send_done),
socket: socket,
inner: Mutex::new(Inner {
iocp: ReadyBinding::new(),
read: State::Empty,
write: State::Empty,
read_buf: SocketAddrBuf::new(),
}),
}),
},
})
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.local_addr()
self.imp.inner.socket.local_addr()
}
pub fn try_clone(&self) -> io::Result<UdpSocket> {
self.io.try_clone().map(|io| UdpSocket {
internal: Arc::new(Mutex::new(None)),
io,
})
self.imp.inner.socket.try_clone().and_then(UdpSocket::new)
}
pub fn send_to(&self, buf: &[u8], target: SocketAddr) -> io::Result<usize> {
wouldblock!(self, send_to, buf, target)
}
pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
wouldblock!(self, recv_from, buf)
}
pub fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
wouldblock!(self, peek_from, buf)
/// Note that unlike `TcpStream::write` this function will not attempt to
/// continue writing `buf` until its entirely written.
///
/// TODO: This... may be wrong in the long run. We're reporting that we
/// successfully wrote all of the bytes in `buf` but it's possible
/// that we don't actually end up writing all of them!
pub fn send_to(&self, buf: &[u8], target: &SocketAddr)
-> io::Result<usize> {
let mut me = self.inner();
let me = &mut *me;
match me.write {
State::Empty => {}
_ => return Err(io::ErrorKind::WouldBlock.into()),
}
if !me.iocp.registered() {
return Err(io::ErrorKind::WouldBlock.into())
}
let interest = me.iocp.readiness();
me.iocp.set_readiness(interest - Ready::writable());
let mut owned_buf = me.iocp.get_buffer(64 * 1024);
let amt = owned_buf.write(buf)?;
unsafe {
trace!("scheduling a send");
self.imp.inner.socket.send_to_overlapped(&owned_buf, target,
self.imp.inner.write.as_mut_ptr())
}?;
me.write = State::Pending(owned_buf);
mem::forget(self.imp.clone());
Ok(amt)
}
/// Note that unlike `TcpStream::write` this function will not attempt to
/// continue writing `buf` until its entirely written.
///
/// TODO: This... may be wrong in the long run. We're reporting that we
/// successfully wrote all of the bytes in `buf` but it's possible
/// that we don't actually end up writing all of them!
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
wouldblock!(self, send, buf)
let mut me = self.inner();
let me = &mut *me;
match me.write {
State::Empty => {}
_ => return Err(io::ErrorKind::WouldBlock.into()),
}
if !me.iocp.registered() {
return Err(io::ErrorKind::WouldBlock.into())
}
let interest = me.iocp.readiness();
me.iocp.set_readiness(interest - Ready::writable());
let mut owned_buf = me.iocp.get_buffer(64 * 1024);
let amt = owned_buf.write(buf)?;
unsafe {
trace!("scheduling a send");
self.imp.inner.socket.send_overlapped(&owned_buf, self.imp.inner.write.as_mut_ptr())
}?;
me.write = State::Pending(owned_buf);
mem::forget(self.imp.clone());
Ok(amt)
}
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
wouldblock!(self, recv, buf)
pub fn recv_from(&self, mut buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
let mut me = self.inner();
match mem::replace(&mut me.read, State::Empty) {
State::Empty => Err(io::ErrorKind::WouldBlock.into()),
State::Pending(b) => { me.read = State::Pending(b); Err(io::ErrorKind::WouldBlock.into()) }
State::Ready(data) => {
// If we weren't provided enough space to receive the message
// then don't actually read any data, just return an error.
if buf.len() < data.len() {
me.read = State::Ready(data);
Err(io::Error::from_raw_os_error(WSAEMSGSIZE as i32))
} else {
let r = if let Some(addr) = me.read_buf.to_socket_addr() {
buf.write(&data).unwrap();
Ok((data.len(), addr))
} else {
Err(io::Error::new(io::ErrorKind::Other,
"failed to parse socket address"))
};
me.iocp.put_buffer(data);
self.imp.schedule_read_from(&mut me);
r
}
}
State::Error(e) => {
self.imp.schedule_read_from(&mut me);
Err(e)
}
}
}
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
wouldblock!(self, peek, buf)
pub fn recv(&self, buf: &mut [u8])
-> io::Result<usize> {
//Since recv_from can be used on connected sockets just call it and drop the address.
self.recv_from(buf).map(|(size,_)| size)
}
pub fn connect(&self, addr: SocketAddr) -> io::Result<()> {
self.io.connect(addr)
self.imp.inner.socket.connect(addr)
}
pub fn broadcast(&self) -> io::Result<bool> {
self.io.broadcast()
self.imp.inner.socket.broadcast()
}
pub fn set_broadcast(&self, on: bool) -> io::Result<()> {
self.io.set_broadcast(on)
self.imp.inner.socket.set_broadcast(on)
}
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
self.io.multicast_loop_v4()
self.imp.inner.socket.multicast_loop_v4()
}
pub fn set_multicast_loop_v4(&self, on: bool) -> io::Result<()> {
self.io.set_multicast_loop_v4(on)
self.imp.inner.socket.set_multicast_loop_v4(on)
}
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
self.io.multicast_ttl_v4()
self.imp.inner.socket.multicast_ttl_v4()
}
pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> {
self.io.set_multicast_ttl_v4(ttl)
self.imp.inner.socket.set_multicast_ttl_v4(ttl)
}
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
self.io.multicast_loop_v6()
self.imp.inner.socket.multicast_loop_v6()
}
pub fn set_multicast_loop_v6(&self, on: bool) -> io::Result<()> {
self.io.set_multicast_loop_v6(on)
self.imp.inner.socket.set_multicast_loop_v6(on)
}
pub fn ttl(&self) -> io::Result<u32> {
self.io.ttl()
self.imp.inner.socket.ttl()
}
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.io.set_ttl(ttl)
self.imp.inner.socket.set_ttl(ttl)
}
pub fn join_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> {
self.io.join_multicast_v4(&multiaddr, &interface)
pub fn join_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.imp.inner.socket.join_multicast_v4(multiaddr, interface)
}
pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
self.io.join_multicast_v6(multiaddr, interface)
pub fn join_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.imp.inner.socket.join_multicast_v6(multiaddr, interface)
}
pub fn leave_multicast_v4(&self, multiaddr: Ipv4Addr, interface: Ipv4Addr) -> io::Result<()> {
self.io.leave_multicast_v4(&multiaddr, &interface)
pub fn leave_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.imp.inner.socket.leave_multicast_v4(multiaddr, interface)
}
pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
self.io.leave_multicast_v6(multiaddr, interface)
pub fn leave_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.imp.inner.socket.leave_multicast_v6(multiaddr, interface)
}
pub fn set_only_v6(&self, only_v6: bool) -> io::Result<()> {
self.imp.inner.socket.set_only_v6(only_v6)
}
pub fn only_v6(&self) -> io::Result<bool> {
self.imp.inner.socket.only_v6()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.io.take_error()
self.imp.inner.socket.take_error()
}
fn inner(&self) -> MutexGuard<Inner> {
self.imp.inner()
}
fn post_register(&self, interest: Ready, me: &mut Inner) {
if interest.is_readable() {
//We use recv_from here since it is well specified for both
//connected and non-connected sockets and we can discard the address
//when calling recv().
self.imp.schedule_read_from(me);
}
// See comments in TcpSocket::post_register for what's going on here
if interest.is_writable() {
if let State::Empty = me.write {
self.imp.add_readiness(me, Ready::writable());
}
}
}
}
impl super::SocketState for UdpSocket {
fn get_sock_state(&self) -> Option<Arc<Mutex<SockState>>> {
let internal = self.internal.lock().unwrap();
match &*internal {
Some(internal) => match &internal.sock_state {
Some(arc) => Some(arc.clone()),
None => None,
},
None => None,
impl Imp {
fn inner(&self) -> MutexGuard<Inner> {
self.inner.inner.lock().unwrap()
}
fn schedule_read_from(&self, me: &mut Inner) {
match me.read {
State::Empty => {}
_ => return,
}
let interest = me.iocp.readiness();
me.iocp.set_readiness(interest - Ready::readable());
let mut buf = me.iocp.get_buffer(64 * 1024);
let res = unsafe {
trace!("scheduling a read");
let cap = buf.capacity();
buf.set_len(cap);
self.inner.socket.recv_from_overlapped(&mut buf, &mut me.read_buf,
self.inner.read.as_mut_ptr())
};
match res {
Ok(_) => {
me.read = State::Pending(buf);
mem::forget(self.clone());
}
Err(e) => {
me.read = State::Error(e);
self.add_readiness(me, Ready::readable());
me.iocp.put_buffer(buf);
}
}
}
fn set_sock_state(&self, sock_state: Option<Arc<Mutex<SockState>>>) {
let mut internal = self.internal.lock().unwrap();
match &mut *internal {
Some(internal) => {
internal.sock_state = sock_state;
}
None => {}
};
// See comments in tcp::StreamImp::push
fn add_readiness(&self, me: &Inner, set: Ready) {
me.iocp.set_readiness(set | me.iocp.readiness());
}
}
impl event::Source for UdpSocket {
fn register(&self, registry: &Registry, token: Token, interests: Interests) -> io::Result<()> {
{
let mut internal = self.internal.lock().unwrap();
if internal.is_none() {
*internal = Some(InternalState::new(
poll::selector(registry).clone_inner(),
token,
interests,
));
}
}
let result = poll::selector(registry).register(self, token, interests);
match result {
Ok(_) => {}
Err(_) => {
let mut internal = self.internal.lock().unwrap();
*internal = None;
}
}
result
impl Evented for UdpSocket {
fn register(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
let mut me = self.inner();
me.iocp.register_socket(&self.imp.inner.socket,
poll, token, interest, opts,
&self.registration)?;
self.post_register(interest, &mut me);
Ok(())
}
fn reregister(
&self,
registry: &Registry,
token: Token,
interests: Interests,
) -> io::Result<()> {
let result = poll::selector(registry).reregister(self, token, interests);
match result {
Ok(_) => {
let mut internal = self.internal.lock().unwrap();
internal.as_mut().unwrap().token = token;
internal.as_mut().unwrap().interests = interests;
}
Err(_) => {}
};
result
fn reregister(&self, poll: &Poll, token: Token,
interest: Ready, opts: PollOpt) -> io::Result<()> {
let mut me = self.inner();
me.iocp.reregister_socket(&self.imp.inner.socket,
poll, token, interest,
opts, &self.registration)?;
self.post_register(interest, &mut me);
Ok(())
}
fn deregister(&self, registry: &Registry) -> io::Result<()> {
let result = poll::selector(registry).deregister(self);
match result {
Ok(_) => {
let mut internal = self.internal.lock().unwrap();
*internal = None;
}
Err(_) => {}
};
result
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.inner().iocp.deregister(&self.imp.inner.socket,
poll, &self.registration)
}
}
impl fmt::Debug for UdpSocket {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.io, f)
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("UdpSocket")
.finish()
}
}
impl FromRawSocket for UdpSocket {
unsafe fn from_raw_socket(rawsocket: RawSocket) -> UdpSocket {
UdpSocket {
internal: Arc::new(Mutex::new(None)),
io: net::UdpSocket::from_raw_socket(rawsocket),
impl Drop for UdpSocket {
fn drop(&mut self) {
let inner = self.inner();
// If we're still internally reading, we're no longer interested. Note
// though that we don't cancel any writes which may have been issued to
// preserve the same semantics as Unix.
unsafe {
match inner.read {
State::Pending(_) => {
drop(super::cancel(&self.imp.inner.socket,
&self.imp.inner.read));
}
State::Empty |
State::Ready(_) |
State::Error(_) => {}
}
}
}
}
impl IntoRawSocket for UdpSocket {
fn into_raw_socket(self) -> RawSocket {
self.io.as_raw_socket()
}
fn send_done(status: &OVERLAPPED_ENTRY) {
let status = CompletionStatus::from_entry(status);
trace!("finished a send {}", status.bytes_transferred());
let me2 = Imp {
inner: unsafe { overlapped2arc!(status.overlapped(), Io, write) },
};
let mut me = me2.inner();
me.write = State::Empty;
me2.add_readiness(&mut me, Ready::writable());
}
impl AsRawSocket for UdpSocket {
fn as_raw_socket(&self) -> RawSocket {
self.io.as_raw_socket()
fn recv_done(status: &OVERLAPPED_ENTRY) {
let status = CompletionStatus::from_entry(status);
trace!("finished a recv {}", status.bytes_transferred());
let me2 = Imp {
inner: unsafe { overlapped2arc!(status.overlapped(), Io, read) },
};
let mut me = me2.inner();
let mut buf = match mem::replace(&mut me.read, State::Empty) {
State::Pending(buf) => buf,
_ => unreachable!(),
};
unsafe {
buf.set_len(status.bytes_transferred() as usize);
}
me.read = State::Ready(buf);
me2.add_readiness(&mut me, Ready::readable());
}

View File

@ -1,27 +0,0 @@
use crate::sys::windows::{Selector, SelectorInner};
use crate::Token;
use miow::iocp::CompletionStatus;
use std::io;
use std::sync::Arc;
#[derive(Debug)]
pub struct Waker {
token: Token,
selector: Arc<SelectorInner>,
}
impl Waker {
pub fn new(selector: &Selector, token: Token) -> io::Result<Waker> {
Ok(Waker {
token,
selector: selector.clone_inner(),
})
}
pub fn wake(&self) -> io::Result<()> {
// Keep NULL as Overlapped value to notify waking.
let status = CompletionStatus::new(0, self.token.0, 0 as *mut _);
self.selector.port().post(status)
}
}

516
src/timer.rs Normal file
View File

@ -0,0 +1,516 @@
//! Timer optimized for I/O related operations
#![allow(deprecated, missing_debug_implementations)]
use {convert, io, Ready, Poll, PollOpt, Registration, SetReadiness, Token};
use event::Evented;
use lazycell::LazyCell;
use slab::Slab;
use std::{cmp, error, fmt, u64, usize, iter, thread};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::{Duration, Instant};
use self::TimerErrorKind::TimerOverflow;
pub struct Timer<T> {
// Size of each tick in milliseconds
tick_ms: u64,
// Slab of timeout entries
entries: Slab<Entry<T>>,
// Timeout wheel. Each tick, the timer will look at the next slot for
// timeouts that match the current tick.
wheel: Vec<WheelEntry>,
// Tick 0's time instant
start: Instant,
// The current tick
tick: Tick,
// The next entry to possibly timeout
next: Token,
// Masks the target tick to get the slot
mask: u64,
// Set on registration with Poll
inner: LazyCell<Inner>,
}
pub struct Builder {
// Approximate duration of each tick
tick: Duration,
// Number of slots in the timer wheel
num_slots: usize,
// Max number of timeouts that can be in flight at a given time.
capacity: usize,
}
#[derive(Clone, Debug)]
pub struct Timeout {
// Reference into the timer entry slab
token: Token,
// Tick that it should match up with
tick: u64,
}
struct Inner {
registration: Registration,
set_readiness: SetReadiness,
wakeup_state: WakeupState,
wakeup_thread: thread::JoinHandle<()>,
}
impl Drop for Inner {
fn drop(&mut self) {
// 1. Set wakeup state to TERMINATE_THREAD (https://github.com/carllerche/mio/blob/master/src/timer.rs#L451)
self.wakeup_state.store(TERMINATE_THREAD, Ordering::Release);
// 2. Wake him up
self.wakeup_thread.thread().unpark();
}
}
#[derive(Copy, Clone, Debug)]
struct WheelEntry {
next_tick: Tick,
head: Token,
}
// Doubly linked list of timer entries. Allows for efficient insertion /
// removal of timeouts.
struct Entry<T> {
state: T,
links: EntryLinks,
}
#[derive(Copy, Clone)]
struct EntryLinks {
tick: Tick,
prev: Token,
next: Token
}
type Tick = u64;
const TICK_MAX: Tick = u64::MAX;
// Manages communication with wakeup thread
type WakeupState = Arc<AtomicUsize>;
pub type Result<T> = ::std::result::Result<T, TimerError>;
// TODO: remove
pub type TimerResult<T> = Result<T>;
/// Deprecated and unused.
#[derive(Debug)]
pub struct TimerError;
/// Deprecated and unused.
#[derive(Debug)]
pub enum TimerErrorKind {
TimerOverflow,
}
// TODO: Remove
pub type OldTimerResult<T> = Result<T>;
const TERMINATE_THREAD: usize = 0;
const EMPTY: Token = Token(usize::MAX);
impl Builder {
pub fn tick_duration(mut self, duration: Duration) -> Builder {
self.tick = duration;
self
}
pub fn num_slots(mut self, num_slots: usize) -> Builder {
self.num_slots = num_slots;
self
}
pub fn capacity(mut self, capacity: usize) -> Builder {
self.capacity = capacity;
self
}
pub fn build<T>(self) -> Timer<T> {
Timer::new(convert::millis(self.tick), self.num_slots, self.capacity, Instant::now())
}
}
impl Default for Builder {
fn default() -> Builder {
Builder {
tick: Duration::from_millis(100),
num_slots: 256,
capacity: 65_536,
}
}
}
impl<T> Timer<T> {
fn new(tick_ms: u64, num_slots: usize, capacity: usize, start: Instant) -> Timer<T> {
let num_slots = num_slots.next_power_of_two();
let capacity = capacity.next_power_of_two();
let mask = (num_slots as u64) - 1;
let wheel = iter::repeat(WheelEntry { next_tick: TICK_MAX, head: EMPTY })
.take(num_slots).collect();
Timer {
tick_ms,
entries: Slab::with_capacity(capacity),
wheel,
start,
tick: 0,
next: EMPTY,
mask,
inner: LazyCell::new(),
}
}
pub fn set_timeout(&mut self, delay_from_now: Duration, state: T) -> Result<Timeout> {
let delay_from_start = self.start.elapsed() + delay_from_now;
self.set_timeout_at(delay_from_start, state)
}
fn set_timeout_at(&mut self, delay_from_start: Duration, state: T) -> Result<Timeout> {
let mut tick = duration_to_tick(delay_from_start, self.tick_ms);
trace!("setting timeout; delay={:?}; tick={:?}; current-tick={:?}", delay_from_start, tick, self.tick);
// Always target at least 1 tick in the future
if tick <= self.tick {
tick = self.tick + 1;
}
self.insert(tick, state)
}
fn insert(&mut self, tick: Tick, state: T) -> Result<Timeout> {
// Get the slot for the requested tick
let slot = (tick & self.mask) as usize;
let curr = self.wheel[slot];
// Insert the new entry
let entry = Entry::new(state, tick, curr.head);
let token = Token(self.entries.insert(entry));
if curr.head != EMPTY {
// If there was a previous entry, set its prev pointer to the new
// entry
self.entries[curr.head.into()].links.prev = token;
}
// Update the head slot
self.wheel[slot] = WheelEntry {
next_tick: cmp::min(tick, curr.next_tick),
head: token,
};
self.schedule_readiness(tick);
trace!("inserted timeout; slot={}; token={:?}", slot, token);
// Return the new timeout
Ok(Timeout {
token,
tick
})
}
pub fn cancel_timeout(&mut self, timeout: &Timeout) -> Option<T> {
let links = match self.entries.get(timeout.token.into()) {
Some(e) => e.links,
None => return None
};
// Sanity check
if links.tick != timeout.tick {
return None;
}
self.unlink(&links, timeout.token);
Some(self.entries.remove(timeout.token.into()).state)
}
pub fn poll(&mut self) -> Option<T> {
let target_tick = current_tick(self.start, self.tick_ms);
self.poll_to(target_tick)
}
fn poll_to(&mut self, mut target_tick: Tick) -> Option<T> {
trace!("tick_to; target_tick={}; current_tick={}", target_tick, self.tick);
if target_tick < self.tick {
target_tick = self.tick;
}
while self.tick <= target_tick {
let curr = self.next;
trace!("ticking; curr={:?}", curr);
if curr == EMPTY {
self.tick += 1;
let slot = self.slot_for(self.tick);
self.next = self.wheel[slot].head;
// Handle the case when a slot has a single timeout which gets
// canceled before the timeout expires. In this case, the
// slot's head is EMPTY but there is a value for next_tick. Not
// resetting next_tick here causes the timer to get stuck in a
// loop.
if self.next == EMPTY {
self.wheel[slot].next_tick = TICK_MAX;
}
} else {
let slot = self.slot_for(self.tick);
if curr == self.wheel[slot].head {
self.wheel[slot].next_tick = TICK_MAX;
}
let links = self.entries[curr.into()].links;
if links.tick <= self.tick {
trace!("triggering; token={:?}", curr);
// Unlink will also advance self.next
self.unlink(&links, curr);
// Remove and return the token
return Some(self.entries.remove(curr.into()).state);
} else {
let next_tick = self.wheel[slot].next_tick;
self.wheel[slot].next_tick = cmp::min(next_tick, links.tick);
self.next = links.next;
}
}
}
// No more timeouts to poll
if let Some(inner) = self.inner.borrow() {
trace!("unsetting readiness");
let _ = inner.set_readiness.set_readiness(Ready::empty());
if let Some(tick) = self.next_tick() {
self.schedule_readiness(tick);
}
}
None
}
fn unlink(&mut self, links: &EntryLinks, token: Token) {
trace!("unlinking timeout; slot={}; token={:?}",
self.slot_for(links.tick), token);
if links.prev == EMPTY {
let slot = self.slot_for(links.tick);
self.wheel[slot].head = links.next;
} else {
self.entries[links.prev.into()].links.next = links.next;
}
if links.next != EMPTY {
self.entries[links.next.into()].links.prev = links.prev;
if token == self.next {
self.next = links.next;
}
} else if token == self.next {
self.next = EMPTY;
}
}
fn schedule_readiness(&self, tick: Tick) {
if let Some(inner) = self.inner.borrow() {
// Coordinate setting readiness w/ the wakeup thread
let mut curr = inner.wakeup_state.load(Ordering::Acquire);
loop {
if curr as Tick <= tick {
// Nothing to do, wakeup is already scheduled
return;
}
// Attempt to move the wakeup time forward
trace!("advancing the wakeup time; target={}; curr={}", tick, curr);
let actual = inner.wakeup_state.compare_and_swap(curr, tick as usize, Ordering::Release);
if actual == curr {
// Signal to the wakeup thread that the wakeup time has
// been changed.
trace!("unparking wakeup thread");
inner.wakeup_thread.thread().unpark();
return;
}
curr = actual;
}
}
}
// Next tick containing a timeout
fn next_tick(&self) -> Option<Tick> {
if self.next != EMPTY {
let slot = self.slot_for(self.entries[self.next.into()].links.tick);
if self.wheel[slot].next_tick == self.tick {
// There is data ready right now
return Some(self.tick);
}
}
self.wheel.iter().map(|e| e.next_tick).min()
}
fn slot_for(&self, tick: Tick) -> usize {
(self.mask & tick) as usize
}
}
impl<T> Default for Timer<T> {
fn default() -> Timer<T> {
Builder::default().build()
}
}
impl<T> Evented for Timer<T> {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
if self.inner.borrow().is_some() {
return Err(io::Error::new(io::ErrorKind::Other, "timer already registered"));
}
let (registration, set_readiness) = Registration::new(poll, token, interest, opts);
let wakeup_state = Arc::new(AtomicUsize::new(usize::MAX));
let thread_handle = spawn_wakeup_thread(
wakeup_state.clone(),
set_readiness.clone(),
self.start, self.tick_ms);
self.inner.fill(Inner {
registration,
set_readiness,
wakeup_state,
wakeup_thread: thread_handle,
}).expect("timer already registered");
if let Some(next_tick) = self.next_tick() {
self.schedule_readiness(next_tick);
}
Ok(())
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
match self.inner.borrow() {
Some(inner) => inner.registration.update(poll, token, interest, opts),
None => Err(io::Error::new(io::ErrorKind::Other, "receiver not registered")),
}
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
match self.inner.borrow() {
Some(inner) => inner.registration.deregister(poll),
None => Err(io::Error::new(io::ErrorKind::Other, "receiver not registered")),
}
}
}
impl fmt::Debug for Inner {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Inner")
.field("registration", &self.registration)
.field("wakeup_state", &self.wakeup_state.load(Ordering::Relaxed))
.finish()
}
}
fn spawn_wakeup_thread(state: WakeupState, set_readiness: SetReadiness, start: Instant, tick_ms: u64) -> thread::JoinHandle<()> {
thread::spawn(move || {
let mut sleep_until_tick = state.load(Ordering::Acquire) as Tick;
loop {
if sleep_until_tick == TERMINATE_THREAD as Tick {
return;
}
let now_tick = current_tick(start, tick_ms);
trace!("wakeup thread: sleep_until_tick={:?}; now_tick={:?}", sleep_until_tick, now_tick);
if now_tick < sleep_until_tick {
// Calling park_timeout with u64::MAX leads to undefined
// behavior in pthread, causing the park to return immediately
// and causing the thread to tightly spin. Instead of u64::MAX
// on large values, simply use a blocking park.
match tick_ms.checked_mul(sleep_until_tick - now_tick) {
Some(sleep_duration) => {
trace!("sleeping; tick_ms={}; now_tick={}; sleep_until_tick={}; duration={:?}",
tick_ms, now_tick, sleep_until_tick, sleep_duration);
thread::park_timeout(Duration::from_millis(sleep_duration));
}
None => {
trace!("sleeping; tick_ms={}; now_tick={}; blocking sleep",
tick_ms, now_tick);
thread::park();
}
}
sleep_until_tick = state.load(Ordering::Acquire) as Tick;
} else {
let actual = state.compare_and_swap(sleep_until_tick as usize, usize::MAX, Ordering::AcqRel) as Tick;
if actual == sleep_until_tick {
trace!("setting readiness from wakeup thread");
let _ = set_readiness.set_readiness(Ready::readable());
sleep_until_tick = usize::MAX as Tick;
} else {
sleep_until_tick = actual as Tick;
}
}
}
})
}
fn duration_to_tick(elapsed: Duration, tick_ms: u64) -> Tick {
// Calculate tick rounding up to the closest one
let elapsed_ms = convert::millis(elapsed);
elapsed_ms.saturating_add(tick_ms / 2) / tick_ms
}
fn current_tick(start: Instant, tick_ms: u64) -> Tick {
duration_to_tick(start.elapsed(), tick_ms)
}
impl<T> Entry<T> {
fn new(state: T, tick: u64, next: Token) -> Entry<T> {
Entry {
state,
links: EntryLinks {
tick,
prev: EMPTY,
next,
},
}
}
}
impl fmt::Display for TimerError {
fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
// `TimerError` will never be constructed.
unreachable!();
}
}
impl error::Error for TimerError {
fn description(&self) -> &str {
// `TimerError` will never be constructed.
unreachable!();
}
}
impl fmt::Display for TimerErrorKind {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match *self {
TimerOverflow => write!(fmt, "TimerOverflow"),
}
}
}

View File

@ -1,26 +1,19 @@
/// Associates readiness events with [`event::Source`]s.
/// Associates readiness notifications with [`Evented`] handles.
///
/// `Token` is a wrapper around `usize` and is used as an argument to
/// [`Registry::register`] and [`Registry::reregister`].
/// [`Poll::register`] and [`Poll::reregister`].
///
/// See [`Poll`] for more documentation on polling.
///
/// [`event::Source`]: crate::event::Source
/// [`Poll`]: crate::Poll
/// [`Registry::register`]: crate::Registry::register
/// [`Registry::reregister`]: crate::Registry::reregister
///
/// # Example
///
/// Using `Token` to track which socket generated the event. In this example,
/// `HashMap` is used, but usually something like [`slab`] is better.
///
/// [`slab`]: https://crates.io/crates/slab
/// Using `Token` to track which socket generated the notification. In this
/// example, `HashMap` is used, but usually something like [`slab`] is better.
///
/// ```
/// # use std::error::Error;
/// # fn main() -> Result<(), Box<dyn Error>> {
/// use mio::{Events, Interests, Poll, Token};
/// # fn try_main() -> Result<(), Box<Error>> {
/// use mio::{Events, Ready, Poll, PollOpt, Token};
/// use mio::net::TcpListener;
///
/// use std::thread;
@ -41,13 +34,16 @@
/// let mut next_socket_index = 0;
///
/// // The `Poll` instance
/// let mut poll = Poll::new()?;
/// let poll = Poll::new()?;
///
/// // Tcp listener
/// let listener = TcpListener::bind("127.0.0.1:0".parse()?)?;
/// let listener = TcpListener::bind(&"127.0.0.1:0".parse()?)?;
///
/// // Register the listener
/// poll.registry().register(&listener, LISTENER, Interests::READABLE)?;
/// poll.register(&listener,
/// LISTENER,
/// Ready::readable(),
/// PollOpt::edge())?;
///
/// // Spawn a thread that will connect a bunch of sockets then close them
/// let addr = listener.local_addr()?;
@ -57,7 +53,7 @@
/// // +1 here is to connect an extra socket to signal the socket to close
/// for _ in 0..(MAX_SOCKETS+1) {
/// // Connect then drop the socket
/// let _ = TcpStream::connect(addr).unwrap();
/// let _ = TcpStream::connect(&addr).unwrap();
/// }
/// });
///
@ -90,7 +86,10 @@
/// next_socket_index += 1;
///
/// // Register the new socket w/ poll
/// poll.registry().register(&socket, token, Interests::READABLE)?;
/// poll.register(&socket,
/// token,
/// Ready::readable(),
/// PollOpt::edge())?;
///
/// // Store the socket
/// sockets.insert(token, socket);
@ -125,8 +124,19 @@
/// }
/// }
/// }
/// # Ok(())
/// # }
/// #
/// # fn main() {
/// # try_main().unwrap();
/// # }
/// ```
///
/// [`Evented`]: event/trait.Evented.html
/// [`Poll`]: struct.Poll.html
/// [`Poll::register`]: struct.Poll.html#method.register
/// [`Poll::reregister`]: struct.Poll.html#method.reregister
/// [`slab`]: https://crates.io/crates/slab
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Token(pub usize);

326
src/udp.rs Normal file
View File

@ -0,0 +1,326 @@
//! Primitives for working with UDP
//!
//! The types provided in this module are non-blocking by default and are
//! designed to be portable across all supported Mio platforms. As long as the
//! [portability guidelines] are followed, the behavior should be identical no
//! matter the target platform.
//!
//! [portability guidelines]: ../struct.Poll.html#portability
#![allow(deprecated)]
use {sys, Ready, Poll, PollOpt, Token};
use io::{self, MapNonBlock};
use event::Evented;
use poll::SelectorId;
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
/// A User Datagram Protocol socket.
///
/// This is an implementation of a bound UDP socket. This supports both IPv4 and
/// IPv6 addresses, and there is no corresponding notion of a server because UDP
/// is a datagram protocol.
#[derive(Debug)]
pub struct UdpSocket {
sys: sys::UdpSocket,
selector_id: SelectorId,
}
impl UdpSocket {
/// Creates a UDP socket from the given address.
pub fn bind(addr: &SocketAddr) -> io::Result<UdpSocket> {
let socket = net::UdpSocket::bind(addr)?;
UdpSocket::from_socket(socket)
}
/// Creates a new mio-wrapped socket from an underlying and bound std
/// socket.
///
/// This function requires that `socket` has previously been bound to an
/// address to work correctly, and returns an I/O object which can be used
/// with mio to send/receive UDP messages.
///
/// This can be used in conjunction with net2's `UdpBuilder` interface to
/// configure a socket before it's handed off to mio, such as setting
/// options like `reuse_address` or binding to multiple addresses.
pub fn from_socket(socket: net::UdpSocket) -> io::Result<UdpSocket> {
Ok(UdpSocket {
sys: sys::UdpSocket::new(socket)?,
selector_id: SelectorId::new(),
})
}
/// Returns the socket address that this socket was created from.
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.sys.local_addr()
}
/// Creates a new independently owned handle to the underlying socket.
///
/// The returned `UdpSocket` is a reference to the same socket that this
/// object references. Both handles will read and write the same port, and
/// options set on one socket will be propagated to the other.
pub fn try_clone(&self) -> io::Result<UdpSocket> {
self.sys.try_clone()
.map(|s| {
UdpSocket {
sys: s,
selector_id: self.selector_id.clone(),
}
})
}
/// Sends data on the socket to the given address. On success, returns the
/// number of bytes written.
///
/// Address type can be any implementor of `ToSocketAddrs` trait. See its
/// documentation for concrete examples.
pub fn send_to(&self, buf: &[u8], target: &SocketAddr)
-> io::Result<Option<usize>> {
self.sys.send_to(buf, target).map_non_block()
}
/// Receives data from the socket and stores data in the supplied buffer `buf`. On success,
/// returns the number of bytes read and the address from whence the data came.
///
/// The function must be called with valid byte array `buf` of sufficient size to
/// hold the message bytes. If a message is too long to fit in the supplied buffer,
/// excess bytes may be discarded.
///
/// The function does not read from `buf`, but is overwriting previous content of `buf`.
///
/// Assuming the function has read `n` bytes, slicing `&buf[..n]` provides
/// efficient access with iterators and boundary checks.
pub fn recv_from(&self, buf: &mut [u8])
-> io::Result<Option<(usize, SocketAddr)>> {
self.sys.recv_from(buf).map_non_block()
}
/// Sends data on the socket to the address previously bound via connect(). On success,
/// returns the number of bytes written.
pub fn send(&self, buf: &[u8])
-> io::Result<Option<usize>> {
self.sys.send(buf).map_non_block()
}
/// Receives data from the socket previously bound with connect() and stores data in
/// the supplied buffer `buf`. On success, returns the number of bytes read.
///
/// The function must be called with valid byte array `buf` of sufficient size to
/// hold the message bytes. If a message is too long to fit in the supplied buffer,
/// excess bytes may be discarded.
///
/// The function does not read from `buf`, but is overwriting previous content of `buf`.
///
/// Assuming the function has read `n` bytes, slicing `&buf[..n]` provides
/// efficient access with iterators and boundary checks.
pub fn recv(&self, buf: &mut [u8])
-> io::Result<Option<usize>> {
self.sys.recv(buf).map_non_block()
}
/// Connects the UDP socket setting the default destination for `send()`
/// and limiting packets that are read via `recv` from the address specified
/// in `addr`.
pub fn connect(&self, addr: SocketAddr)
-> io::Result<()> {
self.sys.connect(addr)
}
/// Gets the value of the `SO_BROADCAST` option for this socket.
///
/// For more information about this option, see
/// [`set_broadcast`][link].
///
/// [link]: #method.set_broadcast
pub fn broadcast(&self) -> io::Result<bool> {
self.sys.broadcast()
}
/// Sets the value of the `SO_BROADCAST` option for this socket.
///
/// When enabled, this socket is allowed to send packets to a broadcast
/// address.
pub fn set_broadcast(&self, on: bool) -> io::Result<()> {
self.sys.set_broadcast(on)
}
/// Gets the value of the `IP_MULTICAST_LOOP` option for this socket.
///
/// For more information about this option, see
/// [`set_multicast_loop_v4`][link].
///
/// [link]: #method.set_multicast_loop_v4
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
self.sys.multicast_loop_v4()
}
/// Sets the value of the `IP_MULTICAST_LOOP` option for this socket.
///
/// If enabled, multicast packets will be looped back to the local socket.
/// Note that this may not have any affect on IPv6 sockets.
pub fn set_multicast_loop_v4(&self, on: bool) -> io::Result<()> {
self.sys.set_multicast_loop_v4(on)
}
/// Gets the value of the `IP_MULTICAST_TTL` option for this socket.
///
/// For more information about this option, see
/// [`set_multicast_ttl_v4`][link].
///
/// [link]: #method.set_multicast_ttl_v4
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
self.sys.multicast_ttl_v4()
}
/// Sets the value of the `IP_MULTICAST_TTL` option for this socket.
///
/// Indicates the time-to-live value of outgoing multicast packets for
/// this socket. The default value is 1 which means that multicast packets
/// don't leave the local network unless explicitly requested.
///
/// Note that this may not have any affect on IPv6 sockets.
pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> {
self.sys.set_multicast_ttl_v4(ttl)
}
/// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket.
///
/// For more information about this option, see
/// [`set_multicast_loop_v6`][link].
///
/// [link]: #method.set_multicast_loop_v6
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
self.sys.multicast_loop_v6()
}
/// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket.
///
/// Controls whether this socket sees the multicast packets it sends itself.
/// Note that this may not have any affect on IPv4 sockets.
pub fn set_multicast_loop_v6(&self, on: bool) -> io::Result<()> {
self.sys.set_multicast_loop_v6(on)
}
/// Gets the value of the `IP_TTL` option for this socket.
///
/// For more information about this option, see [`set_ttl`][link].
///
/// [link]: #method.set_ttl
pub fn ttl(&self) -> io::Result<u32> {
self.sys.ttl()
}
/// Sets the value for the `IP_TTL` option on this socket.
///
/// This value sets the time-to-live field that is used in every packet sent
/// from this socket.
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
self.sys.set_ttl(ttl)
}
/// Executes an operation of the `IP_ADD_MEMBERSHIP` type.
///
/// This function specifies a new multicast group for this socket to join.
/// The address must be a valid multicast address, and `interface` is the
/// address of the local interface with which the system should join the
/// multicast group. If it's equal to `INADDR_ANY` then an appropriate
/// interface is chosen by the system.
pub fn join_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.sys.join_multicast_v4(multiaddr, interface)
}
/// Executes an operation of the `IPV6_ADD_MEMBERSHIP` type.
///
/// This function specifies a new multicast group for this socket to join.
/// The address must be a valid multicast address, and `interface` is the
/// index of the interface to join/leave (or 0 to indicate any interface).
pub fn join_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.sys.join_multicast_v6(multiaddr, interface)
}
/// Executes an operation of the `IP_DROP_MEMBERSHIP` type.
///
/// For more information about this option, see
/// [`join_multicast_v4`][link].
///
/// [link]: #method.join_multicast_v4
pub fn leave_multicast_v4(&self,
multiaddr: &Ipv4Addr,
interface: &Ipv4Addr) -> io::Result<()> {
self.sys.leave_multicast_v4(multiaddr, interface)
}
/// Executes an operation of the `IPV6_DROP_MEMBERSHIP` type.
///
/// For more information about this option, see
/// [`join_multicast_v6`][link].
///
/// [link]: #method.join_multicast_v6
pub fn leave_multicast_v6(&self,
multiaddr: &Ipv6Addr,
interface: u32) -> io::Result<()> {
self.sys.leave_multicast_v6(multiaddr, interface)
}
/// Get the value of the `SO_ERROR` option on this socket.
///
/// This will retrieve the stored error in the underlying socket, clearing
/// the field in the process. This can be useful for checking errors between
/// calls.
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.sys.take_error()
}
}
impl Evented for UdpSocket {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.selector_id.associate_selector(poll)?;
self.sys.register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
self.sys.reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> {
self.sys.deregister(poll)
}
}
/*
*
* ===== UNIX ext =====
*
*/
#[cfg(all(unix, not(target_os = "fuchsia")))]
use std::os::unix::io::{IntoRawFd, AsRawFd, FromRawFd, RawFd};
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl IntoRawFd for UdpSocket {
fn into_raw_fd(self) -> RawFd {
self.sys.into_raw_fd()
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl AsRawFd for UdpSocket {
fn as_raw_fd(&self) -> RawFd {
self.sys.as_raw_fd()
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
impl FromRawFd for UdpSocket {
unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket {
UdpSocket {
sys: FromRawFd::from_raw_fd(fd),
selector_id: SelectorId::new(),
}
}
}

View File

@ -1,93 +0,0 @@
use crate::{poll, sys, Registry, Token};
use std::io;
/// Waker allows cross-thread waking of [`Poll`].
///
/// When created it will cause events with [`readable`] readiness and the
/// provided `token` if [`wake`] is called, possibly from another thread.
///
/// [`Poll`]: crate::Poll
/// [`readable`]: crate::event::Event::is_readable
/// [`wake`]: Waker::wake
///
/// # Notes
///
/// `Waker` events are only guaranteed to be delivered while the `Waker` value
/// is alive.
///
/// Only a single `Waker` should active per [`Poll`], if multiple threads need
/// access to the `Waker` it can be shared via for example an `Arc`. What
/// happens if multiple `Waker`s are registered with the same `Poll` is
/// undefined.
///
/// # Implementation notes
///
/// On platforms that support kqueue this will use the `EVFILT_USER` event
/// filter, see [implementation notes of `Poll`] to see what platforms support
/// kqueue. On Linux it uses [eventfd].
///
/// [implementation notes of `Poll`]: struct.Poll.html#implementation-notes
/// [eventfd]: http://man7.org/linux/man-pages/man2/eventfd.2.html
///
/// # Examples
///
/// Wake a [`Poll`] instance from another thread.
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use std::thread;
/// use std::time::Duration;
/// use std::sync::Arc;
///
/// use mio::{Events, Token, Poll, Waker};
///
/// const WAKE_TOKEN: Token = Token(10);
///
/// let mut poll = Poll::new()?;
/// let mut events = Events::with_capacity(2);
///
/// let waker = Arc::new(Waker::new(poll.registry(), WAKE_TOKEN)?);
///
/// // We need to keep the Waker alive, so we'll create a clone for the
/// // thread we create below.
/// let waker1 = waker.clone();
/// let handle = thread::spawn(move || {
/// // Working hard, or hardly working?
/// thread::sleep(Duration::from_millis(500));
///
/// // Now we'll wake the queue on the other thread.
/// waker1.wake().expect("unable to wake");
/// });
///
/// // On our current thread we'll poll for events, without a timeout.
/// poll.poll(&mut events, None)?;
///
/// // After about 500 milliseconds we should we awoken by the other thread we
/// // started, getting a single event.
/// assert!(!events.is_empty());
/// let waker_event = events.iter().next().unwrap();
/// assert!(waker_event.is_readable());
/// assert_eq!(waker_event.token(), WAKE_TOKEN);
/// # handle.join().unwrap();
/// # Ok(())
/// # }
/// ```
#[derive(Debug)]
pub struct Waker {
inner: sys::Waker,
}
impl Waker {
/// Create a new `Waker`.
pub fn new(registry: &Registry, token: Token) -> io::Result<Waker> {
sys::Waker::new(poll::selector(&registry), token).map(|inner| Waker { inner })
}
/// Wake up the [`Poll`] associated with this `Waker`.
///
/// [`Poll`]: crate::Poll
pub fn wake(&self) -> io::Result<()> {
self.inner.wake()
}
}

80
test/benchmark.rs Normal file
View File

@ -0,0 +1,80 @@
use std::mem;
use mio::net::{AddressFamily, Inet, Inet6, SockAddr, InetAddr, IPv4Addr, SocketType, Dgram, Stream};
use std::io::net::ip::IpAddr;
use native::NativeTaskBuilder;
use std::task::TaskBuilder;
use mio::os::{from_sockaddr};
use time::Instant;
use std::vec::*;
use std::io::timer;
mod nix {
pub use nix::c_int;
pub use nix::fcntl::{Fd, O_NONBLOCK, O_CLOEXEC};
pub use nix::errno::{EWOULDBLOCK, EINPROGRESS};
pub use nix::sys::socket::*;
pub use nix::unistd::*;
pub use nix::sys::epoll::*;
}
fn timed(label: &str, f: ||) {
let start = Instant::now();
f();
let elapsed = start.elapsed();
println!(" {}: {}", label, elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0);
}
fn init(saddr: &str) -> (nix::Fd, nix::Fd) {
let optval = 1i;
let addr = SockAddr::parse(saddr.as_slice()).expect("could not parse InetAddr");
let srvfd = nix::socket(nix::AF_INET, nix::SOCK_STREAM, nix::SOCK_CLOEXEC).unwrap();
nix::setsockopt(srvfd, nix::SOL_SOCKET, nix::SO_REUSEADDR, &optval).unwrap();
nix::bind(srvfd, &from_sockaddr(&addr)).unwrap();
nix::listen(srvfd, 256u).unwrap();
let fd = nix::socket(nix::AF_INET, nix::SOCK_STREAM, nix::SOCK_CLOEXEC | nix::SOCK_NONBLOCK).unwrap();
let res = nix::connect(fd, &from_sockaddr(&addr));
let start = Instant::now();
println!("connecting : {}", res);
let clifd = nix::accept4(srvfd, nix::SOCK_CLOEXEC | nix::SOCK_NONBLOCK).unwrap();
let elapsed = start.elapsed();
println!("accepted : {} - {}", clifd, elapsed.as_secs() as f64 + elapsed.subsec_nanos() as f64 / 1_000_000_000.0);
(clifd, srvfd)
}
#[test]
fn read_bench() {
let (clifd, srvfd) = init("10.10.1.5:11111");
let mut buf = Vec::with_capacity(1600);
unsafe { buf.set_len(1600); }
timed("read", || {
let mut i = 0u;
while i < 10000000 {
let res = nix::read(clifd, buf.as_mut_slice());
assert_eq!(res.unwrap_err().kind, nix::EWOULDBLOCK);
i = i + 1;
}
});
}
#[test]
fn epollctl_bench() {
let (clifd, srvfd) = init("10.10.1.5:22222");
let epfd = nix::epoll_create().unwrap();
let info = nix::EpollEvent { events: nix::EPOLLIN | nix::EPOLLONESHOT | nix::EPOLLET,
data: 0u64 };
nix::epoll_ctl(epfd, nix::EpollCtlAdd, clifd, &info);
timed("epoll_ctl", || {
let mut i = 0u;
while i < 10000000 {
nix::epoll_ctl(epfd, nix::EpollCtlMod, clifd, &info);
i = i + 1;
}
});
}

215
test/mod.rs Normal file
View File

@ -0,0 +1,215 @@
#![allow(deprecated)]
extern crate mio;
extern crate bytes;
extern crate net2;
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate iovec;
extern crate slab;
extern crate tempdir;
#[cfg(target_os = "fuchsia")]
extern crate fuchsia_zircon as zircon;
pub use ports::localhost;
mod test_custom_evented;
mod test_close_on_drop;
mod test_double_register;
mod test_echo_server;
mod test_local_addr_ready;
mod test_multicast;
mod test_oneshot;
mod test_poll;
mod test_register_deregister;
mod test_register_multiple_event_loops;
mod test_reregister_without_poll;
mod test_smoke;
mod test_tcp;
mod test_tcp_level;
mod test_tcp_shutdown;
mod test_udp_level;
mod test_udp_socket;
mod test_write_then_drop;
#[cfg(feature = "with-deprecated")]
mod test_notify;
#[cfg(feature = "with-deprecated")]
mod test_poll_channel;
#[cfg(feature = "with-deprecated")]
mod test_tick;
// The following tests are for deprecated features. Only run these tests on
// platforms that were supported from before the features were deprecated
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
#[cfg(feature = "with-deprecated")]
mod test_battery;
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[cfg(feature = "with-deprecated")]
mod test_unix_echo_server;
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[cfg(feature = "with-deprecated")]
mod test_unix_pass_fd;
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[cfg(feature = "with-deprecated")]
mod test_uds_shutdown;
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[cfg(feature = "with-deprecated")]
mod test_subprocess_pipe;
#[cfg(any(target_os = "macos", target_os = "linux"))]
#[cfg(feature = "with-deprecated")]
mod test_broken_pipe;
#[cfg(any(target_os = "fuchsia"))]
mod test_fuchsia_handles;
use bytes::{Buf, MutBuf};
use std::io::{self, Read, Write};
use std::time::Duration;
use mio::{Events, Poll};
use mio::event::Event;
pub trait TryRead {
fn try_read_buf<B: MutBuf>(&mut self, buf: &mut B) -> io::Result<Option<usize>>
where Self : Sized
{
// Reads the length of the slice supplied by buf.mut_bytes into the buffer
// This is not guaranteed to consume an entire datagram or segment.
// If your protocol is msg based (instead of continuous stream) you should
// ensure that your buffer is large enough to hold an entire segment (1532 bytes if not jumbo
// frames)
let res = self.try_read(unsafe { buf.mut_bytes() });
if let Ok(Some(cnt)) = res {
unsafe { buf.advance(cnt); }
}
res
}
fn try_read(&mut self, buf: &mut [u8]) -> io::Result<Option<usize>>;
}
pub trait TryWrite {
fn try_write_buf<B: Buf>(&mut self, buf: &mut B) -> io::Result<Option<usize>>
where Self : Sized
{
let res = self.try_write(buf.bytes());
if let Ok(Some(cnt)) = res {
buf.advance(cnt);
}
res
}
fn try_write(&mut self, buf: &[u8]) -> io::Result<Option<usize>>;
}
impl<T: Read> TryRead for T {
fn try_read(&mut self, dst: &mut [u8]) -> io::Result<Option<usize>> {
self.read(dst).map_non_block()
}
}
impl<T: Write> TryWrite for T {
fn try_write(&mut self, src: &[u8]) -> io::Result<Option<usize>> {
self.write(src).map_non_block()
}
}
/*
*
* ===== Helpers =====
*
*/
/// A helper trait to provide the map_non_block function on Results.
trait MapNonBlock<T> {
/// Maps a `Result<T>` to a `Result<Option<T>>` by converting
/// operation-would-block errors into `Ok(None)`.
fn map_non_block(self) -> io::Result<Option<T>>;
}
impl<T> MapNonBlock<T> for io::Result<T> {
fn map_non_block(self) -> io::Result<Option<T>> {
use std::io::ErrorKind::WouldBlock;
match self {
Ok(value) => Ok(Some(value)),
Err(err) => {
if let WouldBlock = err.kind() {
Ok(None)
} else {
Err(err)
}
}
}
}
}
mod ports {
use std::net::SocketAddr;
use std::str::FromStr;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT};
use std::sync::atomic::Ordering::SeqCst;
// Helper for getting a unique port for the task run
// TODO: Reuse ports to not spam the system
static mut NEXT_PORT: AtomicUsize = ATOMIC_USIZE_INIT;
const FIRST_PORT: usize = 18080;
fn next_port() -> usize {
unsafe {
// If the atomic was never used, set it to the initial port
NEXT_PORT.compare_and_swap(0, FIRST_PORT, SeqCst);
// Get and increment the port list
NEXT_PORT.fetch_add(1, SeqCst)
}
}
pub fn localhost() -> SocketAddr {
let s = format!("127.0.0.1:{}", next_port());
FromStr::from_str(&s).unwrap()
}
}
pub fn sleep_ms(ms: u64) {
use std::thread;
use std::time::Duration;
thread::sleep(Duration::from_millis(ms));
}
pub fn expect_events(poll: &Poll,
event_buffer: &mut Events,
poll_try_count: usize,
mut expected: Vec<Event>)
{
const MS: u64 = 1_000;
for _ in 0..poll_try_count {
poll.poll(event_buffer, Some(Duration::from_millis(MS))).unwrap();
for event in event_buffer.iter() {
let pos_opt = match expected.iter().position(|exp_event| {
(event.token() == exp_event.token()) &&
event.readiness().contains(exp_event.readiness())
}) {
Some(x) => Some(x),
None => None,
};
if let Some(pos) = pos_opt { expected.remove(pos); }
}
if expected.is_empty() {
break;
}
}
assert!(expected.is_empty(), "The following expected events were not found: {:?}", expected);
}

269
test/test_battery.rs Normal file
View File

@ -0,0 +1,269 @@
use {localhost, sleep_ms, TryRead, TryWrite};
use mio::*;
use mio::deprecated::{EventLoop, EventLoopBuilder, Handler};
use mio::net::{TcpListener, TcpStream};
use std::collections::LinkedList;
use slab::Slab;
use std::{io, thread};
use std::time::Duration;
// Don't touch the connection slab
const SERVER: Token = Token(10_000_000);
const CLIENT: Token = Token(10_000_001);
#[cfg(windows)]
const N: usize = 10_000;
#[cfg(unix)]
const N: usize = 1_000_000;
struct EchoConn {
sock: TcpStream,
token: Option<Token>,
count: usize,
buf: Vec<u8>
}
impl EchoConn {
fn new(sock: TcpStream) -> EchoConn {
let mut ec =
EchoConn {
sock: sock,
token: None,
buf: Vec::with_capacity(22),
count: 0
};
unsafe { ec.buf.set_len(22) };
ec
}
fn writable(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
event_loop.reregister(&self.sock, self.token.unwrap(),
Ready::readable(),
PollOpt::edge() | PollOpt::oneshot())
}
fn readable(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
loop {
match self.sock.try_read(&mut self.buf[..]) {
Ok(None) => {
break;
}
Ok(Some(_)) => {
self.count += 1;
if self.count % 10000 == 0 {
info!("Received {} messages", self.count);
}
if self.count == N {
event_loop.shutdown();
}
}
Err(_) => {
break;
}
};
}
event_loop.reregister(&self.sock, self.token.unwrap(), Ready::readable(), PollOpt::edge() | PollOpt::oneshot())
}
}
struct EchoServer {
sock: TcpListener,
conns: Slab<EchoConn>
}
impl EchoServer {
fn accept(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
debug!("server accepting socket");
let sock = self.sock.accept().unwrap().0;
let conn = EchoConn::new(sock,);
let tok = self.conns.insert(conn);
// Register the connection
self.conns[tok].token = Some(Token(tok));
event_loop.register(&self.conns[tok].sock, Token(tok), Ready::readable(),
PollOpt::edge() | PollOpt::oneshot())
.expect("could not register socket with event loop");
Ok(())
}
fn conn_readable(&mut self, event_loop: &mut EventLoop<Echo>,
tok: Token) -> io::Result<()> {
debug!("server conn readable; tok={:?}", tok);
self.conn(tok).readable(event_loop)
}
fn conn_writable(&mut self, event_loop: &mut EventLoop<Echo>,
tok: Token) -> io::Result<()> {
debug!("server conn writable; tok={:?}", tok);
self.conn(tok).writable(event_loop)
}
fn conn<'a>(&'a mut self, tok: Token) -> &'a mut EchoConn {
&mut self.conns[tok.into()]
}
}
struct EchoClient {
sock: TcpStream,
backlog: LinkedList<String>,
token: Token,
count: u32
}
// Sends a message and expects to receive the same exact message, one at a time
impl EchoClient {
fn new(sock: TcpStream, tok: Token) -> EchoClient {
EchoClient {
sock: sock,
backlog: LinkedList::new(),
token: tok,
count: 0
}
}
fn readable(&mut self, _event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
Ok(())
}
fn writable(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
debug!("client socket writable");
while self.backlog.len() > 0 {
match self.sock.try_write(self.backlog.front().unwrap().as_bytes()) {
Ok(None) => {
break;
}
Ok(Some(_)) => {
self.backlog.pop_front();
self.count += 1;
if self.count % 10000 == 0 {
info!("Sent {} messages", self.count);
}
}
Err(e) => { debug!("not implemented; client err={:?}", e); break; }
}
}
if self.backlog.len() > 0 {
event_loop.reregister(&self.sock, self.token, Ready::writable(),
PollOpt::edge() | PollOpt::oneshot()).unwrap();
}
Ok(())
}
}
struct Echo {
server: EchoServer,
client: EchoClient,
}
impl Echo {
fn new(srv: TcpListener, client: TcpStream) -> Echo {
Echo {
server: EchoServer {
sock: srv,
conns: Slab::with_capacity(128),
},
client: EchoClient::new(client, CLIENT),
}
}
}
impl Handler for Echo {
type Timeout = usize;
type Message = String;
fn ready(&mut self, event_loop: &mut EventLoop<Echo>, token: Token,
events: Ready) {
if events.is_readable() {
match token {
SERVER => self.server.accept(event_loop).unwrap(),
CLIENT => self.client.readable(event_loop).unwrap(),
i => self.server.conn_readable(event_loop, i).unwrap()
}
}
if events.is_writable() {
match token {
SERVER => panic!("received writable for token 0"),
CLIENT => self.client.writable(event_loop).unwrap(),
_ => self.server.conn_writable(event_loop, token).unwrap()
}
}
}
fn notify(&mut self, event_loop: &mut EventLoop<Echo>, msg: String) {
match self.client.sock.try_write(msg.as_bytes()) {
Ok(Some(n)) => {
self.client.count += 1;
if self.client.count % 10000 == 0 {
info!("Sent {} bytes: count {}", n, self.client.count);
}
},
_ => {
self.client.backlog.push_back(msg);
event_loop.reregister(
&self.client.sock,
self.client.token,
Ready::writable(),
PollOpt::edge() | PollOpt::oneshot()).unwrap();
}
}
}
}
#[test]
pub fn test_echo_server() {
debug!("Starting TEST_ECHO_SERVER");
let mut b = EventLoopBuilder::new();
b.notify_capacity(1_048_576)
.messages_per_tick(64)
.timer_tick(Duration::from_millis(100))
.timer_wheel_size(1_024)
.timer_capacity(65_536);
let mut event_loop = b.build().unwrap();
let addr = localhost();
let srv = TcpListener::bind(&addr).unwrap();
info!("listen for connections");
event_loop.register(&srv, SERVER, Ready::readable(),
PollOpt::edge() | PollOpt::oneshot()).unwrap();
let sock = TcpStream::connect(&addr).unwrap();
// Connect to the server
event_loop.register(&sock, CLIENT, Ready::writable(),
PollOpt::edge() | PollOpt::oneshot()).unwrap();
let chan = event_loop.channel();
let go = move || {
let mut i = N;
sleep_ms(1_000);
let message = "THIS IS A TEST MESSAGE".to_string();
while i > 0 {
chan.send(message.clone()).unwrap();
i -= 1;
if i % 10000 == 0 {
info!("Enqueued {} messages", N - i);
}
}
};
let t = thread::spawn(go);
// Start the event loop
event_loop.run(&mut Echo::new(srv, sock)).unwrap();
t.join().unwrap();
}

28
test/test_broken_pipe.rs Normal file
View File

@ -0,0 +1,28 @@
use mio::{Token, Ready, PollOpt};
use mio::deprecated::{unix, EventLoop, Handler};
use std::time::Duration;
pub struct BrokenPipeHandler;
impl Handler for BrokenPipeHandler {
type Timeout = ();
type Message = ();
fn ready(&mut self, _: &mut EventLoop<Self>, token: Token, _: Ready) {
if token == Token(1) {
panic!("Received ready() on a closed pipe.");
}
}
}
#[test]
pub fn broken_pipe() {
let mut event_loop: EventLoop<BrokenPipeHandler> = EventLoop::new().unwrap();
let (reader, _) = unix::pipe().unwrap();
event_loop.register(&reader, Token(1), Ready::all(), PollOpt::edge())
.unwrap();
let mut handler = BrokenPipeHandler;
drop(reader);
event_loop.run_once(&mut handler, Some(Duration::from_millis(1000))).unwrap();
}

View File

@ -1,14 +1,9 @@
use bytes::BytesMut;
use log::debug;
use {localhost, TryRead};
use mio::{Events, Poll, PollOpt, Ready, Token};
use bytes::ByteBuf;
use mio::net::{TcpListener, TcpStream};
use mio::{Events, Interests, Poll, Token};
mod util;
use util::{any_local_address, init, TryRead};
use self::TestState::{AfterRead, Initial};
use self::TestState::{Initial, AfterRead};
const SERVER: Token = Token(0);
const CLIENT: Token = Token(1);
@ -36,8 +31,8 @@ impl TestHandler {
}
}
fn handle_read(&mut self, poll: &mut Poll, tok: Token) {
debug!("readable; tok={:?}", tok);
fn handle_read(&mut self, poll: &mut Poll, tok: Token, events: Ready) {
debug!("readable; tok={:?}; hint={:?}", tok, events);
match tok {
SERVER => {
@ -52,59 +47,53 @@ impl TestHandler {
let mut buf = [0; 4096];
debug!("GOT={:?}", self.cli.try_read(&mut buf[..]));
self.state = AfterRead;
}
},
AfterRead => {}
}
let mut buf = BytesMut::with_capacity(1024);
let mut buf = ByteBuf::mut_with_capacity(1024);
match self.cli.try_read_buf(&mut buf) {
Ok(Some(0)) => self.shutdown = true,
Ok(_) => panic!("the client socket should not be readable"),
Err(e) => panic!("Unexpected error {:?}", e),
Err(e) => panic!("Unexpected error {:?}", e)
}
}
_ => panic!("received unknown token {:?}", tok),
_ => panic!("received unknown token {:?}", tok)
}
poll.registry()
.reregister(&self.cli, CLIENT, Interests::READABLE)
.unwrap();
poll.reregister(&self.cli, CLIENT, Ready::readable(), PollOpt::edge()).unwrap();
}
fn handle_write(&mut self, poll: &mut Poll, tok: Token) {
fn handle_write(&mut self, poll: &mut Poll, tok: Token, _: Ready) {
match tok {
SERVER => panic!("received writable for token 0"),
CLIENT => {
debug!("client connected");
poll.registry()
.reregister(&self.cli, CLIENT, Interests::READABLE)
.unwrap();
poll.reregister(&self.cli, CLIENT, Ready::readable(), PollOpt::edge()).unwrap();
}
_ => panic!("received unknown token {:?}", tok),
_ => panic!("received unknown token {:?}", tok)
}
}
}
#[test]
pub fn test_close_on_drop() {
init();
let _ = ::env_logger::init();
debug!("Starting TEST_CLOSE_ON_DROP");
let mut poll = Poll::new().unwrap();
// == Create & setup server socket
let srv = TcpListener::bind(any_local_address()).unwrap();
let addr = srv.local_addr().unwrap();
// The address to connect to - localhost + a unique port
let addr = localhost();
poll.registry()
.register(&srv, SERVER, Interests::READABLE)
.unwrap();
// == Create & setup server socket
let srv = TcpListener::bind(&addr).unwrap();
poll.register(&srv, SERVER, Ready::readable(), PollOpt::edge()).unwrap();
// == Create & setup client socket
let sock = TcpStream::connect(addr).unwrap();
let sock = TcpStream::connect(&addr).unwrap();
poll.registry()
.register(&sock, CLIENT, Interests::WRITABLE)
.unwrap();
poll.register(&sock, CLIENT, Ready::writable(), PollOpt::edge()).unwrap();
// == Create storage for events
let mut events = Events::with_capacity(1024);
@ -117,12 +106,12 @@ pub fn test_close_on_drop() {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.is_readable() {
handler.handle_read(&mut poll, event.token());
if event.readiness().is_readable() {
handler.handle_read(&mut poll, event.token(), event.readiness());
}
if event.is_writable() {
handler.handle_write(&mut poll, event.token());
if event.readiness().is_writable() {
handler.handle_write(&mut poll, event.token(), event.readiness());
}
}
}

394
test/test_custom_evented.rs Normal file
View File

@ -0,0 +1,394 @@
use mio::{Events, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
use mio::event::Evented;
use std::time::Duration;
#[test]
fn smoke() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(128);
let (r, set) = Registration::new2();
r.register(&poll, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
let n = poll.poll(&mut events, Some(Duration::from_millis(0))).unwrap();
assert_eq!(n, 0);
set.set_readiness(Ready::readable()).unwrap();
let n = poll.poll(&mut events, Some(Duration::from_millis(0))).unwrap();
assert_eq!(n, 1);
assert_eq!(events.get(0).unwrap().token(), Token(0));
}
#[test]
fn set_readiness_before_register() {
use std::sync::{Arc, Barrier};
use std::thread;
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(128);
for _ in 0..5_000 {
let (r, set) = Registration::new2();
let b1 = Arc::new(Barrier::new(2));
let b2 = b1.clone();
let th = thread::spawn(move || {
// set readiness before register
set.set_readiness(Ready::readable()).unwrap();
// run into barrier so both can pass
b2.wait();
});
// wait for readiness
b1.wait();
// now register
poll.register(&r, Token(123), Ready::readable(), PollOpt::edge()).unwrap();
loop {
let n = poll.poll(&mut events, None).unwrap();
if n == 0 {
continue;
}
assert_eq!(n, 1);
assert_eq!(events.get(0).unwrap().token(), Token(123));
break;
}
th.join().unwrap();
}
}
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
mod stress {
use mio::{Events, Poll, PollOpt, Ready, Registration, SetReadiness, Token};
use mio::event::Evented;
use std::time::Duration;
#[test]
fn single_threaded_poll() {
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{Acquire, Release};
use std::thread;
const NUM_ATTEMPTS: usize = 30;
const NUM_ITERS: usize = 500;
const NUM_THREADS: usize = 4;
const NUM_REGISTRATIONS: usize = 128;
for _ in 0..NUM_ATTEMPTS {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(NUM_REGISTRATIONS);
let registrations: Vec<_> = (0..NUM_REGISTRATIONS).map(|i| {
let (r, s) = Registration::new2();
r.register(&poll, Token(i), Ready::readable(), PollOpt::edge()).unwrap();
(r, s)
}).collect();
let mut ready: Vec<_> = (0..NUM_REGISTRATIONS).map(|_| Ready::empty()).collect();
let remaining = Arc::new(AtomicUsize::new(NUM_THREADS));
for _ in 0..NUM_THREADS {
let remaining = remaining.clone();
let set_readiness: Vec<SetReadiness> =
registrations.iter().map(|r| r.1.clone()).collect();
thread::spawn(move || {
for _ in 0..NUM_ITERS {
for i in 0..NUM_REGISTRATIONS {
set_readiness[i].set_readiness(Ready::readable()).unwrap();
set_readiness[i].set_readiness(Ready::empty()).unwrap();
set_readiness[i].set_readiness(Ready::writable()).unwrap();
set_readiness[i].set_readiness(Ready::readable() | Ready::writable()).unwrap();
set_readiness[i].set_readiness(Ready::empty()).unwrap();
}
}
for i in 0..NUM_REGISTRATIONS {
set_readiness[i].set_readiness(Ready::readable()).unwrap();
}
remaining.fetch_sub(1, Release);
});
}
while remaining.load(Acquire) > 0 {
// Set interest
for (i, &(ref r, _)) in registrations.iter().enumerate() {
r.reregister(&poll, Token(i), Ready::writable(), PollOpt::edge()).unwrap();
}
poll.poll(&mut events, Some(Duration::from_millis(0))).unwrap();
for event in &events {
ready[event.token().0] = event.readiness();
}
// Update registration
// Set interest
for (i, &(ref r, _)) in registrations.iter().enumerate() {
r.reregister(&poll, Token(i), Ready::readable(), PollOpt::edge()).unwrap();
}
}
// Finall polls, repeat until readiness-queue empty
loop {
// Might not read all events from custom-event-queue at once, implementation dependend
poll.poll(&mut events, Some(Duration::from_millis(0))).unwrap();
if events.is_empty() {
// no more events in readiness queue pending
break;
}
for event in &events {
ready[event.token().0] = event.readiness();
}
}
// Everything should be flagged as readable
for ready in ready {
assert_eq!(ready, Ready::readable());
}
}
}
#[test]
fn multi_threaded_poll() {
use std::sync::{Arc, Barrier};
use std::sync::atomic::{AtomicUsize};
use std::sync::atomic::Ordering::{Relaxed, SeqCst};
use std::thread;
const ENTRIES: usize = 10_000;
const PER_ENTRY: usize = 16;
const THREADS: usize = 4;
const NUM: usize = ENTRIES * PER_ENTRY;
struct Entry {
#[allow(dead_code)]
registration: Registration,
set_readiness: SetReadiness,
num: AtomicUsize,
}
impl Entry {
fn fire(&self) {
self.set_readiness.set_readiness(Ready::readable()).unwrap();
}
}
let poll = Arc::new(Poll::new().unwrap());
let mut entries = vec![];
// Create entries
for i in 0..ENTRIES {
let (registration, set_readiness) = Registration::new2();
registration.register(&poll, Token(i), Ready::readable(), PollOpt::edge()).unwrap();
entries.push(Entry {
registration,
set_readiness,
num: AtomicUsize::new(0),
});
}
let total = Arc::new(AtomicUsize::new(0));
let entries = Arc::new(entries);
let barrier = Arc::new(Barrier::new(THREADS));
let mut threads = vec![];
for th in 0..THREADS {
let poll = poll.clone();
let total = total.clone();
let entries = entries.clone();
let barrier = barrier.clone();
threads.push(thread::spawn(move || {
let mut events = Events::with_capacity(128);
barrier.wait();
// Prime all the registrations
let mut i = th;
while i < ENTRIES {
entries[i].fire();
i += THREADS;
}
let mut n = 0;
while total.load(SeqCst) < NUM {
// A poll timeout is necessary here because there may be more
// than one threads blocked in `poll` when the final wakeup
// notification arrives (and only notifies one thread).
n += poll.poll(&mut events, Some(Duration::from_millis(100))).unwrap();
let mut num_this_tick = 0;
for event in &events {
let e = &entries[event.token().0];
let mut num = e.num.load(Relaxed);
loop {
if num < PER_ENTRY {
let actual = e.num.compare_and_swap(num, num + 1, Relaxed);
if actual == num {
num_this_tick += 1;
e.fire();
break;
}
num = actual;
} else {
break;
}
}
}
total.fetch_add(num_this_tick, SeqCst);
}
n
}));
}
let _: Vec<_> = threads.into_iter()
.map(|th| th.join().unwrap())
.collect();
for entry in entries.iter() {
assert_eq!(PER_ENTRY, entry.num.load(Relaxed));
}
}
#[test]
fn with_small_events_collection() {
const N: usize = 8;
const ITER: usize = 1_000;
use std::sync::{Arc, Barrier};
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::{Acquire, Release};
use std::thread;
let poll = Poll::new().unwrap();
let mut registrations = vec![];
let barrier = Arc::new(Barrier::new(N + 1));
let done = Arc::new(AtomicBool::new(false));
for i in 0..N {
let (registration, set_readiness) = Registration::new2();
poll.register(&registration, Token(i), Ready::readable(), PollOpt::edge()).unwrap();
registrations.push(registration);
let barrier = barrier.clone();
let done = done.clone();
thread::spawn(move || {
barrier.wait();
while !done.load(Acquire) {
set_readiness.set_readiness(Ready::readable()).unwrap();
}
// Set one last time
set_readiness.set_readiness(Ready::readable()).unwrap();
});
}
let mut events = Events::with_capacity(4);
barrier.wait();
for _ in 0..ITER {
poll.poll(&mut events, None).unwrap();
}
done.store(true, Release);
let mut final_ready = vec![false; N];
for _ in 0..5 {
poll.poll(&mut events, None).unwrap();
for event in &events {
final_ready[event.token().0] = true;
}
if final_ready.iter().all(|v| *v) {
return;
}
thread::sleep(Duration::from_millis(10));
}
panic!("dead lock?");
}
}
#[test]
fn drop_registration_from_non_main_thread() {
use std::thread;
use std::sync::mpsc::channel;
const THREADS: usize = 8;
const ITERS: usize = 50_000;
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let mut senders = Vec::with_capacity(THREADS);
let mut token_index = 0;
// spawn threads, which will send messages to single receiver
for _ in 0..THREADS {
let (tx, rx) = channel::<(Registration, SetReadiness)>();
senders.push(tx);
thread::spawn(move || {
for (registration, set_readiness) in rx {
let _ = set_readiness.set_readiness(Ready::readable());
drop(registration);
drop(set_readiness);
}
});
}
let mut index: usize = 0;
for _ in 0..ITERS {
let (registration, set_readiness) = Registration::new2();
registration.register(&poll, Token(token_index), Ready::readable(), PollOpt::edge()).unwrap();
let _ = senders[index].send((registration, set_readiness));
token_index += 1;
index += 1;
if index == THREADS {
index = 0;
let (registration, set_readiness) = Registration::new2();
registration.register(&poll, Token(token_index), Ready::readable(), PollOpt::edge()).unwrap();
let _ = set_readiness.set_readiness(Ready::readable());
drop(registration);
drop(set_readiness);
token_index += 1;
thread::park_timeout(Duration::from_millis(0));
let _ = poll.poll(&mut events, None).unwrap();
}
}
}

View File

@ -0,0 +1,17 @@
//! A smoke test for windows compatibility
#[test]
#[cfg(any(target_os = "linux", target_os = "windows"))]
pub fn test_double_register() {
use mio::*;
use mio::net::TcpListener;
let poll = Poll::new().unwrap();
// Create the listener
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
// Register the listener with `Poll`
poll.register(&l, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
assert!(poll.register(&l, Token(1), Ready::readable(), PollOpt::edge()).is_err());
}

303
test/test_echo_server.rs Normal file
View File

@ -0,0 +1,303 @@
use {localhost, TryRead, TryWrite};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::net::{TcpListener, TcpStream};
use bytes::{Buf, ByteBuf, MutByteBuf, SliceBuf};
use slab::Slab;
use std::io;
const SERVER: Token = Token(10_000_000);
const CLIENT: Token = Token(10_000_001);
struct EchoConn {
sock: TcpStream,
buf: Option<ByteBuf>,
mut_buf: Option<MutByteBuf>,
token: Option<Token>,
interest: Ready
}
impl EchoConn {
fn new(sock: TcpStream) -> EchoConn {
EchoConn {
sock,
buf: None,
mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
token: None,
interest: Ready::empty(),
}
}
fn writable(&mut self, poll: &mut Poll) -> io::Result<()> {
let mut buf = self.buf.take().unwrap();
match self.sock.try_write_buf(&mut buf) {
Ok(None) => {
debug!("client flushing buf; WOULDBLOCK");
self.buf = Some(buf);
self.interest.insert(Ready::writable());
}
Ok(Some(r)) => {
debug!("CONN : we wrote {} bytes!", r);
self.mut_buf = Some(buf.flip());
self.interest.insert(Ready::readable());
self.interest.remove(Ready::writable());
}
Err(e) => debug!("not implemented; client err={:?}", e),
}
assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
poll.reregister(&self.sock, self.token.unwrap(), self.interest,
PollOpt::edge() | PollOpt::oneshot())
}
fn readable(&mut self, poll: &mut Poll) -> io::Result<()> {
let mut buf = self.mut_buf.take().unwrap();
match self.sock.try_read_buf(&mut buf) {
Ok(None) => {
debug!("CONN : spurious read wakeup");
self.mut_buf = Some(buf);
}
Ok(Some(r)) => {
debug!("CONN : we read {} bytes!", r);
// prepare to provide this to writable
self.buf = Some(buf.flip());
self.interest.remove(Ready::readable());
self.interest.insert(Ready::writable());
}
Err(e) => {
debug!("not implemented; client err={:?}", e);
self.interest.remove(Ready::readable());
}
};
assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
poll.reregister(&self.sock, self.token.unwrap(), self.interest,
PollOpt::edge())
}
}
struct EchoServer {
sock: TcpListener,
conns: Slab<EchoConn>
}
impl EchoServer {
fn accept(&mut self, poll: &mut Poll) -> io::Result<()> {
debug!("server accepting socket");
let sock = self.sock.accept().unwrap().0;
let conn = EchoConn::new(sock,);
let tok = self.conns.insert(conn);
// Register the connection
self.conns[tok].token = Some(Token(tok));
poll.register(&self.conns[tok].sock, Token(tok), Ready::readable(),
PollOpt::edge() | PollOpt::oneshot())
.expect("could not register socket with event loop");
Ok(())
}
fn conn_readable(&mut self, poll: &mut Poll,
tok: Token) -> io::Result<()> {
debug!("server conn readable; tok={:?}", tok);
self.conn(tok).readable(poll)
}
fn conn_writable(&mut self, poll: &mut Poll,
tok: Token) -> io::Result<()> {
debug!("server conn writable; tok={:?}", tok);
self.conn(tok).writable(poll)
}
fn conn(&mut self, tok: Token) -> &mut EchoConn {
&mut self.conns[tok.into()]
}
}
struct EchoClient {
sock: TcpStream,
msgs: Vec<&'static str>,
tx: SliceBuf<'static>,
rx: SliceBuf<'static>,
mut_buf: Option<MutByteBuf>,
token: Token,
interest: Ready,
shutdown: bool,
}
// Sends a message and expects to receive the same exact message, one at a time
impl EchoClient {
fn new(sock: TcpStream, token: Token, mut msgs: Vec<&'static str>) -> EchoClient {
let curr = msgs.remove(0);
EchoClient {
sock,
msgs,
tx: SliceBuf::wrap(curr.as_bytes()),
rx: SliceBuf::wrap(curr.as_bytes()),
mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
token,
interest: Ready::empty(),
shutdown: false,
}
}
fn readable(&mut self, poll: &mut Poll) -> io::Result<()> {
debug!("client socket readable");
let mut buf = self.mut_buf.take().unwrap();
match self.sock.try_read_buf(&mut buf) {
Ok(None) => {
debug!("CLIENT : spurious read wakeup");
self.mut_buf = Some(buf);
}
Ok(Some(r)) => {
debug!("CLIENT : We read {} bytes!", r);
// prepare for reading
let mut buf = buf.flip();
while buf.has_remaining() {
let actual = buf.read_byte().unwrap();
let expect = self.rx.read_byte().unwrap();
assert!(actual == expect, "actual={}; expect={}", actual, expect);
}
self.mut_buf = Some(buf.flip());
self.interest.remove(Ready::readable());
if !self.rx.has_remaining() {
self.next_msg(poll).unwrap();
}
}
Err(e) => {
panic!("not implemented; client err={:?}", e);
}
};
if !self.interest.is_empty() {
assert!(self.interest.is_readable() || self.interest.is_writable(), "actual={:?}", self.interest);
poll.reregister(&self.sock, self.token, self.interest,
PollOpt::edge() | PollOpt::oneshot())?;
}
Ok(())
}
fn writable(&mut self, poll: &mut Poll) -> io::Result<()> {
debug!("client socket writable");
match self.sock.try_write_buf(&mut self.tx) {
Ok(None) => {
debug!("client flushing buf; WOULDBLOCK");
self.interest.insert(Ready::writable());
}
Ok(Some(r)) => {
debug!("CLIENT : we wrote {} bytes!", r);
self.interest.insert(Ready::readable());
self.interest.remove(Ready::writable());
}
Err(e) => debug!("not implemented; client err={:?}", e)
}
if self.interest.is_readable() || self.interest.is_writable() {
try!(poll.reregister(&self.sock, self.token, self.interest,
PollOpt::edge() | PollOpt::oneshot()));
}
Ok(())
}
fn next_msg(&mut self, poll: &mut Poll) -> io::Result<()> {
if self.msgs.is_empty() {
self.shutdown = true;
return Ok(());
}
let curr = self.msgs.remove(0);
debug!("client prepping next message");
self.tx = SliceBuf::wrap(curr.as_bytes());
self.rx = SliceBuf::wrap(curr.as_bytes());
self.interest.insert(Ready::writable());
poll.reregister(&self.sock, self.token, self.interest,
PollOpt::edge() | PollOpt::oneshot())
}
}
struct Echo {
server: EchoServer,
client: EchoClient,
}
impl Echo {
fn new(srv: TcpListener, client: TcpStream, msgs: Vec<&'static str>) -> Echo {
Echo {
server: EchoServer {
sock: srv,
conns: Slab::with_capacity(128)
},
client: EchoClient::new(client, CLIENT, msgs)
}
}
}
#[test]
pub fn test_echo_server() {
debug!("Starting TEST_ECHO_SERVER");
let mut poll = Poll::new().unwrap();
let addr = localhost();
let srv = TcpListener::bind(&addr).unwrap();
info!("listen for connections");
poll.register(&srv, SERVER, Ready::readable(),
PollOpt::edge() | PollOpt::oneshot()).unwrap();
let sock = TcpStream::connect(&addr).unwrap();
// Connect to the server
poll.register(&sock, CLIENT, Ready::writable(),
PollOpt::edge() | PollOpt::oneshot()).unwrap();
// == Create storage for events
let mut events = Events::with_capacity(1024);
let mut handler = Echo::new(srv, sock, vec!["foo", "bar"]);
// Start the event loop
while !handler.client.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
debug!("ready {:?} {:?}", event.token(), event.readiness());
if event.readiness().is_readable() {
match event.token() {
SERVER => handler.server.accept(&mut poll).unwrap(),
CLIENT => handler.client.readable(&mut poll).unwrap(),
i => handler.server.conn_readable(&mut poll, i).unwrap()
}
}
if event.readiness().is_writable() {
match event.token() {
SERVER => panic!("received writable for token 0"),
CLIENT => handler.client.writable(&mut poll).unwrap(),
i => handler.server.conn_writable(&mut poll, i).unwrap()
};
}
}
}
}

View File

@ -0,0 +1,30 @@
use mio::*;
use mio::fuchsia::EventedHandle;
use zircon::{self, AsHandleRef};
use std::time::Duration;
const MS: u64 = 1_000;
#[test]
pub fn test_fuchsia_channel() {
let poll = Poll::new().unwrap();
let mut event_buffer = Events::with_capacity(1);
let event_buffer = &mut event_buffer;
let (channel0, channel1) = zircon::Channel::create(zircon::ChannelOpts::Normal).unwrap();
let channel1_evented = unsafe { EventedHandle::new(channel1.raw_handle()) };
poll.register(&channel1_evented, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
poll.poll(event_buffer, Some(Duration::from_millis(MS))).unwrap();
assert_eq!(event_buffer.len(), 0);
channel0.write(&[1, 2, 3], &mut vec![], 0).unwrap();
poll.poll(event_buffer, Some(Duration::from_millis(MS))).unwrap();
let event = event_buffer.get(0).unwrap();
assert_eq!(event.token(), Token(1));
assert!(event.readiness().is_readable());
poll.deregister(&channel1_evented).unwrap();
}

View File

@ -0,0 +1,67 @@
use {TryWrite};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::net::{TcpListener, TcpStream};
const LISTEN: Token = Token(0);
const CLIENT: Token = Token(1);
const SERVER: Token = Token(2);
struct MyHandler {
listener: TcpListener,
connected: TcpStream,
accepted: Option<TcpStream>,
shutdown: bool,
}
#[test]
fn local_addr_ready() {
let addr = "127.0.0.1:0".parse().unwrap();
let server = TcpListener::bind(&addr).unwrap();
let addr = server.local_addr().unwrap();
let poll = Poll::new().unwrap();
poll.register(&server, LISTEN, Ready::readable(),
PollOpt::edge()).unwrap();
let sock = TcpStream::connect(&addr).unwrap();
poll.register(&sock, CLIENT, Ready::readable(),
PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(1024);
let mut handler = MyHandler {
listener: server,
connected: sock,
accepted: None,
shutdown: false,
};
while !handler.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
match event.token() {
LISTEN => {
let sock = handler.listener.accept().unwrap().0;
poll.register(&sock,
SERVER,
Ready::writable(),
PollOpt::edge()).unwrap();
handler.accepted = Some(sock);
}
SERVER => {
handler.accepted.as_ref().unwrap().peer_addr().unwrap();
handler.accepted.as_ref().unwrap().local_addr().unwrap();
handler.accepted.as_mut().unwrap().try_write(&[1, 2, 3]).unwrap();
handler.accepted = None;
}
CLIENT => {
handler.connected.peer_addr().unwrap();
handler.connected.local_addr().unwrap();
handler.shutdown = true;
}
_ => panic!("unexpected token"),
}
}
}
}

107
test/test_multicast.rs Normal file
View File

@ -0,0 +1,107 @@
// TODO: This doesn't pass on android 64bit CI...
// Figure out why!
#![cfg(not(target_os = "android"))]
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::net::UdpSocket;
use bytes::{Buf, MutBuf, RingBuf, SliceBuf};
use std::str;
use std::net::IpAddr;
use localhost;
const LISTENER: Token = Token(0);
const SENDER: Token = Token(1);
pub struct UdpHandler {
tx: UdpSocket,
rx: UdpSocket,
msg: &'static str,
buf: SliceBuf<'static>,
rx_buf: RingBuf,
localhost: IpAddr,
shutdown: bool,
}
impl UdpHandler {
fn new(tx: UdpSocket, rx: UdpSocket, msg: &'static str) -> UdpHandler {
let sock = UdpSocket::bind(&"127.0.0.1:12345".parse().unwrap()).unwrap();
UdpHandler {
tx,
rx,
msg,
buf: SliceBuf::wrap(msg.as_bytes()),
rx_buf: RingBuf::new(1024),
localhost: sock.local_addr().unwrap().ip(),
shutdown: false,
}
}
fn handle_read(&mut self, _: &mut Poll, token: Token, _: Ready) {
if let LISTENER = token {
debug!("We are receiving a datagram now...");
match unsafe { self.rx.recv_from(self.rx_buf.mut_bytes()) } {
Ok((cnt, addr)) => {
unsafe { MutBuf::advance(&mut self.rx_buf, cnt); }
assert_eq!(addr.ip(), self.localhost);
}
res => panic!("unexpected result: {:?}", res),
}
assert!(str::from_utf8(self.rx_buf.bytes()).unwrap() == self.msg);
self.shutdown = true;
}
}
fn handle_write(&mut self, _: &mut Poll, token: Token, _: Ready) {
if let SENDER = token {
let addr = self.rx.local_addr().unwrap();
let cnt = self.tx.send_to(self.buf.bytes(), &addr).unwrap();
self.buf.advance(cnt);
}
}
}
#[test]
pub fn test_multicast() {
drop(::env_logger::init());
debug!("Starting TEST_UDP_CONNECTIONLESS");
let mut poll = Poll::new().unwrap();
let addr = localhost();
let any = "0.0.0.0:0".parse().unwrap();
let tx = UdpSocket::bind(&any).unwrap();
let rx = UdpSocket::bind(&addr).unwrap();
info!("Joining group 227.1.1.100");
let any = "0.0.0.0".parse().unwrap();
rx.join_multicast_v4(&"227.1.1.100".parse().unwrap(), &any).unwrap();
info!("Joining group 227.1.1.101");
rx.join_multicast_v4(&"227.1.1.101".parse().unwrap(), &any).unwrap();
info!("Registering SENDER");
poll.register(&tx, SENDER, Ready::writable(), PollOpt::edge()).unwrap();
info!("Registering LISTENER");
poll.register(&rx, LISTENER, Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(1024);
let mut handler = UdpHandler::new(tx, rx, "hello world");
info!("Starting event loop to test with...");
while !handler.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.readiness().is_readable() {
handler.handle_read(&mut poll, event.token(), event.readiness());
}
if event.readiness().is_writable() {
handler.handle_write(&mut poll, event.token(), event.readiness());
}
}
}
}

192
test/test_notify.rs Normal file
View File

@ -0,0 +1,192 @@
use {localhost, sleep_ms};
use mio::*;
use mio::deprecated::{EventLoop, EventLoopBuilder, Handler, Sender, NotifyError};
use mio::net::TcpListener;
use std::thread;
struct TestHandler {
sender: Sender<String>,
notify: usize
}
impl TestHandler {
fn new(sender: Sender<String>) -> TestHandler {
TestHandler {
sender,
notify: 0
}
}
}
impl Handler for TestHandler {
type Timeout = usize;
type Message = String;
fn notify(&mut self, event_loop: &mut EventLoop<TestHandler>, msg: String) {
match self.notify {
0 => {
assert!(msg == "First", "actual={}", msg);
self.sender.send("Second".to_string()).unwrap();
}
1 => {
assert!(msg == "Second", "actual={}", msg);
event_loop.shutdown();
}
v => panic!("unexpected value for notify; val={}", v)
}
self.notify += 1;
}
}
#[test]
pub fn test_notify() {
debug!("Starting TEST_NOTIFY");
let mut event_loop = EventLoop::new().unwrap();
let addr = localhost();
// Setup a server socket so that the event loop blocks
let srv = TcpListener::bind(&addr).unwrap();
event_loop.register(&srv, Token(0), Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
let sender = event_loop.channel();
thread::spawn(move || {
sleep_ms(1_000);
sender.send("First".to_string()).unwrap();
});
let sender = event_loop.channel();
let mut handler = TestHandler::new(sender);
// Start the event loop
event_loop.run(&mut handler).unwrap();
assert!(handler.notify == 2, "actual={}", handler.notify);
}
#[test]
pub fn test_notify_capacity() {
use std::sync::mpsc::*;
use std::thread;
struct Capacity(Receiver<i32>);
impl Handler for Capacity {
type Message = i32;
type Timeout = ();
fn notify(&mut self, event_loop: &mut EventLoop<Capacity>, msg: i32) {
if msg == 1 {
self.0.recv().unwrap();
} else if msg == 3 {
event_loop.shutdown();
}
}
}
let mut builder = EventLoopBuilder::new();
builder.notify_capacity(1);
let (tx, rx) = channel::<i32>();
let mut event_loop = builder.build().unwrap();
let notify = event_loop.channel();
let handle = thread::spawn(move || {
let mut handler = Capacity(rx);
event_loop.run(&mut handler).unwrap();
});
assert!(notify.send(1).is_ok());
loop {
if notify.send(2).is_err() {
break;
}
}
tx.send(1).unwrap();
loop {
if notify.send(3).is_ok() {
break;
}
}
handle.join().unwrap();
}
#[test]
pub fn test_notify_drop() {
use std::sync::mpsc::{self,Sender};
use std::thread;
struct MessageDrop(Sender<u8>);
impl Drop for MessageDrop {
fn drop(&mut self) {
self.0.send(0).unwrap();
}
}
struct DummyHandler;
impl Handler for DummyHandler {
type Timeout = ();
type Message = MessageDrop;
fn notify(&mut self, event_loop: &mut EventLoop<Self>, msg: MessageDrop) {
msg.0.send(1).unwrap();
drop(msg);
// We stop after the first message
event_loop.shutdown();
}
}
let (tx_notif_1, rx_notif_1) = mpsc::channel();
let (tx_notif_2, rx_notif_2) = mpsc::channel();
let (tx_notif_3, _unused) = mpsc::channel();
let (tx_exit_loop, rx_exit_loop) = mpsc::channel();
let (tx_drop_loop, rx_drop_loop) = mpsc::channel();
let mut event_loop = EventLoop::new().unwrap();
let notify = event_loop.channel();
let handle = thread::spawn(move || {
let mut handler = DummyHandler;
event_loop.run(&mut handler).unwrap();
// Confirmation we exited the loop
tx_exit_loop.send(()).unwrap();
// Order to drop the loop
rx_drop_loop.recv().unwrap();
drop(event_loop);
});
notify.send(MessageDrop(tx_notif_1)).unwrap();
assert_eq!(rx_notif_1.recv().unwrap(), 1); // Response from the loop
assert_eq!(rx_notif_1.recv().unwrap(), 0); // Drop notification
// We wait for the event loop to exit before sending the second notification
rx_exit_loop.recv().unwrap();
notify.send(MessageDrop(tx_notif_2)).unwrap();
// We ensure the message is indeed stuck in the queue
sleep_ms(100);
assert!(rx_notif_2.try_recv().is_err());
// Give the order to drop the event loop
tx_drop_loop.send(()).unwrap();
assert_eq!(rx_notif_2.recv().unwrap(), 0); // Drop notification
// Check that sending a new notification will return an error
// We should also get our message back
match notify.send(MessageDrop(tx_notif_3)).unwrap_err() {
NotifyError::Closed(Some(..)) => {}
_ => panic!(),
}
handle.join().unwrap();
}

64
test/test_oneshot.rs Normal file
View File

@ -0,0 +1,64 @@
use mio::*;
use mio::net::{TcpListener, TcpStream};
use std::io::*;
use std::time::Duration;
const MS: u64 = 1_000;
#[test]
pub fn test_tcp_edge_oneshot() {
let _ = ::env_logger::init();
let mut poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
// Create the listener
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
// Register the listener with `Poll`
poll.register(&l, Token(0), Ready::readable(), PollOpt::level()).unwrap();
// Connect a socket, we are going to write to it
let mut s1 = TcpStream::connect(&l.local_addr().unwrap()).unwrap();
poll.register(&s1, Token(1), Ready::writable(), PollOpt::level()).unwrap();
wait_for(&mut poll, &mut events, Token(0));
// Get pair
let (mut s2, _) = l.accept().unwrap();
poll.register(&s2, Token(2), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
wait_for(&mut poll, &mut events, Token(1));
let res = s1.write(b"foo").unwrap();
assert_eq!(3, res);
let mut buf = [0; 1];
for byte in b"foo" {
wait_for(&mut poll, &mut events, Token(2));
assert_eq!(1, s2.read(&mut buf).unwrap());
assert_eq!(*byte, buf[0]);
poll.reregister(&s2, Token(2), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
if *byte == b'o' {
poll.reregister(&s2, Token(2), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
}
}
}
fn wait_for(poll: &mut Poll, events: &mut Events, token: Token) {
loop {
poll.poll(events, Some(Duration::from_millis(MS))).unwrap();
let cnt = (0..events.len()).map(|i| events.get(i).unwrap())
.filter(|e| e.token() == token)
.count();
assert!(cnt < 2, "token appeared multiple times in poll results; cnt={:}", cnt);
if cnt == 1 { return };
}
}

18
test/test_poll.rs Normal file
View File

@ -0,0 +1,18 @@
use mio::*;
use std::time::Duration;
#[test]
fn test_poll_closes_fd() {
for _ in 0..2000 {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(4);
let (registration, set_readiness) = Registration::new2();
poll.register(&registration, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
poll.poll(&mut events, Some(Duration::from_millis(0))).unwrap();
drop(poll);
drop(set_readiness);
drop(registration);
}
}

285
test/test_poll_channel.rs Normal file
View File

@ -0,0 +1,285 @@
use {expect_events, sleep_ms};
use mio::{channel, Events, Poll, PollOpt, Ready, Token};
use mio::event::Event;
use std::sync::mpsc::TryRecvError;
use std::thread;
use std::time::Duration;
#[test]
pub fn test_poll_channel_edge() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let (tx, rx) = channel::channel();
poll.register(&rx, Token(123), Ready::readable(), PollOpt::edge()).unwrap();
// Wait, but nothing should happen
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Push the value
tx.send("hello").unwrap();
// Polling will contain the event
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(1, num);
let event = events.get(0).unwrap();
assert_eq!(event.token(), Token(123));
assert_eq!(event.readiness(), Ready::readable());
// Poll again and there should be no events
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Read the value
assert_eq!("hello", rx.try_recv().unwrap());
// Poll again, nothing
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Push a value
tx.send("goodbye").unwrap();
// Have an event
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(1, num);
let event = events.get(0).unwrap();
assert_eq!(event.token(), Token(123));
assert_eq!(event.readiness(), Ready::readable());
// Read the value
rx.try_recv().unwrap();
// Drop the sender half
drop(tx);
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(1, num);
let event = events.get(0).unwrap();
assert_eq!(event.token(), Token(123));
assert_eq!(event.readiness(), Ready::readable());
match rx.try_recv() {
Err(TryRecvError::Disconnected) => {}
no => panic!("unexpected value {:?}", no),
}
}
#[test]
pub fn test_poll_channel_oneshot() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let (tx, rx) = channel::channel();
poll.register(&rx, Token(123), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
// Wait, but nothing should happen
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Push the value
tx.send("hello").unwrap();
// Polling will contain the event
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(1, num);
let event = events.get(0).unwrap();
assert_eq!(event.token(), Token(123));
assert_eq!(event.readiness(), Ready::readable());
// Poll again and there should be no events
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Read the value
assert_eq!("hello", rx.try_recv().unwrap());
// Poll again, nothing
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Push a value
tx.send("goodbye").unwrap();
// Poll again, nothing
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Reregistering will re-trigger the notification
for _ in 0..3 {
poll.reregister(&rx, Token(123), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
// Have an event
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(1, num);
let event = events.get(0).unwrap();
assert_eq!(event.token(), Token(123));
assert_eq!(event.readiness(), Ready::readable());
}
// Get the value
assert_eq!("goodbye", rx.try_recv().unwrap());
poll.reregister(&rx, Token(123), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
// Have an event
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
poll.reregister(&rx, Token(123), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
// Have an event
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
}
#[test]
pub fn test_poll_channel_level() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let (tx, rx) = channel::channel();
poll.register(&rx, Token(123), Ready::readable(), PollOpt::level()).unwrap();
// Wait, but nothing should happen
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Push the value
tx.send("hello").unwrap();
// Polling will contain the event
for i in 0..5 {
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert!(1 == num, "actually got {} on iteration {}", num, i);
let event = events.get(0).unwrap();
assert_eq!(event.token(), Token(123));
assert_eq!(event.readiness(), Ready::readable());
}
// Read the value
assert_eq!("hello", rx.try_recv().unwrap());
// Wait, but nothing should happen
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
}
#[test]
pub fn test_poll_channel_writable() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let (tx, rx) = channel::channel();
poll.register(&rx, Token(123), Ready::writable(), PollOpt::edge()).unwrap();
// Wait, but nothing should happen
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
// Push the value
tx.send("hello").unwrap();
// Wait, but nothing should happen
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
}
#[test]
pub fn test_dropping_receive_before_poll() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let (tx, rx) = channel::channel();
poll.register(&rx, Token(123), Ready::readable(), PollOpt::edge()).unwrap();
// Push the value
tx.send("hello").unwrap();
// Drop the receive end
drop(rx);
// Wait, but nothing should happen
let num = poll.poll(&mut events, Some(Duration::from_millis(300))).unwrap();
assert_eq!(0, num);
}
#[test]
pub fn test_mixing_channel_with_socket() {
use mio::net::{TcpListener, TcpStream};
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let (tx, rx) = channel::channel();
// Create the listener
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
// Register the listener with `Poll`
poll.register(&l, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
poll.register(&rx, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
// Push a value onto the channel
tx.send("hello").unwrap();
// Connect a TCP socket
let s1 = TcpStream::connect(&l.local_addr().unwrap()).unwrap();
// Register the socket
poll.register(&s1, Token(2), Ready::readable(), PollOpt::edge()).unwrap();
// Sleep a bit to ensure it arrives at dest
sleep_ms(250);
expect_events(&poll, &mut events, 2, vec![
Event::new(Ready::empty(), Token(0)),
Event::new(Ready::empty(), Token(1)),
]);
}
#[test]
pub fn test_sending_from_other_thread_while_polling() {
const ITERATIONS: usize = 20;
const THREADS: usize = 5;
// Make sure to run multiple times
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
for _ in 0..ITERATIONS {
let (tx, rx) = channel::channel();
poll.register(&rx, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
for _ in 0..THREADS {
let tx = tx.clone();
thread::spawn(move || {
sleep_ms(50);
tx.send("ping").unwrap();
});
}
let mut recv = 0;
while recv < THREADS {
let num = poll.poll(&mut events, None).unwrap();
if num != 0 {
assert_eq!(1, num);
assert_eq!(events.get(0).unwrap().token(), Token(0));
while let Ok(_) = rx.try_recv() {
recv += 1;
}
}
}
}
}

View File

@ -0,0 +1,123 @@
use {expect_events, localhost, TryWrite};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::event::Event;
use mio::net::{TcpListener, TcpStream};
use bytes::SliceBuf;
use std::time::Duration;
const SERVER: Token = Token(0);
const CLIENT: Token = Token(1);
struct TestHandler {
server: TcpListener,
client: TcpStream,
state: usize,
}
impl TestHandler {
fn new(srv: TcpListener, cli: TcpStream) -> TestHandler {
TestHandler {
server: srv,
client: cli,
state: 0,
}
}
fn handle_read(&mut self, poll: &mut Poll, token: Token) {
match token {
SERVER => {
trace!("handle_read; token=SERVER");
let mut sock = self.server.accept().unwrap().0;
sock.try_write_buf(&mut SliceBuf::wrap(b"foobar")).unwrap();
}
CLIENT => {
trace!("handle_read; token=CLIENT");
assert!(self.state == 0, "unexpected state {}", self.state);
self.state = 1;
poll.reregister(&self.client, CLIENT, Ready::writable(), PollOpt::level()).unwrap();
}
_ => panic!("unexpected token"),
}
}
fn handle_write(&mut self, poll: &mut Poll, token: Token) {
debug!("handle_write; token={:?}; state={:?}", token, self.state);
assert!(token == CLIENT, "unexpected token {:?}", token);
assert!(self.state == 1, "unexpected state {}", self.state);
self.state = 2;
poll.deregister(&self.client).unwrap();
poll.deregister(&self.server).unwrap();
}
}
#[test]
pub fn test_register_deregister() {
let _ = ::env_logger::init();
debug!("Starting TEST_REGISTER_DEREGISTER");
let mut poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let addr = localhost();
let server = TcpListener::bind(&addr).unwrap();
info!("register server socket");
poll.register(&server, SERVER, Ready::readable(), PollOpt::edge()).unwrap();
let client = TcpStream::connect(&addr).unwrap();
// Register client socket only as writable
poll.register(&client, CLIENT, Ready::readable(), PollOpt::level()).unwrap();
let mut handler = TestHandler::new(server, client);
loop {
poll.poll(&mut events, None).unwrap();
if let Some(event) = events.get(0) {
if event.readiness().is_readable() {
handler.handle_read(&mut poll, event.token());
}
if event.readiness().is_writable() {
handler.handle_write(&mut poll, event.token());
break;
}
}
}
poll.poll(&mut events, Some(Duration::from_millis(100))).unwrap();
assert_eq!(events.len(), 0);
}
#[test]
pub fn test_register_empty_interest() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let addr = localhost();
let sock = TcpListener::bind(&addr).unwrap();
poll.register(&sock, Token(0), Ready::empty(), PollOpt::edge()).unwrap();
let client = TcpStream::connect(&addr).unwrap();
// The connect is not guaranteed to have started until it is registered
// https://docs.rs/mio/0.6.10/mio/struct.Poll.html#registering-handles
poll.register(&client, Token(1), Ready::empty(), PollOpt::edge()).unwrap();
// sock is registered with empty interest, we should not receive any event
poll.poll(&mut events, Some(Duration::from_millis(100))).unwrap();
assert_eq!(events.len(), 0, "Received unexpected event: {:?}", events.get(0).unwrap());
// now sock is reregistered with readable, we should receive the pending event
poll.reregister(&sock, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
expect_events(&poll, &mut events, 2, vec![
Event::new(Ready::readable(), Token(0))
]);
poll.reregister(&sock, Token(0), Ready::empty(), PollOpt::edge()).unwrap();
}

View File

@ -0,0 +1,63 @@
use localhost;
use mio::*;
use mio::net::{TcpListener, TcpStream, UdpSocket};
use std::io::ErrorKind;
#[test]
fn test_tcp_register_multiple_event_loops() {
let addr = localhost();
let listener = TcpListener::bind(&addr).unwrap();
let poll1 = Poll::new().unwrap();
poll1.register(&listener, Token(0), Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
let poll2 = Poll::new().unwrap();
// Try registering the same socket with the initial one
let res = poll2.register(&listener, Token(0), Ready::readable() | Ready::writable(), PollOpt::edge());
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::Other);
// Try cloning the socket and registering it again
let listener2 = listener.try_clone().unwrap();
let res = poll2.register(&listener2, Token(0), Ready::readable() | Ready::writable(), PollOpt::edge());
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::Other);
// Try the stream
let stream = TcpStream::connect(&addr).unwrap();
poll1.register(&stream, Token(1), Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
let res = poll2.register(&stream, Token(1), Ready::readable() | Ready::writable(), PollOpt::edge());
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::Other);
// Try cloning the socket and registering it again
let stream2 = stream.try_clone().unwrap();
let res = poll2.register(&stream2, Token(1), Ready::readable() | Ready::writable(), PollOpt::edge());
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::Other);
}
#[test]
fn test_udp_register_multiple_event_loops() {
let addr = localhost();
let socket = UdpSocket::bind(&addr).unwrap();
let poll1 = Poll::new().unwrap();
poll1.register(&socket, Token(0), Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
let poll2 = Poll::new().unwrap();
// Try registering the same socket with the initial one
let res = poll2.register(&socket, Token(0), Ready::readable() | Ready::writable(), PollOpt::edge());
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::Other);
// Try cloning the socket and registering it again
let socket2 = socket.try_clone().unwrap();
let res = poll2.register(&socket2, Token(0), Ready::readable() | Ready::writable(), PollOpt::edge());
assert!(res.is_err());
assert_eq!(res.unwrap_err().kind(), ErrorKind::Other);
}

View File

@ -0,0 +1,28 @@
use {sleep_ms};
use mio::*;
use mio::net::{TcpListener, TcpStream};
use std::time::Duration;
const MS: u64 = 1_000;
#[test]
pub fn test_reregister_different_without_poll() {
let mut events = Events::with_capacity(1024);
let poll = Poll::new().unwrap();
// Create the listener
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
// Register the listener with `Poll`
poll.register(&l, Token(0), Ready::readable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
let s1 = TcpStream::connect(&l.local_addr().unwrap()).unwrap();
poll.register(&s1, Token(2), Ready::readable(), PollOpt::edge()).unwrap();
sleep_ms(MS);
poll.reregister(&l, Token(0), Ready::writable(), PollOpt::edge() | PollOpt::oneshot()).unwrap();
poll.poll(&mut events, Some(Duration::from_millis(MS))).unwrap();
assert_eq!(events.len(), 0);
}

23
test/test_smoke.rs Normal file
View File

@ -0,0 +1,23 @@
extern crate mio;
use mio::{Events, Poll, Token, Ready, PollOpt};
use mio::net::TcpListener;
use std::time::Duration;
#[test]
fn run_once_with_nothing() {
let mut events = Events::with_capacity(1024);
let poll = Poll::new().unwrap();
poll.poll(&mut events, Some(Duration::from_millis(100))).unwrap();
}
#[test]
fn add_then_drop() {
let mut events = Events::with_capacity(1024);
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let poll = Poll::new().unwrap();
poll.register(&l, Token(1), Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
drop(l);
poll.poll(&mut events, Some(Duration::from_millis(100))).unwrap();
}

View File

@ -0,0 +1,249 @@
use {TryRead, TryWrite};
use std::mem;
use mio::*;
use std::io;
use mio::deprecated::{EventLoop, Handler};
use mio::deprecated::unix::{PipeReader, PipeWriter};
use std::process::{Command, Stdio, Child};
struct SubprocessClient {
stdin: Option<PipeWriter>,
stdout: Option<PipeReader>,
stderr: Option<PipeReader>,
stdin_token : Token,
stdout_token : Token,
stderr_token : Token,
output : Vec<u8>,
output_stderr : Vec<u8>,
input : Vec<u8>,
input_offset : usize,
buf : [u8; 65536],
}
// Sends a message and expects to receive the same exact message, one at a time
impl SubprocessClient {
fn new(stdin: Option<PipeWriter>, stdout : Option<PipeReader>, stderr : Option<PipeReader>, data : &[u8]) -> SubprocessClient {
SubprocessClient {
stdin: stdin,
stdout: stdout,
stderr: stderr,
stdin_token : Token(0),
stdout_token : Token(1),
stderr_token : Token(2),
output : Vec::<u8>::new(),
output_stderr : Vec::<u8>::new(),
buf : [0; 65536],
input : data.to_vec(),
input_offset : 0,
}
}
fn readable(&mut self, event_loop: &mut EventLoop<SubprocessClient>) -> io::Result<()> {
let mut eof = false;
match self.stdout {
None => unreachable!(),
Some (ref mut stdout) => match stdout.try_read(&mut self.buf[..]) {
Ok(None) => {
}
Ok(Some(r)) => {
if r == 0 {
eof = true;
} else {
self.output.extend(&self.buf[0..r]);
}
}
Err(e) => {
return Err(e);
}
}
};
if eof {
drop(self.stdout.take());
match self.stderr {
None => event_loop.shutdown(),
Some(_) => {},
}
}
return Ok(());
}
fn readable_stderr(&mut self, event_loop: &mut EventLoop<SubprocessClient>) -> io::Result<()> {
let mut eof = false;
match self.stderr {
None => unreachable!(),
Some(ref mut stderr) => match stderr.try_read(&mut self.buf[..]) {
Ok(None) => {
}
Ok(Some(r)) => {
if r == 0 {
eof = true;
} else {
self.output_stderr.extend(&self.buf[0..r]);
}
}
Err(e) => {
return Err(e);
}
}
};
if eof {
drop(self.stderr.take());
match self.stdout {
None => event_loop.shutdown(),
Some(_) => {},
}
}
return Ok(());
}
fn writable(&mut self, event_loop: &mut EventLoop<SubprocessClient>) -> io::Result<()> {
let mut ok = true;
match self.stdin {
None => unreachable!(),
Some(ref mut stdin) => match stdin.try_write(&(&self.input)[self.input_offset..]) {
Ok(None) => {
},
Ok(Some(r)) => {
if r == 0 {
ok = false;
} else {
self.input_offset += r;
}
},
Err(_) => {
ok = false;
},
}
}
if self.input_offset == self.input.len() || !ok {
drop(self.stdin.take());
match self.stderr {
None => match self.stdout {
None => event_loop.shutdown(),
Some(_) => {},
},
Some(_) => {},
}
}
return Ok(());
}
}
impl Handler for SubprocessClient {
type Timeout = usize;
type Message = ();
fn ready(&mut self, event_loop: &mut EventLoop<SubprocessClient>, token: Token,
_: Ready) {
if token == self.stderr_token {
let _x = self.readable_stderr(event_loop);
} else {
let _x = self.readable(event_loop);
}
if token == self.stdin_token {
let _y = self.writable(event_loop);
}
}
}
const TEST_DATA : [u8; 1024 * 4096] = [42; 1024 * 4096];
pub fn subprocess_communicate(mut process : Child, input : &[u8]) -> (Vec<u8>, Vec<u8>) {
let mut event_loop = EventLoop::<SubprocessClient>::new().unwrap();
let stdin : Option<PipeWriter>;
let stdin_exists : bool;
match process.stdin {
None => stdin_exists = false,
Some(_) => stdin_exists = true,
}
if stdin_exists {
match PipeWriter::from_stdin(process.stdin.take().unwrap()) {
Err(e) => panic!(e),
Ok(pipe) => stdin = Some(pipe),
}
} else {
stdin = None;
}
let stdout_exists : bool;
let stdout : Option<PipeReader>;
match process.stdout {
None => stdout_exists = false,
Some(_) => stdout_exists = true,
}
if stdout_exists {
match PipeReader::from_stdout(process.stdout.take().unwrap()) {
Err(e) => panic!(e),
Ok(pipe) => stdout = Some(pipe),
}
} else {
stdout = None;
}
let stderr_exists : bool;
let stderr : Option<PipeReader>;
match process.stderr {
None => stderr_exists = false,
Some(_) => stderr_exists = true,
}
if stderr_exists {
match PipeReader::from_stderr(process.stderr.take().unwrap()) {
Err(e) => panic!(e),
Ok(pipe) => stderr = Some(pipe),
}
} else {
stderr = None
}
let mut subprocess = SubprocessClient::new(stdin,
stdout,
stderr,
input);
match subprocess.stdout {
Some(ref sub_stdout) => event_loop.register(sub_stdout, subprocess.stdout_token, Ready::readable(),
PollOpt::level()).unwrap(),
None => {},
}
match subprocess.stderr {
Some(ref sub_stderr) => event_loop.register(sub_stderr, subprocess.stderr_token, Ready::readable(),
PollOpt::level()).unwrap(),
None => {},
}
// Connect to the server
match subprocess.stdin {
Some (ref sub_stdin) => event_loop.register(sub_stdin, subprocess.stdin_token, Ready::writable(),
PollOpt::level()).unwrap(),
None => {},
}
// Start the event loop
event_loop.run(&mut subprocess).unwrap();
let _ = process.wait();
let ret_stdout = mem::replace(&mut subprocess.output, Vec::<u8>::new());
let ret_stderr = mem::replace(&mut subprocess.output_stderr, Vec::<u8>::new());
return (ret_stdout, ret_stderr);
}
#[test]
fn test_subprocess_pipe() {
let process =
Command::new("/bin/cat")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn().unwrap();
let (ret_stdout, ret_stderr) = subprocess_communicate(process, &TEST_DATA[..]);
assert_eq!(TEST_DATA.len(), ret_stdout.len());
assert_eq!(0usize, ret_stderr.len());
let mut i : usize = 0;
for item in TEST_DATA.iter() {
assert_eq!(*item, ret_stdout[i]);
i += 1;
}
}

660
test/test_tcp.rs Normal file
View File

@ -0,0 +1,660 @@
use std::cmp;
use std::io::prelude::*;
use std::io;
use std::net;
use std::sync::mpsc::channel;
use std::thread;
use std::time::Duration;
use net2::{self, TcpStreamExt};
use {TryRead, TryWrite};
use mio::{Token, Ready, PollOpt, Poll, Events};
use iovec::IoVec;
use mio::net::{TcpListener, TcpStream};
#[test]
fn accept() {
struct H { hit: bool, listener: TcpListener, shutdown: bool }
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = l.local_addr().unwrap();
let t = thread::spawn(move || {
net::TcpStream::connect(&addr).unwrap();
});
let poll = Poll::new().unwrap();
poll.register(&l, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(128);
let mut h = H { hit: false, listener: l, shutdown: false };
while !h.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
h.hit = true;
assert_eq!(event.token(), Token(1));
assert!(event.readiness().is_readable());
assert!(h.listener.accept().is_ok());
h.shutdown = true;
}
}
assert!(h.hit);
assert!(h.listener.accept().unwrap_err().kind() == io::ErrorKind::WouldBlock);
t.join().unwrap();
}
#[test]
fn connect() {
struct H { hit: u32, shutdown: bool }
let l = net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let (tx, rx) = channel();
let (tx2, rx2) = channel();
let t = thread::spawn(move || {
let s = l.accept().unwrap();
rx.recv().unwrap();
drop(s);
tx2.send(()).unwrap();
});
let poll = Poll::new().unwrap();
let s = TcpStream::connect(&addr).unwrap();
poll.register(&s, Token(1), Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(128);
let mut h = H { hit: 0, shutdown: false };
while !h.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
assert_eq!(event.token(), Token(1));
match h.hit {
0 => assert!(event.readiness().is_writable()),
1 => assert!(event.readiness().is_readable()),
_ => panic!(),
}
h.hit += 1;
h.shutdown = true;
}
}
assert_eq!(h.hit, 1);
tx.send(()).unwrap();
rx2.recv().unwrap();
h.shutdown = false;
while !h.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
assert_eq!(event.token(), Token(1));
match h.hit {
0 => assert!(event.readiness().is_writable()),
1 => assert!(event.readiness().is_readable()),
_ => panic!(),
}
h.hit += 1;
h.shutdown = true;
}
}
assert_eq!(h.hit, 2);
t.join().unwrap();
}
#[test]
fn read() {
const N: usize = 16 * 1024 * 1024;
struct H { amt: usize, socket: TcpStream, shutdown: bool }
let l = net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let t = thread::spawn(move || {
let mut s = l.accept().unwrap().0;
let b = [0; 1024];
let mut amt = 0;
while amt < N {
amt += s.write(&b).unwrap();
}
});
let poll = Poll::new().unwrap();
let s = TcpStream::connect(&addr).unwrap();
poll.register(&s, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(128);
let mut h = H { amt: 0, socket: s, shutdown: false };
while !h.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
assert_eq!(event.token(), Token(1));
let mut b = [0; 1024];
loop {
if let Some(amt) = h.socket.try_read(&mut b).unwrap() {
h.amt += amt;
} else {
break
}
if h.amt >= N {
h.shutdown = true;
break
}
}
}
}
t.join().unwrap();
}
#[test]
fn peek() {
const N: usize = 16 * 1024 * 1024;
struct H { amt: usize, socket: TcpStream, shutdown: bool }
let l = net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let t = thread::spawn(move || {
let mut s = l.accept().unwrap().0;
let b = [0; 1024];
let mut amt = 0;
while amt < N {
amt += s.write(&b).unwrap();
}
});
let poll = Poll::new().unwrap();
let s = TcpStream::connect(&addr).unwrap();
poll.register(&s, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(128);
let mut h = H { amt: 0, socket: s, shutdown: false };
while !h.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
assert_eq!(event.token(), Token(1));
let mut b = [0; 1024];
match h.socket.peek(&mut b) {
Ok(_) => (),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
continue
},
Err(e) => panic!("unexpected error: {:?}", e),
}
loop {
if let Some(amt) = h.socket.try_read(&mut b).unwrap() {
h.amt += amt;
} else {
break
}
if h.amt >= N {
h.shutdown = true;
break
}
}
}
}
t.join().unwrap();
}
#[test]
fn read_bufs() {
const N: usize = 16 * 1024 * 1024;
let l = net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let t = thread::spawn(move || {
let mut s = l.accept().unwrap().0;
let b = [1; 1024];
let mut amt = 0;
while amt < N {
amt += s.write(&b).unwrap();
}
});
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(128);
let s = TcpStream::connect(&addr).unwrap();
poll.register(&s, Token(1), Ready::readable(), PollOpt::level()).unwrap();
let b1 = &mut [0; 10][..];
let b2 = &mut [0; 383][..];
let b3 = &mut [0; 28][..];
let b4 = &mut [0; 8][..];
let b5 = &mut [0; 128][..];
let mut b: [&mut IoVec; 5] = [
b1.into(),
b2.into(),
b3.into(),
b4.into(),
b5.into(),
];
let mut so_far = 0;
loop {
for buf in b.iter_mut() {
for byte in buf.as_mut_bytes() {
*byte = 0;
}
}
poll.poll(&mut events, None).unwrap();
match s.read_bufs(&mut b) {
Ok(0) => {
assert_eq!(so_far, N);
break
}
Ok(mut n) => {
so_far += n;
for buf in b.iter() {
let buf = buf.as_bytes();
for byte in buf[..cmp::min(n, buf.len())].iter() {
assert_eq!(*byte, 1);
}
n = n.saturating_sub(buf.len());
if n == 0 {
break
}
}
assert_eq!(n, 0);
}
Err(e) => assert_eq!(e.kind(), io::ErrorKind::WouldBlock),
}
}
t.join().unwrap();
}
#[test]
fn write() {
const N: usize = 16 * 1024 * 1024;
struct H { amt: usize, socket: TcpStream, shutdown: bool }
let l = net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let t = thread::spawn(move || {
let mut s = l.accept().unwrap().0;
let mut b = [0; 1024];
let mut amt = 0;
while amt < N {
amt += s.read(&mut b).unwrap();
}
});
let poll = Poll::new().unwrap();
let s = TcpStream::connect(&addr).unwrap();
poll.register(&s, Token(1), Ready::writable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(128);
let mut h = H { amt: 0, socket: s, shutdown: false };
while !h.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
assert_eq!(event.token(), Token(1));
let b = [0; 1024];
loop {
if let Some(amt) = h.socket.try_write(&b).unwrap() {
h.amt += amt;
} else {
break
}
if h.amt >= N {
h.shutdown = true;
break
}
}
}
}
t.join().unwrap();
}
#[test]
fn write_bufs() {
const N: usize = 16 * 1024 * 1024;
let l = net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let t = thread::spawn(move || {
let mut s = l.accept().unwrap().0;
let mut b = [0; 1024];
let mut amt = 0;
while amt < N {
for byte in b.iter_mut() {
*byte = 0;
}
let n = s.read(&mut b).unwrap();
amt += n;
for byte in b[..n].iter() {
assert_eq!(*byte, 1);
}
}
});
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(128);
let s = TcpStream::connect(&addr).unwrap();
poll.register(&s, Token(1), Ready::writable(), PollOpt::level()).unwrap();
let b1 = &[1; 10][..];
let b2 = &[1; 383][..];
let b3 = &[1; 28][..];
let b4 = &[1; 8][..];
let b5 = &[1; 128][..];
let b: [&IoVec; 5] = [
b1.into(),
b2.into(),
b3.into(),
b4.into(),
b5.into(),
];
let mut so_far = 0;
while so_far < N {
poll.poll(&mut events, None).unwrap();
match s.write_bufs(&b) {
Ok(n) => so_far += n,
Err(e) => assert_eq!(e.kind(), io::ErrorKind::WouldBlock),
}
}
t.join().unwrap();
}
#[test]
fn connect_then_close() {
struct H { listener: TcpListener, shutdown: bool }
let poll = Poll::new().unwrap();
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let s = TcpStream::connect(&l.local_addr().unwrap()).unwrap();
poll.register(&l, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
poll.register(&s, Token(2), Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(128);
let mut h = H { listener: l, shutdown: false };
while !h.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.token() == Token(1) {
let s = h.listener.accept().unwrap().0;
poll.register(&s, Token(3), Ready::readable() | Ready::writable(),
PollOpt::edge()).unwrap();
drop(s);
} else if event.token() == Token(2) {
h.shutdown = true;
}
}
}
}
#[test]
fn listen_then_close() {
let poll = Poll::new().unwrap();
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
poll.register(&l, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
drop(l);
let mut events = Events::with_capacity(128);
poll.poll(&mut events, Some(Duration::from_millis(100))).unwrap();
for event in &events {
if event.token() == Token(1) {
panic!("recieved ready() on a closed TcpListener")
}
}
}
fn assert_send<T: Send>() {
}
fn assert_sync<T: Sync>() {
}
#[test]
fn test_tcp_sockets_are_send() {
assert_send::<TcpListener>();
assert_send::<TcpStream>();
assert_sync::<TcpListener>();
assert_sync::<TcpStream>();
}
#[test]
fn bind_twice_bad() {
let l1 = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = l1.local_addr().unwrap();
assert!(TcpListener::bind(&addr).is_err());
}
#[test]
fn multiple_writes_immediate_success() {
const N: usize = 16;
let l = net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = l.local_addr().unwrap();
let t = thread::spawn(move || {
let mut s = l.accept().unwrap().0;
let mut b = [0; 1024];
let mut amt = 0;
while amt < 1024*N {
for byte in b.iter_mut() {
*byte = 0;
}
let n = s.read(&mut b).unwrap();
amt += n;
for byte in b[..n].iter() {
assert_eq!(*byte, 1);
}
}
});
let poll = Poll::new().unwrap();
let mut s = TcpStream::connect(&addr).unwrap();
poll.register(&s, Token(1), Ready::writable(), PollOpt::level()).unwrap();
let mut events = Events::with_capacity(16);
// Wait for our TCP stream to connect
'outer: loop {
poll.poll(&mut events, None).unwrap();
for event in events.iter() {
if event.token() == Token(1) && event.readiness().is_writable() {
break 'outer
}
}
}
for _ in 0..N {
s.write_all(&[1; 1024]).unwrap();
}
t.join().unwrap();
}
#[test]
fn connection_reset_by_peer() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(16);
let mut buf = [0u8; 16];
// Create listener
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = l.local_addr().unwrap();
// Connect client
let client = net2::TcpBuilder::new_v4().unwrap()
.to_tcp_stream().unwrap();
client.set_linger(Some(Duration::from_millis(0))).unwrap();
client.connect(&addr).unwrap();
// Convert to Mio stream
let client = TcpStream::from_stream(client).unwrap();
// Register server
poll.register(&l, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
// Register interest in the client
poll.register(&client, Token(1), Ready::readable() | Ready::writable(), PollOpt::edge()).unwrap();
// Wait for listener to be ready
let mut server;
'outer:
loop {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.token() == Token(0) {
match l.accept() {
Ok((sock, _)) => {
server = sock;
break 'outer;
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {}
Err(e) => panic!("unexpected error {:?}", e),
}
}
}
}
// Close the connection
drop(client);
// Wait a moment
thread::sleep(Duration::from_millis(100));
// Register interest in the server socket
poll.register(&server, Token(3), Ready::readable(), PollOpt::edge()).unwrap();
loop {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.token() == Token(3) {
assert!(event.readiness().is_readable());
match server.read(&mut buf) {
Ok(0) |
Err(_) => {},
Ok(x) => panic!("expected empty buffer but read {} bytes", x),
}
return;
}
}
}
}
#[test]
#[cfg_attr(target_os = "fuchsia", ignore)]
fn connect_error() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(16);
// Pick a "random" port that shouldn't be in use.
let l = match TcpStream::connect(&"127.0.0.1:38381".parse().unwrap()) {
Ok(l) => l,
Err(ref e) if e.kind() == io::ErrorKind::ConnectionRefused => {
// Connection failed synchronously. This is not a bug, but it
// unfortunately doesn't get us the code coverage we want.
return;
},
Err(e) => panic!("TcpStream::connect unexpected error {:?}", e)
};
poll.register(&l, Token(0), Ready::writable(), PollOpt::edge()).unwrap();
'outer:
loop {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.token() == Token(0) {
assert!(event.readiness().is_writable());
break 'outer
}
}
}
assert!(l.take_error().unwrap().is_some());
}
#[test]
fn write_error() {
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(16);
let (tx, rx) = channel();
let listener = net::TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap();
let t = thread::spawn(move || {
let (conn, _addr) = listener.accept().unwrap();
rx.recv().unwrap();
drop(conn);
});
let mut s = TcpStream::connect(&addr).unwrap();
poll.register(&s,
Token(0),
Ready::readable() | Ready::writable(),
PollOpt::edge()).unwrap();
let mut wait_writable = || {
'outer:
loop {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.token() == Token(0) && event.readiness().is_writable() {
break 'outer
}
}
}
};
wait_writable();
tx.send(()).unwrap();
t.join().unwrap();
let buf = [0; 1024];
loop {
match s.write(&buf) {
Ok(_) => {}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
wait_writable()
}
Err(e) => {
println!("good error: {}", e);
break
}
}
}
}

142
test/test_tcp_level.rs Normal file
View File

@ -0,0 +1,142 @@
use {expect_events, sleep_ms, TryRead};
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::event::Event;
use mio::net::{TcpListener, TcpStream};
use std::io::Write;
use std::time::Duration;
const MS: u64 = 1_000;
#[test]
pub fn test_tcp_listener_level_triggered() {
let poll = Poll::new().unwrap();
let mut pevents = Events::with_capacity(1024);
// Create the listener
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
// Register the listener with `Poll`
poll.register(&l, Token(0), Ready::readable(), PollOpt::level()).unwrap();
let s1 = TcpStream::connect(&l.local_addr().unwrap()).unwrap();
poll.register(&s1, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
while filter(&pevents, Token(0)).is_empty() {
poll.poll(&mut pevents, Some(Duration::from_millis(MS))).unwrap();
}
let events = filter(&pevents, Token(0));
assert_eq!(events.len(), 1);
assert_eq!(events[0], Event::new(Ready::readable(), Token(0)));
poll.poll(&mut pevents, Some(Duration::from_millis(MS))).unwrap();
let events = filter(&pevents, Token(0));
assert_eq!(events.len(), 1);
assert_eq!(events[0], Event::new(Ready::readable(), Token(0)));
// Accept the connection then test that the events stop
let _ = l.accept().unwrap();
poll.poll(&mut pevents, Some(Duration::from_millis(MS))).unwrap();
let events = filter(&pevents, Token(0));
assert!(events.is_empty(), "actual={:?}", events);
let s3 = TcpStream::connect(&l.local_addr().unwrap()).unwrap();
poll.register(&s3, Token(2), Ready::readable(), PollOpt::edge()).unwrap();
while filter(&pevents, Token(0)).is_empty() {
poll.poll(&mut pevents, Some(Duration::from_millis(MS))).unwrap();
}
let events = filter(&pevents, Token(0));
assert_eq!(events.len(), 1);
assert_eq!(events[0], Event::new(Ready::readable(), Token(0)));
drop(l);
poll.poll(&mut pevents, Some(Duration::from_millis(MS))).unwrap();
let events = filter(&pevents, Token(0));
assert!(events.is_empty());
}
#[test]
pub fn test_tcp_stream_level_triggered() {
drop(::env_logger::init());
let poll = Poll::new().unwrap();
let mut pevents = Events::with_capacity(1024);
// Create the listener
let l = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
// Register the listener with `Poll`
poll.register(&l, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
let mut s1 = TcpStream::connect(&l.local_addr().unwrap()).unwrap();
poll.register(&s1, Token(1), Ready::readable() | Ready::writable(), PollOpt::level()).unwrap();
// Sleep a bit to ensure it arrives at dest
sleep_ms(250);
expect_events(&poll, &mut pevents, 2, vec![
Event::new(Ready::readable(), Token(0)),
Event::new(Ready::writable(), Token(1)),
]);
// Server side of socket
let (mut s1_tx, _) = l.accept().unwrap();
// Sleep a bit to ensure it arrives at dest
sleep_ms(250);
expect_events(&poll, &mut pevents, 2, vec![
Event::new(Ready::writable(), Token(1))
]);
// Register the socket
poll.register(&s1_tx, Token(123), Ready::readable(), PollOpt::edge()).unwrap();
debug!("writing some data ----------");
// Write some data
let res = s1_tx.write(b"hello world!");
assert!(res.unwrap() > 0);
// Sleep a bit to ensure it arrives at dest
sleep_ms(250);
debug!("looking at rx end ----------");
// Poll rx end
expect_events(&poll, &mut pevents, 2, vec![
Event::new(Ready::readable(), Token(1))
]);
debug!("reading ----------");
// Reading the data should clear it
let mut res = vec![];
while s1.try_read_buf(&mut res).unwrap().is_some() {
}
assert_eq!(res, b"hello world!");
debug!("checking just read ----------");
expect_events(&poll, &mut pevents, 1, vec![
Event::new(Ready::writable(), Token(1))]);
// Closing the socket clears all active level events
drop(s1);
debug!("checking everything is gone ----------");
poll.poll(&mut pevents, Some(Duration::from_millis(MS))).unwrap();
let events = filter(&pevents, Token(1));
assert!(events.is_empty());
}
fn filter(events: &Events, token: Token) -> Vec<Event> {
(0..events.len()).map(|i| events.get(i).unwrap())
.filter(|e| e.token() == token)
.collect()
}

242
test/test_tcp_shutdown.rs Normal file
View File

@ -0,0 +1,242 @@
use std::collections::HashMap;
use std::net::Shutdown;
use std::time::{Duration, Instant};
use mio::{Token, Ready, PollOpt, Poll, Events};
use mio::event::{Evented, Event};
use mio::net::TcpStream;
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();
loop {
if now.elapsed() > Duration::from_secs(1) {
return Err("not ready");
}
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() {
use std::io::prelude::*;
let mut poll = TestPoll::new();
let mut buf = [0; 1024];
let listener = assert_ok!(std::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_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!(socket.shutdown(Shutdown::Write));
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!(std::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());
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::{self, Read, Write};
let mut poll = TestPoll::new();
let mut buf = [0; 1024];
let listener = assert_ok!(std::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_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);
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);
}

64
test/test_tick.rs Normal file
View File

@ -0,0 +1,64 @@
use mio::*;
use mio::deprecated::{EventLoop, Handler};
use mio::net::{TcpListener, TcpStream};
use {sleep_ms};
struct TestHandler {
tick: usize,
state: usize,
}
impl TestHandler {
fn new() -> TestHandler {
TestHandler {
tick: 0,
state: 0,
}
}
}
impl Handler for TestHandler {
type Timeout = usize;
type Message = String;
fn tick(&mut self, _event_loop: &mut EventLoop<TestHandler>) {
debug!("Handler::tick()");
self.tick += 1;
assert_eq!(self.state, 1);
self.state = 0;
}
fn ready(&mut self, _event_loop: &mut EventLoop<TestHandler>, token: Token, events: Ready) {
debug!("READY: {:?} - {:?}", token, events);
if events.is_readable() {
debug!("Handler::ready() readable event");
assert_eq!(token, Token(0));
assert_eq!(self.state, 0);
self.state = 1;
}
}
}
#[test]
pub fn test_tick() {
debug!("Starting TEST_TICK");
let mut event_loop = EventLoop::new().expect("Couldn't make event loop");
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
event_loop.register(&listener, Token(0), Ready::readable(), PollOpt::level()).unwrap();
let client = TcpStream::connect(&listener.local_addr().unwrap()).unwrap();
event_loop.register(&client, Token(1), Ready::readable(), PollOpt::edge()).unwrap();
sleep_ms(250);
let mut handler = TestHandler::new();
for _ in 0..2 {
event_loop.run_once(&mut handler, None).unwrap();
}
assert!(handler.tick == 2, "actual={}", handler.tick);
assert!(handler.state == 0, "actual={}", handler.state);
}

52
test/test_udp_level.rs Normal file
View File

@ -0,0 +1,52 @@
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::event::Event;
use mio::net::UdpSocket;
use {expect_events, sleep_ms};
#[test]
pub fn test_udp_level_triggered() {
let poll = Poll::new().unwrap();
let poll = &poll;
let mut events = Events::with_capacity(1024);
let events = &mut events;
// Create the listener
let tx = UdpSocket::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let rx = UdpSocket::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
poll.register(&tx, Token(0), Ready::readable() | Ready::writable(), PollOpt::level()).unwrap();
poll.register(&rx, Token(1), Ready::readable() | Ready::writable(), PollOpt::level()).unwrap();
for _ in 0..2 {
expect_events(poll, events, 2, vec![
Event::new(Ready::writable(), Token(0)),
Event::new(Ready::writable(), Token(1)),
]);
}
tx.send_to(b"hello world!", &rx.local_addr().unwrap()).unwrap();
sleep_ms(250);
for _ in 0..2 {
expect_events(poll, events, 2, vec![
Event::new(Ready::readable() | Ready::writable(), Token(1))
]);
}
let mut buf = [0; 200];
while rx.recv_from(&mut buf).is_ok() {}
for _ in 0..2 {
expect_events(poll, events, 4, vec![Event::new(Ready::writable(), Token(1))]);
}
tx.send_to(b"hello world!", &rx.local_addr().unwrap()).unwrap();
sleep_ms(250);
expect_events(poll, events, 10,
vec![Event::new(Ready::readable() | Ready::writable(), Token(1))]);
drop(rx);
}

252
test/test_udp_socket.rs Normal file
View File

@ -0,0 +1,252 @@
use mio::{Events, Poll, PollOpt, Ready, Token};
use mio::net::UdpSocket;
use bytes::{Buf, RingBuf, SliceBuf, MutBuf};
use std::io::ErrorKind;
use std::str;
use std::time;
use localhost;
use iovec::IoVec;
const LISTENER: Token = Token(0);
const SENDER: Token = Token(1);
pub struct UdpHandlerSendRecv {
tx: UdpSocket,
rx: UdpSocket,
msg: &'static str,
buf: SliceBuf<'static>,
rx_buf: RingBuf,
connected: bool,
shutdown: bool,
}
impl UdpHandlerSendRecv {
fn new(tx: UdpSocket, rx: UdpSocket, connected: bool, msg : &'static str) -> UdpHandlerSendRecv {
UdpHandlerSendRecv {
tx,
rx,
msg,
buf: SliceBuf::wrap(msg.as_bytes()),
rx_buf: RingBuf::new(1024),
connected,
shutdown: false,
}
}
}
fn assert_send<T: Send>() {
}
fn assert_sync<T: Sync>() {
}
#[cfg(test)]
fn test_send_recv_udp(tx: UdpSocket, rx: UdpSocket, connected: bool) {
debug!("Starting TEST_UDP_SOCKETS");
let poll = Poll::new().unwrap();
assert_send::<UdpSocket>();
assert_sync::<UdpSocket>();
// ensure that the sockets are non-blocking
let mut buf = [0; 128];
assert_eq!(ErrorKind::WouldBlock, rx.recv_from(&mut buf).unwrap_err().kind());
info!("Registering SENDER");
poll.register(&tx, SENDER, Ready::writable(), PollOpt::edge()).unwrap();
info!("Registering LISTENER");
poll.register(&rx, LISTENER, Ready::readable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(1024);
info!("Starting event loop to test with...");
let mut handler = UdpHandlerSendRecv::new(tx, rx, connected, "hello world");
while !handler.shutdown {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.readiness().is_readable() {
if let LISTENER = event.token() {
debug!("We are receiving a datagram now...");
let cnt = unsafe {
if !handler.connected {
handler.rx.recv_from(handler.rx_buf.mut_bytes()).unwrap().0
} else {
handler.rx.recv(handler.rx_buf.mut_bytes()).unwrap()
}
};
unsafe { MutBuf::advance(&mut handler.rx_buf, cnt); }
assert!(str::from_utf8(handler.rx_buf.bytes()).unwrap() == handler.msg);
handler.shutdown = true;
}
}
if event.readiness().is_writable() {
if let SENDER = event.token() {
let cnt = if !handler.connected {
let addr = handler.rx.local_addr().unwrap();
handler.tx.send_to(handler.buf.bytes(), &addr).unwrap()
} else {
handler.tx.send(handler.buf.bytes()).unwrap()
};
handler.buf.advance(cnt);
}
}
}
}
}
/// Returns the sender and the receiver
fn connected_sockets() -> (UdpSocket, UdpSocket) {
let addr = localhost();
let any = localhost();
let tx = UdpSocket::bind(&any).unwrap();
let rx = UdpSocket::bind(&addr).unwrap();
let tx_addr = tx.local_addr().unwrap();
let rx_addr = rx.local_addr().unwrap();
assert!(tx.connect(rx_addr).is_ok());
assert!(rx.connect(tx_addr).is_ok());
(tx, rx)
}
#[test]
pub fn test_udp_socket() {
let addr = localhost();
let any = localhost();
let tx = UdpSocket::bind(&any).unwrap();
let rx = UdpSocket::bind(&addr).unwrap();
test_send_recv_udp(tx, rx, false);
}
#[test]
pub fn test_udp_socket_send_recv() {
let (tx, rx) = connected_sockets();
test_send_recv_udp(tx, rx, true);
}
#[test]
pub fn test_udp_socket_discard() {
let addr = localhost();
let any = localhost();
let outside = localhost();
let tx = UdpSocket::bind(&any).unwrap();
let rx = UdpSocket::bind(&addr).unwrap();
let udp_outside = UdpSocket::bind(&outside).unwrap();
let tx_addr = tx.local_addr().unwrap();
let rx_addr = rx.local_addr().unwrap();
assert!(tx.connect(rx_addr).is_ok());
assert!(udp_outside.connect(rx_addr).is_ok());
assert!(rx.connect(tx_addr).is_ok());
let poll = Poll::new().unwrap();
let r = udp_outside.send(b"hello world");
assert!(r.is_ok() || r.unwrap_err().kind() == ErrorKind::WouldBlock);
poll.register(&rx, LISTENER, Ready::readable(), PollOpt::edge()).unwrap();
poll.register(&tx, SENDER, Ready::writable(), PollOpt::edge()).unwrap();
let mut events = Events::with_capacity(1024);
poll.poll(&mut events, Some(time::Duration::from_secs(5))).unwrap();
for event in &events {
if event.readiness().is_readable() {
if let LISTENER = event.token() {
assert!(false, "Expected to no receive a packet but got something")
}
}
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
#[test]
pub fn test_udp_socket_send_recv_bufs() {
let (tx, rx) = connected_sockets();
let poll = Poll::new().unwrap();
poll.register(&tx, SENDER, Ready::writable(), PollOpt::edge())
.unwrap();
poll.register(&rx, LISTENER, Ready::readable(), PollOpt::edge())
.unwrap();
let mut events = Events::with_capacity(1024);
let data = b"hello, world";
let write_bufs: Vec<_> = vec![b"hello, " as &[u8], b"world"]
.into_iter()
.flat_map(IoVec::from_bytes)
.collect();
let (a, b, c) = (
&mut [0u8; 4] as &mut [u8],
&mut [0u8; 6] as &mut [u8],
&mut [0u8; 8] as &mut [u8],
);
let mut read_bufs: Vec<_> = vec![a, b, c]
.into_iter()
.flat_map(IoVec::from_bytes_mut)
.collect();
let times = 5;
let mut rtimes = 0;
let mut wtimes = 0;
'outer: loop {
poll.poll(&mut events, None).unwrap();
for event in &events {
if event.readiness().is_readable() {
if let LISTENER = event.token() {
loop {
let cnt = match rx.recv_bufs(read_bufs.as_mut()) {
Ok(cnt) => cnt,
Err(ref e) if e.kind() == ErrorKind::WouldBlock => break,
Err(e) => panic!("read error {}", e),
};
assert_eq!(cnt, data.len());
let res: Vec<u8> = read_bufs
.iter()
.flat_map(|buf| buf.iter())
.cloned()
.collect();
assert_eq!(&res[..cnt], &data[..cnt]);
rtimes += 1;
if rtimes == times {
break 'outer;
}
}
}
}
if event.readiness().is_writable() {
if let SENDER = event.token() {
while wtimes < times {
let cnt = match tx.send_bufs(write_bufs.as_slice()) {
Ok(cnt) => cnt,
Err(ref e) if e.kind() == ErrorKind::WouldBlock => break,
Err(e) => panic!("write error {}", e),
};
assert_eq!(cnt, data.len());
wtimes += 1;
}
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More