Compare commits

...
Author SHA1 Message Date
Carl Lerche 9cffda59c9 Bump version to v0.1.4 (#252)
This also bumps:

* tokio-executor: v0.1.1
* tokio-reactor: v0.1.1
* tokio-threadpool: v0.1.1
2018-03-23 10:34:42 -07:00
Carl Lerche 23d95b44e7 Change hammer test consts (#255) 2018-03-23 10:27:58 -07:00
Denis 69e45f4be4 Fix a typo in tokio_reactor::PollEvented (#250)
`read_readiness` -> `write_readiness` in *write* methods
2018-03-22 20:15:31 -07:00
Denis 6bdfa159a7 Fix connect example for UDP (#241)
Close #241
2018-03-22 10:02:01 -07:00
Michal 'vorner' Vaner 5d87a9cee1 Docs: warn about errors from TcpListener::incoming (#247) 2018-03-22 10:01:05 -07:00
Denis 16d3540ce9 Add UDP client example (send/recv_dgram) (#239) 2018-03-22 10:00:27 -07:00
Sean McArthur e5ebd02885 implement poll_vectored_* and initializer method for futures2 (#242) 2018-03-22 09:59:13 -07:00
Carl Lerche 08c21e7bac Fix race condition related bugs (#243)
* Fix races.

This mostly pulls in changes from rust-lang-nursery/futures-rs#881, but
also updates Registration to be a bit more obvious as to what is going
on.

* Reduce spurious wakeups caused by Reactor

This patch adds an ABA guard on token values before registering them
with Mio. This allows catching token reuse and avoid the notification.

This is needed for OS X as the notification is used to determine that a
TCP connect has completed. A spurious notification can potentially cause
write failures.
2018-03-22 09:57:40 -07:00
Aaron Turon 8786741ba9 Update to futures 0.2.0-beta (#246) 2018-03-21 14:21:02 -07:00
Roman 494f0dc176 Runtime builder (#234)
* Split runtime module into files
* Add runtime::Builder to set up thread pool.
2018-03-21 11:23:36 -07:00
Cyril Plisko df9025594c Lapsus clavis (#245) 2018-03-21 11:17:16 -07:00
Aaron Turon 7b1306e6c2 Add top-level tests for futures 0.2 integration (#231) 2018-03-15 16:38:12 -07:00
Sean McArthur b7f4e337be set Runtime thread pool name prefix (#232) 2018-03-15 16:37:32 -07:00
Hiroaki Nakamura d1046db735 Fix condition for updating the current date buffer (#230) 2018-03-15 09:29:55 -07:00
Sam Rijs 923a80e098 Move tokio::net module into tokio tcp/udp crates (#224) 2018-03-14 09:38:59 -07:00
Carl Lerche 64435f5b35 Travis: Move before_deploy out of matrix block (#229) 2018-03-13 15:58:53 -07:00
Aaron Turon d304791c0e Simultaneous futures compat (#172)
This patch adds opt-in support for futures 0.2.
2018-03-13 13:57:35 -07:00
Carl Lerche 5846b3fc2a Reduce AtomicTask spurious notifications (#227) 2018-03-13 13:28:41 -07:00
Carl Lerche 8eb3e58b7d Shutdown the runtime on drop (#214)
Currently, the runtime does not shutdown if the runtime handle is
dropped. This can happen during a panic or when the value is simply
dropped.

This patch forces the runtime to shutdown if it is not explicitly
shutdown.

Fixes #209
2018-03-13 13:14:51 -07:00
Carl Lerche c0a2cc1f9e Add LICENSE file to all sub crates (#226)
* Add LICENSE file to all sub crates
* Update links in README
2018-03-13 13:14:28 -07:00
Carl Lerche 61b2889881 Try compiling Tokio on FreeBSD (#228) 2018-03-13 13:14:12 -07:00
hcpl 5dab821b29 Fix docs markup (#225) 2018-03-13 11:43:05 -07:00
Wesley Moore 2abeff01a5 Fix build on FreeBSD (#218) 2018-03-13 11:24:17 -07:00
Carl Lerche 96a542451d Handle futures that panic on a threadpool (#216)
If a future panics from within the context of a thread pool, the pool
should not be impacted. To do this, polling the future is wrapped with a
catch_unwind. Extra care is taken to ensure that `thread::panicking()`
is set from within the future's drop handle.

Fixes #209
2018-03-13 09:44:14 -07:00
Gray Olson 95899e007d Update comment in udp-codec example (#222) 2018-03-12 10:39:32 -07:00
Jeehoon Kang e6e3c49e0e Remove uses of futures_cpupool (#220) 2018-03-11 11:37:10 -07:00
Carl Lerche 4d514b7eb3 Relicense Tokio exclusively under the MIT license. (#215)
This patch relicenses the Tokio project exclusively under the MIT
license. Before this, the project was dual licensed under MIT and Apache
2. As such, switching to only MIT is permitted.

Fixes #202
2018-03-09 20:07:09 -08:00
Tosil Velkov 2a1585157e Fix wrong file link in examples readme.md (#208) 2018-03-09 12:26:09 -08:00
Igor Gnatenko 189d6baac4 tokio-threadpool: bump rand to 0.4 (#205) 2018-03-09 12:25:38 -08:00
Igor Gnatenko 3ad27e99ec tokio-reactor: bump mio to 0.6.14 (#204)
With 0.6.13 it doesn't compile:
no method named `as_usize` found for type `mio::Ready` in the current scope
2018-03-09 12:25:16 -08:00
Carl Lerche bf1305c421 Bump version to v0.1.3 (#213) 2018-03-09 11:41:31 -08:00
Carl Lerche cf7435ba30 CurrentThread::turn should block on idle. (#212)
This patch fixes a bug where `CurrentThread::turn` is expected to block
even if the executor is idle.

The `turn` API is the low level interface for callers to interact with
the `Sleep` instance used by the `CurrentThread` instance. As such, a
call to `turn` is expected to call `sleep` once if the executor did not
perform any work.
2018-03-09 11:09:10 -08:00
80 changed files with 2776 additions and 792 deletions
+18 -2
View File
@@ -6,18 +6,34 @@ matrix:
include:
- rust: 1.21.0
- rust: stable
before_deploy: cargo doc --all --no-deps
- os: osx
- rust: beta
- rust: nightly
- env: TARGET=x86_64-unknown-freebsd
script:
- |
set -e
if [[ "$TRAVIS_RUST_VERSION" == nightly ]]
then
cargo build --benches --all
fi
- cargo test --all
- |
set -e
if [[ "$TARGET" ]]
then
rustup target add $TARGET
cargo check --all --target $TARGET
cargo check --tests --all --target $TARGET
else
cargo test --all
cargo test --features unstable-futures
cargo test --manifest-path tokio-threadpool/Cargo.toml --features unstable-futures
cargo test --manifest-path tokio-reactor/Cargo.toml --features unstable-futures
fi
before_deploy:
- cargo doc --all --no-deps
deploy:
provider: pages
+13
View File
@@ -1,3 +1,16 @@
# 0.1.4 (March 22, 2018)
* Fix build on FreeBSD (#218)
* Shutdown the Runtime when the handle is dropped (#214)
* Set Runtime thread name prefix for worker threads (#232)
* Add builder for Runtime (#234)
* Extract TCP and UDP types into separate crates (#224)
* Optionally support futures 0.2.
# 0.1.3 (March 09, 2018)
* Fix `CurrentThread::turn` to block on idle (#212).
# 0.1.2 (March 09, 2018)
* Introduce Tokio Runtime (#141)
+26 -10
View File
@@ -5,9 +5,9 @@ name = "tokio"
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.1.2"
version = "0.1.4"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT/Apache-2.0"
license = "MIT"
readme = "README.md"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
@@ -27,6 +27,9 @@ members = [
"tokio-io",
"tokio-reactor",
"tokio-threadpool",
"tokio-tcp",
"tokio-udp",
"futures2",
]
[badges]
@@ -35,17 +38,19 @@ appveyor = { repository = "carllerche/tokio" }
[dependencies]
tokio-io = { version = "0.1.6", path = "tokio-io" }
tokio-executor = { version = "0.1.0", path = "tokio-executor" }
tokio-reactor = { version = "0.1.0", path = "tokio-reactor" }
tokio-threadpool = { version = "0.1.0", path = "tokio-threadpool" }
bytes = "0.4"
log = "0.4"
tokio-executor = { version = "0.1.1", path = "tokio-executor" }
tokio-reactor = { version = "0.1.1", path = "tokio-reactor" }
tokio-threadpool = { version = "0.1.1", path = "tokio-threadpool" }
tokio-tcp = { version = "0.1.0", path = "tokio-tcp" }
tokio-udp = { version = "0.1.0", path = "tokio-udp" }
mio = "0.6.14"
slab = "0.4"
iovec = "0.1"
futures = "0.1.18"
futures = "0.1.19"
# Futures 0.2 integration
futures2 = { version = "0.1.0", path = "futures2", optional = true }
[dev-dependencies]
bytes = "0.4"
env_logger = { version = "0.4", default-features = false }
flate2 = { version = "1", features = ["tokio"] }
futures-cpupool = "0.1"
@@ -60,3 +65,14 @@ time = "0.1"
[patch.crates-io]
tokio-io = { path = "tokio-io" }
[features]
unstable-futures = [
"futures2",
"tokio-reactor/unstable-futures",
"tokio-threadpool/unstable-futures",
"tokio-executor/unstable-futures",
"tokio-tcp/unstable-futures",
"tokio-udp/unstable-futures"
]
default = []
View File
-201
View File
@@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+9 -10
View File
@@ -117,24 +117,23 @@ The crates included as part of Tokio are:
* [`tokio-threadpool`]: Schedules the execution of futures across a pool of
threads.
* [`tokio-tcp`]: TCP bindings for use with `tokio-io` and `tokio-reactor`.
* [`tokio-udp`]: UDP bindings for use with `tokio-io` and `tokio-reactor`.
[`tokio-executor`]: tokio-executor
[`tokio-io`]: tokio-io
[`tokio-reactor`]: tokio-reactor
[`tokio-threadpool`]: tokio-threadpool
[`tokio-tcp`]: tokio-tcp
[`tokio-udp`]: tokio-udp
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in tokio by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
+3 -1
View File
@@ -44,9 +44,11 @@ A high level description of each example is:
spawning tasks, and finally framing a TCP connection to discrete
request/response objects.
* [`tinydb`](tinyhttp.rs) - an in-memory database which shows sharing state
* [`tinydb`](tinydb.rs) - an in-memory database which shows sharing state
between all connected clients, notably the key/value store of this database.
* [`udp-client`](udp-client.rs) - a simple `send_dgram`/`recv_dgram` example.
If you've got an example you'd like to see here, please feel free to open an
issue. Otherwise if you've got an example you'd like to add, please feel free
to make a PR!
+9 -4
View File
@@ -198,24 +198,29 @@ mod udp {
// All bytes from `stdin` will go to the `addr` specified in our
// argument list. Like with TCP this is spawned concurrently
tokio::spawn(stdin.map(move |chunk| {
let forward_stdin = stdin.map(move |chunk| {
(chunk, addr)
}).forward(sink).then(|result| {
if let Err(e) = result {
panic!("failed to write to socket: {}", e)
}
Ok(())
}));
});
// With UDP we could receive data from any source, so filter out
// anything coming from a different address
Box::new(stream.filter_map(move |(chunk, src)| {
let receive = stream.filter_map(move |(chunk, src)| {
if src == addr {
Some(chunk.into())
} else {
None
}
}))
});
Box::new(future::lazy(|| {
tokio::spawn(forward_stdin);
future::ok(receive)
}).flatten_stream())
}
}
+1 -1
View File
@@ -274,7 +274,7 @@ mod date {
LAST.with(|cache| {
let mut cache = cache.borrow_mut();
let now = time::get_time();
if now > cache.next_update {
if now >= cache.next_update {
cache.update(now);
}
f.write_str(cache.buffer())
+74
View File
@@ -0,0 +1,74 @@
//! A UDP client that just sends everything it gets via `stdio` in a single datagram, and then
//! waits for a reply.
//!
//! For the reasons of simplicity data from `stdio` is read until `EOF` in a blocking manner.
//!
//! You can test this out by running an echo server:
//!
//! ```
//! $ cargo run --example echo-udp -- 127.0.0.1:8080
//! ```
//!
//! and running the client in another terminal:
//!
//! ```
//! $ cargo run --example udp-client
//! ```
//!
//! You can optionally provide any custom endpoint address for the client:
//!
//! ```
//! $ cargo run --example udp-client -- 127.0.0.1:8080
//! ```
//!
//! Don't forget to pass `EOF` to the standard input of the client!
//!
//! Please mind that since the UDP protocol doesn't have any capabilities to detect a broken
//! connection the server needs to be run first, otherwise the client will block forever.
extern crate futures;
extern crate tokio;
use std::env;
use std::io::stdin;
use std::net::SocketAddr;
use tokio::net::UdpSocket;
use tokio::prelude::*;
fn get_stdin_data() -> Vec<u8> {
let mut buf = Vec::new();
stdin().read_to_end(&mut buf).unwrap();
buf
}
fn main() {
let remote_addr: SocketAddr = env::args()
.nth(1)
.unwrap_or("127.0.0.1:8080".into())
.parse()
.unwrap();
// We use port 0 to let the operating system allocate an available port for us.
let local_addr: SocketAddr = if remote_addr.is_ipv4() {
"0.0.0.0:0"
} else {
"[::]:0"
}.parse()
.unwrap();
let socket = UdpSocket::bind(&local_addr).unwrap();
const MAX_DATAGRAM_SIZE: usize = 65_507;
let processing = socket
.send_dgram(get_stdin_data(), &remote_addr)
.and_then(|(socket, _)| socket.recv_dgram(vec![0u8; MAX_DATAGRAM_SIZE]))
.map(|(_, data, len, _)| {
println!(
"Received {} bytes:\n{}",
len,
String::from_utf8_lossy(&data[..len])
)
})
.wait();
match processing {
Ok(_) => {}
Err(e) => eprintln!("Encountered an error: {}", e),
}
}
+1 -1
View File
@@ -28,7 +28,7 @@ fn main() {
let b = UdpSocket::bind(&addr).unwrap();
let b_addr = b.local_addr().unwrap();
// We're parsing each socket with the `LineCodec` defined above, and then we
// We're parsing each socket with the `BytesCodec` included in `tokio_io`, and then we
// `split` each codec into the sink/stream halves.
let (a_sink, a_stream) = UdpFramed::new(a, BytesCodec::new()).split();
let (b_sink, b_stream) = UdpFramed::new(b, BytesCodec::new()).split();
+11
View File
@@ -0,0 +1,11 @@
[package]
name = "futures2"
version = "0.1.0"
authors = ["Aaron Turon <[email protected]>"]
license = "MIT/Apache-2.0"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
[dependencies]
futures = "=0.2.0-beta"
+2
View File
@@ -0,0 +1,2 @@
extern crate futures;
pub use futures::*;
+23 -7
View File
@@ -119,6 +119,9 @@ use std::marker::PhantomData;
use std::rc::Rc;
use std::time::{Duration, Instant};
#[cfg(feature = "unstable-futures")]
use futures2;
/// Executes tasks on the current thread
pub struct CurrentThread<P: Park = ParkThread> {
/// Execute futures and receive unpark notifications.
@@ -353,7 +356,9 @@ impl<P: Park> CurrentThread<P> {
self.enter(&mut enter).run_timeout(duration)
}
/// Perform a single iteration of the event loop
/// Perform a single iteration of the event loop.
///
/// This function blocks the current thread even if the executor is idle.
pub fn turn(&mut self, duration: Option<Duration>)
-> Result<Turn, TurnError>
{
@@ -384,6 +389,13 @@ impl tokio_executor::Executor for CurrentThread {
self.borrow().spawn_local(future);
Ok(())
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, _future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
-> Result<(), futures2::executor::SpawnError>
{
panic!("Futures 0.2 integration is not available for current_thread");
}
}
impl<P: Park> fmt::Debug for CurrentThread<P> {
@@ -462,15 +474,12 @@ impl<'a, P: Park> Entered<'a, P> {
self.run_timeout2(Some(duration))
}
/// Perform a single iteration of the event loop
/// Perform a single iteration of the event loop.
///
/// This function blocks the current thread even if the executor is idle.
pub fn turn(&mut self, duration: Option<Duration>)
-> Result<Turn, TurnError>
{
if self.executor.is_idle() {
// Nothing to do
return Ok(Turn(()));
}
if !self.tick() {
let res = match duration {
Some(duration) => self.executor.park.park_timeout(duration),
@@ -592,6 +601,13 @@ impl tokio_executor::Executor for TaskExecutor {
self.spawn_local(future)
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, _future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
-> Result<(), futures2::executor::SpawnError>
{
panic!("Futures 0.2 integration is not available for current_thread");
}
fn status(&self) -> Result<(), SpawnError> {
CURRENT.with(|current| {
if current.spawn.get().is_some() {
+24 -2
View File
@@ -49,7 +49,6 @@
//! [`Executor`]: #
//! [`spawn`]: #
pub mod current_thread;
pub mod thread_pool {
@@ -137,6 +136,9 @@ pub use tokio_executor::{Executor, DefaultExecutor, SpawnError};
use futures::{Future, IntoFuture};
use futures::future::{self, FutureResult};
#[cfg(feature = "unstable-futures")]
use futures2;
/// Return value from the `spawn` function.
///
/// Currently this value doesn't actually provide any functionality. However, it
@@ -198,7 +200,7 @@ pub struct Spawn(());
/// onto the default executor returns an error. To avoid the panic, use
/// [`DefaultExecutor`].
///
/// [`DefaultExecutor`]: #
/// [`DefaultExecutor`]: struct.DefaultExecutor.html
pub fn spawn<F>(f: F) -> Spawn
where F: Future<Item = (), Error = ()> + 'static + Send
{
@@ -206,6 +208,15 @@ where F: Future<Item = (), Error = ()> + 'static + Send
Spawn(())
}
/// Like `spawn`, but compatible with futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn spawn2<F>(f: F) -> Spawn
where F: futures2::Future<Item = (), Error = futures2::Never> + 'static + Send
{
::tokio_executor::spawn2(f);
Spawn(())
}
impl IntoFuture for Spawn {
type Future = FutureResult<(), ()>;
type Item = ();
@@ -215,3 +226,14 @@ impl IntoFuture for Spawn {
future::ok(())
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::IntoFuture for Spawn {
type Future = futures2::future::FutureResult<(), ()>;
type Item = ();
type Error = ();
fn into_future(self) -> Self::Future {
futures2::future::ok(())
}
}
+8 -6
View File
@@ -62,22 +62,21 @@
//! }
//! ```
#![doc(html_root_url = "https://docs.rs/tokio/0.1.2")]
#![doc(html_root_url = "https://docs.rs/tokio/0.1.4")]
#![deny(missing_docs, warnings, missing_debug_implementations)]
extern crate bytes;
#[macro_use]
extern crate futures;
extern crate iovec;
extern crate mio;
extern crate slab;
extern crate tokio_io;
extern crate tokio_executor;
extern crate tokio_reactor;
extern crate tokio_threadpool;
extern crate tokio_tcp;
extern crate tokio_udp;
#[macro_use]
extern crate log;
#[cfg(feature = "unstable-futures")]
extern crate futures2;
pub mod executor;
pub mod net;
@@ -85,6 +84,9 @@ pub mod reactor;
pub mod runtime;
pub use executor::spawn;
#[cfg(feature = "unstable-futures")]
pub use executor::spawn2;
pub use runtime::run;
pub mod io {
+3 -6
View File
@@ -36,9 +36,6 @@
//! [`UdpFramed`]: struct.UdpFramed.html
//! [`framed`]: struct.UdpSocket.html#method.framed
mod tcp;
mod udp;
pub use self::tcp::{TcpStream, ConnectFuture};
pub use self::tcp::{TcpListener, Incoming};
pub use self::udp::{UdpSocket, UdpFramed, SendDgram, RecvDgram};
pub use tokio_tcp::{TcpStream, ConnectFuture};
pub use tokio_tcp::{TcpListener, Incoming};
pub use tokio_udp::{UdpSocket, UdpFramed, SendDgram, RecvDgram};
-8
View File
@@ -1,8 +0,0 @@
mod incoming;
mod listener;
mod stream;
pub use self::incoming::Incoming;
pub use self::listener::TcpListener;
pub use self::stream::TcpStream;
pub use self::stream::ConnectFuture;
-9
View File
@@ -1,9 +0,0 @@
mod frame;
mod socket;
mod send_dgram;
mod recv_dgram;
pub use self::frame::UdpFramed;
pub use self::socket::UdpSocket;
pub use self::send_dgram::SendDgram;
pub use self::recv_dgram::RecvDgram;
-10
View File
@@ -433,16 +433,6 @@ mod platform {
use mio::Ready;
use mio::unix::UnixReady;
#[cfg(target_os = "dragonfly")]
pub fn all() -> Ready {
hup() | UnixReady::aio()
}
#[cfg(target_os = "freebsd")]
pub fn all() -> Ready {
hup() | UnixReady::aio() | UnixReady::lio()
}
const HUP: usize = 1 << 2;
const ERROR: usize = 1 << 3;
const AIO: usize = 1 << 4;
+107
View File
@@ -0,0 +1,107 @@
use runtime::{Inner, Runtime};
use reactor::Reactor;
use std::io;
use tokio_threadpool::Builder as ThreadPoolBuilder;
/// Builds Tokio Runtime with custom configuration values.
///
/// Methods can be chanined in order to set the configuration values. The
/// Runtime is constructed by calling [`build`].
///
/// New instances of `Builder` are obtained via [`Builder::new`].
///
/// See function level documentation for details on the various configuration
/// settings.
///
/// [`build`]: #method.build
/// [`Builder::new`]: #method.new
///
/// # Examples
///
/// ```
/// # extern crate tokio;
/// # extern crate tokio_threadpool;
/// # use tokio::runtime::Builder;
///
/// # pub fn main() {
/// // create and configure ThreadPool
/// let mut threadpool_builder = tokio_threadpool::Builder::new();
/// threadpool_builder
/// .name_prefix("my-runtime-worker-")
/// .pool_size(4);
///
/// // build Runtime
/// let runtime = Builder::new()
/// .threadpool_builder(threadpool_builder)
/// .build();
/// // ... call runtime.run(...)
/// # let _ = runtime;
/// # }
/// ```
#[derive(Debug)]
pub struct Builder {
/// Thread pool specific builder
threadpool_builder: ThreadPoolBuilder,
}
impl Builder {
/// Returns a new runtime builder initialized with default configuration
/// values.
///
/// Configuration methods can be chained on the return value.
pub fn new() -> Builder {
let mut threadpool_builder = ThreadPoolBuilder::new();
threadpool_builder.name_prefix("tokio-runtime-worker-");
Builder { threadpool_builder }
}
/// Set builder to set up the thread pool instance.
pub fn threadpool_builder(&mut self, val: ThreadPoolBuilder) -> &mut Self {
self.threadpool_builder = val;
self
}
/// Create the configured `Runtime`.
///
/// The returned `ThreadPool` instance is ready to spawn tasks.
///
/// # Examples
///
/// ```
/// # extern crate tokio;
/// # use tokio::runtime::Builder;
/// # pub fn main() {
/// let runtime = Builder::new().build();
/// // ... call runtime.run(...)
/// # let _ = runtime;
/// # }
/// ```
pub fn build(&mut self) -> io::Result<Runtime> {
// Spawn a reactor on a background thread.
let reactor = Reactor::new()?.background()?;
// Get a handle to the reactor.
let handle = reactor.handle().clone();
let pool = self.threadpool_builder
.around_worker(move |w, enter| {
::tokio_reactor::with_default(&handle, enter, |_| {
w.run();
});
})
.build();
Ok(Runtime {
inner: Some(Inner {
reactor,
pool,
}),
})
}
}
+49 -132
View File
@@ -104,13 +104,23 @@
//! [idle]: struct.Runtime.html#method.shutdown_on_idle
//! [`tokio::spawn`]: ../executor/fn.spawn.html
use reactor::{Reactor, Handle, Background};
mod builder;
mod shutdown;
mod task_executor;
use tokio_threadpool::{self as threadpool, ThreadPool, Sender};
use futures::Poll;
use futures::future::{self, Future};
pub use self::builder::Builder;
pub use self::shutdown::Shutdown;
pub use self::task_executor::TaskExecutor;
use std::{fmt, io};
use reactor::{Background, Handle};
use std::io;
use tokio_threadpool as threadpool;
use futures::future::Future;
#[cfg(feature = "unstable-futures")]
use futures2;
/// Handle to the Tokio runtime.
///
@@ -125,29 +135,13 @@ pub struct Runtime {
inner: Option<Inner>,
}
/// Executes futures on the runtime
///
/// All futures spawned using this executor will be submitted to the associated
/// Runtime's executor. This executor is usually a thread pool.
///
/// For more details, see the [module level](index.html) documentation.
#[derive(Debug, Clone)]
pub struct TaskExecutor {
inner: Sender,
}
/// A future that resolves when the Tokio `Runtime` is shut down.
pub struct Shutdown {
inner: Box<Future<Item = (), Error = ()> + Send>,
}
#[derive(Debug)]
struct Inner {
/// Reactor running on a background thread.
reactor: Background,
/// Task execution pool.
pool: ThreadPool,
pool: threadpool::ThreadPool,
}
// ===== impl Runtime =====
@@ -205,6 +199,18 @@ where F: Future<Item = (), Error = ()> + Send + 'static,
runtime.shutdown_on_idle().wait().unwrap();
}
/// Start the Tokio runtime using the supplied future to bootstrap execution.
///
/// Identical to `run` but works with futures 0.2-style futures.
#[cfg(feature = "unstable-futures")]
pub fn run2<F>(future: F)
where F: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
{
let mut runtime = Runtime::new().unwrap();
runtime.spawn2(future);
runtime.shutdown_on_idle().wait().unwrap();
}
impl Runtime {
/// Create a new runtime instance with default configuration values.
///
@@ -212,26 +218,7 @@ impl Runtime {
///
/// [mod]: index.html
pub fn new() -> io::Result<Self> {
// Spawn a reactor on a background thread.
let reactor = Reactor::new()?.background()?;
// Get a handle to the reactor.
let handle = reactor.handle().clone();
let pool = threadpool::Builder::new()
.around_worker(move |w, enter| {
::tokio_reactor::with_default(&handle, enter, |_| {
w.run();
});
})
.build();
Ok(Runtime {
inner: Some(Inner {
reactor,
pool,
}),
})
Builder::new().build()
}
/// Return a reference to the reactor handle for this runtime instance.
@@ -287,6 +274,19 @@ impl Runtime {
self
}
/// Spawn a futures 0.2-style future onto the Tokio runtime.
///
/// Otherwise identical to `spawn`
#[cfg(feature = "unstable-futures")]
pub fn spawn2<F>(&mut self, future: F) -> &mut Self
where F: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
{
futures2::executor::Executor::spawn(
self.inner_mut().pool.sender_mut(), Box::new(future)
).unwrap();
self
}
/// Signals the runtime to shutdown once it becomes idle.
///
/// Returns a future that completes once the shutdown operation has
@@ -339,17 +339,7 @@ impl Runtime {
/// [mod]: index.html
pub fn shutdown_now(mut self) -> Shutdown {
let inner = self.inner.take().unwrap();
let inner = Box::new({
let pool = inner.pool;
let reactor = inner.reactor;
pool.shutdown_now().and_then(|_| {
reactor.shutdown_now()
})
});
Shutdown { inner }
Shutdown::shutdown_now(inner)
}
fn inner(&self) -> &Inner {
@@ -361,84 +351,11 @@ impl Runtime {
}
}
// ===== impl TaskExecutor =====
impl TaskExecutor {
/// Spawn a future onto the Tokio runtime.
///
/// This spawns the given future onto the runtime's executor, usually a
/// thread pool. The thread pool is then responsible for polling the future
/// until it completes.
///
/// See [module level][mod] documentation for more details.
///
/// [mod]: index.html
///
/// # Examples
///
/// ```rust
/// # extern crate tokio;
/// # extern crate futures;
/// # use futures::{future, Future, Stream};
/// use tokio::runtime::Runtime;
///
/// # fn dox() {
/// // Create the runtime
/// let mut rt = Runtime::new().unwrap();
/// let executor = rt.executor();
///
/// // Spawn a future onto the runtime
/// executor.spawn(future::lazy(|| {
/// println!("now running on a worker thread");
/// Ok(())
/// }));
/// # }
/// # pub fn main() {}
/// ```
///
/// # Panics
///
/// This function panics if the spawn fails. Failure occurs if the executor
/// is currently at capacity and is unable to spawn a new future.
pub fn spawn<F>(&self, future: F)
where F: Future<Item = (), Error = ()> + Send + 'static,
{
self.inner.spawn(future).unwrap();
}
}
impl<T> future::Executor<T> for TaskExecutor
where T: Future<Item = (), Error = ()> + Send + 'static,
{
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
self.inner.execute(future)
}
}
impl ::executor::Executor for TaskExecutor {
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
-> Result<(), ::executor::SpawnError>
{
self.inner.spawn(future)
}
}
// ===== impl Shutdown =====
impl Future for Shutdown {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
try_ready!(self.inner.poll());
Ok(().into())
}
}
impl fmt::Debug for Shutdown {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Shutdown")
.field("inner", &"Box<Future<Item = (), Error = ()>>")
.finish()
impl Drop for Runtime {
fn drop(&mut self) {
if let Some(inner) = self.inner.take() {
let shutdown = Shutdown::shutdown_now(inner);
let _ = shutdown.wait();
}
}
}
+46
View File
@@ -0,0 +1,46 @@
use runtime::Inner;
use std::fmt;
use futures::{Future, Poll};
/// A future that resolves when the Tokio `Runtime` is shut down.
pub struct Shutdown {
pub(super) inner: Box<Future<Item = (), Error = ()> + Send>,
}
impl Shutdown {
pub(super) fn shutdown_now(inner: Inner) -> Self {
let inner = Box::new({
let pool = inner.pool;
let reactor = inner.reactor;
pool.shutdown_now().and_then(|_| {
reactor.shutdown_now()
.then(|_| {
Ok(())
})
})
});
Shutdown { inner }
}
}
impl Future for Shutdown {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
try_ready!(self.inner.poll());
Ok(().into())
}
}
impl fmt::Debug for Shutdown {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("Shutdown")
.field("inner", &"Box<Future<Item = (), Error = ()>>")
.finish()
}
}
+98
View File
@@ -0,0 +1,98 @@
use tokio_threadpool::Sender;
use futures::future::{self, Future};
#[cfg(feature = "unstable-futures")]
use futures2;
/// Executes futures on the runtime
///
/// All futures spawned using this executor will be submitted to the associated
/// Runtime's executor. This executor is usually a thread pool.
///
/// For more details, see the [module level](index.html) documentation.
#[derive(Debug, Clone)]
pub struct TaskExecutor {
pub(super) inner: Sender,
}
impl TaskExecutor {
/// Spawn a future onto the Tokio runtime.
///
/// This spawns the given future onto the runtime's executor, usually a
/// thread pool. The thread pool is then responsible for polling the future
/// until it completes.
///
/// See [module level][mod] documentation for more details.
///
/// [mod]: index.html
///
/// # Examples
///
/// ```rust
/// # extern crate tokio;
/// # extern crate futures;
/// # use futures::{future, Future, Stream};
/// use tokio::runtime::Runtime;
///
/// # fn dox() {
/// // Create the runtime
/// let mut rt = Runtime::new().unwrap();
/// let executor = rt.executor();
///
/// // Spawn a future onto the runtime
/// executor.spawn(future::lazy(|| {
/// println!("now running on a worker thread");
/// Ok(())
/// }));
/// # }
/// # pub fn main() {}
/// ```
///
/// # Panics
///
/// This function panics if the spawn fails. Failure occurs if the executor
/// is currently at capacity and is unable to spawn a new future.
pub fn spawn<F>(&self, future: F)
where F: Future<Item = (), Error = ()> + Send + 'static,
{
self.inner.spawn(future).unwrap();
}
}
impl<T> future::Executor<T> for TaskExecutor
where T: Future<Item = (), Error = ()> + Send + 'static,
{
fn execute(&self, future: T) -> Result<(), future::ExecuteError<T>> {
self.inner.execute(future)
}
}
impl ::executor::Executor for TaskExecutor {
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
-> Result<(), ::executor::SpawnError>
{
self.inner.spawn(future)
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
-> Result<(), futures2::executor::SpawnError>
{
self.inner.spawn2(future)
}
}
#[cfg(feature = "unstable-futures")]
type Task2 = Box<futures2::Future<Item = (), Error = futures2::Never> + Send>;
#[cfg(feature = "unstable-futures")]
impl futures2::executor::Executor for TaskExecutor {
fn spawn(&mut self, f: Task2) -> Result<(), futures2::executor::SpawnError> {
futures2::executor::Executor::spawn(&mut self.inner, f)
}
fn status(&self) -> Result<(), futures2::executor::SpawnError> {
futures2::executor::Executor::status(&self.inner)
}
}
Regular → Executable
+2
View File
@@ -1,3 +1,5 @@
#![cfg(not(feature = "unstable-futures"))]
extern crate tokio;
extern crate tokio_executor;
extern crate futures;
Executable
+53
View File
@@ -0,0 +1,53 @@
#![cfg(feature = "unstable-futures")]
// This test is the same as `echo.rs`, but ported to futures 0.2
extern crate env_logger;
extern crate futures2;
extern crate tokio;
extern crate tokio_io;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::thread;
use futures2::prelude::*;
use futures2::executor::block_on;
use tokio::net::TcpListener;
macro_rules! t {
($e:expr) => (match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
})
}
#[test]
fn echo_server() {
drop(env_logger::init());
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
let addr = t!(srv.local_addr());
let msg = "foo bar baz";
let t = thread::spawn(move || {
let mut s = TcpStream::connect(&addr).unwrap();
for _i in 0..1024 {
assert_eq!(t!(s.write(msg.as_bytes())), msg.len());
let mut buf = [0; 1024];
assert_eq!(t!(s.read(&mut buf)), msg.len());
assert_eq!(&buf[..msg.len()], msg.as_bytes());
}
});
let clients = srv.incoming();
let client = clients.next().map(|e| e.0.unwrap()).map_err(|e| e.0);
let halves = client.map(|s| s.split());
let copied = halves.and_then(|(a, b)| a.copy_into(b));
let (amt, _, _) = t!(block_on(copied));
t.join().unwrap();
assert_eq!(amt, msg.len() as u64 * 1024);
}
+48 -32
View File
@@ -5,6 +5,8 @@ extern crate env_logger;
use std::{io, thread};
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::Relaxed;
use futures::prelude::*;
use tokio::net::{TcpStream, TcpListener};
@@ -18,7 +20,7 @@ macro_rules! t {
}
#[test]
fn hammer() {
fn hammer_old() {
let _ = env_logger::init();
let threads = (0..10).map(|_| {
@@ -73,48 +75,62 @@ fn hammer_split() {
use tokio_io::io;
const N: usize = 100;
const ITER: usize = 10;
let _ = env_logger::init();
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
let addr = t!(srv.local_addr());
for _ in 0..ITER {
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
let addr = t!(srv.local_addr());
let mut rt = Runtime::new().unwrap();
let cnt = Arc::new(AtomicUsize::new(0));
fn split(socket: TcpStream) {
let socket = Arc::new(socket);
let rd = Rd(socket.clone());
let wr = Wr(socket);
let mut rt = Runtime::new().unwrap();
let rd = io::read(rd, vec![0; 1])
.map(|_| ())
.map_err(|e| panic!("read error = {:?}", e));
fn split(socket: TcpStream, cnt: Arc<AtomicUsize>) {
let socket = Arc::new(socket);
let rd = Rd(socket.clone());
let wr = Wr(socket);
let wr = io::write_all(wr, b"1")
.map(|_| ())
.map_err(|e| panic!("write error = {:?}", e));
let cnt2 = cnt.clone();
tokio::spawn(rd);
tokio::spawn(wr);
}
let rd = io::read(rd, vec![0; 1])
.map(move |_| {
cnt2.fetch_add(1, Relaxed);
})
.map_err(|e| panic!("read error = {:?}", e));
rt.spawn({
srv.incoming()
.map_err(|e| panic!("accept error = {:?}", e))
.take(N as u64)
.for_each(|socket| {
split(socket);
Ok(())
})
});
let wr = io::write_all(wr, b"1")
.map(move |_| {
cnt.fetch_add(1, Relaxed);
})
.map_err(move |e| panic!("write error = {:?}", e));
tokio::spawn(rd);
tokio::spawn(wr);
}
for _ in 0..N {
rt.spawn({
TcpStream::connect(&addr)
.map_err(|e| panic!("connect error = {:?}", e))
.map(|socket| split(socket))
let cnt = cnt.clone();
srv.incoming()
.map_err(|e| panic!("accept error = {:?}", e))
.take(N as u64)
.for_each(move |socket| {
split(socket, cnt.clone());
Ok(())
})
});
}
rt.shutdown_on_idle().wait().unwrap();
for _ in 0..N {
rt.spawn({
let cnt = cnt.clone();
TcpStream::connect(&addr)
.map_err(move |e| panic!("connect error = {:?}", e))
.map(move |socket| split(socket, cnt))
});
}
rt.shutdown_on_idle().wait().unwrap();
assert_eq!(N * 4, cnt.load(Relaxed));
}
}
+122
View File
@@ -0,0 +1,122 @@
#![cfg(feature = "unstable-futures")]
// This test is the same as `global.rs`, but ported to futures 0.2
extern crate futures;
extern crate futures2;
extern crate tokio;
extern crate tokio_io;
extern crate env_logger;
use std::{io, thread};
use std::sync::Arc;
use futures2::prelude::*;
use futures2::executor::block_on;
use futures2::task;
use tokio::net::{TcpStream, TcpListener};
use tokio::runtime::Runtime;
macro_rules! t {
($e:expr) => (match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
})
}
#[test]
fn hammer() {
let _ = env_logger::init();
let threads = (0..10).map(|_| {
thread::spawn(|| {
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
let addr = t!(srv.local_addr());
let mine = TcpStream::connect(&addr);
let theirs = srv.incoming().next()
.map(|(s, _)| s.unwrap())
.map_err(|(s, _)| s);
let (mine, theirs) = t!(block_on(mine.join(theirs)));
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
})
}).collect::<Vec<_>>();
for thread in threads {
thread.join().unwrap();
}
}
struct Rd(Arc<TcpStream>);
struct Wr(Arc<TcpStream>);
impl AsyncRead for Rd {
fn poll_read(&mut self, cx: &mut task::Context, dst: &mut [u8]) -> Poll<usize, io::Error> {
<&TcpStream>::poll_read(&mut &*self.0, cx, dst)
}
}
impl AsyncWrite for Wr {
fn poll_write(&mut self, cx: &mut task::Context, src: &[u8]) -> Poll<usize, io::Error> {
<&TcpStream>::poll_write(&mut &*self.0, cx, src)
}
fn poll_flush(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
Ok(().into())
}
fn poll_close(&mut self, _cx: &mut task::Context) -> Poll<(), io::Error> {
Ok(().into())
}
}
#[test]
fn hammer_split() {
const N: usize = 100;
let _ = env_logger::init();
let srv = t!(TcpListener::bind(&"127.0.0.1:0".parse().unwrap()));
let addr = t!(srv.local_addr());
let mut rt = Runtime::new().unwrap();
fn split(socket: TcpStream) {
let socket = Arc::new(socket);
let rd = Rd(socket.clone());
let wr = Wr(socket);
let rd = rd.read(vec![0; 1])
.map(|_| ())
.map_err(|e| panic!("read error = {:?}", e));
let wr = wr.write_all(b"1")
.map(|_| ())
.map_err(|e| panic!("write error = {:?}", e));
tokio::spawn2(rd);
tokio::spawn2(wr);
}
rt.spawn2({
srv.incoming()
.map_err(|e| panic!("accept error = {:?}", e))
.take(N as u64)
.for_each(|socket| {
split(socket);
Ok(())
})
.map(|_| ())
});
for _ in 0..N {
rt.spawn2({
TcpStream::connect(&addr)
.map_err(|e| panic!("connect error = {:?}", e))
.map(|socket| split(socket))
});
}
futures::Future::wait(rt.shutdown_on_idle()).unwrap();
}
+8 -7
View File
@@ -1,8 +1,8 @@
extern crate env_logger;
extern crate futures;
extern crate futures_cpupool;
extern crate tokio;
extern crate tokio_io;
extern crate tokio_threadpool;
extern crate bytes;
use std::io;
@@ -10,12 +10,11 @@ use std::net::Shutdown;
use bytes::{BytesMut, BufMut};
use futures::{Future, Stream, Sink};
use futures::future::Executor;
use futures_cpupool::CpuPool;
use tokio::net::{TcpListener, TcpStream};
use tokio_io::codec::{Encoder, Decoder};
use tokio_io::io::{write_all, read};
use tokio_io::AsyncRead;
use tokio_threadpool::Builder;
pub struct LineCodec;
@@ -54,18 +53,20 @@ impl Encoder for LineCodec {
fn echo() {
drop(env_logger::init());
let pool = CpuPool::new(1);
let pool = Builder::new()
.pool_size(1)
.build();
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
let addr = listener.local_addr().unwrap();
let pool_inner = pool.clone();
let sender = pool.sender().clone();
let srv = listener.incoming().for_each(move |socket| {
let (sink, stream) = socket.framed(LineCodec).split();
pool_inner.execute(sink.send_all(stream).map(|_| ()).map_err(|_| ())).unwrap();
sender.spawn(sink.send_all(stream).map(|_| ()).map_err(|_| ())).unwrap();
Ok(())
});
pool.execute(srv.map_err(|e| panic!("srv error: {}", e))).unwrap();
pool.sender().spawn(srv.map_err(|e| panic!("srv error: {}", e))).unwrap();
let client = TcpStream::connect(&addr);
let client = client.wait().unwrap();
Executable
+136
View File
@@ -0,0 +1,136 @@
#![cfg(feature = "unstable-futures")]
// This test is the same as `tcp.rs`, but ported to futures 0.2
extern crate env_logger;
extern crate tokio;
extern crate mio;
extern crate futures2;
use std::{net, thread};
use std::sync::mpsc::channel;
use tokio::net::{TcpListener, TcpStream};
use futures2::executor::block_on;
use futures2::prelude::*;
macro_rules! t {
($e:expr) => (match $e {
Ok(e) => e,
Err(e) => panic!("{} failed with {:?}", stringify!($e), e),
})
}
#[test]
fn connect() {
drop(env_logger::init());
let srv = t!(net::TcpListener::bind("127.0.0.1:0"));
let addr = t!(srv.local_addr());
let t = thread::spawn(move || {
t!(srv.accept()).0
});
let stream = TcpStream::connect(&addr);
let mine = t!(block_on(stream));
let theirs = t.join().unwrap();
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
}
#[test]
fn accept() {
drop(env_logger::init());
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
let addr = t!(srv.local_addr());
let (tx, rx) = channel();
let client = srv.incoming().map(move |t| {
tx.send(()).unwrap();
t
}).next().map_err(|e| e.0);
assert!(rx.try_recv().is_err());
let t = thread::spawn(move || {
net::TcpStream::connect(&addr).unwrap()
});
let (mine, _remaining) = t!(block_on(client));
let mine = mine.unwrap();
let theirs = t.join().unwrap();
assert_eq!(t!(mine.local_addr()), t!(theirs.peer_addr()));
assert_eq!(t!(theirs.local_addr()), t!(mine.peer_addr()));
}
#[test]
fn accept2() {
drop(env_logger::init());
let srv = t!(TcpListener::bind(&t!("127.0.0.1:0".parse())));
let addr = t!(srv.local_addr());
let t = thread::spawn(move || {
net::TcpStream::connect(&addr).unwrap()
});
let (tx, rx) = channel();
let client = srv.incoming().map(move |t| {
tx.send(()).unwrap();
t
}).next().map_err(|e| e.0);
assert!(rx.try_recv().is_err());
let (mine, _remaining) = t!(block_on(client));
mine.unwrap();
t.join().unwrap();
}
#[cfg(unix)]
mod unix {
use tokio::net::TcpStream;
use tokio::prelude::*;
use env_logger;
use futures2::future;
use futures2::executor::block_on;
use futures2::io::AsyncRead;
use mio::unix::UnixReady;
use std::{net, thread};
use std::time::Duration;
#[test]
fn poll_hup() {
drop(env_logger::init());
let srv = t!(net::TcpListener::bind("127.0.0.1:0"));
let addr = t!(srv.local_addr());
let t = thread::spawn(move || {
let mut client = t!(srv.accept()).0;
client.write(b"hello world").unwrap();
thread::sleep(Duration::from_millis(200));
});
let mut stream = t!(block_on(TcpStream::connect(&addr)));
// Poll for HUP before reading.
block_on(future::poll_fn(|cx| {
stream.poll_read_ready2(cx, UnixReady::hup().into())
})).unwrap();
// Same for write half
block_on(future::poll_fn(|cx| {
stream.poll_write_ready2(cx)
})).unwrap();
let mut buf = vec![0; 11];
// Read the data
block_on(future::poll_fn(|cx| {
stream.poll_read(cx, &mut buf)
})).unwrap();
assert_eq!(b"hello world", &buf[..]);
t.join().unwrap();
}
}
+4
View File
@@ -1,3 +1,7 @@
# 0.1.1 (March 22, 2018)
* Optionally support futures 0.2.
# 0.1.0 (March 09, 2018)
* Initial release
+15 -3
View File
@@ -1,10 +1,15 @@
[package]
name = "tokio-executor"
version = "0.1.0"
# When releasing to crates.io:
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.1.1"
documentation = "https://docs.rs/tokio-executor"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://github.com/tokio-rs/tokio"
license = "MIT/Apache-2.0"
license = "MIT"
authors = ["Carl Lerche <[email protected]>"]
description = """
Future execution primitives
@@ -13,4 +18,11 @@ keywords = ["futures", "tokio"]
categories = ["concurrency", "asynchronous"]
[dependencies]
futures = "0.1.18"
futures = "0.1.19"
# Futures 0.2 integration
futures2 = { version = "0.1.0", path = "../futures2", optional = true }
[features]
unstable-futures = ["futures2"]
default = []
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2018 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+3 -10
View File
@@ -38,17 +38,10 @@ executor, including:
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
+9
View File
@@ -2,6 +2,9 @@ use std::prelude::v1::*;
use std::cell::Cell;
use std::fmt;
#[cfg(feature = "unstable-futures")]
use futures2;
thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
/// Represents an executor context.
@@ -10,6 +13,9 @@ thread_local!(static ENTERED: Cell<bool> = Cell::new(false));
pub struct Enter {
on_exit: Vec<Box<Callback>>,
permanent: bool,
#[cfg(feature = "unstable-futures")]
_enter2: futures2::executor::Enter,
}
/// An error returned by `enter` if an execution scope has already been
@@ -40,6 +46,9 @@ pub fn enter() -> Result<Enter, EnterError> {
Ok(Enter {
on_exit: Vec::new(),
permanent: false,
#[cfg(feature = "unstable-futures")]
_enter2: futures2::executor::enter().unwrap(),
})
}
})
+30 -1
View File
@@ -6,6 +6,9 @@ use std::cell::Cell;
use std::marker::PhantomData;
use std::rc::Rc;
#[cfg(feature = "unstable-futures")]
use futures2;
/// Executes futures on the default executor for the current execution context.
///
/// `DefaultExecutor` implements `Executor` and can be used to spawn futures
@@ -28,7 +31,7 @@ impl DefaultExecutor {
/// Futures may be spawned onto the default executor using this handle.
///
/// The returned handle will reference whichever executor is configured as
/// the default **at the time `spawn` is called`. This enables
/// the default **at the time `spawn` is called**. This enables
/// `DefaultExecutor::current()` to be called before an execution context is
/// setup, then passed **into** an execution context before it is used.
pub fn current() -> DefaultExecutor {
@@ -59,6 +62,23 @@ impl super::Executor for DefaultExecutor {
}
})
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
-> Result<(), futures2::executor::SpawnError>
{
EXECUTOR.with(|current_executor| {
match current_executor.get() {
Some(executor) => {
let executor = unsafe { &mut *executor };
executor.spawn2(future)
}
None => {
Err(futures2::executor::SpawnError::shutdown())
}
}
})
}
}
// ===== global spawn fns =====
@@ -109,6 +129,15 @@ pub fn spawn<T>(future: T)
.unwrap()
}
/// Like `spawn` but compatible with futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn spawn2<T>(future: T)
where T: futures2::Future<Item = (), Error = futures2::Never> + Send + 'static,
{
DefaultExecutor::current().spawn2(Box::new(future))
.unwrap()
}
/// Set the default executor for the duration of the closure
///
/// # Panics
+13 -2
View File
@@ -31,10 +31,13 @@
//! [`Park`]: park/index.html
#![deny(missing_docs, missing_debug_implementations, warnings)]
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.0")]
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.1")]
extern crate futures;
#[cfg(feature = "unstable-futures")]
extern crate futures2;
mod enter;
mod global;
pub mod park;
@@ -42,6 +45,9 @@ pub mod park;
pub use enter::{enter, Enter, EnterError};
pub use global::{spawn, with_default, DefaultExecutor};
#[cfg(feature = "unstable-futures")]
pub use global::spawn2;
use futures::Future;
/// A value that executes futures.
@@ -129,7 +135,12 @@ pub trait Executor {
/// # fn main() {}
/// ```
fn spawn(&mut self, future: Box<Future<Item = (), Error = ()> + Send>)
-> Result<(), SpawnError>;
-> Result<(), SpawnError>;
/// Like `spawn`, but compatible with futures 0.2
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, future: Box<futures2::Future<Item = (), Error = futures2::Never> + Send>)
-> Result<(), futures2::executor::SpawnError>;
/// Provides a best effort **hint** to whether or not `spawn` will succeed.
///
+1 -1
View File
@@ -7,7 +7,7 @@ name = "tokio-io"
# - Create "v0.1.x" git tag.
version = "0.1.6"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT/Apache-2.0"
license = "MIT"
repository = "https://github.com/tokio-rs/tokio-io"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-io/0.1"
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2018 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+4 -11
View File
@@ -26,19 +26,12 @@ online at [https://tokio.rs](https://tokio.rs). The [API
documentation](https://docs.rs/tokio-io) is also a great place to get started
for the nitty-gritty.
# License
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
+6
View File
@@ -1,3 +1,9 @@
# 0.1.1 (March 22, 2018)
* Fix threading bugs (#227)
* Fix notification bugs (#243)
* Optionally support futures 0.2 (#172)
# 0.1.0 (March 09, 2018)
* Initial release
+12 -5
View File
@@ -5,9 +5,9 @@ name = "tokio-reactor"
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.1.0"
version = "0.1.1"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT/Apache-2.0"
license = "MIT"
readme = "README.md"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
@@ -18,9 +18,16 @@ Event loop that drives Tokio I/O resources.
categories = ["asynchronous", "network-programming"]
[dependencies]
futures = "0.1.18"
futures = "0.1.19"
log = "0.4.1"
mio = "0.6.13"
mio = "0.6.14"
slab = "0.4.0"
tokio-executor = { version = "0.1.0", path = "../tokio-executor" }
tokio-executor = { version = "0.1.1", path = "../tokio-executor" }
tokio-io = { version = "0.1.6", path = "../tokio-io" }
# Futures 0.2 integration
futures2 = { version = "0.1", path = "../futures2", optional = true }
[features]
unstable-futures = ["futures2"]
default = []
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2018 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+3 -10
View File
@@ -33,17 +33,10 @@ are building a custom I/O resource.
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
+191 -93
View File
@@ -1,9 +1,11 @@
use futures::task::{self, Task};
#![allow(dead_code)]
use super::Task;
use std::fmt;
use std::cell::UnsafeCell;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{Acquire, Release};
use std::sync::atomic::Ordering::{Acquire, Release, AcqRel};
/// A synchronization primitive for task notification.
///
@@ -24,37 +26,115 @@ use std::sync::atomic::Ordering::{Acquire, Release};
/// `AtomicTask` does not provide any memory ordering guarantees, as such the
/// user should use caution and use other synchronization primitives to guard
/// the result of the underlying computation.
pub struct AtomicTask {
pub(crate) struct AtomicTask {
state: AtomicUsize,
task: UnsafeCell<Option<Task>>,
}
/// Initial state, the `AtomicTask` is currently not being used.
///
/// The value `2` is picked specifically because it between the write lock &
/// read lock values. Since the read lock is represented by an incrementing
/// counter, this enables an atomic fetch_sub operation to be used for releasing
/// a lock.
const WAITING: usize = 2;
// `AtomicTask` is a multi-consumer, single-producer transfer cell. The cell
// stores a `Task` value produced by calls to `register` and many threads can
// race to take the task (to notify it) by calling `notify.
//
// If a new `Task` instance is produced by calling `register` before an existing
// one is consumed, then the existing one is overwritten.
//
// While `AtomicTask` is single-producer, the implementation ensures memory
// safety. In the event of concurrent calls to `register`, there will be a
// single winner whose task will get stored in the cell. The losers will not
// have their tasks notified. As such, callers should ensure to add
// synchronization to calls to `register`.
//
// The implementation uses a single `AtomicUsize` value to coordinate access to
// the `Task` cell. There are two bits that are operated on independently. These
// are represented by `REGISTERING` and `NOTIFYING`.
//
// The `REGISTERING` bit is set when a producer enters the critical section. The
// `NOTIFYING` bit is set when a consumer enters the critical section. Neither
// bit being set is represented by `WAITING`.
//
// A thread obtains an exclusive lock on the task cell by transitioning the
// state from `WAITING` to `REGISTERING` or `NOTIFYING`, depending on the
// operation the thread wishes to perform. When this transition is made, it is
// guaranteed that no other thread will access the task cell.
//
// # Registering
//
// On a call to `register`, an attempt to transition the state from WAITING to
// REGISTERING is made. On success, the caller obtains a lock on the task cell.
//
// If the lock is obtained, then the thread sets the task cell to the task
// provided as an argument. Then it attempts to transition the state back from
// `REGISTERING` -> `WAITING`.
//
// If this transition is successful, then the registering process is complete
// and the next call to `notify` will observe the task.
//
// If the transition fails, then there was a concurrent call to `notify` that
// was unable to access the task cell (due to the registering thread holding the
// lock). To handle this, the registering thread removes the task it just set
// from the cell and calls `notify` on it. This call to notify represents the
// attempt to notify by the other thread (that set the `NOTIFYING` bit). The
// state is then transitioned from `REGISTERING | NOTIFYING` back to `WAITING`.
// This transition must succeed because, at this point, the state cannot be
// transitioned by another thread.
//
// # Notifying
//
// On a call to `notify`, an attempt to transition the state from `WAITING` to
// `NOTIFYING` is made. On success, the caller obtains a lock on the task cell.
//
// If the lock is obtained, then the thread takes ownership of the current value
// in teh task cell, and calls `notify` on it. The state is then transitioned
// back to `WAITING`. This transition must succeed as, at this point, the state
// cannot be transitioned by another thread.
//
// If the thread is unable to obtain the lock, the `NOTIFYING` bit is still.
// This is because it has either been set by the current thread but the previous
// value included the `REGISTERING` bit **or** a concurrent thread is in the
// `NOTIFYING` critical section. Either way, no action must be taken.
//
// If the current thread is the only concurrent call to `notify` and another
// thread is in the `register` critical section, when the other thread **exits**
// the `register` critical section, it will observe the `NOTIFYING` bit and
// handle the notify itself.
//
// If another thread is in the `notify` critical section, then it will handle
// notifying the task.
//
// # A potential race (is safely handled).
//
// Imagine the following situation:
//
// * Thread A obtains the `notify` lock and notifies a task.
//
// * Before thread A releases the `notify` lock, the notified task is scheduled.
//
// * Thread B attempts to notify the task. In theory this should result in the
// task being notified, but it cannot because thread A still holds the notify
// lock.
//
// This case is handled by requiring users of `AtomicTask` to call `register`
// **before** attempting to observe the application state change that resulted
// in the task being notified. The notifiers also change the application state
// before calling notify.
//
// Because of this, the task will do one of two things.
//
// 1) Observe the application state change that Thread B is notifying on. In
// this case, it is OK for Thread B's notification to be lost.
//
// 2) Call register before attempting to observe the application state. Since
// Thread A still holds the `notify` lock, the call to `register` will result
// in the task notifying itself and get scheduled again.
/// The `register` function has determined that the task is no longer current.
/// This implies that `AtomicTask::register` is being called from a different
/// task than is represented by the currently stored task. The write lock is
/// obtained to update the task cell.
const LOCKED_WRITE: usize = 0;
/// Idle state
const WAITING: usize = 0;
/// At least one call to `notify` happened concurrently to `register` updating
/// the task cell. This state is detected when `register` exits the mutation
/// code and signals to `register` that it is responsible for notifying its own
/// task.
const LOCKED_WRITE_NOTIFIED: usize = 1;
/// A new task value is being registered with the `AtomicTask` cell.
const REGISTERING: usize = 0b01;
/// The `notify` function has locked access to the task cell for notification.
///
/// The constant is left here mostly for documentation reasons.
#[allow(dead_code)]
const LOCKED_READ: usize = 3;
/// The task currently registered with the `AtomicTask` cell is being notified.
const NOTIFYING: usize = 0b10;
impl AtomicTask {
/// Create an `AtomicTask` initialized with the given `Task`
@@ -69,12 +149,7 @@ impl AtomicTask {
}
}
/// Registers the **current** task to be notified on calls to `notify`.
pub fn register(&self) {
self.register_task(task::current());
}
/// Registers the task to be notified on calls to `notify`.
/// Registers the provided task to be notified on calls to `notify`.
///
/// The new task will take place of any previous tasks that were registered
/// by previous calls to `register`. Any calls to `notify` that happen after
@@ -90,35 +165,74 @@ impl AtomicTask {
/// tasks to be notified. One of the callers will win and have its task set,
/// but there is no guarantee as to which caller will succeed.
pub fn register_task(&self, task: Task) {
match self.state.compare_and_swap(WAITING, LOCKED_WRITE, Acquire) {
match self.state.compare_and_swap(WAITING, REGISTERING, Acquire) {
WAITING => {
unsafe {
// Locked acquired, update the task cell
*self.task.get() = Some(task);
// Locked acquired, update the waker cell
*self.task.get() = Some(task.clone());
// Release the lock. If the state transitioned to
// `LOCKED_NOTIFIED`, this means that an notify has been
// signaled, so notify the task.
if LOCKED_WRITE_NOTIFIED == self.state.swap(WAITING, Release) {
(*self.task.get()).as_ref().unwrap().notify();
// Release the lock. If the state transitioned to include
// the `NOTIFYING` bit, this means that a notify has been
// called concurrently, so we have to remove the task and
// notify it.`
//
// Start by assuming that the state is `REGISTERING` as this
// is what we jut set it to.
let mut curr = REGISTERING;
// If a task has to be notified, it will be set here.
let mut notify: Option<Task> = None;
loop {
let res = self.state.compare_exchange(
curr, WAITING, AcqRel, Acquire);
match res {
Ok(_) => {
// The atomic exchange was successful, now
// notify the task (if set) and return.
if let Some(task) = notify {
task.notify();
}
return;
}
Err(actual) => {
// This branch can only be reached if a
// concurrent thread called `notify`. In this
// case, `actual` **must** be `REGISTERING |
// `NOTIFYING`.
debug_assert_eq!(actual, REGISTERING | NOTIFYING);
// Take the task to notify once the atomic operation has
// completed.
notify = (*self.task.get()).take();
// Update `curr` for the next iteration of the
// loop
curr = actual;
}
}
}
}
}
LOCKED_WRITE | LOCKED_WRITE_NOTIFIED => {
// A thread is concurrently calling `register`. This shouldn't
// happen as it doesn't really make much sense, but it isn't
// unsafe per se. Since two threads are concurrently trying to
// update the task, it's undefined which one "wins" (no ordering
// guarantees), so we can just do nothing.
NOTIFYING => {
// Currently in the process of notifying the task, i.e.,
// `notify` is currently being called on the old task handle.
// So, we call notify on the new task handle
task.notify();
}
state => {
debug_assert!(state != LOCKED_WRITE, "unexpected state LOCKED_WRITE");
debug_assert!(state != LOCKED_WRITE_NOTIFIED, "unexpected state LOCKED_WRITE_NOTIFIED");
// Currently in a read locked state, this implies that `notify`
// is currently being called on the old task handle. So, we call
// notify on the new task handle
task.notify();
// In this case, a concurrent thread is holding the
// "registering" lock. This probably indicates a bug in the
// caller's code as racing to call `register` doesn't make much
// sense.
//
// We just want to maintain memory safety. It is ok to drop the
// call to `register`.
debug_assert!(
state == REGISTERING ||
state == REGISTERING | NOTIFYING);
}
}
}
@@ -127,49 +241,33 @@ impl AtomicTask {
///
/// If `register` has not been called yet, then this does nothing.
pub fn notify(&self) {
let mut curr = WAITING;
// AcqRel ordering is used in order to acquire the value of the `task`
// cell as well as to establish a `release` ordering with whatever
// memory the `AtomicTask` is associated with.
match self.state.fetch_or(NOTIFYING, AcqRel) {
WAITING => {
// The notifying lock has been acquired.
let task = unsafe { (*self.task.get()).take() };
loop {
if curr == LOCKED_WRITE {
// Transition the state to LOCKED_NOTIFIED
let actual = self.state.compare_and_swap(LOCKED_WRITE, LOCKED_WRITE_NOTIFIED, Release);
// Release the lock
self.state.fetch_and(!NOTIFYING, Release);
if curr == actual {
// Success, return
return;
if let Some(task) = task {
task.notify();
}
// update current state variable and try again
curr = actual;
} else if curr == LOCKED_WRITE_NOTIFIED {
// Currently in `LOCKED_WRITE_NOTIFIED` state, nothing else to do.
return;
} else {
// Currently in a LOCKED_READ state, so attempt to increment the
// lock count.
let actual = self.state.compare_and_swap(curr, curr + 1, Acquire);
// Locked acquired
if actual == curr {
// Notify the task
unsafe {
if let Some(ref task) = *self.task.get() {
task.notify();
}
}
// Release the lock
self.state.fetch_sub(1, Release);
// Done
return;
}
// update current state variable and try again
curr = actual;
}
state => {
// There is a concurrent thread currently updating the
// associated task.
//
// Nothing more to do as the `NOTIFYING` bit has been set. It
// doesn't matter if there are concurrent registering threads or
// not.
//
debug_assert!(
state == REGISTERING ||
state == REGISTERING | NOTIFYING ||
state == NOTIFYING);
}
}
}
+4 -3
View File
@@ -1,7 +1,7 @@
use {Reactor, Handle};
use {Reactor, Handle, Task};
use atomic_task::AtomicTask;
use futures::{Future, Async, Poll};
use futures::{Future, Async, Poll, task};
use std::io;
use std::thread;
@@ -136,7 +136,8 @@ impl Future for Shutdown {
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
self.inner.shared.shutdown_task.register();
let task = Task::Futures1(task::current());
self.inner.shared.shutdown_task.register_task(task);
if !self.inner.is_shutdown() {
return Ok(Async::NotReady);
+60 -8
View File
@@ -27,7 +27,7 @@
//! [`PollEvented`]: struct.PollEvented.html
//! [reactor module]: https://docs.rs/tokio/0.1/tokio/reactor/index.html
#![doc(html_root_url = "https://docs.rs/tokio-reactor/0.1.0")]
#![doc(html_root_url = "https://docs.rs/tokio-reactor/0.1.1")]
#![deny(missing_docs, warnings, missing_debug_implementations)]
#[macro_use]
@@ -39,8 +39,11 @@ extern crate slab;
extern crate tokio_executor;
extern crate tokio_io;
pub(crate) mod background;
#[cfg(feature = "unstable-futures")]
extern crate futures2;
mod atomic_task;
pub(crate) mod background;
mod poll_evented;
mod registration;
@@ -69,7 +72,6 @@ use std::time::{Duration, Instant};
use log::Level;
use mio::event::Evented;
use slab::Slab;
use futures::task::Task;
/// The core reactor, or event loop.
///
@@ -118,6 +120,9 @@ struct Inner {
/// The underlying system event queue.
io: mio::Poll,
/// ABA guard counter
next_aba_guard: AtomicUsize,
/// Dispatch slabs for I/O and futures events
io_dispatch: RwLock<Slab<ScheduledIo>>,
@@ -126,6 +131,7 @@ struct Inner {
}
struct ScheduledIo {
aba_guard: usize,
readiness: AtomicUsize,
reader: AtomicTask,
writer: AtomicTask,
@@ -143,11 +149,11 @@ static HANDLE_FALLBACK: AtomicUsize = ATOMIC_USIZE_INIT;
/// Tracks the reactor for the current execution context.
thread_local!(static CURRENT_REACTOR: RefCell<Option<Handle>> = RefCell::new(None));
const TOKEN_WAKEUP: mio::Token = mio::Token(0);
const TOKEN_START: usize = 1;
const TOKEN_SHIFT: usize = 22;
// Kind of arbitrary, but this reserves some token space for later usage.
const MAX_SOURCES: usize = usize::MAX >> 4;
const MAX_SOURCES: usize = (1 << TOKEN_SHIFT) - 1;
const TOKEN_WAKEUP: mio::Token = mio::Token(MAX_SOURCES);
fn _assert_kinds() {
fn _assert<T: Send + Sync>() {}
@@ -155,6 +161,14 @@ fn _assert_kinds() {
_assert::<Handle>();
}
/// A wakeup handle for a task, which may be either a futures 0.1 or 0.2 task
#[derive(Debug, Clone)]
pub(crate) enum Task {
Futures1(futures::task::Task),
#[cfg(feature = "unstable-futures")]
Futures2(futures2::task::Waker),
}
// ===== impl Reactor =====
/// Set the default reactor for the duration of the closure
@@ -211,6 +225,7 @@ impl Reactor {
_wakeup_registration: wakeup_pair.0,
inner: Arc::new(Inner {
io: io,
next_aba_guard: AtomicUsize::new(0),
io_dispatch: RwLock::new(Slab::with_capacity(1)),
wakeup: wakeup_pair.1,
}),
@@ -348,10 +363,16 @@ impl Reactor {
}
fn dispatch(&self, token: mio::Token, ready: mio::Ready) {
let token = usize::from(token) - TOKEN_START;
let aba_guard = token.0 & !MAX_SOURCES;
let token = token.0 & MAX_SOURCES;
let io_dispatch = self.inner.io_dispatch.read().unwrap();
if let Some(io) = io_dispatch.get(token) {
if aba_guard != io.aba_guard {
return;
}
io.readiness.fetch_or(ready.as_usize(), Relaxed);
if ready.is_writable() || platform::is_hup(&ready) {
@@ -535,6 +556,9 @@ impl Inner {
fn add_source(&self, source: &Evented)
-> io::Result<usize>
{
// Get an ABA guard value
let aba_guard = self.next_aba_guard.fetch_add(1 << TOKEN_SHIFT, Relaxed);
let mut io_dispatch = self.io_dispatch.write().unwrap();
if io_dispatch.len() == MAX_SOURCES {
@@ -544,13 +568,14 @@ impl Inner {
// Acquire a write lock
let key = io_dispatch.insert(ScheduledIo {
aba_guard,
readiness: AtomicUsize::new(0),
reader: AtomicTask::new(),
writer: AtomicTask::new(),
});
try!(self.io.register(source,
mio::Token(TOKEN_START + key),
mio::Token(aba_guard | key),
mio::Ready::all(),
mio::PollOpt::edge()));
@@ -611,6 +636,17 @@ impl Direction {
}
}
impl Task {
fn notify(&self) {
match *self {
Task::Futures1(ref task) => task.notify(),
#[cfg(feature = "unstable-futures")]
Task::Futures2(ref waker) => waker.wake(),
}
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
mod platform {
use mio::Ready;
@@ -637,3 +673,19 @@ mod platform {
false
}
}
#[cfg(feature = "unstable-futures")]
fn lift_async<T>(old: futures::Async<T>) -> futures2::Async<T> {
match old {
futures::Async::Ready(x) => futures2::Async::Ready(x),
futures::Async::NotReady => futures2::Async::Pending,
}
}
#[cfg(feature = "unstable-futures")]
fn lower_async<T>(new: futures2::Async<T>) -> futures::Async<T> {
match new {
futures2::Async::Ready(x) => futures::Async::Ready(x),
futures2::Async::Pending => futures::Async::NotReady,
}
}
+209 -12
View File
@@ -5,6 +5,9 @@ use mio;
use mio::event::Evented;
use tokio_io::{AsyncRead, AsyncWrite};
#[cfg(feature = "unstable-futures")]
use futures2;
use std::fmt;
use std::io::{self, Read, Write};
use std::sync::atomic::AtomicUsize;
@@ -99,7 +102,7 @@ struct Inner {
// ===== impl PollEvented =====
macro_rules! poll_ready {
($me:expr, $mask:expr, $cache:ident, $poll:ident, $take:ident) => {{
($me:expr, $mask:expr, $cache:ident, $take:ident, $poll:expr) => {{
$me.register()?;
// Load cached & encoded readiness.
@@ -114,7 +117,7 @@ macro_rules! poll_ready {
// stream. This happens in a loop to ensure that the stream gets
// drained.
loop {
let ready = try_ready!($me.inner.registration.$poll());
let ready = try_ready!($poll);
cached |= ready.as_usize();
// Update the cache store
@@ -210,7 +213,23 @@ where E: Evented
/// * called from outside of a task context.
pub fn poll_read_ready(&self, mask: mio::Ready) -> Poll<mio::Ready, io::Error> {
assert!(!mask.is_writable(), "cannot poll for write readiness");
poll_ready!(self, mask, read_readiness, poll_read_ready, take_read_ready)
poll_ready!(
self, mask, read_readiness, take_read_ready,
self.inner.registration.poll_read_ready()
)
}
/// Like `poll_read_ready` but compatible with futures 0.2.
#[cfg(feature = "unstable-futures")]
pub fn poll_read_ready2(&self, cx: &mut futures2::task::Context, mask: mio::Ready)
-> futures2::Poll<mio::Ready, io::Error>
{
assert!(!mask.is_writable(), "cannot poll for write readiness");
let mut res = || poll_ready!(
self, mask, read_readiness, take_read_ready,
self.inner.registration.poll_read_ready2(cx).map(::lower_async)
);
res().map(::lift_async)
}
/// Clears the I/O resource's read readiness state and registers the current
@@ -243,6 +262,25 @@ where E: Evented
Ok(())
}
/// Like `clear_read_ready` but compatible with futures 0.2.
#[cfg(feature = "unstable-futures")]
pub fn clear_read_ready2(&self, cx: &mut futures2::task::Context, ready: mio::Ready)
-> io::Result<()>
{
// Cannot clear write readiness
assert!(!ready.is_writable(), "cannot clear write readiness");
assert!(!::platform::is_hup(&ready), "cannot clear HUP readiness");
self.inner.read_readiness.fetch_and(!ready.as_usize(), Relaxed);
if self.poll_read_ready2(cx, ready)?.is_ready() {
// Notify the current task
cx.waker().wake()
}
Ok(())
}
/// Check the I/O resource's write readiness state.
///
/// This always checks for writable readiness and also checks for HUP
@@ -263,13 +301,31 @@ where E: Evented
/// * `ready` contains bits besides `writable` and `hup`.
/// * called from outside of a task context.
pub fn poll_write_ready(&self) -> Poll<mio::Ready, io::Error> {
poll_ready!(self,
mio::Ready::writable(),
write_readiness,
poll_write_ready,
take_write_ready)
poll_ready!(
self,
mio::Ready::writable(),
write_readiness,
take_write_ready,
self.inner.registration.poll_write_ready()
)
}
/// Like `poll_write_ready` but compatible with futures 0.2.
#[cfg(feature = "unstable-futures")]
pub fn poll_write_ready2(&self, cx: &mut futures2::task::Context)
-> futures2::Poll<mio::Ready, io::Error>
{
let mut res = || poll_ready!(
self,
mio::Ready::writable(),
write_readiness,
take_write_ready,
self.inner.registration.poll_write_ready2(cx).map(::lower_async)
);
res().map(::lift_async)
}
/// Resets the I/O resource's write readiness state and registers the current
/// task to be notified once a write readiness event is received.
///
@@ -285,7 +341,7 @@ where E: Evented
pub fn clear_write_ready(&self) -> io::Result<()> {
let ready = mio::Ready::writable();
self.inner.read_readiness.fetch_and(!ready.as_usize(), Relaxed);
self.inner.write_readiness.fetch_and(!ready.as_usize(), Relaxed);
if self.poll_write_ready()?.is_ready() {
// Notify the current task
@@ -295,6 +351,21 @@ where E: Evented
Ok(())
}
/// Like `clear_write_ready`, but compatible with futures 0.2.
#[cfg(feature = "unstable-futures")]
pub fn clear_write_ready2(&self, cx: &mut futures2::task::Context) -> io::Result<()> {
let ready = mio::Ready::writable();
self.inner.write_readiness.fetch_and(!ready.as_usize(), Relaxed);
if self.poll_write_ready2(cx)?.is_ready() {
// Notify the current task
cx.waker().wake()
}
Ok(())
}
/// Ensure that the I/O resource is registered with the reactor.
fn register(&self) -> io::Result<()> {
self.inner.registration.register(self.io.as_ref().unwrap())?;
@@ -322,6 +393,28 @@ where E: Evented + Read,
}
}
#[cfg(feature = "unstable-futures")]
impl<E> futures2::io::AsyncRead for PollEvented<E>
where E: Evented, E: Read,
{
fn poll_read(&mut self, cx: &mut futures2::task::Context, buf: &mut [u8])
-> futures2::Poll<usize, io::Error>
{
if let futures2::Async::Pending = self.poll_read_ready2(cx, mio::Ready::readable())? {
return Ok(futures2::Async::Pending);
}
match self.get_mut().read(buf) {
Ok(n) => Ok(futures2::Async::Ready(n)),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.clear_read_ready2(cx, mio::Ready::readable())?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
}
impl<E> Write for PollEvented<E>
where E: Evented + Write,
{
@@ -354,6 +447,48 @@ where E: Evented + Write,
}
}
#[cfg(feature = "unstable-futures")]
impl<E> futures2::io::AsyncWrite for PollEvented<E>
where E: Evented, E: Write,
{
fn poll_write(&mut self, cx: &mut futures2::task::Context, buf: &[u8])
-> futures2::Poll<usize, io::Error>
{
if let futures2::Async::Pending = self.poll_write_ready2(cx)? {
return Ok(futures2::Async::Pending);
}
match self.get_mut().write(buf) {
Ok(n) => Ok(futures2::Async::Ready(n)),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.clear_write_ready2(cx)?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
fn poll_flush(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
if let futures2::Async::Pending = self.poll_write_ready2(cx)? {
return Ok(futures2::Async::Pending);
}
match self.get_mut().flush() {
Ok(_) => Ok(futures2::Async::Ready(())),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.clear_write_ready2(cx)?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
fn poll_close(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_flush(self, cx)
}
}
impl<E> AsyncRead for PollEvented<E>
where E: Evented + Read,
{
@@ -387,6 +522,28 @@ where E: Evented, &'a E: Read,
}
}
#[cfg(feature = "unstable-futures")]
impl<'a, E> futures2::io::AsyncRead for &'a PollEvented<E>
where E: Evented, &'a E: Read,
{
fn poll_read(&mut self, cx: &mut futures2::task::Context, buf: &mut [u8])
-> futures2::Poll<usize, io::Error>
{
if let futures2::Async::Pending = self.poll_read_ready2(cx, mio::Ready::readable())? {
return Ok(futures2::Async::Pending);
}
match self.get_ref().read(buf) {
Ok(n) => Ok(futures2::Async::Ready(n)),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.clear_read_ready2(cx, mio::Ready::readable())?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
}
impl<'a, E> Write for &'a PollEvented<E>
where E: Evented, &'a E: Write,
{
@@ -419,6 +576,47 @@ where E: Evented, &'a E: Write,
}
}
#[cfg(feature = "unstable-futures")]
impl<'a, E> futures2::io::AsyncWrite for &'a PollEvented<E>
where E: Evented, &'a E: Write,
{
fn poll_write(&mut self, cx: &mut futures2::task::Context, buf: &[u8])
-> futures2::Poll<usize, io::Error>
{
if let futures2::Async::Pending = self.poll_write_ready2(cx)? {
return Ok(futures2::Async::Pending);
}
match self.get_ref().write(buf) {
Ok(n) => Ok(futures2::Async::Ready(n)),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.clear_write_ready2(cx)?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
fn poll_flush(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
if let futures2::Async::Pending = self.poll_write_ready2(cx)? {
return Ok(futures2::Async::Pending);
}
match self.get_ref().flush() {
Ok(_) => Ok(futures2::Async::Ready(())),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.clear_write_ready2(cx)?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
fn poll_close(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_flush(self, cx)
}
}
impl<'a, E> AsyncRead for &'a PollEvented<E>
where E: Evented, &'a E: Read,
{
@@ -439,7 +637,6 @@ fn is_wouldblock<T>(r: &io::Result<T>) -> bool {
}
}
impl<E: Evented + fmt::Debug> fmt::Debug for PollEvented<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("PollEvented")
@@ -450,9 +647,9 @@ impl<E: Evented + fmt::Debug> fmt::Debug for PollEvented<E> {
impl<E: Evented> Drop for PollEvented<E> {
fn drop(&mut self) {
if let Some(io) = self.io.as_ref() {
if let Some(io) = self.io.take() {
// Ignore errors
let _ = self.inner.registration.deregister(io);
let _ = self.inner.registration.deregister(&io);
}
}
}
+78 -57
View File
@@ -1,10 +1,12 @@
use {Handle, Direction};
use {Handle, Direction, Task};
use futures::{Async, Poll};
use futures::task::{self, Task};
use futures::{Async, Poll, task};
use mio::{self, Evented};
use std::{io, mem, usize};
#[cfg(feature = "unstable-futures")]
use futures2;
use std::{io, ptr, usize};
use std::cell::UnsafeCell;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
@@ -66,7 +68,7 @@ struct Inner {
struct Node {
direction: Direction,
task: Task,
next: Option<Box<Node>>,
next: *mut Node,
}
/// Initial state. The handle is not set and the registration is idle.
@@ -195,40 +197,35 @@ impl Registration {
// are pending readiness notifications.
let actual = self.state.swap(READY, SeqCst);
// Consume the stack of nodes.
let ptr = actual & !LIFECYCLE_MASK;
// Consume the stack of nodes
if ptr != 0 {
let mut read = false;
let mut write = false;
let mut curr = unsafe { Box::from_raw(ptr as *mut Node) };
let mut read = false;
let mut write = false;
let mut ptr = (actual & !LIFECYCLE_MASK) as *mut Node;
let inner = unsafe { (*self.inner.get()).as_ref().unwrap() };
let inner = unsafe { (*self.inner.get()).as_ref().unwrap() };
loop {
let node = *curr;
let Node {
direction,
task,
next,
} = node;
while !ptr.is_null() {
let node = unsafe { Box::from_raw(ptr) };
let node = *node;
let Node {
direction,
task,
next,
} = node;
let flag = match direction {
Direction::Read => &mut read,
Direction::Write => &mut write,
};
let flag = match direction {
Direction::Read => &mut read,
Direction::Write => &mut write,
};
if !*flag {
*flag = true;
if !*flag {
*flag = true;
inner.register(direction, task);
}
match next {
Some(next) => curr = next,
None => break,
}
inner.register(direction, task);
}
ptr = next;
}
return res.map(|_| true);
@@ -271,13 +268,26 @@ impl Registration {
///
/// This function will panic if called from outside of a task context.
pub fn poll_read_ready(&self) -> Poll<mio::Ready, io::Error> {
self.poll_ready(Direction::Read, true)
self.poll_ready(Direction::Read, true, || Task::Futures1(task::current()))
.map(|v| match v {
Some(v) => Async::Ready(v),
_ => Async::NotReady,
})
}
/// Like `poll_ready_ready`, but compatible with futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn poll_read_ready2(&self, cx: &mut futures2::task::Context)
-> futures2::Poll<mio::Ready, io::Error>
{
use futures2::Async as Async2;
self.poll_ready(Direction::Read, true, || Task::Futures2(cx.waker().clone()))
.map(|v| match v {
Some(v) => Async2::Ready(v),
_ => Async2::Pending,
})
}
/// Consume any pending read readiness event.
///
/// This function is identical to [`poll_read_ready`] **except** that it
@@ -286,7 +296,7 @@ impl Registration {
///
/// [`poll_read_ready`]: #method.poll_read_ready
pub fn take_read_ready(&self) -> io::Result<Option<mio::Ready>> {
self.poll_ready(Direction::Read, false)
self.poll_ready(Direction::Read, false, || panic!())
}
@@ -323,13 +333,26 @@ impl Registration {
///
/// This function will panic if called from outside of a task context.
pub fn poll_write_ready(&self) -> Poll<mio::Ready, io::Error> {
self.poll_ready(Direction::Write, true)
self.poll_ready(Direction::Write, true, || Task::Futures1(task::current()))
.map(|v| match v {
Some(v) => Async::Ready(v),
_ => Async::NotReady,
})
}
/// Like `poll_write_ready`, but compatible with futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn poll_write_ready2(&self, cx: &mut futures2::task::Context)
-> futures2::Poll<mio::Ready, io::Error>
{
use futures2::Async as Async2;
self.poll_ready(Direction::Write, true, || Task::Futures2(cx.waker().clone()))
.map(|v| match v {
Some(v) => Async2::Ready(v),
_ => Async2::Pending,
})
}
/// Consume any pending write readiness event.
///
/// This function is identical to [`poll_write_ready`] **except** that it
@@ -338,11 +361,12 @@ impl Registration {
///
/// [`poll_write_ready`]: #method.poll_write_ready
pub fn take_write_ready(&self) -> io::Result<Option<mio::Ready>> {
self.poll_ready(Direction::Write, false)
self.poll_ready(Direction::Write, false, || unreachable!())
}
fn poll_ready(&self, direction: Direction, notify: bool)
fn poll_ready<F>(&self, direction: Direction, notify: bool, task: F)
-> io::Result<Option<mio::Ready>>
where F: Fn() -> Task
{
let mut state = self.state.load(SeqCst);
@@ -357,43 +381,37 @@ impl Registration {
}
READY => {
let inner = unsafe { (*self.inner.get()).as_ref().unwrap() };
return inner.poll_ready(direction, notify);
return inner.poll_ready(direction, notify, task);
}
_ => {
LOCKED => {
if !notify {
// Skip the notification tracking junk.
return Ok(None);
}
let ptr = state & !LIFECYCLE_MASK;
let next_ptr = (state & !LIFECYCLE_MASK) as *mut Node;
let task = task();
// Get the node
let mut n = node.take().unwrap_or_else(|| {
Box::new(Node {
direction,
task: task::current(),
next: None,
task: task,
next: ptr::null_mut(),
})
});
n.next = if ptr == 0 {
None
} else {
// Great care must be taken of the CAS fails
Some(unsafe { Box::from_raw(ptr as *mut Node) })
};
n.next = next_ptr;
let ptr = Box::into_raw(n);
let next = ptr as usize | (state & LIFECYCLE_MASK);
let node_ptr = Box::into_raw(n);
let next = node_ptr as usize | (state & LIFECYCLE_MASK);
let actual = self.state.compare_and_swap(state, next, SeqCst);
if actual != state {
// Back out of the node boxing
let mut n = unsafe { Box::from_raw(ptr) };
// We don't really own this
mem::forget(n.next.take());
let n = unsafe { Box::from_raw(node_ptr) };
// Save this for next loop
node = Some(n);
@@ -404,6 +422,7 @@ impl Registration {
return Ok(None);
}
_ => unreachable!(),
}
}
}
@@ -472,8 +491,9 @@ impl Inner {
inner.deregister_source(io)
}
fn poll_ready(&self, direction: Direction, notify: bool)
fn poll_ready<F>(&self, direction: Direction, notify: bool, task: F)
-> io::Result<Option<mio::Ready>>
where F: FnOnce() -> Task
{
if self.token == ERROR {
return Err(io::Error::new(io::ErrorKind::Other, "failed to associate with reactor"));
@@ -502,10 +522,11 @@ impl Inner {
sched.readiness.fetch_and(!mask_no_hup, SeqCst));
if ready.is_empty() && notify {
let task = task();
// Update the task info
match direction {
Direction::Read => sched.reader.register(),
Direction::Write => sched.writer.register(),
Direction::Read => sched.reader.register_task(task),
Direction::Write => sched.writer.register_task(task),
}
// Try again
+3
View File
@@ -0,0 +1,3 @@
# 0.1.0 (unreleased)
* Initial release
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "tokio-tcp"
# When releasing to crates.io:
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.1.0"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-tcp/0.1"
description = """
TCP bindings for tokio.
"""
categories = ["asynchronous"]
[dependencies]
tokio-io = { version = "0.1.6", path = "../tokio-io" }
tokio-reactor = { version = "0.1.1", path = "../tokio-reactor" }
bytes = "0.4"
mio = "0.6.14"
iovec = "0.1"
futures = "0.1.19"
# Futures 0.2 integration
futures2 = { version = "0.1", path = "../futures2", optional = true }
[dev-dependencies]
env_logger = { version = "0.4", default-features = false }
[features]
unstable-futures = ["futures2"]
default = []
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2018 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+15
View File
@@ -0,0 +1,15 @@
# tokio-tcp
TCP bindings for `tokio`.
[Documentation](https://tokio-rs.github.io/tokio/tokio_tcp/)
## License
This project is licensed under the [MIT license](./LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
@@ -1,10 +1,13 @@
use net::tcp::TcpListener;
use net::tcp::TcpStream;
use super::TcpListener;
use super::TcpStream;
use std::io;
use futures::stream::Stream;
use futures::{Poll, Async};
#[cfg(feature = "unstable-futures")]
use futures2;
/// Stream returned by the `TcpListener::incoming` function representing the
/// stream of sockets received from a listener.
#[must_use = "streams do nothing unless polled"]
@@ -28,3 +31,15 @@ impl Stream for Incoming {
Ok(Async::Ready(Some(socket)))
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::Stream for Incoming {
type Item = TcpStream;
type Error = io::Error;
fn poll_next(&mut self, cx: &mut futures2::task::Context)
-> futures2::Poll<Option<Self::Item>, io::Error>
{
Ok(self.inner.poll_accept2(cx)?.map(|(sock, _)| Some(sock)))
}
}
+59
View File
@@ -0,0 +1,59 @@
//! TCP bindings for `tokio`.
//!
//! This module contains the TCP networking types, similar to the standard
//! library, which can be used to implement networking protocols.
//!
//! Connecting to an address, via TCP, can be done using [`TcpStream`]'s
//! [`connect`] method, which returns [`ConnectFuture`]. `ConnectFuture`
//! implements a future which returns a `TcpStream`.
//!
//! To listen on an address [`TcpListener`] can be used. `TcpListener`'s
//! [`incoming`][incoming_method] method can be used to accept new connections.
//! It return the [`Incoming`] struct, which implements a stream which returns
//! `TcpStream`s.
//!
//! [`TcpStream`]: struct.TcpStream.html
//! [`connect`]: struct.TcpStream.html#method.connect
//! [`ConnectFuture`]: struct.ConnectFuture.html
//! [`TcpListener`]: struct.TcpListener.html
//! [incoming_method]: struct.TcpListener.html#method.incoming
//! [`Incoming`]: struct.Incoming.html
#![doc(html_root_url = "https://docs.rs/tokio-tcp/0.1.0")]
#![deny(missing_docs, warnings, missing_debug_implementations)]
extern crate bytes;
#[macro_use]
extern crate futures;
extern crate iovec;
extern crate mio;
extern crate tokio_io;
extern crate tokio_reactor;
#[cfg(feature = "unstable-futures")]
extern crate futures2;
mod incoming;
mod listener;
mod stream;
pub use self::incoming::Incoming;
pub use self::listener::TcpListener;
pub use self::stream::TcpStream;
pub use self::stream::ConnectFuture;
#[cfg(feature = "unstable-futures")]
fn lift_async<T>(old: futures::Async<T>) -> futures2::Async<T> {
match old {
futures::Async::Ready(x) => futures2::Async::Ready(x),
futures::Async::NotReady => futures2::Async::Pending,
}
}
#[cfg(feature = "unstable-futures")]
fn lower_async<T>(new: futures2::Async<T>) -> futures::Async<T> {
match new {
futures2::Async::Ready(x) => futures::Async::Ready(x),
futures2::Async::Pending => futures::Async::NotReady,
}
}
@@ -1,5 +1,5 @@
use net::tcp::Incoming;
use net::tcp::TcpStream;
use super::Incoming;
use super::TcpStream;
use std::fmt;
use std::io;
@@ -7,15 +7,17 @@ use std::net::{self, SocketAddr};
use futures::{Poll, Async};
use mio;
use tokio_reactor::{Handle, PollEvented};
use reactor::{Handle, PollEvented2};
#[cfg(feature = "unstable-futures")]
use futures2;
/// An I/O object representing a TCP socket listening for incoming connections.
///
/// This object can be converted into a stream of incoming connections for
/// various forms of processing.
pub struct TcpListener {
io: PollEvented2<mio::net::TcpListener>,
io: PollEvented<mio::net::TcpListener>,
}
impl TcpListener {
@@ -64,6 +66,22 @@ impl TcpListener {
Ok((io, addr).into())
}
/// Like `poll_accept`, but for futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn poll_accept2(&mut self, cx: &mut futures2::task::Context)
-> futures2::Poll<(TcpStream, SocketAddr), io::Error>
{
let (io, addr) = match self.poll_accept_std2(cx)? {
futures2::Async::Ready(x) => x,
futures2::Async::Pending => return Ok(futures2::Async::Pending),
};
let io = mio::net::TcpStream::from_stream(io)?;
let io = TcpStream::new(io);
Ok((io, addr).into())
}
#[deprecated(since = "0.1.2", note = "use poll_accept_std instead")]
#[doc(hidden)]
pub fn accept_std(&mut self) -> io::Result<(net::TcpStream, SocketAddr)> {
@@ -76,7 +94,7 @@ impl TcpListener {
/// Attempt to accept a connection and create a new connected `TcpStream` if
/// successful.
///
/// This function is the asme as `accept` above except that it returns a
/// This function is the same as `accept` above except that it returns a
/// `std::net::TcpStream` instead of a `tokio::net::TcpStream`. This in turn
/// can then allow for the TCP stream to be assoiated with a different
/// reactor than the one this `TcpListener` is associated with.
@@ -105,6 +123,25 @@ impl TcpListener {
}
}
/// Like `poll_accept_std`, but for futures 0.2.
#[cfg(feature = "unstable-futures")]
pub fn poll_accept_std2(&mut self, cx: &mut futures2::task::Context)
-> futures2::Poll<(net::TcpStream, SocketAddr), io::Error>
{
if let futures2::Async::Pending = self.io.poll_read_ready2(cx, mio::Ready::readable())? {
return Ok(futures2::Async::Pending);
}
match self.io.get_ref().accept_std() {
Ok(pair) => Ok(pair.into()),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.clear_read_ready2(cx, mio::Ready::readable())?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
/// Create a new TCP listener from the standard library's TCP listener.
///
/// This method can be used when the `Handle::tcp_listen` method isn't
@@ -136,12 +173,12 @@ impl TcpListener {
-> io::Result<TcpListener>
{
let io = mio::net::TcpListener::from_std(listener)?;
let io = PollEvented2::new_with_handle(io, handle)?;
let io = PollEvented::new_with_handle(io, handle)?;
Ok(TcpListener { io })
}
fn new(listener: mio::net::TcpListener) -> TcpListener {
let io = PollEvented2::new(listener);
let io = PollEvented::new(listener);
TcpListener { io }
}
@@ -158,6 +195,16 @@ impl TcpListener {
///
/// This method returns an implementation of the `Stream` trait which
/// resolves to the sockets the are accepted on this listener.
///
/// # Errors
///
/// Note that accepting a connection can lead to various errors and not all of them are
/// necessarily fatal for example having too many open file descriptors or the other side
/// closing the connection while it waits in an accept queue. These would terminate the stream
/// if not handled in any way.
///
/// If aiming for production, decision what to do about them must be made. The
/// [`tk-listen`](https://crates.io/crates/tk-listen) crate might be of some help.
pub fn incoming(self) -> Incoming {
Incoming::new(self)
}
@@ -9,8 +9,10 @@ use futures::{Future, Poll, Async};
use iovec::IoVec;
use mio;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_reactor::{Handle, PollEvented};
use reactor::{Handle, PollEvented2};
#[cfg(feature = "unstable-futures")]
use futures2;
/// An I/O object representing a TCP stream connected to a remote endpoint.
///
@@ -21,7 +23,7 @@ use reactor::{Handle, PollEvented2};
/// [accepting]: struct.TcpListener.html#method.accept
/// [listener]: struct.TcpListener.html
pub struct TcpStream {
io: PollEvented2<mio::net::TcpStream>,
io: PollEvented<mio::net::TcpStream>,
}
/// Future returned by `TcpStream::connect` which will resolve to a `TcpStream`
@@ -59,7 +61,7 @@ impl TcpStream {
}
pub(crate) fn new(connected: mio::net::TcpStream) -> TcpStream {
let io = PollEvented2::new(connected);
let io = PollEvented::new(connected);
TcpStream { io }
}
@@ -73,7 +75,7 @@ impl TcpStream {
-> io::Result<TcpStream>
{
let io = mio::net::TcpStream::from_stream(stream)?;
let io = PollEvented2::new_with_handle(io, handle)?;
let io = PollEvented::new_with_handle(io, handle)?;
Ok(TcpStream { io })
}
@@ -104,7 +106,7 @@ impl TcpStream {
use self::ConnectFutureState::*;
let io = mio::net::TcpStream::connect_stream(stream, addr)
.and_then(|io| PollEvented2::new_with_handle(io, handle));
.and_then(|io| PollEvented::new_with_handle(io, handle));
let inner = match io {
Ok(io) => Waiting(TcpStream { io }),
@@ -137,6 +139,14 @@ impl TcpStream {
self.io.poll_read_ready(mask)
}
/// Like `poll_read_ready`, but compatible with futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn poll_read_ready2(&self, cx: &mut futures2::task::Context, mask: mio::Ready)
-> futures2::Poll<mio::Ready, io::Error>
{
self.io.poll_read_ready2(cx, mask)
}
/// Check the TCP stream's write readiness state.
///
/// This always checks for writable readiness and also checks for HUP
@@ -158,6 +168,14 @@ impl TcpStream {
self.io.poll_write_ready()
}
/// Like `poll_write_ready`, but compatible with futures 0.2.
#[cfg(feature = "unstable-futures")]
pub fn poll_write_ready2(&self, cx: &mut futures2::task::Context)
-> futures2::Poll<mio::Ready, io::Error>
{
self.io.poll_write_ready2(cx)
}
/// Returns the local address that this stream is bound to.
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.io.get_ref().local_addr()
@@ -208,6 +226,25 @@ impl TcpStream {
}
}
/// Like `poll_peek` but compatible with futures 0.2
#[cfg(feature = "unstable-futures")]
pub fn poll_peek2(&mut self, cx: &mut futures2::task::Context, buf: &mut [u8])
-> futures2::Poll<usize, io::Error>
{
if let futures2::Async::Pending = self.io.poll_read_ready2(cx, mio::Ready::readable())? {
return Ok(futures2::Async::Pending);
}
match self.io.get_ref().peek(buf) {
Ok(ret) => Ok(ret.into()),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.clear_read_ready2(cx, mio::Ready::readable())?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
/// Shuts down the read, write, or both halves of this connection.
///
/// This function will cause all pending and future I/O on the specified
@@ -367,6 +404,25 @@ impl AsyncRead for TcpStream {
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::io::AsyncRead for TcpStream {
fn poll_read(&mut self, cx: &mut futures2::task::Context, buf: &mut [u8])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncRead::poll_read(&mut self.io, cx, buf)
}
fn poll_vectored_read(&mut self, cx: &mut futures2::task::Context, vec: &mut [&mut IoVec])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncRead::poll_vectored_read(&mut &*self, cx, vec)
}
unsafe fn initializer(&self) -> futures2::io::Initializer {
futures2::io::Initializer::nop()
}
}
impl AsyncWrite for TcpStream {
fn shutdown(&mut self) -> Poll<(), io::Error> {
<&TcpStream>::shutdown(&mut &*self)
@@ -377,6 +433,29 @@ impl AsyncWrite for TcpStream {
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::io::AsyncWrite for TcpStream {
fn poll_write(&mut self, cx: &mut futures2::task::Context, buf: &[u8])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncWrite::poll_write(&mut self.io, cx, buf)
}
fn poll_vectored_write(&mut self, cx: &mut futures2::task::Context, vec: &[&IoVec])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncWrite::poll_vectored_write(&mut &*self, cx, vec)
}
fn poll_flush(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_flush(&mut self.io, cx)
}
fn poll_close(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_close(&mut self.io, cx)
}
}
// ===== impl Read / Write for &'a =====
impl<'a> Read for &'a TcpStream {
@@ -449,6 +528,40 @@ impl<'a> AsyncRead for &'a TcpStream {
}
}
#[cfg(feature = "unstable-futures")]
impl<'a> futures2::io::AsyncRead for &'a TcpStream {
fn poll_read(&mut self, cx: &mut futures2::task::Context, buf: &mut [u8])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncRead::poll_read(&mut &self.io, cx, buf)
}
fn poll_vectored_read(&mut self, cx: &mut futures2::task::Context, vec: &mut [&mut IoVec])
-> futures2::Poll<usize, io::Error>
{
if let futures2::Async::Pending = self.io.poll_read_ready2(cx, mio::Ready::readable())? {
return Ok(futures2::Async::Pending)
}
let r = self.io.get_ref().read_bufs(vec);
match r {
Ok(n) => {
Ok(futures2::Async::Ready(n))
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.clear_read_ready2(cx, mio::Ready::readable())?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
unsafe fn initializer(&self) -> futures2::io::Initializer {
futures2::io::Initializer::nop()
}
}
impl<'a> AsyncWrite for &'a TcpStream {
fn shutdown(&mut self) -> Poll<(), io::Error> {
Ok(().into())
@@ -483,13 +596,50 @@ impl<'a> AsyncWrite for &'a TcpStream {
}
}
#[cfg(feature = "unstable-futures")]
impl<'a> futures2::io::AsyncWrite for &'a TcpStream {
fn poll_write(&mut self, cx: &mut futures2::task::Context, buf: &[u8])
-> futures2::Poll<usize, io::Error>
{
futures2::io::AsyncWrite::poll_write(&mut &self.io, cx, buf)
}
fn poll_vectored_write(&mut self, cx: &mut futures2::task::Context, vec: &[&IoVec])
-> futures2::Poll<usize, io::Error>
{
if let futures2::Async::Pending = self.io.poll_write_ready2(cx)? {
return Ok(futures2::Async::Pending)
}
let r = self.io.get_ref().write_bufs(vec);
match r {
Ok(n) => {
Ok(futures2::Async::Ready(n))
}
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.clear_write_ready()?;
Ok(futures2::Async::Pending)
}
Err(e) => Err(e),
}
}
fn poll_flush(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_flush(&mut &self.io, cx)
}
fn poll_close(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), io::Error> {
futures2::io::AsyncWrite::poll_close(&mut &self.io, cx)
}
}
impl fmt::Debug for TcpStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.io.get_ref().fmt(f)
}
}
impl Future for ConnectFuture {
type Item = TcpStream;
type Error = io::Error;
@@ -499,11 +649,20 @@ impl Future for ConnectFuture {
}
}
impl Future for ConnectFutureState {
#[cfg(feature = "unstable-futures")]
impl futures2::Future for ConnectFuture {
type Item = TcpStream;
type Error = io::Error;
fn poll(&mut self) -> Poll<TcpStream, io::Error> {
fn poll(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<TcpStream, io::Error> {
futures2::Future::poll(&mut self.inner, cx)
}
}
impl ConnectFutureState {
fn poll_inner<F>(&mut self, f: F) -> Poll<TcpStream, io::Error>
where F: FnOnce(&mut PollEvented<mio::net::TcpStream>) -> Poll<mio::Ready, io::Error>
{
{
let stream = match *self {
ConnectFutureState::Waiting(ref mut s) => s,
@@ -523,7 +682,7 @@ impl Future for ConnectFutureState {
// actually hit an error or not.
//
// If all that succeeded then we ship everything on up.
if let Async::NotReady = stream.io.poll_write_ready()? {
if let Async::NotReady = f(&mut stream.io)? {
return Ok(Async::NotReady)
}
@@ -531,6 +690,7 @@ impl Future for ConnectFutureState {
return Err(e)
}
}
match mem::replace(self, ConnectFutureState::Empty) {
ConnectFutureState::Waiting(stream) => Ok(Async::Ready(stream)),
_ => panic!(),
@@ -538,6 +698,26 @@ impl Future for ConnectFutureState {
}
}
impl Future for ConnectFutureState {
type Item = TcpStream;
type Error = io::Error;
fn poll(&mut self) -> Poll<TcpStream, io::Error> {
self.poll_inner(|io| io.poll_write_ready())
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::Future for ConnectFutureState {
type Item = TcpStream;
type Error = io::Error;
fn poll(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<TcpStream, io::Error> {
self.poll_inner(|io| io.poll_write_ready2(cx).map(::lower_async))
.map(::lift_async)
}
}
#[cfg(all(unix, not(target_os = "fuchsia")))]
mod sys {
use std::os::unix::prelude::*;
+2 -2
View File
@@ -1,5 +1,5 @@
extern crate futures;
extern crate tokio;
extern crate tokio_tcp;
extern crate tokio_io;
use std::net::TcpStream;
@@ -9,7 +9,7 @@ use std::io::{Write, Read};
use futures::Future;
use futures::stream::Stream;
use tokio_io::io::read_to_end;
use tokio::net::TcpListener;
use tokio_tcp::TcpListener;
macro_rules! t {
($e:expr) => (match $e {
+2 -2
View File
@@ -1,6 +1,6 @@
extern crate env_logger;
extern crate futures;
extern crate tokio;
extern crate tokio_tcp;
extern crate tokio_io;
use std::io::{Read, Write};
@@ -9,7 +9,7 @@ use std::thread;
use futures::Future;
use futures::stream::Stream;
use tokio::net::TcpListener;
use tokio_tcp::TcpListener;
use tokio_io::AsyncRead;
use tokio_io::io::copy;
+2 -2
View File
@@ -1,5 +1,5 @@
extern crate futures;
extern crate tokio;
extern crate tokio_tcp;
extern crate tokio_io;
use std::net::TcpStream;
@@ -9,7 +9,7 @@ use std::io::{Write, Read};
use futures::Future;
use futures::stream::Stream;
use tokio_io::io::read_to_end;
use tokio::net::TcpListener;
use tokio_tcp::TcpListener;
macro_rules! t {
($e:expr) => (match $e {
@@ -1,6 +1,6 @@
extern crate env_logger;
extern crate futures;
extern crate tokio;
extern crate tokio_tcp;
extern crate tokio_io;
use std::io::{Read, Write};
@@ -11,7 +11,7 @@ use futures::Future;
use futures::stream::Stream;
use tokio_io::io::copy;
use tokio_io::AsyncRead;
use tokio::net::TcpListener;
use tokio_tcp::TcpListener;
macro_rules! t {
($e:expr) => (match $e {
+8 -6
View File
@@ -1,13 +1,14 @@
extern crate env_logger;
extern crate tokio;
extern crate tokio_io;
extern crate tokio_tcp;
extern crate mio;
extern crate futures;
use std::{net, thread};
use std::sync::mpsc::channel;
use tokio::net::{TcpListener, TcpStream};
use tokio::prelude::*;
use futures::{Future, Stream};
use tokio_tcp::{TcpListener, TcpStream};
macro_rules! t {
@@ -82,13 +83,14 @@ fn accept2() {
#[cfg(unix)]
mod unix {
use tokio::net::TcpStream;
use tokio::prelude::*;
use tokio_tcp::TcpStream;
use env_logger;
use futures::future;
use futures::{Future, future};
use mio::unix::UnixReady;
use tokio_io::AsyncRead;
use std::io::Write;
use std::{net, thread};
use std::time::Duration;
+5
View File
@@ -1,3 +1,8 @@
# 0.1.1 (March 22, 2018)
* Handle futures that panic on the threadpool.
* Optionally support futures 0.2.
# 0.1.0 (March 09, 2018)
* Initial release
+12 -5
View File
@@ -1,10 +1,10 @@
[package]
name = "tokio-threadpool"
version = "0.1.0"
version = "0.1.1"
documentation = "https://docs.rs/tokio-threadpool"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://github.com/tokio-rs/tokio"
license = "MIT/Apache-2.0"
license = "MIT"
authors = ["Carl Lerche <[email protected]>"]
description = """
A task scheduler backed by a work-stealing thread pool.
@@ -13,14 +13,21 @@ keywords = ["futures", "tokio"]
categories = ["concurrency", "asynchronous"]
[dependencies]
tokio-executor = { version = "0.1.0", path = "../tokio-executor" }
futures = "0.1.18"
tokio-executor = { version = "0.1.1", path = "../tokio-executor" }
futures = "0.1.19"
crossbeam-deque = "0.3"
num_cpus = "1.2"
rand = "0.3"
rand = "0.4"
log = "0.3"
# Futures 0.2 integration
futures2 = { version = "0.1", path = "../futures2", optional = true }
[dev-dependencies]
tokio-timer = "0.1"
env_logger = "0.4"
futures-cpupool = "0.1.7"
[features]
unstable-futures = ["futures2", "tokio-executor/unstable-futures"]
default = []
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2018 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+3 -10
View File
@@ -45,17 +45,10 @@ pub fn main() {
## License
This project is licensed under either of
* Apache License, Version 2.0, ([LICENSE-APACHE](../LICENSE-APACHE) or
http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](../LICENSE-MIT) or
http://opensource.org/licenses/MIT)
at your option.
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, as defined in the Apache-2.0 license, shall be
dual licensed as above, without any additional terms or conditions.
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
+1 -1
View File
@@ -87,7 +87,7 @@ mod threadpool {
// benchmark quickly but results in poor runtime characteristics for a thread
// pool.
//
// See alexcrichton/futures-rs#617
// See rust-lang-nursery/futures-rs#617
//
mod cpupool {
use futures::{task, Async};
+173 -12
View File
@@ -1,6 +1,6 @@
//! A work-stealing based thread pool for executing futures.
#![doc(html_root_url = "https://docs.rs/tokio-threadpool/0.1.0")]
#![doc(html_root_url = "https://docs.rs/tokio-threadpool/0.1.1")]
#![deny(warnings, missing_docs, missing_debug_implementations)]
extern crate tokio_executor;
@@ -12,6 +12,9 @@ extern crate rand;
#[macro_use]
extern crate log;
#[cfg(feature = "unstable-futures")]
extern crate futures2;
mod task;
use tokio_executor::{Enter, SpawnError};
@@ -33,6 +36,14 @@ use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release, Relaxed};
use std::time::{Instant, Duration};
#[derive(Debug)]
struct ShutdownTask {
task1: AtomicTask,
#[cfg(feature = "unstable-futures")]
task2: futures2::task::AtomicWaker,
}
/// Work-stealing based thread pool for executing futures.
///
/// If a `ThreadPool` instance is dropped without explicitly being shutdown,
@@ -160,7 +171,7 @@ struct Inner {
workers: Box<[WorkerEntry]>,
// Task notified when the worker shuts down
shutdown_task: AtomicTask,
shutdown_task: ShutdownTask,
// Configuration
config: Config,
@@ -180,6 +191,12 @@ struct Notifier {
inner: Weak<Inner>,
}
#[cfg(feature = "unstable-futures")]
struct Futures2Wake {
notifier: Arc<Notifier>,
id: usize,
}
/// ThreadPool state.
///
/// The two least significant bits are the shutdown flags. (0 for active, 1 for
@@ -532,7 +549,11 @@ impl Builder {
num_workers: AtomicUsize::new(self.pool_size),
next_thread_id: AtomicUsize::new(0),
workers: workers.into_boxed_slice(),
shutdown_task: AtomicTask::new(),
shutdown_task: ShutdownTask {
task1: AtomicTask::new(),
#[cfg(feature = "unstable-futures")]
task2: futures2::task::AtomicWaker::new(),
},
config: self.config.clone(),
});
@@ -772,6 +793,11 @@ impl tokio_executor::Executor for Sender {
let mut s = &*self;
tokio_executor::Executor::spawn(&mut s, future)
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, f: Task2) -> Result<(), futures2::executor::SpawnError> {
futures2::executor::Executor::spawn(self, f)
}
}
impl<'a> tokio_executor::Executor for &'a Sender {
@@ -806,6 +832,11 @@ impl<'a> tokio_executor::Executor for &'a Sender {
Ok(())
}
#[cfg(feature = "unstable-futures")]
fn spawn2(&mut self, f: Task2) -> Result<(), futures2::executor::SpawnError> {
futures2::executor::Executor::spawn(self, f)
}
}
impl<T> future::Executor<T> for Sender
@@ -827,6 +858,48 @@ where T: Future<Item = (), Error = ()> + Send + 'static,
}
}
#[cfg(feature = "unstable-futures")]
type Task2 = Box<futures2::Future<Item = (), Error = futures2::Never> + Send>;
#[cfg(feature = "unstable-futures")]
impl futures2::executor::Executor for Sender {
fn spawn(&mut self, f: Task2) -> Result<(), futures2::executor::SpawnError> {
let mut s = &*self;
futures2::executor::Executor::spawn(&mut s, f)
}
fn status(&self) -> Result<(), futures2::executor::SpawnError> {
let s = &*self;
futures2::executor::Executor::status(&s)
}
}
#[cfg(feature = "unstable-futures")]
impl<'a> futures2::executor::Executor for &'a Sender {
fn spawn(&mut self, f: Task2) -> Result<(), futures2::executor::SpawnError> {
self.prepare_for_spawn()
// TODO: get rid of this once the futures crate adds more error types
.map_err(|_| futures2::executor::SpawnError::shutdown())?;
// At this point, the pool has accepted the future, so schedule it for
// execution.
// Create a new task for the future
let task = Task::new2(f, |id| into_waker(Arc::new(Futures2Wake::new(id, &self.inner))));
self.inner.submit(task, &self.inner);
Ok(())
}
fn status(&self) -> Result<(), futures2::executor::SpawnError> {
tokio_executor::Executor::status(self)
// TODO: get rid of this once the futures crate adds more error types
.map_err(|_| futures2::executor::SpawnError::shutdown())
}
}
impl Clone for Sender {
#[inline]
fn clone(&self) -> Sender {
@@ -835,6 +908,21 @@ impl Clone for Sender {
}
}
// ===== impl ShutdownTask =====
impl ShutdownTask {
#[cfg(not(feature = "unstable-futures"))]
fn notify(&self) {
self.task1.notify();
}
#[cfg(feature = "unstable-futures")]
fn notify(&self) {
self.task1.notify();
self.task2.wake();
}
}
// ===== impl Shutdown =====
impl Shutdown {
@@ -848,9 +936,10 @@ impl Future for Shutdown {
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
use futures::task;
trace!("Shutdown::poll");
self.inner().shutdown_task.register();
self.inner().shutdown_task.task1.register_task(task::current());
if 0 != self.inner().num_workers.load(Acquire) {
return Ok(Async::NotReady);
@@ -860,6 +949,24 @@ impl Future for Shutdown {
}
}
#[cfg(feature = "unstable-futures")]
impl futures2::Future for Shutdown {
type Item = ();
type Error = ();
fn poll(&mut self, cx: &mut futures2::task::Context) -> futures2::Poll<(), ()> {
trace!("Shutdown::poll");
self.inner().shutdown_task.task2.register(cx.waker());
if 0 != self.inner().num_workers.load(Acquire) {
return Ok(futures2::Async::Pending);
}
Ok(().into())
}
}
// ===== impl Inner =====
impl Inner {
@@ -1346,6 +1453,7 @@ impl Worker {
let notify = Arc::new(Notifier {
inner: Arc::downgrade(&self.inner),
});
let mut sender = Sender { inner: self.inner.clone() };
let mut first = true;
let mut spin_cnt = 0;
@@ -1358,14 +1466,14 @@ impl Worker {
let consistent = self.drain_inbound();
// Run the next available task
if self.try_run_task(&notify) {
if self.try_run_task(&notify, &mut sender) {
spin_cnt = 0;
// As long as there is work, keep looping.
continue;
}
// No work in this worker's queue, it is time to try stealing.
if self.try_steal_task(&notify) {
if self.try_steal_task(&notify, &mut sender) {
spin_cnt = 0;
continue;
}
@@ -1448,13 +1556,13 @@ impl Worker {
///
/// Returns `true` if work was found.
#[inline]
fn try_run_task(&self, notify: &Arc<Notifier>) -> bool {
fn try_run_task(&self, notify: &Arc<Notifier>, sender: &mut Sender) -> bool {
use deque::Steal::*;
// Poll the internal queue for a task to run
match self.entry().deque.steal() {
Data(task) => {
self.run_task(task, notify);
self.run_task(task, notify, sender);
true
}
Empty => false,
@@ -1466,7 +1574,7 @@ impl Worker {
///
/// Returns `true` if work was found
#[inline]
fn try_steal_task(&self, notify: &Arc<Notifier>) -> bool {
fn try_steal_task(&self, notify: &Arc<Notifier>, sender: &mut Sender) -> bool {
use deque::Steal::*;
let len = self.inner.workers.len();
@@ -1480,7 +1588,7 @@ impl Worker {
Data(task) => {
trace!("stole task");
self.run_task(task, notify);
self.run_task(task, notify, sender);
trace!("try_steal_task -- signal_work; self={}; from={}",
self.idx, idx);
@@ -1507,10 +1615,10 @@ impl Worker {
found_work
}
fn run_task(&self, task: Task, notify: &Arc<Notifier>) {
fn run_task(&self, task: Task, notify: &Arc<Notifier>, sender: &mut Sender) {
use task::Run::*;
match task.run(notify) {
match task.run(notify, sender) {
Idle => {}
Schedule => {
self.entry().push_internal(task);
@@ -2111,3 +2219,56 @@ impl fmt::Debug for Callback {
write!(fmt, "Fn")
}
}
// ===== impl Futures2Wake =====
#[cfg(feature = "unstable-futures")]
impl Futures2Wake {
fn new(id: usize, inner: &Arc<Inner>) -> Futures2Wake {
let notifier = Arc::new(Notifier {
inner: Arc::downgrade(inner),
});
Futures2Wake { id, notifier }
}
}
#[cfg(feature = "unstable-futures")]
impl Drop for Futures2Wake {
fn drop(&mut self) {
self.notifier.drop_id(self.id)
}
}
#[cfg(feature = "unstable-futures")]
struct ArcWrapped(PhantomData<Futures2Wake>);
#[cfg(feature = "unstable-futures")]
unsafe impl futures2::task::UnsafeWake for ArcWrapped {
unsafe fn clone_raw(&self) -> futures2::task::Waker {
let me: *const ArcWrapped = self;
let arc = (*(&me as *const *const ArcWrapped as *const Arc<Futures2Wake>)).clone();
arc.notifier.clone_id(arc.id);
into_waker(arc)
}
unsafe fn drop_raw(&self) {
let mut me: *const ArcWrapped = self;
let me = &mut me as *mut *const ArcWrapped as *mut Arc<Futures2Wake>;
(*me).notifier.drop_id((*me).id);
::std::ptr::drop_in_place(me);
}
unsafe fn wake(&self) {
let me: *const ArcWrapped = self;
let me = &me as *const *const ArcWrapped as *const Arc<Futures2Wake>;
(*me).notifier.notify((*me).id)
}
}
#[cfg(feature = "unstable-futures")]
fn into_waker(rc: Arc<Futures2Wake>) -> futures2::task::Waker {
unsafe {
let ptr = mem::transmute::<Arc<Futures2Wake>, *mut ArcWrapped>(rc);
futures2::task::Waker::new(ptr)
}
}
+99 -15
View File
@@ -1,14 +1,17 @@
use Notifier;
use {Notifier, Sender};
use futures::{future, Future, Async};
use futures::{self, future, Future, Async};
use futures::executor::{self, Spawn};
use std::{fmt, mem, ptr};
use std::{fmt, mem, panic, ptr};
use std::cell::Cell;
use std::sync::Arc;
use std::sync::atomic::{self, AtomicUsize, AtomicPtr};
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release, Relaxed};
#[cfg(feature = "unstable-futures")]
use futures2;
pub(crate) struct Task {
ptr: *mut Inner,
}
@@ -34,6 +37,22 @@ pub(crate) enum Run {
Complete,
}
type BoxFuture = Box<Future<Item = (), Error = ()> + Send + 'static>;
#[cfg(feature = "unstable-futures")]
type BoxFuture2 = Box<futures2::Future<Item = (), Error = futures2::Never> + Send>;
enum TaskFuture {
Futures1(Spawn<BoxFuture>),
#[cfg(feature = "unstable-futures")]
Futures2 {
tls: futures2::task::LocalMap,
waker: futures2::task::Waker,
fut: BoxFuture2,
}
}
struct Inner {
// Next pointer in the queue that submits tasks to a worker.
next: AtomicPtr<Inner>,
@@ -47,7 +66,7 @@ struct Inner {
// Store the future at the head of the struct
//
// The future is dropped immediately when it transitions to Complete
future: Option<Spawn<BoxFuture>>,
future: Option<TaskFuture>,
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
@@ -64,23 +83,41 @@ enum State {
Complete,
}
type BoxFuture = Box<Future<Item = (), Error = ()> + Send + 'static>;
// ===== impl Task =====
impl Task {
/// Create a new task handle
pub fn new(future: BoxFuture) -> Task {
let task_fut = TaskFuture::Futures1(executor::spawn(future));
let inner = Box::new(Inner {
next: AtomicPtr::new(ptr::null_mut()),
state: AtomicUsize::new(State::new().into()),
ref_count: AtomicUsize::new(1),
future: Some(executor::spawn(future)),
future: Some(task_fut),
});
Task { ptr: Box::into_raw(inner) }
}
/// Create a new task handle for a futures 0.2 future
#[cfg(feature = "unstable-futures")]
pub fn new2<F>(fut: BoxFuture2, make_waker: F) -> Task
where F: FnOnce(usize) -> futures2::task::Waker
{
let mut inner = Box::new(Inner {
next: AtomicPtr::new(ptr::null_mut()),
state: AtomicUsize::new(State::new().into()),
ref_count: AtomicUsize::new(1),
future: None,
});
let waker = make_waker((&*inner) as *const _ as usize);
let tls = futures2::task::LocalMap::new();
inner.future = Some(TaskFuture::Futures2 { waker, tls, fut });
Task { ptr: Box::into_raw(inner) }
}
/// Transmute a u64 to a Task
pub unsafe fn from_notify_id(unpark_id: usize) -> Task {
mem::transmute(unpark_id)
@@ -93,7 +130,7 @@ impl Task {
/// Execute the task returning `Run::Schedule` if the task needs to be
/// scheduled again.
pub fn run(&self, unpark: &Arc<Notifier>) -> Run {
pub fn run(&self, unpark: &Arc<Notifier>, exec: &mut Sender) -> Run {
use self::State::*;
// Transition task to running state. At this point, the task must be
@@ -110,11 +147,38 @@ impl Task {
trace!("Task::run; state={:?}", State::from(self.inner().state.load(Relaxed)));
let res = self.inner_mut().future.as_mut().unwrap()
.poll_future_notify(unpark, self.ptr as usize);
let fut = &mut self.inner_mut().future;
// This block deals with the future panicking while being polled.
//
// If the future panics, then the drop handler must be called such that
// `thread::panicking() -> true`. To do this, the future is dropped from
// within the catch_unwind block.
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
struct Guard<'a>(&'a mut Option<TaskFuture>, bool);
impl<'a> Drop for Guard<'a> {
fn drop(&mut self) {
// This drops the future
if self.1 {
let _ = self.0.take();
}
}
}
let mut g = Guard(fut, true);
let ret = g.0.as_mut().unwrap()
.poll(unpark, self.ptr as usize, exec);
g.1 = false;
ret
}));
match res {
Ok(Async::Ready(_)) | Err(_) => {
Ok(Ok(Async::Ready(_))) | Ok(Err(_)) | Err(_) => {
trace!(" -> task complete");
// Drop the future
@@ -125,7 +189,7 @@ impl Task {
Run::Complete
}
_ => {
Ok(Ok(Async::NotReady)) => {
trace!(" -> not ready");
// Attempt to transition from Running -> Idle, if successful,
@@ -158,13 +222,13 @@ impl Task {
let actual = self.inner().state.compare_and_swap(
Idle.into(),
Scheduled.into(),
Relaxed).into();
AcqRel).into();
match actual {
Idle => return true,
Running => {
let actual = self.inner().state.compare_and_swap(
Running.into(), Notified.into(), Relaxed).into();
Running.into(), Notified.into(), AcqRel).into();
match actual {
Idle => continue,
@@ -275,7 +339,7 @@ impl Inner {
next: AtomicPtr::new(ptr::null_mut()),
state: AtomicUsize::new(State::stub().into()),
ref_count: AtomicUsize::new(0),
future: Some(executor::spawn(Box::new(future::empty()))),
future: Some(TaskFuture::Futures1(executor::spawn(Box::new(future::empty())))),
}
}
@@ -427,3 +491,23 @@ impl From<State> for usize {
}
}
}
// ===== impl TaskFuture =====
impl TaskFuture {
#[allow(unused_variables)]
fn poll(&mut self, unpark: &Arc<Notifier>, id: usize, exec: &mut Sender) -> futures::Poll<(), ()> {
match *self {
TaskFuture::Futures1(ref mut fut) => fut.poll_future_notify(unpark, id),
#[cfg(feature = "unstable-futures")]
TaskFuture::Futures2 { ref mut fut, ref waker, ref mut tls } => {
let mut cx = futures2::task::Context::new(tls, waker, exec);
match fut.poll(&mut cx).unwrap() {
futures2::Async::Pending => Ok(Async::NotReady),
futures2::Async::Ready(x) => Ok(Async::Ready(x)),
}
}
}
}
}
+304 -42
View File
@@ -3,9 +3,26 @@ extern crate tokio_executor;
extern crate futures;
extern crate env_logger;
#[cfg(feature = "unstable-futures")]
extern crate futures2;
use tokio_threadpool::*;
use futures::{Poll, Sink, Stream, Async};
use futures::future::{Future, lazy};
#[cfg(not(feature = "unstable-futures"))]
use futures::{Poll, Sink, Stream, Async, Future};
#[cfg(not(feature = "unstable-futures"))]
use futures::future::lazy;
#[cfg(feature = "unstable-futures")]
use futures2::prelude::*;
#[cfg(feature = "unstable-futures")]
fn lazy<R, F>(f: F) -> Box<Future<Item = R::Item, Error = R::Error> + Send> where
F: Send + 'static + FnOnce() -> R,
R: Send + 'static + IntoFuture,
R::Future: Send,
{
Box::new(::futures2::future::lazy(|_| f()))
}
use std::cell::Cell;
use std::sync::{mpsc, Arc};
@@ -15,6 +32,57 @@ use std::time::Duration;
thread_local!(static FOO: Cell<u32> = Cell::new(0));
#[cfg(not(feature = "unstable-futures"))]
fn spawn_pool<F>(pool: &mut Sender, f: F)
where F: Future<Item = (), Error = ()> + Send + 'static
{
pool.spawn(f).unwrap()
}
#[cfg(feature = "unstable-futures")]
fn spawn_pool<F>(pool: &mut Sender, f: F)
where F: Future<Item = (), Error = ()> + Send + 'static
{
futures2::executor::Executor::spawn(
pool,
Box::new(f.map_err(|_| panic!()))
).unwrap()
}
#[cfg(not(feature = "unstable-futures"))]
fn spawn_default<F>(f: F)
where F: Future<Item = (), Error = ()> + Send + 'static
{
tokio_executor::spawn(f)
}
#[cfg(feature = "unstable-futures")]
fn spawn_default<F>(f: F)
where F: Future<Item = (), Error = ()> + Send + 'static
{
tokio_executor::spawn2(Box::new(f.map_err(|_| panic!())))
}
fn ignore_results<F: Future + Send + 'static>(f: F) -> Box<Future<Item = (), Error = ()> + Send> {
Box::new(f.map(|_| ()).map_err(|_| ()))
}
#[cfg(feature = "unstable-futures")]
fn await_shutdown(shutdown: Shutdown) {
futures::Future::wait(shutdown).unwrap()
}
#[cfg(not(feature = "unstable-futures"))]
fn await_shutdown(shutdown: Shutdown) {
shutdown.wait().unwrap()
}
#[cfg(not(feature = "unstable-futures"))]
fn block_on<F: Future>(f: F) -> Result<F::Item, F::Error> {
f.wait()
}
#[cfg(feature = "unstable-futures")]
fn block_on<F: Future>(f: F) -> Result<F::Item, F::Error> {
futures2::executor::block_on(f)
}
#[test]
fn natural_shutdown_simple_futures() {
let _ = ::env_logger::init();
@@ -33,29 +101,29 @@ fn natural_shutdown_simple_futures() {
NUM_DEC.fetch_add(1, Relaxed);
})
.build();
let tx = pool.sender().clone();
let mut tx = pool.sender().clone();
let a = {
let (t, rx) = mpsc::channel();
tx.spawn(lazy(move || {
spawn_pool(&mut tx, lazy(move || {
// Makes sure this runs on a worker thread
FOO.with(|f| assert_eq!(f.get(), 0));
t.send("one").unwrap();
Ok(())
})).unwrap();
}));
rx
};
let b = {
let (t, rx) = mpsc::channel();
tx.spawn(lazy(move || {
spawn_pool(&mut tx, lazy(move || {
// Makes sure this runs on a worker thread
FOO.with(|f| assert_eq!(f.get(), 0));
t.send("two").unwrap();
Ok(())
})).unwrap();
}));
rx
};
@@ -65,7 +133,7 @@ fn natural_shutdown_simple_futures() {
assert_eq!("two", b.recv().unwrap());
// Wait for the pool to shutdown
pool.shutdown().wait().unwrap();
await_shutdown(pool.shutdown());
// Assert that at least one thread started
let num_inc = NUM_INC.load(Relaxed);
@@ -89,6 +157,7 @@ fn force_shutdown_drops_futures() {
struct Never(Arc<AtomicUsize>);
#[cfg(not(feature = "unstable-futures"))]
impl Future for Never {
type Item = ();
type Error = ();
@@ -98,6 +167,16 @@ fn force_shutdown_drops_futures() {
}
}
#[cfg(feature = "unstable-futures")]
impl Future for Never {
type Item = ();
type Error = ();
fn poll(&mut self, _: &mut futures2::task::Context) -> Poll<(), ()> {
Ok(Async::Pending)
}
}
impl Drop for Never {
fn drop(&mut self) {
self.0.fetch_add(1, Relaxed);
@@ -107,7 +186,7 @@ fn force_shutdown_drops_futures() {
let a = num_inc.clone();
let b = num_dec.clone();
let mut pool = Builder::new()
let pool = Builder::new()
.around_worker(move |w, _| {
a.fetch_add(1, Relaxed);
w.run();
@@ -116,10 +195,10 @@ fn force_shutdown_drops_futures() {
.build();
let mut tx = pool.sender().clone();
tx.spawn(Never(num_drop.clone())).unwrap();
spawn_pool(&mut tx, Never(num_drop.clone()));
// Wait for the pool to shutdown
pool.shutdown_now().wait().unwrap();
await_shutdown(pool.shutdown_now());
// Assert that only a single thread was spawned.
let a = num_inc.load(Relaxed);
@@ -146,6 +225,7 @@ fn drop_threadpool_drops_futures() {
struct Never(Arc<AtomicUsize>);
#[cfg(not(feature = "unstable-futures"))]
impl Future for Never {
type Item = ();
type Error = ();
@@ -155,6 +235,16 @@ fn drop_threadpool_drops_futures() {
}
}
#[cfg(feature = "unstable-futures")]
impl Future for Never {
type Item = ();
type Error = ();
fn poll(&mut self, _: &mut futures2::task::Context) -> Poll<(), ()> {
Ok(Async::Pending)
}
}
impl Drop for Never {
fn drop(&mut self) {
self.0.fetch_add(1, Relaxed);
@@ -173,7 +263,7 @@ fn drop_threadpool_drops_futures() {
.build();
let mut tx = pool.sender().clone();
tx.spawn(Never(num_drop.clone())).unwrap();
spawn_pool(&mut tx, Never(num_drop.clone()));
// Wait for the pool to shutdown
drop(pool);
@@ -211,13 +301,13 @@ fn thread_shutdown_timeout() {
let _ = t.lock().unwrap().send(());
})
.build();
let tx = pool.sender().clone();
let mut tx = pool.sender().clone();
let t = complete_tx.clone();
tx.spawn(lazy(move || {
spawn_pool(&mut tx, lazy(move || {
t.send(()).unwrap();
Ok(())
})).unwrap();
}));
// The future completes
complete_rx.recv().unwrap();
@@ -226,14 +316,14 @@ fn thread_shutdown_timeout() {
shutdown_rx.recv().unwrap();
// Futures can still be run
tx.spawn(lazy(move || {
spawn_pool(&mut tx, lazy(move || {
complete_tx.send(()).unwrap();
Ok(())
})).unwrap();
}));
complete_rx.recv().unwrap();
pool.shutdown().wait().unwrap();
await_shutdown(pool.shutdown());
}
#[test]
@@ -249,14 +339,14 @@ fn many_oneshot_futures() {
for _ in 0..NUM {
let cnt = cnt.clone();
tx.spawn(lazy(move || {
spawn_pool(&mut tx, lazy(move || {
cnt.fetch_add(1, Relaxed);
Ok(())
})).unwrap();
}));
}
// Wait for the pool to shutdown
pool.shutdown().wait().unwrap();
await_shutdown(pool.shutdown());
let num = cnt.load(Relaxed);
assert_eq!(num, NUM);
@@ -265,8 +355,12 @@ fn many_oneshot_futures() {
#[test]
fn many_multishot_futures() {
#[cfg(not(feature = "unstable-futures"))]
use futures::sync::mpsc;
#[cfg(feature = "unstable-futures")]
use futures2::channel::mpsc;
const CHAIN: usize = 200;
const CYCLES: usize = 5;
const TRACKS: usize = 50;
@@ -290,11 +384,11 @@ fn many_multishot_futures() {
.map_err(|e| panic!("{:?}", e));
// Forward all the messages
pool_tx.spawn(next_tx
spawn_pool(&mut pool_tx, next_tx
.send_all(rx)
.map(|_| ())
.map_err(|e| panic!("{:?}", e))
).unwrap();
);
chain_rx = next_rx;
}
@@ -304,7 +398,7 @@ fn many_multishot_futures() {
let cycle_tx = start_tx.clone();
let mut rem = CYCLES;
pool_tx.spawn(chain_rx.take(CYCLES as u64).for_each(move |msg| {
let task = chain_rx.take(CYCLES as u64).for_each(move |msg| {
rem -= 1;
let send = if rem == 0 {
final_tx.clone().send(msg)
@@ -316,73 +410,241 @@ fn many_multishot_futures() {
res.unwrap();
Ok(())
})
})).unwrap();
});
spawn_pool(&mut pool_tx, ignore_results(task));
start_txs.push(start_tx);
final_rxs.push(final_rx);
}
for start_tx in start_txs {
start_tx.send("ping").wait().unwrap();
block_on(start_tx.send("ping")).unwrap();
}
for final_rx in final_rxs {
final_rx.wait().next().unwrap().unwrap();
{#![cfg(feature = "unstable-futures")]
block_on(final_rx.next()).unwrap();
}
{#![cfg(not(feature = "unstable-futures"))]
block_on(final_rx.into_future()).unwrap();
}
}
// Shutdown the pool
pool.shutdown().wait().unwrap();
await_shutdown(pool.shutdown());
}
}
#[test]
fn global_executor_is_configured() {
let pool = ThreadPool::new();
let tx = pool.sender().clone();
let mut tx = pool.sender().clone();
let (signal_tx, signal_rx) = mpsc::channel();
tx.spawn(lazy(move || {
tokio_executor::spawn(lazy(move || {
spawn_pool(&mut tx, lazy(move || {
spawn_default(lazy(move || {
signal_tx.send(()).unwrap();
Ok(())
}));
Ok(())
})).unwrap();
}));
signal_rx.recv().unwrap();
pool.shutdown().wait().unwrap();
await_shutdown(pool.shutdown());
}
#[test]
fn new_threadpool_is_idle() {
let pool = ThreadPool::new();
pool.shutdown_on_idle().wait().unwrap();
await_shutdown(pool.shutdown_on_idle());
}
#[test]
fn busy_threadpool_is_not_idle() {
#[cfg(not(feature = "unstable-futures"))]
use futures::sync::oneshot;
#[cfg(feature = "unstable-futures")]
use futures2::channel::oneshot;
let pool = ThreadPool::new();
let tx = pool.sender().clone();
let mut tx = pool.sender().clone();
let (term_tx, term_rx) = oneshot::channel();
tx.spawn(term_rx.then(|_| {
spawn_pool(&mut tx, term_rx.then(|_| {
Ok(())
})).unwrap();
}));
let mut idle = pool.shutdown_on_idle();
futures::lazy(|| {
assert!(idle.poll().unwrap().is_not_ready());
Ok::<_, ()>(())
}).wait().unwrap();
struct IdleFut<'a>(&'a mut Shutdown);
#[cfg(not(feature = "unstable-futures"))]
impl<'a> Future for IdleFut<'a> {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
assert!(self.0.poll().unwrap().is_not_ready());
Ok(Async::Ready(()))
}
}
#[cfg(feature = "unstable-futures")]
impl<'a> Future for IdleFut<'a> {
type Item = ();
type Error = ();
fn poll(&mut self, cx: &mut futures2::task::Context) -> Poll<(), ()> {
assert!(self.0.poll(cx).unwrap().is_pending());
Ok(Async::Ready(()))
}
}
block_on(IdleFut(&mut idle)).unwrap();
term_tx.send(()).unwrap();
idle.wait().unwrap();
await_shutdown(idle);
}
#[test]
fn panic_in_task() {
let pool = ThreadPool::new();
let mut tx = pool.sender().clone();
struct Boom;
#[cfg(not(feature = "unstable-futures"))]
impl Future for Boom {
type Item = ();
type Error = ();
fn poll(&mut self) -> Poll<(), ()> {
panic!();
}
}
#[cfg(feature = "unstable-futures")]
impl Future for Boom {
type Item = ();
type Error = ();
fn poll(&mut self, _cx: &mut futures2::task::Context) -> Poll<(), ()> {
panic!();
}
}
impl Drop for Boom {
fn drop(&mut self) {
assert!(::std::thread::panicking());
}
}
spawn_pool(&mut tx, Boom);
await_shutdown(pool.shutdown_on_idle());
}
#[test]
#[cfg(not(feature = "unstable-futures"))]
fn hammer() {
use futures::future;
use futures::sync::{oneshot, mpsc};
const N: usize = 1000;
const ITER: usize = 20;
struct Counted<T> {
cnt: Arc<AtomicUsize>,
inner: T,
}
impl<T: Future> Future for Counted<T> {
type Item = T::Item;
type Error = T::Error;
fn poll(&mut self) -> Poll<T::Item, T::Error> {
self.inner.poll()
}
}
impl<T> Drop for Counted<T> {
fn drop(&mut self) {
self.cnt.fetch_add(1, Relaxed);
}
}
for i in 0.. ITER {
println!("~~~ ITER {} ~~~", i);
let pool = Builder::new()
// .pool_size(30)
.build();
let cnt = Arc::new(AtomicUsize::new(0));
let (listen_tx, listen_rx) = mpsc::unbounded::<oneshot::Sender<oneshot::Sender<()>>>();
let mut listen_tx = listen_tx.wait();
pool.spawn({
let c1 = cnt.clone();
let c2 = cnt.clone();
let pool = pool.sender().clone();
let task = listen_rx
.map_err(|e| panic!("accept error = {:?}", e))
.for_each(move |tx| {
let task = future::lazy(|| {
let (tx2, rx2) = oneshot::channel();
tx.send(tx2).unwrap();
rx2
})
.map_err(|e| panic!("e={:?}", e))
.and_then(|_| {
Ok(())
});
pool.spawn(Counted {
inner: task,
cnt: c1.clone(),
}).unwrap();
Ok(())
});
Counted {
inner: task,
cnt: c2,
}
});
for _ in 0..N {
let cnt = cnt.clone();
let (tx, rx) = oneshot::channel();
listen_tx.send(tx).unwrap();
pool.spawn({
let task = rx
.map_err(|e| panic!("rx err={:?}", e))
.and_then(|tx| {
tx.send(()).unwrap();
Ok(())
});
Counted {
inner: task,
cnt,
}
});
}
drop(listen_tx);
pool.shutdown_on_idle().wait().unwrap();
assert_eq!(N * 2 + 1, cnt.load(Relaxed));
}
}
+3
View File
@@ -0,0 +1,3 @@
# 0.1.0 (unreleased)
* Initial release
+35
View File
@@ -0,0 +1,35 @@
[package]
name = "tokio-udp"
# When releasing to crates.io:
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Create "v0.1.x" git tag.
version = "0.1.0"
authors = ["Carl Lerche <[email protected]>"]
license = "MIT"
repository = "https://github.com/tokio-rs/tokio"
homepage = "https://tokio.rs"
documentation = "https://docs.rs/tokio-udp/0.1"
description = """
UDP bindings for tokio.
"""
categories = ["asynchronous"]
[dependencies]
tokio-io = { version = "0.1.6", path = "../tokio-io" }
tokio-reactor = { version = "0.1.1", path = "../tokio-reactor" }
bytes = "0.4"
mio = "0.6.14"
log = "0.4"
futures = "0.1.19"
# Futures 0.2 integration
futures2 = { version = "0.1", path = "../futures2", optional = true }
[dev-dependencies]
env_logger = { version = "0.4", default-features = false }
[features]
unstable-futures = ["futures2"]
default = []
+25
View File
@@ -0,0 +1,25 @@
Copyright (c) 2018 Tokio Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
+15
View File
@@ -0,0 +1,15 @@
# tokio-udp
UDP bindings for `tokio`.
[Documentation](https://tokio-rs.github.io/tokio/tokio_udp/)
## License
This project is licensed under the [MIT license](./LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
terms or conditions.
@@ -3,7 +3,7 @@ use std::net::{SocketAddr, Ipv4Addr, SocketAddrV4};
use futures::{Async, Poll, Stream, Sink, StartSend, AsyncSink};
use net::UdpSocket;
use super::UdpSocket;
use tokio_io::codec::{Decoder, Encoder};
use bytes::{BytesMut, BufMut};
+42
View File
@@ -0,0 +1,42 @@
//! UDP bindings for `tokio`.
//!
//! This module contains the UDP networking types, similar to the standard
//! library, which can be used to implement networking protocols.
//!
//! The main struct for UDP is the [`UdpSocket`], which represents a UDP socket.
//! Reading and writing to it can be done using futures, which return the
//! [`RecvDgram`] and [`SendDgram`] structs respectively.
//!
//! For convience it's also possible to convert raw datagrams into higher-level
//! frames.
//!
//! [`UdpSocket`]: struct.UdpSocket.html
//! [`RecvDgram`]: struct.RecvDgram.html
//! [`SendDgram`]: struct.SendDgram.html
//! [`UdpFramed`]: struct.UdpFramed.html
//! [`framed`]: struct.UdpSocket.html#method.framed
#![doc(html_root_url = "https://docs.rs/tokio-tcp/0.1.0")]
#![deny(missing_docs, warnings, missing_debug_implementations)]
extern crate bytes;
#[macro_use]
extern crate futures;
extern crate mio;
#[macro_use]
extern crate log;
extern crate tokio_io;
extern crate tokio_reactor;
#[cfg(feature = "unstable-futures")]
extern crate futures2;
mod frame;
mod socket;
mod send_dgram;
mod recv_dgram;
pub use self::frame::UdpFramed;
pub use self::socket::UdpSocket;
pub use self::send_dgram::SendDgram;
pub use self::recv_dgram::RecvDgram;
@@ -1,4 +1,4 @@
use net::udp::socket::UdpSocket;
use super::socket::UdpSocket;
use std::io;
use std::net::SocketAddr;
@@ -1,4 +1,4 @@
use net::udp::socket::UdpSocket;
use super::socket::UdpSocket;
use std::io;
use std::net::SocketAddr;
@@ -1,4 +1,4 @@
use net::udp::{SendDgram, RecvDgram};
use super::{SendDgram, RecvDgram};
use std::io;
use std::net::{self, SocketAddr, Ipv4Addr, Ipv6Addr};
@@ -7,11 +7,11 @@ use std::fmt;
use futures::{Async, Poll};
use mio;
use reactor::{Handle, PollEvented2};
use tokio_reactor::{Handle, PollEvented};
/// An I/O object representing a UDP socket.
pub struct UdpSocket {
io: PollEvented2<mio::net::UdpSocket>,
io: PollEvented<mio::net::UdpSocket>,
}
impl UdpSocket {
@@ -23,7 +23,7 @@ impl UdpSocket {
}
fn new(socket: mio::net::UdpSocket) -> UdpSocket {
let io = PollEvented2::new(socket);
let io = PollEvented::new(socket);
UdpSocket { io: io }
}
@@ -39,7 +39,7 @@ impl UdpSocket {
pub fn from_std(socket: net::UdpSocket,
handle: &Handle) -> io::Result<UdpSocket> {
let io = mio::net::UdpSocket::from_socket(socket)?;
let io = PollEvented2::new_with_handle(io, handle)?;
let io = PollEvented::new_with_handle(io, handle)?;
Ok(UdpSocket { io })
}
+2 -2
View File
@@ -1,7 +1,7 @@
#![allow(deprecated)]
extern crate futures;
extern crate tokio;
extern crate tokio_udp;
#[macro_use]
extern crate tokio_io;
extern crate bytes;
@@ -12,7 +12,7 @@ use std::net::SocketAddr;
use futures::{Future, Poll, Stream, Sink};
use tokio::net::{UdpSocket, UdpFramed};
use tokio_udp::{UdpSocket, UdpFramed};
use tokio_io::codec::{Encoder, Decoder};
use bytes::{BytesMut, BufMut};