Compare commits
142 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
803573fa28 | ||
|
|
017d018d75 | ||
|
|
d6edcd13fd | ||
|
|
092d6fdc7d | ||
|
|
5a5b5f7d43 | ||
|
|
29fd58deda | ||
|
|
19d5ebe61c | ||
|
|
3550494f0b | ||
|
|
72a001a8e3 | ||
|
|
e739790cb8 | ||
|
|
e054a7d587 | ||
|
|
4276d071e3 | ||
|
|
d07989a3b8 | ||
|
|
c7674d64ad | ||
|
|
d7fa70644f | ||
|
|
588e808576 | ||
|
|
1dd6000a6d | ||
|
|
2653117b84 | ||
|
|
5de1317418 | ||
|
|
bbc2f774c9 | ||
|
|
6b810629c3 | ||
|
|
4dc7bae2a0 | ||
|
|
7b06041b4c | ||
|
|
fbecef6684 | ||
|
|
b60218d9b2 | ||
|
|
c6d88a5810 | ||
|
|
8e0b0364ab | ||
|
|
3626fdd2e5 | ||
|
|
ce66285747 | ||
|
|
7f7c62de8d | ||
|
|
fd38052231 | ||
|
|
e134e798d8 | ||
|
|
eee0a9f10a | ||
|
|
9ae6ee58b4 | ||
|
|
0b81ecb175 | ||
|
|
e0be6e4fb3 | ||
|
|
9a246b632e | ||
|
|
d43717e44c | ||
|
|
2c0ecd2dbf | ||
|
|
59443cd224 | ||
|
|
51e1c4587f | ||
|
|
200aaa7a2c | ||
|
|
6072de2b84 | ||
|
|
6668d44d3b | ||
|
|
254e537e22 | ||
|
|
fe5d6caed7 | ||
|
|
14c049b923 | ||
|
|
f30debef5c | ||
|
|
2f688de26c | ||
|
|
211223e928 | ||
|
|
cd7331ad05 | ||
|
|
a160244f1d | ||
|
|
65f19ddcb3 | ||
|
|
7f89495375 | ||
|
|
5aeebe2b4e | ||
|
|
2a55db6314 | ||
|
|
ec0f5c9fad | ||
|
|
b09bcd7253 | ||
|
|
b63d2ca4fb | ||
|
|
5439526f7b | ||
|
|
c59dbc1812 | ||
|
|
ce76cf6d2b | ||
|
|
e5b46055a4 | ||
|
|
d1879f4a90 | ||
|
|
0f09be5bd9 | ||
|
|
e5288e06eb | ||
|
|
5a6bb732bd | ||
|
|
79123b738e | ||
|
|
512230851d | ||
|
|
d312503a84 | ||
|
|
eadafc2bed | ||
|
|
754d4c0022 | ||
|
|
96ccddf7e2 | ||
|
|
af47a84ea1 | ||
|
|
943d093d56 | ||
|
|
85dfc79daf | ||
|
|
058186003b | ||
|
|
dfc987db6e | ||
|
|
cb0ab723c9 | ||
|
|
dc0643c7ae | ||
|
|
418ff89602 | ||
|
|
8a5455da6e | ||
|
|
5042c6c853 | ||
|
|
b392693351 | ||
|
|
5d61c5e6b5 | ||
|
|
18113ce739 | ||
|
|
178f51e8da | ||
|
|
84798948c5 | ||
|
|
aa84849c61 | ||
|
|
12156ce81b | ||
|
|
819f65eb62 | ||
|
|
8dd95d5ddb | ||
|
|
479ae7c5e2 | ||
|
|
5ffb91a094 | ||
|
|
8f6f5613f2 | ||
|
|
61b40436d5 | ||
|
|
7a7b7fc130 | ||
|
|
e4a49ef58c | ||
|
|
9d0018c879 | ||
|
|
76e2a23c02 | ||
|
|
2d8ecd6301 | ||
|
|
8397102dc0 | ||
|
|
2862ed2bff | ||
|
|
353046503f | ||
|
|
de208b3355 | ||
|
|
3d46dd4be6 | ||
|
|
2180ff894f | ||
|
|
855305cb3d | ||
|
|
140dd392ba | ||
|
|
1eb579cd95 | ||
|
|
47809ef14e | ||
|
|
8b81ab2047 | ||
|
|
308ced4b0c | ||
|
|
e0dcc01a14 | ||
|
|
340bef90a8 | ||
|
|
a859407e1a | ||
|
|
b506eef756 | ||
|
|
66c5bdd64f | ||
|
|
e9791d71e9 | ||
|
|
d355edff1a | ||
|
|
a4c3c6636e | ||
|
|
7326ee8f3f | ||
|
|
527826ef58 | ||
|
|
ee2ec5998f | ||
|
|
434405a66c | ||
|
|
46f7f6a2b3 | ||
|
|
ab099cb5e1 | ||
|
|
756bf282fd | ||
|
|
1185510833 | ||
|
|
87d7241833 | ||
|
|
1b68512dc3 | ||
|
|
4c523ef45d | ||
|
|
1afa93820a | ||
|
|
f09a6cfc10 | ||
|
|
42e6e96ae2 | ||
|
|
73652b0395 | ||
|
|
413a7d3c43 | ||
|
|
7798b5b829 | ||
|
|
631e80a9d3 | ||
|
|
c24bba7ddf | ||
|
|
ebd086e4c4 | ||
|
|
e739c8a8a7 |
10
.cirrus.yml
10
.cirrus.yml
@ -1,6 +1,9 @@
|
||||
freebsd_instance:
|
||||
image: freebsd-11-2-release-amd64
|
||||
|
||||
env:
|
||||
RUST_BACKTRACE: full
|
||||
|
||||
task:
|
||||
name: FreeBSD 11.2 amd64
|
||||
setup_script:
|
||||
@ -13,9 +16,12 @@ task:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo build
|
||||
- cargo build --no-default-features
|
||||
test_script:
|
||||
amd64_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test
|
||||
- cargo test --no-default-features
|
||||
i386_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- rustup target add i686-unknown-freebsd
|
||||
- cargo test --target i686-unknown-freebsd
|
||||
before_cache_script:
|
||||
- rm -rf $HOME/.cargo/registry/index
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
# 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
|
||||
|
||||
47
Cargo.toml
47
Cargo.toml
@ -1,18 +1,18 @@
|
||||
[package]
|
||||
|
||||
name = "mio"
|
||||
edition = "2018"
|
||||
name = "mio"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Create git tag
|
||||
version = "0.6.19"
|
||||
version = "0.7.0"
|
||||
license = "MIT"
|
||||
authors = ["Carl Lerche <me@carllerche.com>"]
|
||||
description = "Lightweight non-blocking IO"
|
||||
documentation = "https://docs.rs/mio/0.6.19/mio/"
|
||||
homepage = "https://github.com/carllerche/mio"
|
||||
repository = "https://github.com/carllerche/mio"
|
||||
documentation = "https://docs.rs/mio/0.7.0/mio/"
|
||||
homepage = "https://github.com/tokio-rs/mio"
|
||||
repository = "https://github.com/tokio-rs/mio"
|
||||
readme = "README.md"
|
||||
keywords = ["io", "async", "non-blocking"]
|
||||
categories = ["asynchronous"]
|
||||
@ -21,34 +21,27 @@ exclude = [
|
||||
".travis.yml",
|
||||
"deploy.sh",
|
||||
]
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
with-deprecated = []
|
||||
default = ["with-deprecated"]
|
||||
|
||||
[dependencies]
|
||||
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"
|
||||
log = "0.4.6"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.42"
|
||||
libc = "0.2.58"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
winapi = "0.2.6"
|
||||
miow = "0.2.1"
|
||||
kernel32-sys = "0.2"
|
||||
miow = "0.3.3"
|
||||
winapi = { version = "0.3", features = ["winsock2", "mswsock"] }
|
||||
ntapi = "0.3"
|
||||
lazy_static = "1.3.0"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.4.0", default-features = false }
|
||||
tempdir = "0.3.4"
|
||||
bytes = "0.3.0"
|
||||
|
||||
[[test]]
|
||||
name = "test"
|
||||
path = "test/mod.rs"
|
||||
# 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"
|
||||
|
||||
33
README.md
33
README.md
@ -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/carllerche/mio.svg
|
||||
[cirrus-url]: https://cirrus-ci.com/github/carllerche/mio
|
||||
[cirrus-badge]: https://api.cirrus-ci.com/github/tokio-rs/mio.svg
|
||||
[cirrus-url]: https://cirrus-ci.com/github/tokio-rs/mio
|
||||
|
||||
**API documentation**
|
||||
|
||||
@ -42,10 +42,10 @@ extern crate mio;
|
||||
|
||||
## Features
|
||||
|
||||
* Non-blocking TCP, UDP.
|
||||
* I/O event notification queue backed by epoll, kqueue, and IOCP.
|
||||
* Non-blocking TCP, UDP
|
||||
* I/O event queue backed by epoll, kqueue, and IOCP
|
||||
* Zero allocations at runtime
|
||||
* Platform specific extensions.
|
||||
* Platform specific extensions
|
||||
|
||||
## Non-goals
|
||||
|
||||
@ -60,22 +60,27 @@ or higher-level libraries.
|
||||
|
||||
Currently supported platforms:
|
||||
|
||||
* Linux
|
||||
* OS X
|
||||
* Windows
|
||||
* FreeBSD
|
||||
* NetBSD
|
||||
* Solaris
|
||||
* Android
|
||||
* Bitrig
|
||||
* DragonFly BSD
|
||||
* FreeBSD
|
||||
* Linux
|
||||
* NetBSD
|
||||
* OpenBSD
|
||||
* Solaris
|
||||
* Windows
|
||||
* 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 in the #mio channel on the Mozilla IRC
|
||||
server (irc.mozilla.org). This can be a good place to go for questions.
|
||||
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
|
||||
|
||||
## Contributing
|
||||
|
||||
@ -87,4 +92,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/conduct.html).
|
||||
Finally, be kind. We support the [Rust Code of Conduct](https://www.rust-lang.org/policies/code-of-conduct).
|
||||
|
||||
@ -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,13 +14,20 @@ 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-04-22
|
||||
rust_version: nightly-2019-07-16
|
||||
benches: true
|
||||
|
||||
# This represents the minimum Rust version supported by
|
||||
@ -32,10 +39,18 @@ jobs:
|
||||
parameters:
|
||||
name: minrust
|
||||
displayName: Min Rust
|
||||
rust_version: 1.18.0
|
||||
rust_version: 1.36.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
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
#![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();
|
||||
}
|
||||
})
|
||||
}
|
||||
17
ci/azure-clippy.yml
Normal file
17
ci/azure-clippy.yml
Normal file
@ -0,0 +1,17 @@
|
||||
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"
|
||||
@ -6,18 +6,30 @@ jobs:
|
||||
displayName: Cross
|
||||
strategy:
|
||||
matrix:
|
||||
iOS:
|
||||
iOS_64:
|
||||
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_64:
|
||||
Android_ARM64:
|
||||
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
|
||||
|
||||
@ -2,7 +2,10 @@ steps:
|
||||
# Linux and macOS.
|
||||
- script: |
|
||||
set -e
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none
|
||||
export PATH=$PATH:$HOME/.cargo/bin
|
||||
rustup toolchain install $RUSTUP_TOOLCHAIN
|
||||
rustup default $RUSTUP_TOOLCHAIN
|
||||
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
@ -12,8 +15,10 @@ steps:
|
||||
# Windows.
|
||||
- script: |
|
||||
curl -sSf -o rustup-init.exe https://win.rustup.rs
|
||||
rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
|
||||
rustup-init.exe -y --default-toolchain none
|
||||
set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
rustup toolchain install %RUSTUP_TOOLCHAIN%
|
||||
rustup default %RUSTUP_TOOLCHAIN%
|
||||
echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
@ -22,6 +27,7 @@ steps:
|
||||
|
||||
# All platforms.
|
||||
- script: |
|
||||
rustup toolchain list
|
||||
rustc -Vv
|
||||
cargo -V
|
||||
displayName: Query rust and cargo versions
|
||||
|
||||
37
ci/azure-minimal-versions.yml
Normal file
37
ci/azure-minimal-versions.yml
Normal file
@ -0,0 +1,37 @@
|
||||
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'
|
||||
16
ci/azure-rustfmt.yml
Normal file
16
ci/azure-rustfmt.yml
Normal file
@ -0,0 +1,16 @@
|
||||
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
|
||||
@ -18,6 +18,9 @@ jobs:
|
||||
pool:
|
||||
vmImage: $(vmImage)
|
||||
|
||||
variables:
|
||||
RUST_BACKTRACE: full
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
|
||||
390
src/channel.rs
390
src/channel.rs
@ -1,390 +0,0 @@
|
||||
//! 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"),
|
||||
}
|
||||
}
|
||||
@ -1,346 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
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>) {
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
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>>;
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
#![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,
|
||||
};
|
||||
@ -1,63 +0,0 @@
|
||||
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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,420 +0,0 @@
|
||||
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) }
|
||||
}
|
||||
}
|
||||
200
src/event/event.rs
Normal file
200
src/event/event.rs
Normal file
@ -0,0 +1,200 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
218
src/event/events.rs
Normal file
218
src/event/events.rs
Normal file
@ -0,0 +1,218 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
10
src/event/mod.rs
Normal file
10
src/event/mod.rs
Normal file
@ -0,0 +1,10 @@
|
||||
//! 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;
|
||||
126
src/event/source.rs
Normal file
126
src/event/source.rs
Normal file
@ -0,0 +1,126 @@
|
||||
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
1164
src/event_imp.rs
File diff suppressed because it is too large
Load Diff
165
src/interests.rs
Normal file
165
src/interests.rs
Normal file
@ -0,0 +1,165 @@
|
||||
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
35
src/io.rs
@ -1,35 +0,0 @@
|
||||
// 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
554
src/lazycell.rs
@ -1,554 +0,0 @@
|
||||
// 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));
|
||||
}
|
||||
}
|
||||
256
src/lib.rs
256
src/lib.rs
@ -1,13 +1,9 @@
|
||||
#![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)]
|
||||
#![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.
|
||||
#![cfg_attr(test, 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))]
|
||||
// Disallow warnings in examples.
|
||||
#![doc(test(attr(deny(warnings))))]
|
||||
|
||||
//! A fast, low-level IO library for Rust focusing on non-blocking APIs, event
|
||||
//! notification, and other useful utilities for building high performance IO
|
||||
@ -16,13 +12,14 @@
|
||||
//! # Features
|
||||
//!
|
||||
//! * Non-blocking TCP, UDP
|
||||
//! * I/O event notification queue backed by epoll, kqueue, and IOCP
|
||||
//! * I/O event 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
|
||||
@ -32,21 +29,26 @@
|
||||
//!
|
||||
//! Currently supported platforms:
|
||||
//!
|
||||
//! * Linux
|
||||
//! * OS X
|
||||
//! * Windows
|
||||
//! * FreeBSD
|
||||
//! * NetBSD
|
||||
//! * Android
|
||||
//! * Bitrig
|
||||
//! * DragonFly BSD
|
||||
//! * FreeBSD
|
||||
//! * Linux
|
||||
//! * NetBSD
|
||||
//! * OpenBSD
|
||||
//! * Solaris
|
||||
//! * Windows
|
||||
//! * iOS
|
||||
//! * macOS
|
||||
//!
|
||||
//! 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`].
|
||||
//! Mio can handle interfacing with each of the event 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
|
||||
//! put 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
|
||||
//! puts them into [`Events`]. You can handle IO events from the OS with it.
|
||||
//!
|
||||
//! For more detail, see [`Poll`].
|
||||
//!
|
||||
@ -67,21 +69,19 @@
|
||||
//! 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 poll = Poll::new().unwrap();
|
||||
//! let mut poll = Poll::new().unwrap();
|
||||
//!
|
||||
//! // Start listening for incoming connections
|
||||
//! poll.register(&server, SERVER, Ready::readable(),
|
||||
//! PollOpt::edge()).unwrap();
|
||||
//! poll.registry().register(&server, SERVER, Interests::READABLE).unwrap();
|
||||
//!
|
||||
//! // Setup the client socket
|
||||
//! let sock = TcpStream::connect(&addr).unwrap();
|
||||
//! let sock = TcpStream::connect(addr).unwrap();
|
||||
//!
|
||||
//! // Register the socket
|
||||
//! poll.register(&sock, CLIENT, Ready::readable(),
|
||||
//! PollOpt::edge()).unwrap();
|
||||
//! poll.registry().register(&sock, CLIENT, Interests::READABLE).unwrap();
|
||||
//!
|
||||
//! // Create storage for events
|
||||
//! let mut events = Events::with_capacity(1024);
|
||||
@ -108,204 +108,24 @@
|
||||
//!
|
||||
//! ```
|
||||
|
||||
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 interests;
|
||||
mod poll;
|
||||
mod sys;
|
||||
mod token;
|
||||
mod lazycell;
|
||||
mod waker;
|
||||
|
||||
pub mod event;
|
||||
pub mod net;
|
||||
|
||||
#[deprecated(since = "0.6.5", note = "use mio-extras instead")]
|
||||
#[cfg(feature = "with-deprecated")]
|
||||
#[doc(hidden)]
|
||||
pub mod channel;
|
||||
|
||||
#[deprecated(since = "0.6.5", note = "use mio-extras instead")]
|
||||
#[cfg(feature = "with-deprecated")]
|
||||
#[doc(hidden)]
|
||||
pub mod timer;
|
||||
|
||||
#[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 event::Events;
|
||||
pub use interests::Interests;
|
||||
pub use poll::{Poll, Registry};
|
||||
pub use token::Token;
|
||||
pub use waker::Waker;
|
||||
|
||||
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")))]
|
||||
#[cfg(unix)]
|
||||
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))
|
||||
}
|
||||
//! Unix only extensions.
|
||||
|
||||
pub use crate::sys::SourceFd;
|
||||
}
|
||||
|
||||
@ -7,8 +7,11 @@
|
||||
//!
|
||||
//! [portability guidelines]: ../struct.Poll.html#portability
|
||||
|
||||
mod tcp;
|
||||
mod udp;
|
||||
mod tcp_listener;
|
||||
pub use self::tcp_listener::TcpListener;
|
||||
|
||||
pub use self::tcp::{TcpListener, TcpStream};
|
||||
mod tcp_stream;
|
||||
pub use self::tcp_stream::TcpStream;
|
||||
|
||||
mod udp;
|
||||
pub use self::udp::UdpSocket;
|
||||
|
||||
737
src/net/tcp.rs
737
src/net/tcp.rs
@ -1,737 +0,0 @@
|
||||
//! 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
170
src/net/tcp_listener.rs
Normal file
170
src/net/tcp_listener.rs
Normal file
@ -0,0 +1,170 @@
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
256
src/net/tcp_stream.rs
Normal file
256
src/net/tcp_stream.rs
Normal file
@ -0,0 +1,256 @@
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
303
src/net/udp.rs
303
src/net/udp.rs
@ -1,20 +1,21 @@
|
||||
//! 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
|
||||
//! [portability guidelines]: ../struct.Poll.html#portability
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
use crate::poll::SelectorId;
|
||||
use crate::{event, sys, Interests, Registry, Token};
|
||||
|
||||
use {io, sys, Ready, Poll, PollOpt, Token};
|
||||
use event::Evented;
|
||||
use poll::SelectorId;
|
||||
use std::fmt;
|
||||
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
use iovec::IoVec;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
|
||||
/// A User Datagram Protocol socket.
|
||||
///
|
||||
@ -27,13 +28,13 @@ use iovec::IoVec;
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// // An Echo program:
|
||||
/// // SENDER -> sends a message.
|
||||
/// // ECHOER -> listens and prints the message received.
|
||||
///
|
||||
/// use mio::net::UdpSocket;
|
||||
/// use mio::{Events, Ready, Poll, PollOpt, Token};
|
||||
/// use mio::{Events, Interests, Poll, Token};
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// const SENDER: Token = Token(0);
|
||||
@ -41,8 +42,8 @@ use iovec::IoVec;
|
||||
///
|
||||
/// // 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.
|
||||
@ -50,11 +51,11 @@ use iovec::IoVec;
|
||||
///
|
||||
/// // We need a Poll to check if SENDER is ready to be written into, and if ECHOER is ready to be
|
||||
/// // read from.
|
||||
/// let poll = Poll::new()?;
|
||||
/// let mut poll = Poll::new()?;
|
||||
///
|
||||
/// // We register our sockets here so that we can check if they are ready to be written/read.
|
||||
/// poll.register(&sender_socket, SENDER, Ready::writable(), PollOpt::edge())?;
|
||||
/// poll.register(&echoer_socket, ECHOER, Ready::readable(), PollOpt::edge())?;
|
||||
/// poll.registry().register(&sender_socket, SENDER, Interests::WRITABLE)?;
|
||||
/// poll.registry().register(&echoer_socket, ECHOER, Interests::READABLE)?;
|
||||
///
|
||||
/// let msg_to_send = [9; 9];
|
||||
/// let mut buffer = [0; 9];
|
||||
@ -75,22 +76,18 @@ use iovec::IoVec;
|
||||
/// 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,
|
||||
}
|
||||
|
||||
@ -102,11 +99,11 @@ impl UdpSocket {
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn 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.
|
||||
@ -115,31 +112,14 @@ 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> {
|
||||
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)?,
|
||||
pub fn bind(addr: SocketAddr) -> io::Result<UdpSocket> {
|
||||
sys::UdpSocket::bind(addr).map(|sys| UdpSocket {
|
||||
sys,
|
||||
#[cfg(debug_assertions)]
|
||||
selector_id: SelectorId::new(),
|
||||
})
|
||||
}
|
||||
@ -155,17 +135,14 @@ impl UdpSocket {
|
||||
#[cfg_attr(target_os = "freebsd", doc = " ```no_run")]
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use mio::net::UdpSocket;
|
||||
///
|
||||
/// let addr = "127.0.0.1:0".parse()?;
|
||||
/// let socket = UdpSocket::bind(&addr)?;
|
||||
/// let socket = UdpSocket::bind(addr)?;
|
||||
/// assert_eq!(socket.local_addr()?.ip(), addr.ip());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # try_main().unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.sys.local_addr()
|
||||
@ -182,30 +159,24 @@ impl UdpSocket {
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn 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,
|
||||
selector_id: self.selector_id.clone(),
|
||||
}
|
||||
})
|
||||
self.sys.try_clone().map(|s| UdpSocket {
|
||||
sys: s,
|
||||
#[cfg(debug_assertions)]
|
||||
selector_id: self.selector_id.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the given address. On success, returns the
|
||||
@ -218,25 +189,21 @@ impl UdpSocket {
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use std::error::Error;
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn 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)
|
||||
}
|
||||
|
||||
@ -248,10 +215,10 @@ impl UdpSocket {
|
||||
/// ```no_run
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn 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.
|
||||
@ -262,15 +229,41 @@ 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> {
|
||||
@ -283,6 +276,12 @@ 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`.
|
||||
@ -300,10 +299,10 @@ impl UdpSocket {
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn 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)?;
|
||||
/// }
|
||||
@ -312,10 +311,6 @@ impl UdpSocket {
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # try_main().unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_broadcast(&self, on: bool) -> io::Result<()> {
|
||||
self.sys.set_broadcast(on)
|
||||
@ -333,18 +328,14 @@ impl UdpSocket {
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn 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()
|
||||
@ -417,10 +408,10 @@ impl UdpSocket {
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn 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)?;
|
||||
/// }
|
||||
@ -429,10 +420,6 @@ impl UdpSocket {
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # try_main().unwrap();
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
|
||||
self.sys.set_ttl(ttl)
|
||||
@ -449,20 +436,16 @@ impl UdpSocket {
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// # fn main() -> Result<(), Box<dyn 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()
|
||||
@ -475,9 +458,7 @@ 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)
|
||||
}
|
||||
|
||||
@ -486,9 +467,7 @@ 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)
|
||||
}
|
||||
|
||||
@ -498,9 +477,7 @@ 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)
|
||||
}
|
||||
|
||||
@ -510,33 +487,10 @@ 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
|
||||
@ -545,67 +499,31 @@ 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 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)
|
||||
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)
|
||||
}
|
||||
|
||||
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
|
||||
self.sys.reregister(poll, token, interest, opts)
|
||||
fn reregister(
|
||||
&self,
|
||||
registry: &Registry,
|
||||
token: Token,
|
||||
interests: Interests,
|
||||
) -> io::Result<()> {
|
||||
self.sys.reregister(registry, token, interests)
|
||||
}
|
||||
|
||||
fn deregister(&self, poll: &Poll) -> io::Result<()> {
|
||||
self.sys.deregister(poll)
|
||||
fn deregister(&self, registry: &Registry) -> io::Result<()> {
|
||||
self.sys.deregister(registry)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -616,30 +534,27 @@ impl fmt::Debug for UdpSocket {
|
||||
*
|
||||
*/
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
use std::os::unix::io::{IntoRawFd, AsRawFd, FromRawFd, RawFd};
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
#[cfg(unix)]
|
||||
impl IntoRawFd for UdpSocket {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.sys.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
#[cfg(unix)]
|
||||
impl AsRawFd for UdpSocket {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.sys.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
#[cfg(unix)]
|
||||
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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
2762
src/poll.rs
2762
src/poll.rs
File diff suppressed because it is too large
Load Diff
@ -1,73 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@ -1,263 +0,0 @@
|
||||
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
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@ -1,177 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,444 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -1,181 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -1,353 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -1,56 +1,20 @@
|
||||
#[cfg(all(unix, not(target_os = "fuchsia")))]
|
||||
//! 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)]
|
||||
pub use self::unix::{
|
||||
Awakener,
|
||||
EventedFd,
|
||||
Events,
|
||||
Io,
|
||||
Selector,
|
||||
TcpStream,
|
||||
TcpListener,
|
||||
UdpSocket,
|
||||
pipe,
|
||||
set_nonblock,
|
||||
event, Event, Events, Selector, SourceFd, TcpListener, TcpStream, UdpSocket, Waker,
|
||||
};
|
||||
|
||||
#[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(unix)]
|
||||
mod unix;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub use self::windows::{
|
||||
Awakener,
|
||||
Events,
|
||||
Selector,
|
||||
TcpStream,
|
||||
TcpListener,
|
||||
UdpSocket,
|
||||
Overlapped,
|
||||
Binding,
|
||||
};
|
||||
pub use self::windows::{event, Event, Events, Selector, TcpListener, TcpStream, UdpSocket, Waker};
|
||||
|
||||
#[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;
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
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,
|
||||
}
|
||||
}
|
||||
@ -1,248 +1,169 @@
|
||||
#![allow(deprecated)]
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::io::RawFd;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
||||
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};
|
||||
use std::time::Duration;
|
||||
use std::{cmp, i32};
|
||||
use std::{cmp, i32, io, ptr};
|
||||
|
||||
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;
|
||||
/// Unique id for use as `SelectorId`.
|
||||
#[cfg(debug_assertions)]
|
||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(1);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Selector {
|
||||
#[cfg(debug_assertions)]
|
||||
id: usize,
|
||||
epfd: RawFd,
|
||||
ep: RawFd,
|
||||
}
|
||||
|
||||
impl Selector {
|
||||
pub fn new() -> io::Result<Selector> {
|
||||
let epfd = unsafe {
|
||||
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,
|
||||
// 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,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn id(&self) -> usize {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// 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)
|
||||
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)
|
||||
.unwrap_or(-1);
|
||||
|
||||
// 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)
|
||||
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) };
|
||||
})
|
||||
}
|
||||
|
||||
/// 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
|
||||
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,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, fd, &mut info))?;
|
||||
Ok(())
|
||||
}
|
||||
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_ADD, fd, &mut event)).map(|_| ())
|
||||
}
|
||||
|
||||
/// 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
|
||||
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,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
cvt(libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_MOD, fd, &mut info))?;
|
||||
Ok(())
|
||||
}
|
||||
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_MOD, fd, &mut event)).map(|_| ())
|
||||
}
|
||||
|
||||
/// Deregister event interests for the given IO handle with the OS
|
||||
pub fn deregister(&self, fd: RawFd) -> io::Result<()> {
|
||||
// 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(())
|
||||
}
|
||||
syscall!(epoll_ctl(self.ep, libc::EPOLL_CTL_DEL, fd, ptr::null_mut())).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
fn ioevent_to_epoll(interest: Ready, opts: PollOpt) -> u32 {
|
||||
let mut kind = 0;
|
||||
fn interests_to_epoll(interests: Interests) -> u32 {
|
||||
let mut kind = EPOLLET;
|
||||
|
||||
if interest.is_readable() {
|
||||
if interests.is_readable() {
|
||||
kind |= EPOLLIN;
|
||||
}
|
||||
|
||||
if interest.is_writable() {
|
||||
if interests.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.epfd
|
||||
self.ep
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Selector {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = libc::close(self.epfd);
|
||||
if let Err(err) = syscall!(close(self.ep)) {
|
||||
error!("error closing epoll: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Events {
|
||||
events: Vec<libc::epoll_event>,
|
||||
}
|
||||
pub type Event = libc::epoll_event;
|
||||
|
||||
impl Events {
|
||||
pub fn with_capacity(u: usize) -> Events {
|
||||
Events {
|
||||
events: Vec::with_capacity(u)
|
||||
}
|
||||
pub mod event {
|
||||
use crate::sys::Event;
|
||||
use crate::Token;
|
||||
|
||||
pub fn token(event: &Event) -> Token {
|
||||
Token(event.u64 as usize)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.events.len()
|
||||
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 capacity(&self) -> usize {
|
||||
self.events.capacity()
|
||||
pub fn is_writable(event: &Event) -> bool {
|
||||
(event.events as libc::c_int & libc::EPOLLOUT) != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.events.is_empty()
|
||||
pub fn is_error(event: &Event) -> bool {
|
||||
(event.events as libc::c_int & libc::EPOLLERR) != 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_hup(event: &Event) -> bool {
|
||||
(event.events as libc::c_int & libc::EPOLLHUP) != 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_read_hup(event: &Event) -> bool {
|
||||
(event.events as libc::c_int & libc::EPOLLRDHUP) != 0
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
unsafe { self.events.set_len(0); }
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
#[test]
|
||||
fn assert_close_on_exec_flag() {
|
||||
// This assertion need to be true for Selector::new.
|
||||
assert_eq!(libc::O_CLOEXEC, libc::EPOLL_CLOEXEC);
|
||||
}
|
||||
|
||||
@ -1,107 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -1,107 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -1,212 +1,293 @@
|
||||
use std::{cmp, fmt, ptr};
|
||||
#[cfg(not(target_os = "netbsd"))]
|
||||
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 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};
|
||||
|
||||
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;
|
||||
/// 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.
|
||||
#[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 Count = libc::c_int;
|
||||
#[cfg(target_os = "netbsd")]
|
||||
type Count = libc::size_t;
|
||||
|
||||
// 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;
|
||||
#[cfg(target_os = "netbsd")]
|
||||
type Count = usize;
|
||||
type UData = libc::intptr_t;
|
||||
|
||||
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> {
|
||||
// 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));
|
||||
|
||||
Ok(Selector {
|
||||
id,
|
||||
kq,
|
||||
})
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
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),
|
||||
}
|
||||
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,
|
||||
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),
|
||||
});
|
||||
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());
|
||||
|
||||
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))
|
||||
}
|
||||
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) };
|
||||
})
|
||||
}
|
||||
|
||||
pub fn register(&self, fd: RawFd, token: Token, interests: Ready, opts: PollOpt) -> io::Result<()> {
|
||||
trace!("registering; token={:?}; interests={:?}", token, interests);
|
||||
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;
|
||||
|
||||
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_writable() {
|
||||
let kevent = kevent!(fd, libc::EVFILT_WRITE, flags, token.0);
|
||||
changes[n_changes] = MaybeUninit::new(kevent);
|
||||
n_changes += 1;
|
||||
}
|
||||
|
||||
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: 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 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 deregister(&self, fd: RawFd) -> io::Result<()> {
|
||||
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()),
|
||||
];
|
||||
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),
|
||||
];
|
||||
|
||||
#[cfg(target_os = "netbsd")]
|
||||
let mut changes = [
|
||||
kevent!(fd, libc::EVFILT_READ, filter, 0),
|
||||
kevent!(fd, libc::EVFILT_WRITE, filter, 0),
|
||||
];
|
||||
// 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])
|
||||
}
|
||||
|
||||
cvt(libc::kevent(self.kq,
|
||||
changes.as_ptr(),
|
||||
changes.len() as Count,
|
||||
changes.as_mut_ptr(),
|
||||
changes.len() as Count,
|
||||
::std::ptr::null())).map(|_| ())?;
|
||||
// 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
|
||||
);
|
||||
|
||||
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));
|
||||
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));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 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(())
|
||||
}
|
||||
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()
|
||||
/// 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 AsRawFd for Selector {
|
||||
@ -217,144 +298,110 @@ impl AsRawFd for Selector {
|
||||
|
||||
impl Drop for Selector {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let _ = libc::close(self.kq);
|
||||
if let Err(err) = syscall!(close(self.kq)) {
|
||||
error!("error closing kqueue: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Events {
|
||||
sys_events: KeventList,
|
||||
events: Vec<Event>,
|
||||
event_map: HashMap<Token, usize>,
|
||||
}
|
||||
pub type Event = libc::kevent;
|
||||
|
||||
struct KeventList(Vec<libc::kevent>);
|
||||
pub mod event {
|
||||
use crate::sys::Event;
|
||||
use crate::Token;
|
||||
|
||||
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 token(event: &Event) -> Token {
|
||||
Token(event.udata as usize)
|
||||
}
|
||||
|
||||
#[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;
|
||||
}
|
||||
|
||||
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"))]
|
||||
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.
|
||||
{
|
||||
if e.filter == libc::EVFILT_AIO {
|
||||
event::kind_mut(&mut self.events[idx]).insert(UnixReady::aio());
|
||||
}
|
||||
event.filter == libc::EVFILT_USER
|
||||
}
|
||||
#[cfg(any(target_os = "freebsd"))]
|
||||
#[cfg(not(any(target_os = "freebsd", target_os = "ios", target_os = "macos")))]
|
||||
{
|
||||
if e.filter == libc::EVFILT_LIO {
|
||||
event::kind_mut(&mut self.events[idx]).insert(UnixReady::lio());
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn push_event(&mut self, event: Event) {
|
||||
self.events.push(event);
|
||||
pub fn is_writable(event: &Event) -> bool {
|
||||
event.filter == libc::EVFILT_WRITE
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.sys_events.0.truncate(0);
|
||||
self.events.truncate(0);
|
||||
self.event_map.clear();
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_register_rw() {
|
||||
use {Poll, Ready, PollOpt, Token};
|
||||
use unix::EventedFd;
|
||||
use crate::unix::SourceFd;
|
||||
use crate::{Poll, Token};
|
||||
|
||||
let kq = unsafe { libc::kqueue() };
|
||||
let kqf = EventedFd(&kq);
|
||||
let kqf = SourceFd(&kq);
|
||||
let poll = Poll::new().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));
|
||||
// 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();
|
||||
}
|
||||
|
||||
@ -1,91 +1,56 @@
|
||||
use libc::{self, c_int};
|
||||
|
||||
#[macro_use]
|
||||
pub mod dlsym;
|
||||
/// 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)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[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::{Events, Selector};
|
||||
pub use self::epoll::{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"))]
|
||||
#[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::{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"
|
||||
))]
|
||||
pub use self::kqueue::{event, Event, Selector};
|
||||
|
||||
mod awakener;
|
||||
mod eventedfd;
|
||||
mod io;
|
||||
mod ready;
|
||||
mod tcp;
|
||||
mod net;
|
||||
mod sourcefd;
|
||||
mod tcp_listener;
|
||||
mod tcp_stream;
|
||||
mod udp;
|
||||
mod uio;
|
||||
mod waker;
|
||||
|
||||
#[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::sourcefd::SourceFd;
|
||||
pub use self::tcp_listener::TcpListener;
|
||||
pub use self::tcp_stream::TcpStream;
|
||||
pub use self::udp::UdpSocket;
|
||||
pub use self::waker::Waker;
|
||||
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
pub type Events = Vec<Event>;
|
||||
|
||||
55
src/sys/unix/net.rs
Normal file
55
src/sys/unix/net.rs
Normal file
@ -0,0 +1,55 @@
|
||||
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,
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -1,499 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
103
src/sys/unix/sourcefd.rs
Normal file
103
src/sys/unix/sourcefd.rs
Normal file
@ -0,0 +1,103 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -1,286 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
113
src/sys/unix/tcp_listener.rs
Normal file
113
src/sys/unix/tcp_listener.rs
Normal file
@ -0,0 +1,113 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
150
src/sys/unix/tcp_stream.rs
Normal file
150
src/sys/unix/tcp_stream.rs
Normal file
@ -0,0 +1,150 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -1,24 +1,46 @@
|
||||
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 crate::sys::unix::net::{new_socket, socket_addr};
|
||||
use crate::unix::SourceFd;
|
||||
use crate::{event, Interests, Registry, Token};
|
||||
|
||||
#[allow(unused_imports)] // only here for Rust 1.8
|
||||
use net2::UdpSocketExt;
|
||||
use iovec::IoVec;
|
||||
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::{fmt, io, net};
|
||||
|
||||
pub struct UdpSocket {
|
||||
io: net::UdpSocket,
|
||||
}
|
||||
|
||||
impl UdpSocket {
|
||||
pub fn new(socket: net::UdpSocket) -> io::Result<UdpSocket> {
|
||||
socket.set_nonblocking(true)?;
|
||||
Ok(UdpSocket {
|
||||
io: socket,
|
||||
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) },
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -27,14 +49,10 @@ 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)
|
||||
}
|
||||
|
||||
@ -42,6 +60,10 @@ 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)
|
||||
}
|
||||
@ -50,8 +72,11 @@ impl UdpSocket {
|
||||
self.io.recv(buf)
|
||||
}
|
||||
|
||||
pub fn connect(&self, addr: SocketAddr)
|
||||
-> io::Result<()> {
|
||||
pub fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.io.peek(buf)
|
||||
}
|
||||
|
||||
pub fn connect(&self, addr: SocketAddr) -> io::Result<()> {
|
||||
self.io.connect(addr)
|
||||
}
|
||||
|
||||
@ -95,67 +120,48 @@ 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 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)
|
||||
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)
|
||||
}
|
||||
|
||||
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
|
||||
EventedFd(&self.as_raw_fd()).reregister(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 deregister(&self, poll: &Poll) -> io::Result<()> {
|
||||
EventedFd(&self.as_raw_fd()).deregister(poll)
|
||||
fn deregister(&self, registry: &Registry) -> io::Result<()> {
|
||||
SourceFd(&self.as_raw_fd()).deregister(registry)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
@ -163,7 +169,7 @@ impl fmt::Debug for UdpSocket {
|
||||
impl FromRawFd for UdpSocket {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket {
|
||||
UdpSocket {
|
||||
io: net::UdpSocket::from_raw_fd(fd),
|
||||
io: std::net::UdpSocket::from_raw_fd(fd),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,262 +0,0 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
174
src/sys/unix/waker.rs
Normal file
174
src/sys/unix/waker.rs
Normal file
@ -0,0 +1,174 @@
|
||||
#[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;
|
||||
222
src/sys/windows/afd.rs
Normal file
222
src/sys/windows/afd.rs
Normal file
@ -0,0 +1,222 @@
|
||||
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;
|
||||
@ -1,66 +0,0 @@
|
||||
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(())
|
||||
}
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
97
src/sys/windows/event.rs
Normal file
97
src/sys/windows/event.rs
Normal file
@ -0,0 +1,97 @@
|
||||
#[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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,116 +0,0 @@
|
||||
//! 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);
|
||||
}
|
||||
}
|
||||
32
src/sys/windows/io_status_block.rs
Normal file
32
src/sys/windows/io_status_block.rs
Normal file
@ -0,0 +1,32 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
@ -1,187 +1,127 @@
|
||||
//! 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::os::windows::prelude::*;
|
||||
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 kernel32;
|
||||
use winapi;
|
||||
/// 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)
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
mod awakener;
|
||||
#[macro_use]
|
||||
mod afd;
|
||||
pub mod event;
|
||||
mod io_status_block;
|
||||
mod selector;
|
||||
mod tcp;
|
||||
mod udp;
|
||||
mod from_raw_arc;
|
||||
mod buffer_pool;
|
||||
mod 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 use event::{Event, Events};
|
||||
pub use selector::{Selector, SelectorInner, SockState};
|
||||
pub use tcp::{TcpListener, TcpStream};
|
||||
pub use udp::UdpSocket;
|
||||
pub use waker::Waker;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
enum Family {
|
||||
V4, V6,
|
||||
pub trait SocketState {
|
||||
fn get_sock_state(&self) -> Option<Arc<Mutex<SockState>>>;
|
||||
fn set_sock_state(&self, sock_state: Option<Arc<Mutex<SockState>>>);
|
||||
}
|
||||
|
||||
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(())
|
||||
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 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())
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,413 +1,267 @@
|
||||
//! 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 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};
|
||||
|
||||
use std::fmt;
|
||||
use std::io::prelude::*;
|
||||
use std::io;
|
||||
use std::mem;
|
||||
use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
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};
|
||||
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};
|
||||
|
||||
pub struct UdpSocket {
|
||||
imp: Imp,
|
||||
registration: Mutex<Option<poll::Registration>>,
|
||||
internal: Arc<Mutex<Option<InternalState>>>,
|
||||
io: net::UdpSocket,
|
||||
}
|
||||
|
||||
#[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),
|
||||
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
|
||||
}};
|
||||
}
|
||||
|
||||
impl UdpSocket {
|
||||
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 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 local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.imp.inner.socket.local_addr()
|
||||
self.io.local_addr()
|
||||
}
|
||||
|
||||
pub fn try_clone(&self) -> io::Result<UdpSocket> {
|
||||
self.imp.inner.socket.try_clone().and_then(UdpSocket::new)
|
||||
self.io.try_clone().map(|io| UdpSocket {
|
||||
internal: Arc::new(Mutex::new(None)),
|
||||
io,
|
||||
})
|
||||
}
|
||||
|
||||
/// 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)
|
||||
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(&self, buf: &[u8]) -> 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_overlapped(&owned_buf, self.imp.inner.write.as_mut_ptr())
|
||||
|
||||
}?;
|
||||
me.write = State::Pending(owned_buf);
|
||||
mem::forget(self.imp.clone());
|
||||
Ok(amt)
|
||||
wouldblock!(self, send, 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 recv(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
wouldblock!(self, recv, 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 peek(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
wouldblock!(self, peek, buf)
|
||||
}
|
||||
|
||||
pub fn connect(&self, addr: SocketAddr) -> io::Result<()> {
|
||||
self.imp.inner.socket.connect(addr)
|
||||
self.io.connect(addr)
|
||||
}
|
||||
|
||||
pub fn broadcast(&self) -> io::Result<bool> {
|
||||
self.imp.inner.socket.broadcast()
|
||||
self.io.broadcast()
|
||||
}
|
||||
|
||||
pub fn set_broadcast(&self, on: bool) -> io::Result<()> {
|
||||
self.imp.inner.socket.set_broadcast(on)
|
||||
self.io.set_broadcast(on)
|
||||
}
|
||||
|
||||
pub fn multicast_loop_v4(&self) -> io::Result<bool> {
|
||||
self.imp.inner.socket.multicast_loop_v4()
|
||||
self.io.multicast_loop_v4()
|
||||
}
|
||||
|
||||
pub fn set_multicast_loop_v4(&self, on: bool) -> io::Result<()> {
|
||||
self.imp.inner.socket.set_multicast_loop_v4(on)
|
||||
self.io.set_multicast_loop_v4(on)
|
||||
}
|
||||
|
||||
pub fn multicast_ttl_v4(&self) -> io::Result<u32> {
|
||||
self.imp.inner.socket.multicast_ttl_v4()
|
||||
self.io.multicast_ttl_v4()
|
||||
}
|
||||
|
||||
pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> {
|
||||
self.imp.inner.socket.set_multicast_ttl_v4(ttl)
|
||||
self.io.set_multicast_ttl_v4(ttl)
|
||||
}
|
||||
|
||||
pub fn multicast_loop_v6(&self) -> io::Result<bool> {
|
||||
self.imp.inner.socket.multicast_loop_v6()
|
||||
self.io.multicast_loop_v6()
|
||||
}
|
||||
|
||||
pub fn set_multicast_loop_v6(&self, on: bool) -> io::Result<()> {
|
||||
self.imp.inner.socket.set_multicast_loop_v6(on)
|
||||
self.io.set_multicast_loop_v6(on)
|
||||
}
|
||||
|
||||
pub fn ttl(&self) -> io::Result<u32> {
|
||||
self.imp.inner.socket.ttl()
|
||||
self.io.ttl()
|
||||
}
|
||||
|
||||
pub fn set_ttl(&self, ttl: u32) -> io::Result<()> {
|
||||
self.imp.inner.socket.set_ttl(ttl)
|
||||
self.io.set_ttl(ttl)
|
||||
}
|
||||
|
||||
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_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.imp.inner.socket.join_multicast_v6(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.imp.inner.socket.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<()> {
|
||||
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 leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> {
|
||||
self.io.leave_multicast_v6(multiaddr, interface)
|
||||
}
|
||||
|
||||
pub fn take_error(&self) -> io::Result<Option<io::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());
|
||||
}
|
||||
}
|
||||
self.io.take_error()
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
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,
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
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 => {}
|
||||
};
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See comments in tcp::StreamImp::push
|
||||
fn add_readiness(&self, me: &Inner, set: Ready) {
|
||||
me.iocp.set_readiness(set | me.iocp.readiness());
|
||||
}
|
||||
}
|
||||
|
||||
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(())
|
||||
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
|
||||
}
|
||||
|
||||
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 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 deregister(&self, poll: &Poll) -> io::Result<()> {
|
||||
self.inner().iocp.deregister(&self.imp.inner.socket,
|
||||
poll, &self.registration)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for UdpSocket {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("UdpSocket")
|
||||
.finish()
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.io, f)
|
||||
}
|
||||
}
|
||||
|
||||
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 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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 IntoRawSocket for UdpSocket {
|
||||
fn into_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);
|
||||
impl AsRawSocket for UdpSocket {
|
||||
fn as_raw_socket(&self) -> RawSocket {
|
||||
self.io.as_raw_socket()
|
||||
}
|
||||
me.read = State::Ready(buf);
|
||||
me2.add_readiness(&mut me, Ready::readable());
|
||||
}
|
||||
|
||||
27
src/sys/windows/waker.rs
Normal file
27
src/sys/windows/waker.rs
Normal file
@ -0,0 +1,27 @@
|
||||
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
516
src/timer.rs
@ -1,516 +0,0 @@
|
||||
//! 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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/token.rs
46
src/token.rs
@ -1,19 +1,26 @@
|
||||
/// Associates readiness notifications with [`Evented`] handles.
|
||||
/// Associates readiness events with [`event::Source`]s.
|
||||
///
|
||||
/// `Token` is a wrapper around `usize` and is used as an argument to
|
||||
/// [`Poll::register`] and [`Poll::reregister`].
|
||||
/// [`Registry::register`] and [`Registry::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 notification. In this
|
||||
/// example, `HashMap` is used, but usually something like [`slab`] is better.
|
||||
/// 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
|
||||
///
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// # fn try_main() -> Result<(), Box<Error>> {
|
||||
/// use mio::{Events, Ready, Poll, PollOpt, Token};
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use mio::{Events, Interests, Poll, Token};
|
||||
/// use mio::net::TcpListener;
|
||||
///
|
||||
/// use std::thread;
|
||||
@ -34,16 +41,13 @@
|
||||
/// let mut next_socket_index = 0;
|
||||
///
|
||||
/// // The `Poll` instance
|
||||
/// let poll = Poll::new()?;
|
||||
/// let mut 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.register(&listener,
|
||||
/// LISTENER,
|
||||
/// Ready::readable(),
|
||||
/// PollOpt::edge())?;
|
||||
/// poll.registry().register(&listener, LISTENER, Interests::READABLE)?;
|
||||
///
|
||||
/// // Spawn a thread that will connect a bunch of sockets then close them
|
||||
/// let addr = listener.local_addr()?;
|
||||
@ -53,7 +57,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();
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
@ -86,10 +90,7 @@
|
||||
/// next_socket_index += 1;
|
||||
///
|
||||
/// // Register the new socket w/ poll
|
||||
/// poll.register(&socket,
|
||||
/// token,
|
||||
/// Ready::readable(),
|
||||
/// PollOpt::edge())?;
|
||||
/// poll.registry().register(&socket, token, Interests::READABLE)?;
|
||||
///
|
||||
/// // Store the socket
|
||||
/// sockets.insert(token, socket);
|
||||
@ -124,19 +125,8 @@
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// # 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
326
src/udp.rs
@ -1,326 +0,0 @@
|
||||
//! 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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
93
src/waker.rs
Normal file
93
src/waker.rs
Normal file
@ -0,0 +1,93 @@
|
||||
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(®istry), 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()
|
||||
}
|
||||
}
|
||||
@ -1,80 +0,0 @@
|
||||
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
215
test/mod.rs
@ -1,215 +0,0 @@
|
||||
#![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);
|
||||
}
|
||||
|
||||
@ -1,269 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,394 +0,0 @@
|
||||
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(®istration, 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
//! 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());
|
||||
}
|
||||
@ -1,303 +0,0 @@
|
||||
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()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
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"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,107 +0,0 @@
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,192 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
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 };
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
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(®istration, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
|
||||
poll.poll(&mut events, Some(Duration::from_millis(0))).unwrap();
|
||||
|
||||
drop(poll);
|
||||
drop(set_readiness);
|
||||
drop(registration);
|
||||
}
|
||||
}
|
||||
@ -1,285 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,123 +0,0 @@
|
||||
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();
|
||||
}
|
||||
@ -1,63 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,23 +0,0 @@
|
||||
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();
|
||||
|
||||
}
|
||||
@ -1,249 +0,0 @@
|
||||
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
660
test/test_tcp.rs
@ -1,660 +0,0 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,142 +0,0 @@
|
||||
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()
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
use std::net::Shutdown;
|
||||
use std::time::Duration;
|
||||
|
||||
use mio::{Token, Ready, PollOpt, Poll, Events};
|
||||
use mio::net::TcpStream;
|
||||
|
||||
macro_rules! wait {
|
||||
($poll:ident, $ready:ident) => {{
|
||||
use std::time::Instant;
|
||||
|
||||
let now = Instant::now();
|
||||
let mut events = Events::with_capacity(16);
|
||||
let mut found = false;
|
||||
|
||||
while !found {
|
||||
if now.elapsed() > Duration::from_secs(5) {
|
||||
panic!("not ready");
|
||||
}
|
||||
|
||||
$poll.poll(&mut events, Some(Duration::from_secs(1))).unwrap();
|
||||
|
||||
for event in &events {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use mio::unix::UnixReady;
|
||||
assert!(!UnixReady::from(event.readiness()).is_hup());
|
||||
}
|
||||
|
||||
if event.token() == Token(0) && event.readiness().$ready() {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_shutdown() {
|
||||
let poll = Poll::new().unwrap();
|
||||
|
||||
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
let mut ready = Ready::readable() | Ready::writable();
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
ready |= mio::unix::UnixReady::hup();
|
||||
}
|
||||
|
||||
let client = TcpStream::connect(&addr).unwrap();
|
||||
poll.register(&client,
|
||||
Token(0),
|
||||
ready,
|
||||
PollOpt::edge()).unwrap();
|
||||
|
||||
let (socket, _) = listener.accept().unwrap();
|
||||
|
||||
wait!(poll, is_writable);
|
||||
|
||||
let mut events = Events::with_capacity(16);
|
||||
|
||||
// Polling should not have any events
|
||||
poll.poll(&mut events, Some(Duration::from_millis(100))).unwrap();
|
||||
assert!(events.iter().next().is_none());
|
||||
|
||||
println!("SHUTTING DOWN");
|
||||
// Now, shutdown the write half of the socket.
|
||||
socket.shutdown(Shutdown::Write).unwrap();
|
||||
|
||||
wait!(poll, is_readable);
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
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);
|
||||
}
|
||||
@ -1,252 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,300 +0,0 @@
|
||||
use {TryRead, TryWrite};
|
||||
use mio::*;
|
||||
use mio::deprecated::{EventLoop, Handler};
|
||||
use mio::deprecated::unix::*;
|
||||
use bytes::{Buf, ByteBuf, MutByteBuf, SliceBuf};
|
||||
use slab::Slab;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use tempdir::TempDir;
|
||||
|
||||
const SERVER: Token = Token(10_000_000);
|
||||
const CLIENT: Token = Token(10_000_001);
|
||||
|
||||
struct EchoConn {
|
||||
sock: UnixStream,
|
||||
buf: Option<ByteBuf>,
|
||||
mut_buf: Option<MutByteBuf>,
|
||||
token: Option<Token>,
|
||||
interest: Ready
|
||||
}
|
||||
|
||||
impl EchoConn {
|
||||
fn new(sock: UnixStream) -> EchoConn {
|
||||
EchoConn {
|
||||
sock: sock,
|
||||
buf: None,
|
||||
mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
|
||||
token: None,
|
||||
interest: Ready::hup()
|
||||
}
|
||||
}
|
||||
|
||||
fn writable(&mut self, event_loop: &mut EventLoop<Echo>) -> 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());
|
||||
match self.sock.shutdown(Shutdown::Write) {
|
||||
Err(e) => panic!(e),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
Err(e) => debug!("not implemented; client err={:?}", e),
|
||||
}
|
||||
|
||||
event_loop.reregister(&self.sock, self.token.unwrap(), self.interest,
|
||||
PollOpt::edge() | PollOpt::oneshot())
|
||||
}
|
||||
|
||||
fn readable(&mut self, event_loop: &mut EventLoop<Echo>) -> 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());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
event_loop.reregister(&self.sock, self.token.unwrap(), self.interest,
|
||||
PollOpt::edge())
|
||||
}
|
||||
}
|
||||
|
||||
struct EchoServer {
|
||||
sock: UnixListener,
|
||||
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();
|
||||
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: UnixStream,
|
||||
msgs: Vec<&'static str>,
|
||||
tx: SliceBuf<'static>,
|
||||
rx: SliceBuf<'static>,
|
||||
mut_buf: Option<MutByteBuf>,
|
||||
token: Token,
|
||||
interest: Ready
|
||||
}
|
||||
|
||||
|
||||
// Sends a message and expects to receive the same exact message, one at a time
|
||||
impl EchoClient {
|
||||
fn new(sock: UnixStream, tok: Token, mut msgs: Vec<&'static str>) -> EchoClient {
|
||||
let curr = msgs.remove(0);
|
||||
|
||||
EchoClient {
|
||||
sock: sock,
|
||||
msgs: msgs,
|
||||
tx: SliceBuf::wrap(curr.as_bytes()),
|
||||
rx: SliceBuf::wrap(curr.as_bytes()),
|
||||
mut_buf: Some(ByteBuf::mut_with_capacity(2048)),
|
||||
token: tok,
|
||||
interest: Ready::none()
|
||||
}
|
||||
}
|
||||
|
||||
fn readable(&mut self, event_loop: &mut EventLoop<Echo>) -> 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)) => {
|
||||
if r == 0 {
|
||||
self.interest.remove(Ready::readable());
|
||||
event_loop.shutdown();
|
||||
} else {
|
||||
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());
|
||||
if !self.rx.has_remaining() {
|
||||
self.next_msg(event_loop).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("not implemented; client err={:?}", e);
|
||||
}
|
||||
};
|
||||
|
||||
event_loop.reregister(&self.sock, self.token, self.interest,
|
||||
PollOpt::edge() | PollOpt::oneshot())
|
||||
}
|
||||
|
||||
fn writable(&mut self, event_loop: &mut EventLoop<Echo>) -> 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)
|
||||
}
|
||||
|
||||
event_loop.reregister(&self.sock, self.token, self.interest,
|
||||
PollOpt::edge() | PollOpt::oneshot())
|
||||
}
|
||||
|
||||
fn next_msg(&mut self, event_loop: &mut EventLoop<Echo>) -> io::Result<()> {
|
||||
if self.msgs.is_empty() {
|
||||
|
||||
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());
|
||||
event_loop.reregister(&self.sock, self.token, self.interest,
|
||||
PollOpt::edge() | PollOpt::oneshot())
|
||||
}
|
||||
}
|
||||
|
||||
struct Echo {
|
||||
server: EchoServer,
|
||||
client: EchoClient,
|
||||
}
|
||||
|
||||
impl Echo {
|
||||
fn new(srv: UnixListener, client: UnixStream, msgs: Vec<&'static str>) -> Echo {
|
||||
Echo {
|
||||
server: EchoServer {
|
||||
sock: srv,
|
||||
conns: Slab::with_capacity(128)
|
||||
},
|
||||
client: EchoClient::new(client, CLIENT, msgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Handler for Echo {
|
||||
type Timeout = usize;
|
||||
type Message = ();
|
||||
|
||||
fn ready(&mut self, event_loop: &mut EventLoop<Echo>, token: Token,
|
||||
events: Ready) {
|
||||
debug!("ready {:?} {:?}", token, events);
|
||||
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()
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_echo_server() {
|
||||
debug!("Starting TEST_ECHO_SERVER");
|
||||
let mut event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let tmp_dir = TempDir::new("mio").unwrap();
|
||||
let addr = tmp_dir.path().join(&PathBuf::from("sock"));
|
||||
|
||||
let srv = UnixListener::bind(&addr).unwrap();
|
||||
|
||||
event_loop.register(&srv, SERVER, Ready::readable(),
|
||||
PollOpt::edge() | PollOpt::oneshot()).unwrap();
|
||||
|
||||
let sock = UnixStream::connect(&addr).unwrap();
|
||||
|
||||
// Connect to the server
|
||||
event_loop.register(&sock, CLIENT, Ready::writable(),
|
||||
PollOpt::edge() | PollOpt::oneshot()).unwrap();
|
||||
|
||||
// Start the event loop
|
||||
event_loop.run(&mut Echo::new(srv, sock, vec!["foo", "bar"])).unwrap();
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user