mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9cffda59c9 | ||
|
|
23d95b44e7 | ||
|
|
69e45f4be4 | ||
|
|
6bdfa159a7 | ||
|
|
5d87a9cee1 | ||
|
|
16d3540ce9 | ||
|
|
e5ebd02885 | ||
|
|
08c21e7bac | ||
|
|
8786741ba9 | ||
|
|
494f0dc176 | ||
|
|
df9025594c | ||
|
|
7b1306e6c2 | ||
|
|
b7f4e337be | ||
|
|
d1046db735 | ||
|
|
923a80e098 | ||
|
|
64435f5b35 | ||
|
|
d304791c0e | ||
|
|
5846b3fc2a | ||
|
|
8eb3e58b7d | ||
|
|
c0a2cc1f9e | ||
|
|
61b2889881 | ||
|
|
5dab821b29 | ||
|
|
2abeff01a5 | ||
|
|
96a542451d | ||
|
|
95899e007d | ||
|
|
e6e3c49e0e | ||
|
|
4d514b7eb3 | ||
|
|
2a1585157e | ||
|
|
189d6baac4 | ||
|
|
3ad27e99ec | ||
|
|
bf1305c421 | ||
|
|
cf7435ba30 |
+18
-2
@@ -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
|
||||
|
||||
@@ -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
@@ -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 = []
|
||||
|
||||
-201
@@ -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.
|
||||
@@ -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
@@ -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
@@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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"
|
||||
@@ -0,0 +1,2 @@
|
||||
extern crate futures;
|
||||
pub use futures::*;
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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};
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -1,3 +1,5 @@
|
||||
#![cfg(not(feature = "unstable-futures"))]
|
||||
|
||||
extern crate tokio;
|
||||
extern crate tokio_executor;
|
||||
extern crate futures;
|
||||
|
||||
Executable
+53
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Executable
+122
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
# 0.1.1 (March 22, 2018)
|
||||
|
||||
* Optionally support futures 0.2.
|
||||
|
||||
# 0.1.0 (March 09, 2018)
|
||||
|
||||
* Initial release
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# 0.1.0 (unreleased)
|
||||
|
||||
* Initial release
|
||||
@@ -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 = []
|
||||
@@ -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.
|
||||
@@ -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)))
|
||||
}
|
||||
}
|
||||
@@ -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::*;
|
||||
@@ -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};
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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(¬ify) {
|
||||
if self.try_run_task(¬ify, &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(¬ify) {
|
||||
if self.try_steal_task(¬ify, &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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# 0.1.0 (unreleased)
|
||||
|
||||
* Initial release
|
||||
@@ -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 = []
|
||||
@@ -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.
|
||||
@@ -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};
|
||||
@@ -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 })
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
Reference in New Issue
Block a user