Compare commits

..

142 Commits

Author SHA1 Message Date
Thomas de Zeeuw
803573fa28 Replace IRC channel with Gitter (#1069)
The Mozilla IRC network is being shutdown, see
https://blog.rust-lang.org/2019/04/26/Mozilla-IRC-Sunset-and-the-Rust-Channel.html
and http://exple.tive.org/blarg/2019/04/26/synchronous-text.
2019-08-29 11:56:08 -07:00
Carl Lerche
017d018d75 try fixing ci again 2019-08-27 18:44:28 +00:00
Carl Lerche
d6edcd13fd fix CI 2019-08-27 18:44:28 +00:00
Carl Lerche
092d6fdc7d fix clippy 2019-08-27 18:44:28 +00:00
Carl Lerche
5a5b5f7d43 include testing --release in CI 2019-08-27 18:44:28 +00:00
Carl Lerche
29fd58deda Fix size test 2019-08-27 18:44:28 +00:00
Carl Lerche
19d5ebe61c misc cleanup
* split up TCP files into stream / listener
* organize import order
* split up misc other files
2019-08-27 18:44:28 +00:00
Thomas de Zeeuw
3550494f0b Use consistent use statement order 2019-08-23 10:16:30 +00:00
Thomas de Zeeuw
72a001a8e3 Use Selector::try_clone in kqueue::Waker
Removing the special Selector::try_clone_waker method.
2019-08-23 10:16:30 +00:00
Thomas de Zeeuw
e739790cb8 Add a test for polling with zero duration (#1065)
This ensures that the relevant system call is made even if the passed
duration is zero.
2019-08-21 19:35:13 -07:00
PerfectLaugh
e054a7d587 Fix issue in #1062 2019-08-21 12:21:47 +00:00
PerfectLaugh
4276d071e3 Reverts two issues
This reverts commit 9dc562ca0f9c2a260a3e2e953a5a636103d8a903.
2019-08-21 11:26:00 +00:00
PerfectLaugh
d07989a3b8 Make is_read_hup detects AFD_POLL_DISCONNECT 2019-08-21 11:26:00 +00:00
PerfectLaugh
c7674d64ad Add test in pr #1065 2019-08-21 11:26:00 +00:00
PerfectLaugh
d7fa70644f Fix udp issue in #1062 2019-08-21 11:26:00 +00:00
PerfectLaugh
588e808576 Changed as requested 2019-08-21 11:26:00 +00:00
PerfectLaugh
1dd6000a6d Recover non-blocking connect and bind 2019-08-21 11:26:00 +00:00
PerfectLaugh
2653117b84 Fix several flags handling 2019-08-21 11:26:00 +00:00
PerfectLaugh
5de1317418 Refactored InternalState, improved select timeout handling and AFD flags fixes 2019-08-21 11:26:00 +00:00
PerfectLaugh
bbc2f774c9 Improve afd_group locks 2019-08-21 11:26:00 +00:00
PerfectLaugh
6b810629c3 Changes RefCell to UnsafeCell 2019-08-21 11:26:00 +00:00
PerfectLaugh
4dc7bae2a0 Comply with rustfmt 2019-08-21 11:26:00 +00:00
PerfectLaugh
7b06041b4c Test without timeout 2019-08-21 11:26:00 +00:00
PerfectLaugh
fbecef6684 Test with extended time length. 2019-08-21 11:26:00 +00:00
PerfectLaugh
b60218d9b2 Cancel ignore of test_registry_behind_arc 2019-08-21 11:26:00 +00:00
PerfectLaugh
c6d88a5810 Fix for https://github.com/tokio-rs/mio/issues/1041#issuecomment-519674615 2019-08-21 11:26:00 +00:00
PerfectLaugh
8e0b0364ab Temporary fix for https://github.com/tokio-rs/mio/issues/1041#issuecomment-519674615 2019-08-21 11:26:00 +00:00
Thomas de Zeeuw
3626fdd2e5 Rename Interests::and to Interests::add 2019-08-20 16:45:33 +00:00
Thomas de Zeeuw
ce66285747 Make Interests::is_* methods constant 2019-08-20 16:45:33 +00:00
Thomas de Zeeuw
7f7c62de8d Add Interests::and
This allows adding two interests together, much like the BitOr
implementation, but is a constant function.
2019-08-20 16:45:33 +00:00
Thomas de Zeeuw
fd38052231 Test cleanups (#1063)
* Move double register test into poll's test module

Reduces the output size.

* Replace custom port selection by using port 0

Port 0 is selection as it tells the OS to use any port available,
perfect for our use case.

* Move smoke tests into poll test module

* Make all Sync and Send assertions uniform

Moves them all to the top of the tests files and uses the same function
name. In addition this also checks if Waker is Send and Sync.

* Start each test with calling init

Now also doesn't ignore the possible error return by env_logger.
2019-08-20 08:25:44 -07:00
Thomas de Zeeuw
e134e798d8 Add a regression test for issue 776 2019-08-19 19:09:36 +00:00
Thomas de Zeeuw
eee0a9f10a Small cleanups in poll module
Also fixes a small type-o.
2019-08-19 19:09:36 +00:00
Thomas de Zeeuw
9ae6ee58b4 Test with minimal versions on CI 2019-08-19 15:56:04 +00:00
Thomas de Zeeuw
0b81ecb175 Change checking source back to cargo check
It should be faster.
2019-08-19 15:46:35 +00:00
Thomas de Zeeuw
e0be6e4fb3 Rename Linux targets that actually use Android
Also removes the Linux_ARM target as it is the same as the Android target.
2019-08-19 15:46:35 +00:00
Thomas de Zeeuw
9a246b632e Fix FreeBSD i686 target CI
By adding the target (via rustup) before testing.
2019-08-19 15:46:35 +00:00
Carl Lerche
d43717e44c Add more CI targets for cross compilation
This should hopefully catch most errors related to secondary platforms.
2019-08-19 15:46:35 +00:00
Thomas de Zeeuw
2c0ecd2dbf Update Bytes to v0.5 (unreleased)
As bytes v0.5 is not yet released we'll use the git version (using a
fixed commit to reduce breakage).
2019-08-19 15:43:43 +00:00
Thomas de Zeeuw
59443cd224 Remove poll interruptible (#1056)
Poll::poll is changed to not retry the system call if is its
interrupted.

Removes Poll::poll_interruptible as Poll::poll now does this.
2019-08-19 08:18:49 -07:00
Thomas de Zeeuw
51e1c4587f Disable test_registry_behind_arc on Windows
On Windows concurrently polling and register doesn't work.
2019-08-13 22:30:56 +00:00
Thomas de Zeeuw
200aaa7a2c Remove repr(transparent) from Registry and Poll
It is no longer needed.
2019-08-13 22:30:56 +00:00
Thomas de Zeeuw
6072de2b84 Remove Deref<Registry> from Poll
And revert back to using Poll::registry.
2019-08-13 22:30:56 +00:00
Thomas de Zeeuw
6668d44d3b Add writable interests in test_registry_behind_arc
Hopefully this will create an event on Windows.
2019-08-13 22:30:56 +00:00
Thomas de Zeeuw
254e537e22 Fix unused mut warning
Strange that rustc didn't warn, but clippy does.
2019-08-13 22:30:56 +00:00
Thomas de Zeeuw
fe5d6caed7 Add test for use of Registry behind an Arc 2019-08-13 22:30:56 +00:00
Thomas de Zeeuw
14c049b923 Remove Arc from Poll and Registry
And add back register, register and register to Poll.
2019-08-13 22:30:56 +00:00
Thomas de Zeeuw
f30debef5c Remove net2 dependency (#1029)
Removing the need for the net2 crate.

On Unix platforms (expect for iOS, macOS and Solaris) this reduces the
number of system calls from four to two, on iOS, macOS and Solaris this
reduces it to three system calls.

# Windows net initialization

The standard library calls WSAStartup for us, ensuring (with an assert)
that the return value is ok. This means that we can't initialise it
ourselves and need to let the standard library do it for us. To work
around this we create and drop a UdpSocket (from std::net).
2019-08-13 10:02:58 -07:00
Thomas de Zeeuw
2f688de26c Add UdpSocket::peek(_from) methods (#1052) 2019-08-07 15:32:12 -07:00
PerfectLaugh
211223e928 Improve overlapped deallocation strategy and docs (#1042) 2019-08-07 12:09:20 -07:00
Thomas de Zeeuw
cd7331ad05 Add test for registering after de-registering (#1043) 2019-07-22 09:47:09 -07:00
Thomas de Zeeuw
a160244f1d Split Event::is_hup into is_hup and is_read_hup and refer to OS selector docs (#1037) 2019-07-21 10:57:47 -07:00
PerfectLaugh
65f19ddcb3 rewrite windows implementation (#1034)
Rewrite the windows implementation using the [wepoll] strategy. This uses the
windows AFD system to access socket readiness events. By doing this, mio no
longer needs to buffer reads and writes internally.

Closes #1024.

[wepoll]: https://github.com/piscisaureus/wepoll
2019-07-19 10:05:03 -07:00
Thomas de Zeeuw
7f89495375 Remove the IoVec dependency
It is no longer used.
2019-07-17 12:48:36 +00:00
Thomas de Zeeuw
5aeebe2b4e Remove UdpSocket::{recv_bufs, send_bufs}
Still uses IoVec, which we want to remove, and it was unix only.
2019-07-17 12:48:36 +00:00
Thomas de Zeeuw
2a55db6314 Implement vectored I/O for TcpStream (#1016) 2019-07-16 10:07:51 -07:00
Thomas de Zeeuw
ec0f5c9fad Move write_then_drop tests into tcp test file 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
b09bcd7253 Move test_echo_server into tcp test file 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
b63d2ca4fb Move local_addr_ready test into the tcp test file 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
5439526f7b Use Events::is_empty in expect_no_events 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
c59dbc1812 Remove the sleep_ms helper function 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
ce76cf6d2b Move all registering tests into a single file 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
e5b46055a4 Update Evented name in evented tests 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
d1879f4a90 Move multicast test into udp test file 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
0f09be5bd9 test_write_shutdown test into tcp test file 2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
e5288e06eb Remove benchmarks
These depend on the nix crate, but that isn't specified as a (development) dependencies. So these benchmarks can't even be run currently.
2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
5a6bb732bd Move tests to tests directory
Removes the `test_` prefix from the files and renames the `test` directory to `tests`.
2019-07-16 10:05:44 +00:00
Thomas de Zeeuw
79123b738e Remove unused import 2019-07-15 15:46:37 +00:00
Thomas de Zeeuw
512230851d Don't leak file descriptors if registering Waker fails 2019-07-15 15:46:37 +00:00
Thomas de Zeeuw
d312503a84 Replace unsafe transmute with to_ne_bytes in Waker 2019-07-15 15:46:37 +00:00
Thomas de Zeeuw
eadafc2bed Run Clippy on Ubuntu image 2019-07-11 17:39:59 +00:00
Thomas de Zeeuw
754d4c0022 Force rustup update after installing on Azure
Hopefully this will ensure we install the latests version available.
2019-07-11 17:39:59 +00:00
Thomas de Zeeuw
96ccddf7e2 Reduce unsafe scope in pipe::Waker::new 2019-07-11 17:39:59 +00:00
Thomas de Zeeuw
af47a84ea1 Update minimum Rust version to 1.36 in CI 2019-07-11 17:39:59 +00:00
Thomas de Zeeuw
943d093d56 Add clippy check to Azure 2019-07-11 17:39:59 +00:00
Thomas de Zeeuw
85dfc79daf Allow module_inception in event::event 2019-07-11 17:39:59 +00:00
Thomas de Zeeuw
058186003b Replace uninitialized with MaybeUninit 2019-07-11 17:39:59 +00:00
Thomas de Zeeuw
dfc987db6e Change sys::Events to Vec<sys::Event> on Unix 2019-07-11 16:57:30 +00:00
Thomas de Zeeuw
cb0ab723c9 Remove cvt (#1026)
And replace the last use of it with the syscall macro.
2019-07-11 08:41:38 -07:00
Thomas de Zeeuw
dc0643c7ae System module cleanup (#1021)
* Remove unused export of IoVec
* Fix type definitions for kevent
* Add syscall macro
* Start NEXT_ID at 1
* Optimize Selector::register to pass a single event to kevent when possible.
* Document when and why some errors returned by kevent are ignored.
* Move calling kevent and checking of errors into there own functions to allow for greater reuse.
* Rename epfd to ep, the type already says it's a file descriptor.
* Use Duration::as_millis, removing the millis function.
* Document unsafe blocks.
* Remove the unneeded info event from deregister as Linux < 2.6.9 is no longer supported.
* Log errors when closing the epoll fd.
2019-07-10 11:25:07 -07:00
Alan Somers
418ff89602 Prefer casting pointers over mem::transmute 2019-07-09 06:24:42 +00:00
Alan Somers
8a5455da6e Prefer pass-by-value over pass-by-reference for public methods
pass-by-value is faster than pass-by-reference for types that are small
and Copy.  This is technically a breaking change since these methods are
public.  However, most crates should compile fine because the only
method argument that's changing is `&self`, which is provided
implicitly.  The only way to get into trouble would be something like
this:

let interests = Interests::new;
Interests::is_readable(&ready)
2019-07-09 06:24:42 +00:00
Alan Somers
5042c6c853 Clippy: fix write_literal warnings
Some diagnostics were using "{}" to format string literals
2019-07-09 06:24:42 +00:00
Alan Somers
b392693351 Clippy: silence an if-same-then-else warning
In this case, it makes the code more clear to duplicate two if clauses
2019-07-09 06:24:42 +00:00
Thomas de Zeeuw
5d61c5e6b5 Log interests and token when registering (#1022) 2019-07-08 13:11:39 -07:00
atouchet
18113ce739 Remove duplicate FreeBSD listings 2019-07-08 09:05:14 +00:00
Thomas de Zeeuw
178f51e8da Rename Evented to event::Source (#1014) 2019-07-05 13:13:10 -07:00
Thomas de Zeeuw
84798948c5 Remove SysEvent (#1018)
Changes sys::Event to be an alias for the platform specific event, e.g.
kevent or epoll_event, what SysEvent used to be.

Next all methods on the old sys::Event become standalone functions in a
new sys::event module, which are used by crate::Event.
2019-07-04 13:32:00 -07:00
Kam Y. Tse
aa84849c61 Switch to use cvt utility function (#1009) 2019-07-02 14:23:49 -07:00
Alex Touchet
12156ce81b Update Code of Conduct link 2019-06-27 16:09:40 -06:00
Alex Touchet
819f65eb62 Update Readme to match lib.rs docs (#1007) 2019-06-27 09:02:09 -07:00
Thomas de Zeeuw
8dd95d5ddb Change addresses in poll example to be local (#1006)
All these example tests failed when offline.
2019-06-26 13:04:42 -07:00
Thomas de Zeeuw
479ae7c5e2 Cleanup of sys::unix (#1005)
* Remove pipe and set_nonblock unix export (not used).

* Move sys::unix::pipe to sys::Waker

Only place where it is used. And since the Waker based is only used on certain platforms directly use pipe2. All the platforms that use pipe based Waker have the pipe2 system call.

* Always use epoll_create1 on epoll platforms

epoll_create1 was introduces in Linux kernel version 2.6.27.

* Remove dlsym module (not used).

* Silence unused warning for set_cloexec (not used).

* Remove sys::unix::io::Io (not used).
2019-06-26 06:52:30 -07:00
Thomas de Zeeuw
5ffb91a094 Only use SelectorId when debug assertions are enabled (#1004) 2019-06-25 09:23:31 -07:00
Thomas de Zeeuw
8f6f5613f2 Remove wake token from Selector::select (#1000)
This is now defined by the user.
2019-06-24 13:54:29 -07:00
Thomas de Zeeuw
61b40436d5 Disallow warnings in examples
We want to set a good example!
2019-06-24 14:39:55 +00:00
Thomas de Zeeuw
7a7b7fc130 Remove try_main from examples
Main can now return results.
2019-06-24 14:39:55 +00:00
Thomas de Zeeuw
e4a49ef58c Fix a number of warnings in the examples 2019-06-24 14:39:55 +00:00
Thomas de Zeeuw
9d0018c879 Remove some unneeded cast to raw pointers 2019-06-24 14:39:55 +00:00
Thomas de Zeeuw
76e2a23c02 Various doc cleanups (#998)
* Fix doc link in Interests
* Update list of supported platforms
* Update link in Evented
* Use intra-doc links for Events
* Remove mention of custom readiness queue
2019-06-20 20:50:57 -07:00
Thomas de Zeeuw
2d8ecd6301 Fixup import paths 2019-06-20 19:17:53 +00:00
Thomas de Zeeuw
8397102dc0 Implement Evented for any type Deref<Target = Evented> 2019-06-20 19:17:53 +00:00
Thomas de Zeeuw
2862ed2bff Run rust fmt 2019-06-20 18:21:25 +00:00
Thomas de Zeeuw
353046503f Change Iter to return &Event
Before Event was 16 bytes, but since it became a wrapper for platform specific event it grew on some platforms. For example kevent is 32 bytes. Furthermore all methods on Event only take a reference to, so we can do away with the cloning.

Removes IntoIter.
2019-06-20 18:21:25 +00:00
Thomas de Zeeuw
de208b3355 Document the requirements for Event and SysEvent in sys 2019-06-20 18:21:25 +00:00
Carl Lerche
3d46dd4be6 Remove types from poll.rs 2019-06-20 17:44:36 +00:00
Carl Lerche
2180ff894f Split event_imp into multiple files 2019-06-20 17:44:36 +00:00
Thomas de Zeeuw
855305cb3d Docs & cleanup (#992)
* Remove io module
* Use intra-doc links in Token doc
* Use event over notification
* Mark Event::token as inline
* Make Interests::is_(aio,lio) available on all platforms
* Trim down the Interests documentation
* Remove unused constants in event_imp
* Make the allowing of dead_code stricter.
* Reduce the values of AIO and LIO
* Expand testing of Interests
* Use intra-doc links in Interests doc
* Doc dropping `Evented` types
2019-06-20 09:36:48 -07:00
Thomas de Zeeuw
140dd392ba Expand Event::is_hup documentation (#991) 2019-06-20 09:13:18 -07:00
Thomas de Zeeuw
1eb579cd95 Make Event a wrapper around a platform event (#983)
Now Event is just a wrapper around a platform specific event to provide
a nice API, with methods such as is_readable.

This removes the Ready type and moves all its methods to the Event type.
2019-06-18 09:52:28 -07:00
Thomas de Zeeuw
47809ef14e Remove Sub for Interests (#985)
Closes #934.
2019-06-17 14:58:21 -07:00
Thomas de Zeeuw
8b81ab2047 Always accept SocketAddr, rather then a reference to it (#988)
SocketAddr is Copy so there is little point in accept a reference to it.

Closes #667.
2019-06-17 11:32:13 -07:00
Thomas de Zeeuw
308ced4b0c Remove allowing of deprecated in tests (#989) 2019-06-17 11:25:09 -07:00
Thomas de Zeeuw
e0dcc01a14 Remove unix::TcpStream::(set_)only_v6 (#987)
Not used.
2019-06-17 11:23:43 -07:00
Carl Lerche
340bef90a8
Remove unused io module (#981) 2019-06-14 15:05:46 -07:00
Carl Lerche
a859407e1a
Move AtomicLazyCell into windows mod (#982) 2019-06-14 14:49:09 -07:00
Thomas de Zeeuw
b506eef756 Remove PollOpt from public API (#975) 2019-06-14 14:25:39 -07:00
Thomas de Zeeuw
66c5bdd64f Add RUST_BACKTRACE env variable to CI build (#980)
Closes #965.
2019-06-14 11:08:12 -07:00
Carl Lerche
e9791d71e9
Move custom readiness queue into sys::windows (#978) 2019-06-13 10:50:15 -07:00
Thomas de Zeeuw
d355edff1a Rename Awakener to waker (#976) 2019-06-12 20:00:59 -07:00
Thomas de Zeeuw
a4c3c6636e Change Interests constructors into constants (#972)
Matching the constants on Ready.

Now the underlying values of Ready and Interests are also shared between
the to types, ensuring the conversions between them are always correct.
2019-06-11 13:41:54 -07:00
Thomas de Zeeuw
7326ee8f3f Add Awakener (#969)
Awakener allows cross-thread waking of Poll.
2019-06-11 12:45:21 -07:00
Alan Somers
527826ef58 Spellcheck a comment (#974) 2019-06-11 12:35:20 -07:00
Thomas de Zeeuw
ee2ec5998f Remove custom readiness queue from the public API (#973)
The plan is to remove it completely, however Windows currently depends on it. So this change only removes it from the public API and hopefully after a rewrite of Windows it will be removed completely.
2019-06-11 11:27:05 -07:00
Thomas de Zeeuw
434405a66c Merge UnixReady into Ready (#971)
The constructors are only available on platforms that support it, e.g. `Ready::lio` is only
available on FreeBSD. But checking for the presence of the indicator is available on all
platforms.

Also:

* Change Ready constructors into constants
* Don't allow clippy::trivially_copy_pass_by_ref any more
* Suppress various unused code warnings
2019-06-10 10:50:29 -07:00
Thomas de Zeeuw
46f7f6a2b3 Update deps (#964) 2019-06-06 16:25:46 -07:00
Carl Lerche
ab099cb5e1 Merge branch 'v0.6.x' into master 2019-05-28 11:33:52 -07:00
aloucks
756bf282fd Upgrade winapi to 0.3.7 and miow to 0.3.3 (#956) 2019-05-27 10:32:14 -07:00
atouchet
1185510833 Update links 2019-05-21 08:16:53 +00:00
Thomas de Zeeuw
87d7241833 Fix kqueue on platforms where C's long is i32 2019-05-19 14:51:28 +00:00
FXTi
1b68512dc3 Switch to NonZeroU8 2019-05-15 19:33:49 +00:00
FXTi
4c523ef45d Fix broken test cases 2019-05-15 19:33:49 +00:00
FXTi
1afa93820a Make Option<Interests> the same size as Interests
Issue #934
2019-05-15 19:33:49 +00:00
Stjepan Glavina
f09a6cfc10
Remove Interests::{both,all} (#938)
* Remove Interests::{both,all}

* Fix warning on windows
2019-05-10 09:28:49 +02:00
Carl Lerche
42e6e96ae2
Switch to use Rust Edition 2018 (#937) 2019-05-09 09:47:30 -07:00
Carl Lerche
73652b0395
remove remaining fuchsia refs (#936) 2019-05-08 13:09:01 -07:00
Carl Lerche
413a7d3c43
Split Poll into Poll and Register (#932)
This also makes `Poll::poll` require `&mut self` since `Poll` is no
longer expected to be stored in a `Sync` context.

Resolves #757
2019-05-08 10:47:44 -07:00
Jim Peters
7798b5b829 Document guarantee that after deregister, token is gone (#935)
Following discussion on issue #788, document that after an explicit
deregister, token will not be returned again.  (Important if using a
pointer as a Token.)
2019-05-08 09:02:28 -07:00
FXTi
631e80a9d3 Add Interests for registering process (#925)
Closes #908
2019-05-06 14:49:38 -07:00
Alan Somers
c24bba7ddf
Pass Ipv4Addr arguments by value (#894)
* Pass Ipv4Addr arguments by value

This is faster than passing them by reference, since they're Copy and
only 4 bytes big.  But it's a breaking change.

* Clippy cleanup

Prefer `panic` to `assert(false, ...)`
2019-04-28 11:25:22 -06:00
Carl Lerche
ebd086e4c4
Format with rustfmt (#930)
Also add a CI check ensuring formatting
2019-04-25 13:24:10 -07:00
Carl Lerche
e739c8a8a7
remove deprecated code (#929)
A quick pass of the code base removing anything that has previously been
annotated as deprecated. The tests were updated in the simplest way
possible. Most of these tests will be deleted as part of future pruning
of the API. Tests that remain can be refactored at that point.
2019-04-25 12:51:14 -07:00
114 changed files with 7085 additions and 18209 deletions

View File

@ -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
- RUST_BACKTRACE=1 cargo test
- cargo test --no-default-features
- cargo test
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

View File

@ -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

View File

@ -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"

View File

@ -14,8 +14,8 @@ overhead as possible over the OS abstractions.
[mit-url]: LICENSE
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.mio?branchName=master
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=2&branchName=master
[cirrus-badge]: https://api.cirrus-ci.com/github/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).

View File

@ -3,9 +3,9 @@ pr: ["master", "v0.6.x"]
jobs:
# Check formatting
# - template: ci/azure-rustfmt.yml
# parameters:
# name: rustfmt
- template: ci/azure-rustfmt.yml
parameters:
name: rustfmt
# Stable
- template: ci/azure-test-stable.yml
@ -14,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

View File

@ -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
View 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"

View File

@ -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

View File

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

View 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
View 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

View File

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

View File

@ -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"),
}
}

View File

@ -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(())
}
}

View File

@ -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>) {
}
}

View File

@ -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>>;
}

View File

@ -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,
};

View File

@ -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)),
}
}
}

View File

@ -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
View 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
View 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
View 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
View 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)
}
}

File diff suppressed because it is too large Load Diff

165
src/interests.rs Normal file
View 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(())
}
}

View File

@ -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()
}
}

View File

@ -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));
}
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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
View 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
View 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(),
}
}
}

View File

@ -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(),
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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(())
}
}

View File

@ -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
}

View File

@ -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(())
}
}

View File

@ -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);
}
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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;

View File

@ -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)
}
}
}

View File

@ -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,
}
}

View File

@ -1,260 +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 {
// Emulate `epoll_create` by using `epoll_create1` if it's available
// and otherwise falling back to `epoll_create` followed by a call to
// set the CLOEXEC flag.
dlsym!(fn epoll_create1(c_int) -> c_int);
match epoll_create1.get() {
Some(epoll_create1_fn) => {
cvt(epoll_create1_fn(libc::EPOLL_CLOEXEC))?
}
None => {
let fd = cvt(libc::epoll_create(1024))?;
drop(set_cloexec(fd));
fd
}
}
};
// offset by 1 to avoid choosing 0 as the id of a selector
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed) + 1;
Ok(Selector {
id: id,
epfd: epfd,
// 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);
}

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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();
}

View File

@ -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
View 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,
),
}
}

View File

@ -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
View 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)
}
}

View File

@ -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()
}
}

View 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
View 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()
}
}

View File

@ -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),
}
}
}

View File

@ -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()
}
}

View File

@ -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
View 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
View 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;

View File

@ -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(())
}
}

View File

@ -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
View 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();
}
}
}

View File

@ -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);
}
}

View 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()
}
}

View File

@ -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

View File

@ -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
View 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)
}
}

View File

@ -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"),
}
}
}

View File

@ -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);

View File

@ -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
View 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(&registry), token).map(|inner| Waker { inner })
}
/// Wake up the [`Poll`] associated with this `Waker`.
///
/// [`Poll`]: crate::Poll
pub fn wake(&self) -> io::Result<()> {
self.inner.wake()
}
}

View File

@ -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;
}
});
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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(&registration, Token(i), Ready::readable(), PollOpt::edge()).unwrap();
registrations.push(registration);
let barrier = barrier.clone();
let done = done.clone();
thread::spawn(move || {
barrier.wait();
while !done.load(Acquire) {
set_readiness.set_readiness(Ready::readable()).unwrap();
}
// Set one last time
set_readiness.set_readiness(Ready::readable()).unwrap();
});
}
let mut events = Events::with_capacity(4);
barrier.wait();
for _ in 0..ITER {
poll.poll(&mut events, None).unwrap();
}
done.store(true, Release);
let mut final_ready = vec![false; N];
for _ in 0..5 {
poll.poll(&mut events, None).unwrap();
for event in &events {
final_ready[event.token().0] = true;
}
if final_ready.iter().all(|v| *v) {
return;
}
thread::sleep(Duration::from_millis(10));
}
panic!("dead lock?");
}
}
#[test]
fn drop_registration_from_non_main_thread() {
use std::thread;
use std::sync::mpsc::channel;
const THREADS: usize = 8;
const ITERS: usize = 50_000;
let poll = Poll::new().unwrap();
let mut events = Events::with_capacity(1024);
let mut senders = Vec::with_capacity(THREADS);
let mut token_index = 0;
// spawn threads, which will send messages to single receiver
for _ in 0..THREADS {
let (tx, rx) = channel::<(Registration, SetReadiness)>();
senders.push(tx);
thread::spawn(move || {
for (registration, set_readiness) in rx {
let _ = set_readiness.set_readiness(Ready::readable());
drop(registration);
drop(set_readiness);
}
});
}
let mut index: usize = 0;
for _ in 0..ITERS {
let (registration, set_readiness) = Registration::new2();
registration.register(&poll, Token(token_index), Ready::readable(), PollOpt::edge()).unwrap();
let _ = senders[index].send((registration, set_readiness));
token_index += 1;
index += 1;
if index == THREADS {
index = 0;
let (registration, set_readiness) = Registration::new2();
registration.register(&poll, Token(token_index), Ready::readable(), PollOpt::edge()).unwrap();
let _ = set_readiness.set_readiness(Ready::readable());
drop(registration);
drop(set_readiness);
token_index += 1;
thread::park_timeout(Duration::from_millis(0));
let _ = poll.poll(&mut events, None).unwrap();
}
}
}

View File

@ -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());
}

View File

@ -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()
};
}
}
}
}

View File

@ -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();
}

View File

@ -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"),
}
}
}
}

View File

@ -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());
}
}
}
}

View File

@ -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();
}

View File

@ -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 };
}
}

View File

@ -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(&registration, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
poll.poll(&mut events, Some(Duration::from_millis(0))).unwrap();
drop(poll);
drop(set_readiness);
drop(registration);
}
}

View File

@ -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;
}
}
}
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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
}
}
}
}

View File

@ -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()
}

View File

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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}
}
}
}
}

View File

@ -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