mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4cb3226ab | ||
|
|
4446eb4db8 | ||
|
|
cad0c35623 | ||
|
|
5dcb379f6d | ||
|
|
970f75f830 | ||
|
|
01052f930a | ||
|
|
940f2c3431 | ||
|
|
475dabe96d | ||
|
|
951f2fd910 | ||
|
|
4ef736b9d5 | ||
|
|
219f24cbf1 | ||
|
|
ea282efb2e | ||
|
|
042224d33c | ||
|
|
927eb80ad4 | ||
|
|
6a8934e897 | ||
|
|
0e400af78c | ||
|
|
df702130d6 | ||
|
|
949adbb887 | ||
|
|
b2b796a228 | ||
|
|
abb014efc2 | ||
|
|
e5cf0cc717 |
+2
-4
@@ -31,13 +31,11 @@ task:
|
||||
folder: $HOME/.cargo/registry
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --no-fail-fast
|
||||
- (cd tokio-trace/test-log-support && cargo test)
|
||||
- (cd tokio-trace/test_static_max_level_features && cargo test)
|
||||
- cargo test --all
|
||||
- cargo doc --all
|
||||
i686_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- |
|
||||
cargo test --all --exclude tokio-tls --no-fail-fast --target i686-unknown-freebsd
|
||||
cargo test --all --exclude tokio-tls --exclude tokio-macros --target i686-unknown-freebsd
|
||||
before_cache_script:
|
||||
- rm -rf $HOME/.cargo/registry/index
|
||||
|
||||
+3
-3
@@ -2,22 +2,22 @@
|
||||
|
||||
members = [
|
||||
"tokio",
|
||||
"tokio-async-await",
|
||||
"tokio-buf",
|
||||
"tokio-codec",
|
||||
"tokio-current-thread",
|
||||
"tokio-executor",
|
||||
"tokio-fs",
|
||||
"tokio-futures",
|
||||
"tokio-io",
|
||||
"tokio-macros",
|
||||
"tokio-reactor",
|
||||
"tokio-signal",
|
||||
"tokio-sync",
|
||||
"tokio-test",
|
||||
"tokio-threadpool",
|
||||
"tokio-timer",
|
||||
"tokio-tcp",
|
||||
"tokio-tls",
|
||||
"tokio-trace",
|
||||
"tokio-trace/tokio-trace-core",
|
||||
"tokio-udp",
|
||||
"tokio-uds",
|
||||
]
|
||||
|
||||
@@ -20,7 +20,7 @@ the Rust programming language. It is:
|
||||
[crates-badge]: https://img.shields.io/crates/v/tokio.svg
|
||||
[crates-url]: https://crates.io/crates/tokio
|
||||
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[mit-url]: LICENSE-MIT
|
||||
[mit-url]: LICENSE
|
||||
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.tokio?branchName=master
|
||||
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=1&branchName=master
|
||||
[gitter-badge]: https://img.shields.io/gitter/room/tokio-rs/tokio.svg
|
||||
@@ -28,7 +28,7 @@ the Rust programming language. It is:
|
||||
|
||||
[Website](https://tokio.rs) |
|
||||
[Guides](https://tokio.rs/docs/getting-started/hello-world/) |
|
||||
[API Docs](https://docs.rs/tokio/0.1.19/tokio) |
|
||||
[API Docs](https://docs.rs/tokio/0.1.20/tokio) |
|
||||
[Chat](https://gitter.im/tokio-rs/tokio)
|
||||
|
||||
The API docs for the master branch are published [here][master-dox].
|
||||
@@ -49,9 +49,9 @@ level, it provides a few major components:
|
||||
These components provide the runtime components necessary for building
|
||||
an asynchronous application.
|
||||
|
||||
[net]: https://docs.rs/tokio/0.1.19/tokio/net/index.html
|
||||
[reactor]: https://docs.rs/tokio/0.1.19/tokio/reactor/index.html
|
||||
[scheduler]: https://docs.rs/tokio/0.1.19/tokio/runtime/index.html
|
||||
[net]: https://docs.rs/tokio/0.1.20/tokio/net/index.html
|
||||
[reactor]: https://docs.rs/tokio/0.1.20/tokio/reactor/index.html
|
||||
[scheduler]: https://docs.rs/tokio/0.1.20/tokio/runtime/index.html
|
||||
|
||||
## Example
|
||||
|
||||
@@ -126,10 +126,6 @@ have greater guarantees of stability.
|
||||
|
||||
The crates included as part of Tokio are:
|
||||
|
||||
* [`tokio-async-await`]: Experimental `async` / `await` support.
|
||||
|
||||
* [`tokio-codec`]: Utilities for encoding and decoding protocol frames.
|
||||
|
||||
* [`tokio-current-thread`]: Schedule the execution of futures on the current
|
||||
thread.
|
||||
|
||||
@@ -137,8 +133,14 @@ The crates included as part of Tokio are:
|
||||
|
||||
* [`tokio-fs`]: Filesystem (and standard in / out) APIs.
|
||||
|
||||
* [`tokio-futures`]: Experimental `std::future::Future` and `async` / `await` support.
|
||||
|
||||
* [`tokio-codec`]: Utilities for encoding and decoding protocol frames.
|
||||
|
||||
* [`tokio-io`]: Asynchronous I/O related traits and utilities.
|
||||
|
||||
* [`tokio-macros`]: Macros for usage with Tokio.
|
||||
|
||||
* [`tokio-reactor`]: Event loop that drives I/O resources (like TCP and UDP
|
||||
sockets).
|
||||
|
||||
@@ -154,12 +156,13 @@ The crates included as part of Tokio are:
|
||||
* [`tokio-uds`]: Unix Domain Socket bindings for use with `tokio-io` and
|
||||
`tokio-reactor`.
|
||||
|
||||
[`tokio-async-await`]: tokio-async-await
|
||||
[`tokio-codec`]: tokio-codec
|
||||
[`tokio-current-thread`]: tokio-current-thread
|
||||
[`tokio-executor`]: tokio-executor
|
||||
[`tokio-fs`]: tokio-fs
|
||||
[`tokio-futures`]: tokio-futures
|
||||
[`tokio-io`]: tokio-io
|
||||
[`tokio-macros`]: tokio-macros
|
||||
[`tokio-reactor`]: tokio-reactor
|
||||
[`tokio-tcp`]: tokio-tcp
|
||||
[`tokio-threadpool`]: tokio-threadpool
|
||||
@@ -167,6 +170,23 @@ The crates included as part of Tokio are:
|
||||
[`tokio-udp`]: tokio-udp
|
||||
[`tokio-uds`]: tokio-uds
|
||||
|
||||
## Related Projects
|
||||
|
||||
In addition to the crates in this repository, the Tokio project also maintains
|
||||
several other libraries, including:
|
||||
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level
|
||||
tracing and async-aware diagnostics.
|
||||
|
||||
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers
|
||||
`tokio`.
|
||||
|
||||
* [`bytes`]: Utilities for working with bytes, including efficient byte buffers.
|
||||
|
||||
[`tracing`]: https://github.com/tokio-rs/tracing
|
||||
[`mio`]: https://github.com/tokio-rs/mio
|
||||
[`bytes`]: https://github.com/tokio-rs/bytes
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
Tokio is built against the latest stable, nightly, and beta Rust releases. The
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target-dir = "../target"
|
||||
@@ -0,0 +1,49 @@
|
||||
[package]
|
||||
name = "examples"
|
||||
edition = "2018"
|
||||
version = "0.1.0"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
|
||||
# Break out of the parent workspace
|
||||
[workspace]
|
||||
|
||||
[[bin]]
|
||||
name = "chat"
|
||||
path = "src/chat.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "echo_client"
|
||||
path = "src/echo_client.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "echo_server"
|
||||
path = "src/echo_server.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hyper"
|
||||
path = "src/hyper.rs"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.1.18", features = ["async-await-preview"] }
|
||||
futures = "0.1.23"
|
||||
bytes = "0.4.9"
|
||||
hyper = "0.12.8"
|
||||
|
||||
# Avoid using crates.io for Tokio dependencies
|
||||
[patch.crates-io]
|
||||
tokio = { path = "../tokio" }
|
||||
tokio-codec = { path = "../tokio-codec" }
|
||||
tokio-current-thread = { path = "../tokio-current-thread" }
|
||||
tokio-executor = { path = "../tokio-executor" }
|
||||
tokio-fs = { path = "../tokio-fs" }
|
||||
tokio-futures = { path = "../tokio-futures" }
|
||||
tokio-io = { path = "../tokio-io" }
|
||||
tokio-reactor = { path = "../tokio-reactor" }
|
||||
tokio-signal = { path = "../tokio-signal" }
|
||||
tokio-tcp = { path = "../tokio-tcp" }
|
||||
tokio-threadpool = { path = "../tokio-threadpool" }
|
||||
tokio-timer = { path = "../tokio-timer" }
|
||||
tokio-tls = { path = "../tokio-tls" }
|
||||
tokio-udp = { path = "../tokio-udp" }
|
||||
tokio-uds = { path = "../tokio-uds" }
|
||||
@@ -1,9 +1,6 @@
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
extern crate futures; // v0.1
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::await;
|
||||
use tokio::codec::{LinesCodec, Decoder};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
@@ -95,7 +92,8 @@ async fn process(stream: TcpStream, state: Arc<Mutex<Shared>>) -> io::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// Create the shared state. This is how all the peers communicate.
|
||||
//
|
||||
// The server task will hold a handle to this. For every new client, the
|
||||
@@ -113,23 +111,21 @@ fn main() {
|
||||
println!("server running on localhost:6142");
|
||||
|
||||
// Start the Tokio runtime.
|
||||
tokio::run_async(async move {
|
||||
let mut incoming = listener.incoming();
|
||||
let mut incoming = listener.incoming();
|
||||
|
||||
while let Some(stream) = await!(incoming.next()) {
|
||||
let stream = match stream {
|
||||
Ok(stream) => stream,
|
||||
Err(_) => continue,
|
||||
};
|
||||
while let Some(stream) = await!(incoming.next()) {
|
||||
let stream = match stream {
|
||||
Ok(stream) => stream,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
let state = state.clone();
|
||||
let state = state.clone();
|
||||
|
||||
tokio::spawn_async(async move {
|
||||
if let Err(_) = await!(process(stream, state)) {
|
||||
eprintln!("failed to process connection");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
tokio::spawn_async(async move {
|
||||
if let Err(_) = await!(process(stream, state)) {
|
||||
eprintln!("failed to process connection");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::await;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::prelude::*;
|
||||
|
||||
@@ -36,7 +34,8 @@ async fn run_client(addr: &SocketAddr) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use std::env;
|
||||
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
@@ -44,10 +43,8 @@ fn main() {
|
||||
|
||||
// Connect to the echo serveer
|
||||
|
||||
tokio::run_async(async move {
|
||||
match await!(run_client(&addr)) {
|
||||
Ok(_) => println!("done."),
|
||||
Err(e) => eprintln!("echo client failed; error = {:?}", e),
|
||||
}
|
||||
});
|
||||
match await!(run_client(&addr)) {
|
||||
Ok(_) => println!("done."),
|
||||
Err(e) => eprintln!("echo client failed; error = {:?}", e),
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::await;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::prelude::*;
|
||||
|
||||
@@ -24,7 +22,8 @@ fn handle(mut stream: TcpStream) {
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
use std::env;
|
||||
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
@@ -34,12 +33,10 @@ fn main() {
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
tokio::run_async(async {
|
||||
let mut incoming = listener.incoming();
|
||||
let mut incoming = listener.incoming();
|
||||
|
||||
while let Some(stream) = await!(incoming.next()) {
|
||||
let stream = stream.unwrap();
|
||||
handle(stream);
|
||||
}
|
||||
});
|
||||
while let Some(stream) = await!(incoming.next()) {
|
||||
let stream = stream.unwrap();
|
||||
handle(stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::await;
|
||||
use tokio::prelude::*;
|
||||
use hyper::Client;
|
||||
|
||||
use std::time::Duration;
|
||||
use std::str;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let client = Client::new();
|
||||
|
||||
let uri = "http://httpbin.org/ip".parse().unwrap();
|
||||
|
||||
let response = await!({
|
||||
client.get(uri)
|
||||
.timeout(Duration::from_secs(10))
|
||||
}).unwrap();
|
||||
|
||||
println!("Response: {}", response.status());
|
||||
|
||||
let mut body = response.into_body();
|
||||
|
||||
while let Some(chunk) = await!(body.next()) {
|
||||
let chunk = chunk.unwrap();
|
||||
println!("chunk = {}", str::from_utf8(&chunk[..]).unwrap());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
use tokio::await;
|
||||
use tokio::timer::Delay;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[tokio::test]
|
||||
async fn success_no_async() {
|
||||
assert!(true);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[should_panic]
|
||||
async fn fail_no_async() {
|
||||
assert!(false);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn use_timer() {
|
||||
let when = Instant::now() + Duration::from_millis(10);
|
||||
await!(Delay::new(when));
|
||||
}
|
||||
+10
-17
@@ -1,5 +1,5 @@
|
||||
trigger: ["master"]
|
||||
pr: ["master"]
|
||||
trigger: ["master", "v0.1.x"]
|
||||
pr: ["master", "v0.1.x"]
|
||||
|
||||
jobs:
|
||||
# Check formatting
|
||||
@@ -45,10 +45,7 @@ jobs:
|
||||
- tokio-sync
|
||||
- tokio-threadpool
|
||||
- tokio-timer
|
||||
- tokio-trace
|
||||
- tokio-trace/tokio-trace-core
|
||||
- tokio-trace/test-log-support
|
||||
- tokio-trace/test_static_max_level_features
|
||||
- tokio-test
|
||||
|
||||
- template: ci/azure-cargo-check.yml
|
||||
parameters:
|
||||
@@ -67,20 +64,16 @@ jobs:
|
||||
- udp
|
||||
- uds
|
||||
- sync
|
||||
- experimental-tracing
|
||||
tokio-buf:
|
||||
- util
|
||||
|
||||
# Check async / await
|
||||
- template: ci/azure-cargo-check.yml
|
||||
# Run async-await tests
|
||||
- template: ci/azure-test-nightly.yml
|
||||
parameters:
|
||||
name: async_await
|
||||
displayName: Async / Await
|
||||
rust: nightly-2019-04-22
|
||||
noDefaultFeatures: ''
|
||||
benches: true
|
||||
crates:
|
||||
tokio:
|
||||
- async-await-preview
|
||||
name: test_nightly
|
||||
displayName: Test Async / Await
|
||||
rust: nightly-2019-04-25
|
||||
|
||||
# Try cross compiling
|
||||
- template: ci/azure-cross-compile.yml
|
||||
@@ -112,7 +105,7 @@ jobs:
|
||||
- test_sub_cross
|
||||
- test_linux
|
||||
- features
|
||||
- async_await
|
||||
- test_nightly
|
||||
- cross_32bit_linux
|
||||
- minrust
|
||||
- tsan
|
||||
|
||||
@@ -27,7 +27,3 @@ jobs:
|
||||
- script: cargo check ${{ parameters.noDefaultFeatures }} --features ${{ feature }}
|
||||
displayName: Check `${{ crate.key }}`, features = ${{ feature }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate.key }}
|
||||
|
||||
- ${{ if parameters.benches }}:
|
||||
- script: cargo check --benches --all
|
||||
displayName: Check benchmarks
|
||||
|
||||
@@ -8,6 +8,9 @@ jobs:
|
||||
parameters:
|
||||
rust_version: stable
|
||||
|
||||
- script: sudo apt-get update
|
||||
displayName: "apt-get update"
|
||||
|
||||
- script: sudo apt-get install gcc-multilib
|
||||
displayName: "Install gcc-multilib"
|
||||
|
||||
|
||||
@@ -2,7 +2,10 @@ steps:
|
||||
# Linux and macOS.
|
||||
- script: |
|
||||
set -e
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN
|
||||
curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain none
|
||||
export PATH=$PATH:$HOME/.cargo/bin
|
||||
rustup toolchain install $RUSTUP_TOOLCHAIN
|
||||
rustup default $RUSTUP_TOOLCHAIN
|
||||
echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
@@ -11,13 +14,16 @@ steps:
|
||||
|
||||
# Windows.
|
||||
- script: |
|
||||
echo "windows"
|
||||
curl -sSf -o rustup-init.exe https://win.rustup.rs
|
||||
rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN%
|
||||
rustup-init.exe -y --default-toolchain none
|
||||
set PATH=%PATH%;%USERPROFILE%\.cargo\bin
|
||||
rustup toolchain install %RUSTUP_TOOLCHAIN%
|
||||
rustup default %RUSTUP_TOOLCHAIN%
|
||||
echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin"
|
||||
env:
|
||||
RUSTUP_TOOLCHAIN: ${{parameters.rust_version}}
|
||||
displayName: "Install rust (windows)"
|
||||
displayName: Install rust (windows)
|
||||
condition: eq(variables['Agent.OS'], 'Windows_NT')
|
||||
|
||||
# All platforms.
|
||||
|
||||
@@ -2,7 +2,7 @@ steps:
|
||||
- bash: |
|
||||
set -e
|
||||
|
||||
if git log --no-merges -1 --format='%s' | grep -q '[ci-release]'; then
|
||||
if git log --no-merges -1 --format='%B' | grep -qF '[ci-release]'; then
|
||||
echo "##vso[task.setvariable variable=isRelease]true"
|
||||
fi
|
||||
failOnStderr: true
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
jobs:
|
||||
- job: ${{ parameters.name }}
|
||||
displayName: ${{ parameters.displayName }}
|
||||
pool:
|
||||
vmImage: ubuntu-16.04
|
||||
|
||||
steps:
|
||||
- template: azure-install-rust.yml
|
||||
parameters:
|
||||
rust_version: ${{ parameters.rust }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- script: cargo check --all
|
||||
displayName: cargo check --all
|
||||
|
||||
# Check benches
|
||||
- script: cargo check --benches --all
|
||||
displayName: Check benchmarks
|
||||
@@ -28,7 +28,7 @@ jobs:
|
||||
CI: 'True'
|
||||
displayName: cargo test -p ${{ crate }}
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
condition: and(succeeded(), not(variables['isRelease']))
|
||||
condition: and(succeeded(), ne(variables['isRelease'], 'true'))
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
|
||||
+1
-3
@@ -2,12 +2,12 @@
|
||||
# repository.
|
||||
[patch.crates-io]
|
||||
tokio = { path = "tokio" }
|
||||
tokio-async-await = { path = "tokio-async-await" }
|
||||
tokio-buf = { path = "tokio-buf" }
|
||||
tokio-codec = { path = "tokio-codec" }
|
||||
tokio-current-thread = { path = "tokio-current-thread" }
|
||||
tokio-executor = { path = "tokio-executor" }
|
||||
tokio-fs = { path = "tokio-fs" }
|
||||
tokio-futures = { path = "tokio-futures" }
|
||||
tokio-io = { path = "tokio-io" }
|
||||
tokio-reactor = { path = "tokio-reactor" }
|
||||
tokio-signal = { path = "tokio-signal" }
|
||||
@@ -16,7 +16,5 @@ tokio-threadpool = { path = "tokio-threadpool" }
|
||||
tokio-timer = { path = "tokio-timer" }
|
||||
tokio-tcp = { path = "tokio-tcp" }
|
||||
tokio-tls = { path = "tokio-tls" }
|
||||
tokio-trace = { path = "tokio-trace" }
|
||||
tokio-trace-core = { path = "tokio-trace/tokio-trace-core" }
|
||||
tokio-udp = { path = "tokio-udp" }
|
||||
tokio-uds = { path = "tokio-uds" }
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[build]
|
||||
target-dir = "../../target"
|
||||
@@ -1,49 +0,0 @@
|
||||
[package]
|
||||
name = "examples"
|
||||
edition = "2018"
|
||||
version = "0.1.0"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
|
||||
# Break out of the parent workspace
|
||||
[workspace]
|
||||
|
||||
[[bin]]
|
||||
name = "chat"
|
||||
path = "src/chat.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "echo_client"
|
||||
path = "src/echo_client.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "echo_server"
|
||||
path = "src/echo_server.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "hyper"
|
||||
path = "src/hyper.rs"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.1.18", features = ["async-await-preview"] }
|
||||
futures = "0.1.23"
|
||||
bytes = "0.4.9"
|
||||
hyper = "0.12.8"
|
||||
|
||||
# Avoid using crates.io for Tokio dependencies
|
||||
[patch.crates-io]
|
||||
tokio = { path = "../../tokio" }
|
||||
tokio-async-await = { path = "../" }
|
||||
tokio-codec = { path = "../../tokio-codec" }
|
||||
tokio-current-thread = { path = "../../tokio-current-thread" }
|
||||
tokio-executor = { path = "../../tokio-executor" }
|
||||
tokio-fs = { path = "../../tokio-fs" }
|
||||
tokio-io = { path = "../../tokio-io" }
|
||||
tokio-reactor = { path = "../../tokio-reactor" }
|
||||
tokio-signal = { path = "../../tokio-signal" }
|
||||
tokio-tcp = { path = "../../tokio-tcp" }
|
||||
tokio-threadpool = { path = "../../tokio-threadpool" }
|
||||
tokio-timer = { path = "../../tokio-timer" }
|
||||
tokio-tls = { path = "../../tokio-tls" }
|
||||
tokio-udp = { path = "../../tokio-udp" }
|
||||
tokio-uds = { path = "../../tokio-uds" }
|
||||
@@ -1,33 +0,0 @@
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
extern crate hyper;
|
||||
|
||||
use tokio::prelude::*;
|
||||
use hyper::Client;
|
||||
|
||||
use std::time::Duration;
|
||||
use std::str;
|
||||
|
||||
pub fn main() {
|
||||
tokio::run_async(async {
|
||||
let client = Client::new();
|
||||
|
||||
let uri = "http://httpbin.org/ip".parse().unwrap();
|
||||
|
||||
let response = await!({
|
||||
client.get(uri)
|
||||
.timeout(Duration::from_secs(10))
|
||||
}).unwrap();
|
||||
|
||||
println!("Response: {}", response.status());
|
||||
|
||||
let mut body = response.into_body();
|
||||
|
||||
while let Some(chunk) = await!(body.next()) {
|
||||
let chunk = chunk.unwrap();
|
||||
println!("chunk = {}", str::from_utf8(&chunk[..]).unwrap());
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
#![doc(hidden)]
|
||||
|
||||
pub mod backward;
|
||||
pub mod forward;
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "tokio-buf"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
[package]
|
||||
name = "tokio-codec"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc URL.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.1"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "tokio-current-thread"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# 0.1.8 (June 2, 2019)
|
||||
|
||||
### Added
|
||||
- Add `executor::exit` to allow other executors inside `threadpool::blocking` (#1155).
|
||||
|
||||
# 0.1.7 (March 22, 2019)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
[package]
|
||||
name = "tokio-executor"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
documentation = "https://docs.rs/tokio-executor/0.1.7/tokio_executor"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Task execution related traits and utilities.
|
||||
|
||||
[Documentation](https://docs.rs/tokio-executor/0.1.7/tokio_executor)
|
||||
[Documentation](https://docs.rs/tokio-executor/0.1.8/tokio_executor)
|
||||
|
||||
## Overview
|
||||
|
||||
@@ -31,10 +31,10 @@ executor, including:
|
||||
|
||||
* [`Park`] abstracts over blocking and unblocking the current thread.
|
||||
|
||||
[`Executor`]: https://docs.rs/tokio-executor/0.1.7/tokio_executor/trait.Executor.html
|
||||
[`enter`]: https://docs.rs/tokio-executor/0.1.7/tokio_executor/fn.enter.html
|
||||
[`DefaultExecutor`]: https://docs.rs/tokio-executor/0.1.7/tokio_executor/struct.DefaultExecutor.html
|
||||
[`Park`]: https://docs.rs/tokio-executor/0.1.7/tokio_executor/park/trait.Park.html
|
||||
[`Executor`]: https://docs.rs/tokio-executor/0.1.8/tokio_executor/trait.Executor.html
|
||||
[`enter`]: https://docs.rs/tokio-executor/0.1.8/tokio_executor/fn.enter.html
|
||||
[`DefaultExecutor`]: https://docs.rs/tokio-executor/0.1.8/tokio_executor/struct.DefaultExecutor.html
|
||||
[`Park`]: https://docs.rs/tokio-executor/0.1.8/tokio_executor/park/trait.Park.html
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -67,6 +67,42 @@ pub fn enter() -> Result<Enter, EnterError> {
|
||||
})
|
||||
}
|
||||
|
||||
// Forces the current "entered" state to be cleared while the closure
|
||||
// is executed.
|
||||
//
|
||||
// # Warning
|
||||
//
|
||||
// This is hidden for a reason. Do not use without fully understanding
|
||||
// executors. Misuing can easily cause your program to deadlock.
|
||||
#[doc(hidden)]
|
||||
pub fn exit<F: FnOnce() -> R, R>(f: F) -> R {
|
||||
// Reset in case the closure panics
|
||||
struct Reset;
|
||||
impl Drop for Reset {
|
||||
fn drop(&mut self) {
|
||||
ENTERED.with(|c| {
|
||||
c.set(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ENTERED.with(|c| {
|
||||
debug_assert!(c.get());
|
||||
c.set(false);
|
||||
});
|
||||
|
||||
let reset = Reset;
|
||||
let ret = f();
|
||||
::std::mem::forget(reset);
|
||||
|
||||
ENTERED.with(|c| {
|
||||
assert!(!c.get(), "closure claimed permanent executor");
|
||||
c.set(true);
|
||||
});
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
impl Enter {
|
||||
/// Register a callback to be invoked if and when the thread
|
||||
/// ceased to act as an executor.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#![deny(missing_docs, missing_debug_implementations, warnings)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.7")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-executor/0.1.8")]
|
||||
// Our MSRV doesn't allow us to fix these warnings yet
|
||||
#![allow(rust_2018_idioms)]
|
||||
|
||||
//! Task execution related traits and utilities.
|
||||
//!
|
||||
@@ -61,7 +63,7 @@ mod global;
|
||||
pub mod park;
|
||||
mod typed;
|
||||
|
||||
pub use enter::{enter, Enter, EnterError};
|
||||
pub use enter::{enter, exit, Enter, EnterError};
|
||||
pub use error::SpawnError;
|
||||
pub use executor::Executor;
|
||||
pub use global::{spawn, with_default, DefaultExecutor};
|
||||
|
||||
+3
-3
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "tokio-fs"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.6"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "tokio-async-await"
|
||||
name = "tokio-futures"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
version = "0.1.7"
|
||||
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-async-await/0.1.7"
|
||||
documentation = "https://docs.rs/tokio-futures/0.1.0"
|
||||
description = """
|
||||
Experimental async/await support for Tokio
|
||||
Experimental std::future::Future and async/await support for Tokio
|
||||
"""
|
||||
categories = ["asynchronous"]
|
||||
|
||||
@@ -25,5 +25,5 @@ tokio-io = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
bytes = "0.4.9"
|
||||
tokio = "0.1.8"
|
||||
hyper = "0.12.8"
|
||||
tokio = { version = "0.1.8", path = "../tokio" }
|
||||
@@ -25,9 +25,9 @@ Then, get started. In your application, add:
|
||||
|
||||
```rust
|
||||
// The nightly features that are commonly needed with async / await
|
||||
#![feature(await_macro, async_await, futures_api)]
|
||||
#![feature(await_macro, async_await)]
|
||||
|
||||
// This pulls in the `tokio-async-await` crate. While Rust 2018 doesn't require
|
||||
// This pulls in the `tokio-futures` crate. While Rust 2018 doesn't require
|
||||
// `extern crate`, we need to pull in the macros.
|
||||
#[macro_use]
|
||||
extern crate tokio;
|
||||
@@ -1,3 +1,5 @@
|
||||
//! Converts a `std::future::Future` into an 0.1 `Future.
|
||||
|
||||
use futures::{Future, Poll};
|
||||
|
||||
use std::future::Future as StdFuture;
|
||||
@@ -5,18 +7,18 @@ use std::pin::Pin;
|
||||
use std::ptr;
|
||||
use std::task::{Context, Poll as StdPoll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
/// Convert an 0.3 `Future` to an 0.1 `Future`.
|
||||
/// Converts a `std::future::Future` into an 0.1 `Future.
|
||||
#[derive(Debug)]
|
||||
pub struct Compat<T>(Pin<Box<T>>);
|
||||
|
||||
impl<T> Compat<T> {
|
||||
/// Create a new `Compat` backed by `future`.
|
||||
pub fn new(future: T) -> Compat<T> {
|
||||
pub(crate) fn new(future: T) -> Compat<T> {
|
||||
Compat(Box::pin(future))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a value into one that can be used with `await!`.
|
||||
#[doc(hidden)]
|
||||
pub trait IntoAwaitable {
|
||||
type Awaitable;
|
||||
|
||||
@@ -74,7 +76,11 @@ unsafe fn clone_raw(_data: *const ()) -> RawWaker {
|
||||
unsafe fn drop_raw(_data: *const ()) {}
|
||||
|
||||
unsafe fn wake(_data: *const ()) {
|
||||
unimplemented!("async-await-preview currently only supports futures 0.1. Use the compatibility layer of futures 0.3 instead, if you want to use futures 0.3.");
|
||||
unimplemented!(
|
||||
"async-await-preview currently only supports futures 0.1. Use \
|
||||
the compatibility layer of futures 0.3 instead, if you want \
|
||||
to use futures 0.3."
|
||||
);
|
||||
}
|
||||
|
||||
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(clone_raw, wake, wake, drop_raw);
|
||||
@@ -1,10 +1,12 @@
|
||||
//! Converts an 0.1 `Future` into a `std::future::Future`.
|
||||
//!
|
||||
use futures::{Async, Future};
|
||||
|
||||
use std::future::Future as StdFuture;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll as StdPoll};
|
||||
|
||||
/// Converts an 0.1 `Future` into an 0.3 `Future`.
|
||||
/// Converts an 0.1 `Future` into a `std::future::Future`.
|
||||
#[derive(Debug)]
|
||||
pub struct Compat<T>(T);
|
||||
|
||||
@@ -31,7 +33,7 @@ pub(crate) fn convert_poll_stream<T, E>(
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert a value into one that can be used with `await!`.
|
||||
#[doc(hidden)]
|
||||
pub trait IntoAwaitable {
|
||||
type Awaitable;
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
//! Compatibility layer between futures 0.1 and `std`.
|
||||
|
||||
pub mod backward;
|
||||
pub mod forward;
|
||||
|
||||
/// Convert a `std::future::Future` yielding `Result` into an 0.1 `Future`.
|
||||
pub fn into_01<T, Item, Error>(future: T) -> backward::Compat<T>
|
||||
where
|
||||
T: std::future::Future<Output = Result<Item, Error>>,
|
||||
{
|
||||
backward::Compat::new(future)
|
||||
}
|
||||
|
||||
/// Convert a `std::future::Future` into an 0.1 `Future` with unit error.
|
||||
pub fn infallible_into_01<T>(future: T) -> impl futures::Future<Item = T::Output, Error = ()>
|
||||
where
|
||||
T: std::future::Future,
|
||||
{
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pub struct Map<T>(T);
|
||||
|
||||
impl<T> Map<T> {
|
||||
fn future<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
|
||||
unsafe { Pin::map_unchecked_mut(self, |x| &mut x.0) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: std::future::Future> std::future::Future for Map<T> {
|
||||
type Output = Result<T::Output, ()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
|
||||
match self.future().poll(cx) {
|
||||
Poll::Ready(v) => Poll::Ready(Ok(v)),
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
into_01(Map(future))
|
||||
}
|
||||
@@ -25,7 +25,7 @@ pub trait AsyncReadExt: AsyncRead {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// #![feature(async_await, await_macro)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
@@ -59,7 +59,7 @@ pub trait AsyncReadExt: AsyncRead {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// #![feature(async_await, await_macro)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
@@ -78,7 +78,7 @@ pub trait AsyncReadExt: AsyncRead {
|
||||
/// ## EOF is hit before `buf` is filled
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// #![feature(async_await, await_macro)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
@@ -110,7 +110,7 @@ pub trait AsyncWriteExt: AsyncWrite {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// #![feature(async_await, await_macro)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
@@ -139,7 +139,7 @@ pub trait AsyncWriteExt: AsyncWrite {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// #![feature(async_await, await_macro)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
@@ -163,7 +163,7 @@ pub trait AsyncWriteExt: AsyncWrite {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(async_await, await_macro, futures_api)]
|
||||
/// #![feature(async_await, await_macro)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg(feature = "async-await-preview")]
|
||||
#![feature(rust_2018_preview, async_await, await_macro, futures_api)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-async-await/0.1.7")]
|
||||
#![feature(await_macro)]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-futures/0.1.0")]
|
||||
#![deny(missing_docs, missing_debug_implementations)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
@@ -13,7 +13,7 @@ pub trait StreamExt: Stream {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```edition2018
|
||||
/// #![feature(await_macro, async_await, futures_api)]
|
||||
/// #![feature(await_macro, async_await)]
|
||||
/// tokio::run_async(async {
|
||||
/// // The extension trait can also be imported with
|
||||
/// // `use tokio::prelude::*`.
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "tokio-io"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - Readme.md
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.12"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
[package]
|
||||
name = "tokio-macros"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[features]
|
||||
# This feature comes with no promise of stability. Things will
|
||||
# break with each patch release. Use at your own risk.
|
||||
async-await-preview = []
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "0.4.27"
|
||||
quote = "0.6.11"
|
||||
syn = { version = "0.15.27", features = ["full", "extra-traits", "visit-mut"] }
|
||||
@@ -0,0 +1,47 @@
|
||||
Copyright (c) 2019 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.
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Yoshua Wuyts
|
||||
|
||||
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,13 @@
|
||||
# Tokio Macros
|
||||
|
||||
Procedural macros for use with Tokio
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,81 @@
|
||||
#![cfg(feature = "async-await-preview")]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Define the program entry point
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// println!("Hello world");
|
||||
/// }
|
||||
#[proc_macro_attribute]
|
||||
pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
|
||||
let ret = &input.decl.output;
|
||||
let name = &input.ident;
|
||||
let body = &input.block;
|
||||
|
||||
if input.asyncness.is_none() {
|
||||
let tokens = quote_spanned! { input.span() =>
|
||||
compile_error!("the async keyword is missing from the function declaration");
|
||||
};
|
||||
|
||||
return TokenStream::from(tokens);
|
||||
}
|
||||
|
||||
let result = quote! {
|
||||
fn #name() #ret {
|
||||
let mut rt = tokio::runtime::Runtime::new().unwrap();
|
||||
rt.block_on_async(async { #body })
|
||||
}
|
||||
};
|
||||
|
||||
result.into()
|
||||
}
|
||||
|
||||
/// Define a Tokio aware unit test
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #[tokio::test]
|
||||
/// async fn my_test() {
|
||||
/// assert!(true);
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
|
||||
let ret = &input.decl.output;
|
||||
let name = &input.ident;
|
||||
let body = &input.block;
|
||||
let attrs = &input.attrs;
|
||||
|
||||
if input.asyncness.is_none() {
|
||||
let tokens = quote_spanned! { input.span() =>
|
||||
compile_error!("the async keyword is missing from the function declaration");
|
||||
};
|
||||
|
||||
return TokenStream::from(tokens);
|
||||
}
|
||||
|
||||
let result = quote! {
|
||||
#[test]
|
||||
#(#attrs)*
|
||||
fn #name() #ret {
|
||||
let mut rt = tokio::runtime::current_thread::Runtime::new().unwrap();
|
||||
rt.block_on_async(async { #body })
|
||||
}
|
||||
};
|
||||
|
||||
result.into()
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
[package]
|
||||
name = "tokio-reactor"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.9"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
[package]
|
||||
name = "tokio-signal"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Update doc URL.
|
||||
# - Create "v0.2.x" git tag.
|
||||
version = "0.2.8"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
@@ -31,7 +33,7 @@ tokio-io = "0.1"
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
mio-uds = "0.6"
|
||||
signal-hook = "0.1"
|
||||
signal-hook-registry = "~1"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = "0.1.8"
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
pub extern crate libc;
|
||||
extern crate mio;
|
||||
extern crate mio_uds;
|
||||
extern crate signal_hook;
|
||||
extern crate signal_hook_registry;
|
||||
|
||||
use std::io::prelude::*;
|
||||
use std::io::{self, Error, ErrorKind};
|
||||
@@ -153,7 +153,7 @@ fn action(slot: &SignalInfo, mut sender: &UnixStream) {
|
||||
/// This will register the signal handler if it hasn't already been registered,
|
||||
/// returning any error along the way if that fails.
|
||||
fn signal_enable(signal: c_int) -> io::Result<()> {
|
||||
if signal_hook::FORBIDDEN.contains(&signal) {
|
||||
if signal_hook_registry::FORBIDDEN.contains(&signal) {
|
||||
return Err(Error::new(
|
||||
ErrorKind::Other,
|
||||
format!("Refusing to register signal {}", signal),
|
||||
@@ -168,7 +168,8 @@ fn signal_enable(signal: c_int) -> io::Result<()> {
|
||||
let mut registered = Ok(());
|
||||
siginfo.init.call_once(|| {
|
||||
registered = unsafe {
|
||||
signal_hook::register(signal, move || action(siginfo, &globals.sender)).map(|_| ())
|
||||
signal_hook_registry::register(signal, move || action(siginfo, &globals.sender))
|
||||
.map(|_| ())
|
||||
};
|
||||
if registered.is_ok() {
|
||||
siginfo.initialized.store(true, Ordering::Relaxed);
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# 0.1.6 (June 4, 2019)
|
||||
|
||||
### Added
|
||||
- Add Sync impl for Lock (#1117).
|
||||
|
||||
# 0.1.5 (April 22, 2019)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
[package]
|
||||
name = "tokio-sync"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-sync/0.1.5/tokio_sync"
|
||||
documentation = "https://docs.rs/tokio-sync/0.1.6/tokio_sync"
|
||||
description = """
|
||||
Synchronization utilities.
|
||||
"""
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Synchronization utilities
|
||||
|
||||
[Documentation](https://docs.rs/tokio-sync/0.1.5/tokio_sync/)
|
||||
[Documentation](https://docs.rs/tokio-sync/0.1.6/tokio_sync/)
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-sync/0.1.5")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-sync/0.1.6")]
|
||||
#![deny(missing_debug_implementations, missing_docs, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
|
||||
@@ -72,9 +72,11 @@ pub struct Lock<T> {
|
||||
#[derive(Debug)]
|
||||
pub struct LockGuard<T>(Lock<T>);
|
||||
|
||||
// As long as T: Send, it's fine to send Lock<T> to other threads.
|
||||
// If T was not Send, sending a Lock<T> would be bad, since you can access T through Lock<T>.
|
||||
// As long as T: Send, it's fine to send and share Lock<T> between threads.
|
||||
// If T was not Send, sending and sharing a Lock<T> would be bad, since you can access T through
|
||||
// Lock<T>.
|
||||
unsafe impl<T> Send for Lock<T> where T: Send {}
|
||||
unsafe impl<T> Sync for Lock<T> where T: Send {}
|
||||
unsafe impl<T> Sync for LockGuard<T> where T: Send + Sync {}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "tokio-tcp"
|
||||
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
@@ -29,4 +29,5 @@ futures = "0.1.19"
|
||||
|
||||
[dev-dependencies]
|
||||
env_logger = { version = "0.5", default-features = false }
|
||||
net2 = "0.2"
|
||||
tokio = "0.1.13"
|
||||
|
||||
@@ -61,7 +61,7 @@ impl TcpListener {
|
||||
/// use tokio::net::TcpListener;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// let addr = "127.0.0.1:8080".parse::<SocketAddr>()?;
|
||||
/// let addr = "127.0.0.1:0".parse::<SocketAddr>()?;
|
||||
/// let listener = TcpListener::bind(&addr)?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -109,7 +109,7 @@ impl TcpListener {
|
||||
/// use futures::Async;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// let addr = "127.0.0.1:8080".parse::<SocketAddr>()?;
|
||||
/// let addr = "127.0.0.1:0".parse::<SocketAddr>()?;
|
||||
/// let mut listener = TcpListener::bind(&addr)?;
|
||||
/// match listener.poll_accept() {
|
||||
/// Ok(Async::Ready((_socket, addr))) => println!("listener ready to accept: {:?}", addr),
|
||||
@@ -167,7 +167,7 @@ impl TcpListener {
|
||||
/// use futures::Async;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// let addr = "127.0.0.1:8080".parse::<SocketAddr>()?;
|
||||
/// let addr = "127.0.0.1:0".parse::<SocketAddr>()?;
|
||||
/// let mut listener = TcpListener::bind(&addr)?;
|
||||
/// match listener.poll_accept_std() {
|
||||
/// Ok(Async::Ready((_socket, addr))) => println!("listener ready to accept: {:?}", addr),
|
||||
@@ -230,7 +230,7 @@ impl TcpListener {
|
||||
/// use tokio::reactor::Handle;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// let std_listener = StdTcpListener::bind("127.0.0.1:8080")?;
|
||||
/// let std_listener = StdTcpListener::bind("127.0.0.1:0")?;
|
||||
/// let listener = TcpListener::from_std(std_listener, &Handle::default())?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
@@ -296,7 +296,7 @@ impl TcpListener {
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// let addr = "127.0.0.1:8080".parse::<SocketAddr>()?;
|
||||
/// let addr = "127.0.0.1:0".parse::<SocketAddr>()?;
|
||||
/// let listener = TcpListener::bind(&addr)?;
|
||||
///
|
||||
/// listener.incoming()
|
||||
@@ -326,7 +326,7 @@ impl TcpListener {
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// let addr = "127.0.0.1:8080".parse::<SocketAddr>()?;
|
||||
/// let addr = "127.0.0.1:0".parse::<SocketAddr>()?;
|
||||
/// let listener = TcpListener::bind(&addr)?;
|
||||
/// listener.set_ttl(100).expect("could not set TTL");
|
||||
/// assert_eq!(listener.ttl()?, 100);
|
||||
@@ -350,7 +350,7 @@ impl TcpListener {
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// # fn main() -> Result<(), Box<std::error::Error>> {
|
||||
/// let addr = "127.0.0.1:8080".parse::<SocketAddr>()?;
|
||||
/// let addr = "127.0.0.1:0".parse::<SocketAddr>()?;
|
||||
/// let listener = TcpListener::bind(&addr)?;
|
||||
/// listener.set_ttl(100).expect("could not set TTL");
|
||||
/// # Ok(())
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
extern crate env_logger;
|
||||
extern crate futures;
|
||||
extern crate mio;
|
||||
extern crate net2;
|
||||
extern crate tokio_io;
|
||||
extern crate tokio_tcp;
|
||||
|
||||
@@ -84,13 +85,14 @@ fn accept2() {
|
||||
t.join().unwrap();
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix {
|
||||
#[cfg(target_os = "linux")]
|
||||
mod linux {
|
||||
use tokio_tcp::TcpStream;
|
||||
|
||||
use env_logger;
|
||||
use futures::{future, Future};
|
||||
use mio::unix::UnixReady;
|
||||
use net2::TcpStreamExt;
|
||||
use tokio_io::AsyncRead;
|
||||
|
||||
use std::io::Write;
|
||||
@@ -105,6 +107,7 @@ mod unix {
|
||||
let addr = t!(srv.local_addr());
|
||||
let t = thread::spawn(move || {
|
||||
let mut client = t!(srv.accept()).0;
|
||||
client.set_linger(Some(Duration::from_millis(0))).unwrap();
|
||||
client.write(b"hello world").unwrap();
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
});
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
[package]
|
||||
name = "tokio-trace-core"
|
||||
name = "tokio-test"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.2.x" git tag.
|
||||
version = "0.2.0"
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-trace-core/0.2.0/tokio_trace_core"
|
||||
documentation = "https://docs.rs/tokio-test/0.1.0/tokio_test"
|
||||
description = """
|
||||
Core primitives for tokio-trace.
|
||||
Testing utilities for Tokio- and futures-based code
|
||||
"""
|
||||
categories = ["development-tools::debugging"]
|
||||
keywords = ["logging", "tracing"]
|
||||
categories = ["asynchronous", "testing"]
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
lazy_static = "1.0.0"
|
||||
futures = "0.1"
|
||||
tokio-timer = "0.2"
|
||||
tokio-executor = "0.1"
|
||||
@@ -0,0 +1,36 @@
|
||||
# tokio-test
|
||||
|
||||
Tokio and Futures based testing utilities
|
||||
|
||||
[Documenation](https://docs.rs/tokio-test)
|
||||
|
||||
## Usage
|
||||
|
||||
First, add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dev-dependencies]
|
||||
tokio-test = "0.1.0"
|
||||
```
|
||||
|
||||
Next, add this to your crate:
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate tokio_test;
|
||||
```
|
||||
|
||||
You can find extensive documentation and examples about how to use this crate
|
||||
online at [https://tokio.rs](https://tokio.rs). The [API
|
||||
documentation](https://docs.rs/tokio-test) is also a great place to get started
|
||||
for the nitty-gritty.
|
||||
|
||||
## 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.
|
||||
@@ -0,0 +1,256 @@
|
||||
//! A mocked clock for use with `tokio_timer` based futures.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate tokio_test;
|
||||
//! # extern crate futures;
|
||||
//! # extern crate tokio_timer;
|
||||
//! # use tokio_test::clock;
|
||||
//! # use tokio_timer::Delay;
|
||||
//! # use std::time::Duration;
|
||||
//! # use futures::Future;
|
||||
//! clock::mock(|handle| {
|
||||
//! let mut delay = Delay::new(handle.now() + Duration::from_secs(1));
|
||||
//!
|
||||
//! assert_not_ready!(delay.poll());
|
||||
//!
|
||||
//! handle.advance(Duration::from_secs(1));
|
||||
//!
|
||||
//! assert_ready!(delay.poll());
|
||||
//! });
|
||||
//! ```
|
||||
|
||||
use futures::{future::lazy, Future};
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio_executor::park::{Park, Unpark};
|
||||
use tokio_timer::clock::{Clock, Now};
|
||||
use tokio_timer::Timer;
|
||||
|
||||
/// Run the provided closure with a `MockClock` that starts at the current time.
|
||||
pub fn mock<F, R>(f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Handle) -> R,
|
||||
{
|
||||
let mut mock = MockClock::new();
|
||||
mock.enter(f)
|
||||
}
|
||||
|
||||
/// Run the provided closure with a `MockClock` that starts at the provided `Instant`.
|
||||
pub fn mock_at<F, R>(instant: Instant, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Handle) -> R,
|
||||
{
|
||||
let mut mock = MockClock::with_instant(instant);
|
||||
mock.enter(f)
|
||||
}
|
||||
|
||||
/// Mock clock for use with `tokio-timer` futures.
|
||||
///
|
||||
/// A mock timer that is able to advance and wake after a
|
||||
/// certain duration.
|
||||
#[derive(Debug)]
|
||||
pub struct MockClock {
|
||||
time: MockTime,
|
||||
clock: Clock,
|
||||
}
|
||||
|
||||
/// A handle to the `MockClock`.
|
||||
#[derive(Debug)]
|
||||
pub struct Handle {
|
||||
timer: Timer<MockPark>,
|
||||
time: MockTime,
|
||||
}
|
||||
|
||||
type Inner = Arc<Mutex<State>>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct MockTime {
|
||||
inner: Inner,
|
||||
_pd: PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockNow {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockPark {
|
||||
inner: Inner,
|
||||
_pd: PhantomData<Rc<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct MockUnpark {
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct State {
|
||||
base: Instant,
|
||||
advance: Duration,
|
||||
unparked: bool,
|
||||
park_for: Option<Duration>,
|
||||
}
|
||||
|
||||
impl MockClock {
|
||||
/// Create a new `MockClock` with the current time.
|
||||
pub fn new() -> Self {
|
||||
MockClock::with_instant(Instant::now())
|
||||
}
|
||||
|
||||
/// Create a `MockClock` with its current time at a duration from now
|
||||
///
|
||||
/// This will create a clock with `Instant::now() + duration` as the current time.
|
||||
pub fn with_duration(duration: Duration) -> Self {
|
||||
let instant = Instant::now() + duration;
|
||||
MockClock::with_instant(instant)
|
||||
}
|
||||
|
||||
/// Create a `MockClock` that sets its current time as the `Instant` provided.
|
||||
pub fn with_instant(instant: Instant) -> Self {
|
||||
let time = MockTime::new(instant);
|
||||
let clock = Clock::new_with_now(time.mock_now());
|
||||
|
||||
MockClock { time, clock }
|
||||
}
|
||||
|
||||
/// Enter the `MockClock` context.
|
||||
pub fn enter<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Handle) -> R,
|
||||
{
|
||||
let mut enter = ::tokio_executor::enter().unwrap();
|
||||
|
||||
::tokio_timer::clock::with_default(&self.clock, &mut enter, |enter| {
|
||||
let park = self.time.mock_park();
|
||||
let timer = Timer::new(park);
|
||||
let handle = timer.handle();
|
||||
let time = self.time.clone();
|
||||
|
||||
::tokio_timer::with_default(&handle, enter, |_| {
|
||||
let mut handle = Handle::new(timer, time);
|
||||
lazy(|| Ok::<_, ()>(f(&mut handle))).wait().unwrap()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Handle {
|
||||
pub(self) fn new(timer: Timer<MockPark>, time: MockTime) -> Self {
|
||||
Handle { timer, time }
|
||||
}
|
||||
|
||||
/// Turn the internal timer and mock park for the provided duration.
|
||||
pub fn turn(&mut self, duration: Option<Duration>) {
|
||||
self.timer.turn(duration).unwrap();
|
||||
}
|
||||
|
||||
/// Advance the `MockClock` by the provided duration.
|
||||
pub fn advance(&mut self, duration: Duration) {
|
||||
let inner = self.timer.get_park().inner.clone();
|
||||
let deadline = inner.lock().unwrap().now() + duration;
|
||||
|
||||
while inner.lock().unwrap().now() < deadline {
|
||||
let dur = deadline - inner.lock().unwrap().now();
|
||||
self.turn(Some(dur));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the currently mocked time
|
||||
pub fn now(&mut self) -> Instant {
|
||||
self.time.now()
|
||||
}
|
||||
}
|
||||
|
||||
impl MockTime {
|
||||
pub(crate) fn new(now: Instant) -> MockTime {
|
||||
let state = State {
|
||||
base: now,
|
||||
advance: Duration::default(),
|
||||
unparked: false,
|
||||
park_for: None,
|
||||
};
|
||||
|
||||
MockTime {
|
||||
inner: Arc::new(Mutex::new(state)),
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn mock_now(&self) -> MockNow {
|
||||
let inner = self.inner.clone();
|
||||
MockNow { inner }
|
||||
}
|
||||
|
||||
pub(crate) fn mock_park(&self) -> MockPark {
|
||||
let inner = self.inner.clone();
|
||||
MockPark {
|
||||
inner,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn now(&self) -> Instant {
|
||||
self.inner.lock().unwrap().now()
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn now(&self) -> Instant {
|
||||
self.base + self.advance
|
||||
}
|
||||
|
||||
fn advance(&mut self, duration: Duration) {
|
||||
self.advance += duration;
|
||||
}
|
||||
}
|
||||
|
||||
impl Park for MockPark {
|
||||
type Unpark = MockUnpark;
|
||||
type Error = ();
|
||||
|
||||
fn unpark(&self) -> Self::Unpark {
|
||||
let inner = self.inner.clone();
|
||||
MockUnpark { inner }
|
||||
}
|
||||
|
||||
fn park(&mut self) -> Result<(), Self::Error> {
|
||||
let mut inner = self.inner.lock().map_err(|_| ())?;
|
||||
|
||||
let duration = inner.park_for.take().expect("call park_for first");
|
||||
|
||||
inner.advance(duration);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
|
||||
if let Some(duration) = inner.park_for.take() {
|
||||
inner.advance(duration);
|
||||
} else {
|
||||
inner.advance(duration);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpark for MockUnpark {
|
||||
fn unpark(&self) {
|
||||
if let Ok(mut inner) = self.inner.lock() {
|
||||
inner.unparked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Now for MockNow {
|
||||
fn now(&self) -> Instant {
|
||||
self.inner.lock().unwrap().now()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-test/0.1.0")]
|
||||
#![deny(missing_docs, missing_debug_implementations, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
|
||||
//! Tokio and Futures based testing utilites
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate futures;
|
||||
//! # #[macro_use] extern crate tokio_test;
|
||||
//! # use futures::{Future, future};
|
||||
//! let mut fut = future::ok::<(), ()>(());
|
||||
//! assert_ready!(fut.poll());
|
||||
//! ```
|
||||
|
||||
extern crate futures;
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_timer;
|
||||
|
||||
pub mod clock;
|
||||
mod macros;
|
||||
pub mod task;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod codegen {
|
||||
pub mod futures {
|
||||
pub use futures::*;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
//! A collection of useful macros for testing futures and tokio based code
|
||||
|
||||
/// Assert if a poll is ready
|
||||
#[macro_export]
|
||||
macro_rules! assert_ready {
|
||||
($e:expr) => {{
|
||||
use $crate::codegen::futures::Async::Ready;
|
||||
match $e {
|
||||
Ok(Ready(v)) => v,
|
||||
Ok(_) => panic!("not ready"),
|
||||
Err(e) => panic!("error = {:?}", e),
|
||||
}
|
||||
}};
|
||||
($e:expr, $($msg:tt),+) => {{
|
||||
use $crate::codegen::futures::Async::Ready;
|
||||
match $e {
|
||||
Ok(Ready(v)) => v,
|
||||
Ok(_) => {
|
||||
let msg = format_args!($($msg),+);
|
||||
panic!("not ready; {}", msg)
|
||||
}
|
||||
Err(e) => {
|
||||
let msg = format!($($msg),+);
|
||||
panic!("error = {:?}; {}", e, msg)
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Asset if the poll is not ready
|
||||
#[macro_export]
|
||||
macro_rules! assert_not_ready {
|
||||
($e:expr) => {{
|
||||
use $crate::codegen::futures::Async::{Ready, NotReady};
|
||||
match $e {
|
||||
Ok(NotReady) => {}
|
||||
Ok(Ready(v)) => panic!("ready; value = {:?}", v),
|
||||
Err(e) => panic!("error = {:?}", e),
|
||||
}
|
||||
}};
|
||||
($e:expr, $($msg:tt),+) => {{
|
||||
use $crate::codegen::futures::Async::{Ready, NotReady};
|
||||
match $e {
|
||||
Ok(NotReady) => {}
|
||||
Ok(Ready(v)) => {
|
||||
let msg = format_args!($($msg),+);
|
||||
panic!("ready; value = {:?}; {}", v, msg)
|
||||
}
|
||||
Err(e) => {
|
||||
let msg = format_args!($($msg),+);
|
||||
panic!("error = {:?}; {}", e, msg)
|
||||
}
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
/// Assert if a poll is ready and check for equality on the value
|
||||
#[macro_export]
|
||||
macro_rules! assert_ready_eq {
|
||||
($e:expr, $expect:expr) => {
|
||||
use $crate::codegen::futures::Async::Ready;
|
||||
match $e {
|
||||
Ok(e) => assert_eq!(e, Ready($expect)),
|
||||
Err(e) => panic!("error = {:?}", e),
|
||||
}
|
||||
};
|
||||
|
||||
($e:expr, $expect:expr, $($msg:tt),+) => {
|
||||
use $crate::codegen::futures::Async::Ready;
|
||||
match $e {
|
||||
Ok(e) => assert_eq!(e, Ready($expect), $($msg)+),
|
||||
Err(e) => {
|
||||
let msg = format_args!($($msg),+);
|
||||
panic!("error = {:?}; {}", e, msg)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Assert if the deadline has passed
|
||||
#[macro_export]
|
||||
macro_rules! assert_elapsed {
|
||||
($e:expr) => {
|
||||
assert!($e.unwrap_err().is_elapsed());
|
||||
};
|
||||
|
||||
($e:expr, $($msg:expr),+) => {
|
||||
assert!($e.unwrap_err().is_elapsed(), $msg);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use futures::{future, Async, Future, Poll};
|
||||
|
||||
#[test]
|
||||
fn assert_ready() {
|
||||
let mut fut = future::ok::<(), ()>(());
|
||||
assert_ready!(fut.poll());
|
||||
let mut fut = future::ok::<(), ()>(());
|
||||
assert_ready!(fut.poll(), "some message");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn assert_ready_err() {
|
||||
let mut fut = future::err::<(), ()>(());
|
||||
assert_ready!(fut.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_not_ready() {
|
||||
let poll: Poll<(), ()> = Ok(Async::NotReady);
|
||||
assert_not_ready!(poll);
|
||||
assert_not_ready!(poll, "some message");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn assert_not_ready_err() {
|
||||
let mut fut = future::err::<(), ()>(());
|
||||
assert_not_ready!(fut.poll());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assert_ready_eq() {
|
||||
let mut fut = future::ok::<(), ()>(());
|
||||
assert_ready_eq!(fut.poll(), ());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
//! Futures task based helpers
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! This example will use the `MockTask` to set the current task on
|
||||
//! poll.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate tokio_test;
|
||||
//! # extern crate futures;
|
||||
//! # use tokio_test::task::MockTask;
|
||||
//! # use futures::{sync::mpsc, Stream, Sink, Future, Async};
|
||||
//! let mut task = MockTask::new();
|
||||
//! let (tx, mut rx) = mpsc::channel(5);
|
||||
//!
|
||||
//! tx.send(()).wait();
|
||||
//!
|
||||
//! assert_ready_eq!(task.enter(|| rx.poll()), Some(()));
|
||||
//! ```
|
||||
|
||||
use futures::executor::{spawn, Notify};
|
||||
use futures::{future, Async};
|
||||
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
|
||||
/// Mock task
|
||||
///
|
||||
/// A mock task is able to intercept and track notifications.
|
||||
#[derive(Debug)]
|
||||
pub struct MockTask {
|
||||
notify: Arc<ThreadNotify>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ThreadNotify {
|
||||
state: AtomicUsize,
|
||||
mutex: Mutex<()>,
|
||||
condvar: Condvar,
|
||||
}
|
||||
|
||||
const IDLE: usize = 0;
|
||||
const NOTIFY: usize = 1;
|
||||
const SLEEP: usize = 2;
|
||||
|
||||
impl MockTask {
|
||||
/// Create a new mock task
|
||||
pub fn new() -> Self {
|
||||
MockTask {
|
||||
notify: Arc::new(ThreadNotify::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Run a closure from the context of the task.
|
||||
///
|
||||
/// Any notifications resulting from the execution of the closure are
|
||||
/// tracked.
|
||||
pub fn enter<F, R>(&mut self, f: F) -> R
|
||||
where
|
||||
F: FnOnce() -> R,
|
||||
{
|
||||
self.notify.clear();
|
||||
|
||||
let res = spawn(future::lazy(|| Ok::<_, ()>(f()))).poll_future_notify(&self.notify, 0);
|
||||
|
||||
match res.unwrap() {
|
||||
Async::Ready(v) => v,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if the inner future has received a readiness notification
|
||||
/// since the last call to `enter`.
|
||||
pub fn is_notified(&self) -> bool {
|
||||
self.notify.is_notified()
|
||||
}
|
||||
|
||||
/// Returns the number of references to the task notifier
|
||||
///
|
||||
/// The task itself holds a reference. The return value will never be zero.
|
||||
pub fn notifier_ref_count(&self) -> usize {
|
||||
Arc::strong_count(&self.notify)
|
||||
}
|
||||
}
|
||||
|
||||
impl ThreadNotify {
|
||||
fn new() -> Self {
|
||||
ThreadNotify {
|
||||
state: AtomicUsize::new(IDLE),
|
||||
mutex: Mutex::new(()),
|
||||
condvar: Condvar::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears any previously received notify, avoiding potential spurrious
|
||||
/// notifications. This should only be called immediately before running the
|
||||
/// task.
|
||||
fn clear(&self) {
|
||||
self.state.store(IDLE, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
fn is_notified(&self) -> bool {
|
||||
match self.state.load(Ordering::SeqCst) {
|
||||
IDLE => false,
|
||||
NOTIFY => true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Notify for ThreadNotify {
|
||||
fn notify(&self, _unpark_id: usize) {
|
||||
// First, try transitioning from IDLE -> NOTIFY, this does not require a
|
||||
// lock.
|
||||
match self.state.compare_and_swap(IDLE, NOTIFY, Ordering::SeqCst) {
|
||||
IDLE | NOTIFY => return,
|
||||
SLEEP => {}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
// The other half is sleeping, this requires a lock
|
||||
let _m = self.mutex.lock().unwrap();
|
||||
|
||||
// Transition from SLEEP -> NOTIFY
|
||||
match self.state.compare_and_swap(SLEEP, NOTIFY, Ordering::SeqCst) {
|
||||
SLEEP => {}
|
||||
_ => return,
|
||||
}
|
||||
|
||||
// Wakeup the sleeper
|
||||
self.condvar.notify_one();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
#[macro_use]
|
||||
extern crate tokio_test;
|
||||
extern crate futures;
|
||||
extern crate tokio_timer;
|
||||
|
||||
use futures::Future;
|
||||
use std::time::{Duration, Instant};
|
||||
use tokio_test::clock::MockClock;
|
||||
use tokio_test::task::MockTask;
|
||||
use tokio_timer::Delay;
|
||||
|
||||
#[test]
|
||||
fn clock() {
|
||||
let mut mock = MockClock::new();
|
||||
|
||||
mock.enter(|handle| {
|
||||
let deadline = Instant::now() + Duration::from_secs(1);
|
||||
let mut delay = Delay::new(deadline);
|
||||
|
||||
assert_not_ready!(delay.poll());
|
||||
|
||||
handle.advance(Duration::from_secs(2));
|
||||
|
||||
assert_ready!(delay.poll());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notify() {
|
||||
let deadline = Instant::now() + Duration::from_secs(1);
|
||||
let mut mock = MockClock::new();
|
||||
let mut task = MockTask::new();
|
||||
|
||||
mock.enter(|handle| {
|
||||
let mut delay = Delay::new(deadline);
|
||||
|
||||
task.enter(|| assert_not_ready!(delay.poll()));
|
||||
|
||||
handle.advance(Duration::from_secs(1));
|
||||
|
||||
assert!(task.is_notified());
|
||||
assert_ready!(delay.poll());
|
||||
});
|
||||
}
|
||||
@@ -1,3 +1,8 @@
|
||||
# 0.1.15 (June 2, 2019)
|
||||
|
||||
### Changed
|
||||
- Allow other executors inside `threadpool::blocking` (#1155).
|
||||
|
||||
# 0.1.14 (April 22, 2019)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
[package]
|
||||
name = "tokio-threadpool"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.1.14"
|
||||
version = "0.1.15"
|
||||
documentation = "https://docs.rs/tokio-threadpool/0.1.14/tokio_threadpool"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
@@ -20,7 +21,7 @@ keywords = ["futures", "tokio"]
|
||||
categories = ["concurrency", "asynchronous"]
|
||||
|
||||
[dependencies]
|
||||
tokio-executor = "0.1.7"
|
||||
tokio-executor = "0.1.8"
|
||||
futures = "0.1.19"
|
||||
crossbeam-deque = "0.7.0"
|
||||
crossbeam-queue = "0.1.0"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
A library for scheduling execution of futures concurrently across a pool of
|
||||
threads.
|
||||
|
||||
[Documentation](https://docs.rs/tokio-threadpool/0.1.14/tokio_threadpool)
|
||||
[Documentation](https://docs.rs/tokio-threadpool/0.1.15/tokio_threadpool)
|
||||
|
||||
### Why not Rayon?
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use worker::Worker;
|
||||
|
||||
use futures::Poll;
|
||||
use tokio_executor;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
@@ -142,8 +143,11 @@ where
|
||||
// If the transition cannot happen, exit early
|
||||
try_ready!(res);
|
||||
|
||||
// Currently in blocking mode, so call the inner closure
|
||||
let ret = f();
|
||||
// Currently in blocking mode, so call the inner closure.
|
||||
//
|
||||
// "Exit" the current executor in case the blocking function wants
|
||||
// to call a different executor.
|
||||
let ret = tokio_executor::exit(move || f());
|
||||
|
||||
// Try to transition out of blocking mode. This is a fast path that takes
|
||||
// back ownership of the worker if the worker handoff didn't complete yet.
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-threadpool/0.1.14")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-threadpool/0.1.15")]
|
||||
#![deny(warnings, missing_docs, missing_debug_implementations)]
|
||||
// Our MSRV doesn't allow us to fix these warnings yet
|
||||
#![allow(rust_2018_idioms)]
|
||||
|
||||
//! A work-stealing based thread pool for executing futures.
|
||||
//!
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
extern crate tokio_executor;
|
||||
extern crate tokio_threadpool;
|
||||
|
||||
extern crate env_logger;
|
||||
@@ -44,6 +45,28 @@ fn basic() {
|
||||
rx2.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn other_executors_can_run_inside_blocking() {
|
||||
let _ = ::env_logger::try_init();
|
||||
|
||||
let pool = Builder::new().pool_size(1).max_blocking(1).build();
|
||||
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
pool.spawn(lazy(move || {
|
||||
let res = blocking(|| {
|
||||
let _e = tokio_executor::enter().expect("nested blocking enter");
|
||||
tx.send(()).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert!(res.is_ready());
|
||||
Ok(().into())
|
||||
}));
|
||||
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn notify_task_on_capacity() {
|
||||
const BLOCKING: usize = 10;
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# 0.2.11 (May 14, 2019)
|
||||
|
||||
### Added
|
||||
- `Handle::timeout` API, replacing the deprecated `Handle::deadline` (#1074).
|
||||
|
||||
# 0.2.10 (February 4, 2019)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
[package]
|
||||
name = "tokio-timer"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.2.x" git tag.
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio-timer/0.2.10/tokio_timer"
|
||||
documentation = "https://docs.rs/tokio-timer/0.2.11/tokio_timer"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://github.com/tokio-rs/tokio"
|
||||
description = """
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Timer facilities for Tokio
|
||||
|
||||
[Documentation](https://docs.rs/tokio-timer/0.2.10/tokio_timer/)
|
||||
[Documentation](https://docs.rs/tokio-timer/0.2.11/tokio_timer/)
|
||||
|
||||
## Overview
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-timer/0.2.10")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-timer/0.2.11")]
|
||||
#![deny(missing_docs, warnings, missing_debug_implementations)]
|
||||
|
||||
//! Utilities for tracking time.
|
||||
|
||||
@@ -126,7 +126,10 @@ impl<T> Timeout<T> {
|
||||
/// ```
|
||||
pub fn new(value: T, timeout: Duration) -> Timeout<T> {
|
||||
let delay = Delay::new_timeout(now() + timeout, timeout);
|
||||
Timeout::new_with_delay(value, delay)
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_delay(value: T, delay: Delay) -> Timeout<T> {
|
||||
Timeout { value, delay }
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use timer::Inner;
|
||||
use {Deadline, Delay, Error, Interval};
|
||||
use {Deadline, Delay, Error, Interval, Timeout};
|
||||
|
||||
use tokio_executor::Enter;
|
||||
|
||||
@@ -139,11 +139,17 @@ impl Handle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a `Deadline` driven by this handle's associated `Timer`.
|
||||
#[doc(hidden)]
|
||||
#[deprecated(since = "0.2.11", note = "use timeout instead")]
|
||||
pub fn deadline<T>(&self, future: T, deadline: Instant) -> Deadline<T> {
|
||||
Deadline::new_with_delay(future, self.delay(deadline))
|
||||
}
|
||||
|
||||
/// Create a `Timeout` driven by this handle's associated `Timer`.
|
||||
pub fn timeout<T>(&self, value: T, deadline: Instant) -> Timeout<T> {
|
||||
Timeout::new_with_delay(value, self.delay(deadline))
|
||||
}
|
||||
|
||||
/// Create a new `Interval` that starts at `at` and yields every `duration`
|
||||
/// interval after that.
|
||||
pub fn interval(&self, at: Instant, duration: Duration) -> Interval {
|
||||
|
||||
@@ -118,7 +118,7 @@ fn hammer_cancel() {
|
||||
let deadline = cmp::min(deadline1, deadline2);
|
||||
|
||||
let delay = handle.delay(deadline1);
|
||||
let join = handle.deadline(delay, deadline2);
|
||||
let join = handle.timeout(delay, deadline2);
|
||||
|
||||
exec.push({
|
||||
join.and_then(move |_| {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
[package]
|
||||
name = "tokio-tls"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-tls-0.2.x" git tag.
|
||||
# - Create "v0.2.x" git tag.
|
||||
version = "0.2.1"
|
||||
authors = ["Carl Lerche <[email protected]>"]
|
||||
license = "MIT"
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# 0.1.0 (April 22, 2019)
|
||||
|
||||
- Initial release
|
||||
@@ -1,52 +0,0 @@
|
||||
[package]
|
||||
name = "tokio-trace"
|
||||
# When releasing to crates.io:
|
||||
# - Update html_root_url.
|
||||
# - Update doc url
|
||||
# - Cargo.toml
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-trace/0.1.0/tokio_trace"
|
||||
description = """
|
||||
A scoped, structured logging and diagnostics system.
|
||||
"""
|
||||
categories = ["development-tools::debugging", "asynchronous"]
|
||||
keywords = ["logging", "tracing"]
|
||||
|
||||
[dependencies]
|
||||
tokio-trace-core = "0.2"
|
||||
log = { version = "0.4", optional = true }
|
||||
cfg-if = "0.1.7"
|
||||
|
||||
[dev-dependencies]
|
||||
ansi_term = "0.11"
|
||||
humantime = "1.1.1"
|
||||
futures = "0.1"
|
||||
log = "0.4"
|
||||
# These are used for the "basic" example from the tokio-trace-prototype repo,
|
||||
# which is currently not included as it used the `tokio-trace-log` crate, and
|
||||
# that crate is currently unstable.
|
||||
# env_logger = "0.5"
|
||||
# tokio-trace-log = { path = "../tokio-trace-log" }
|
||||
|
||||
[features]
|
||||
max_level_off = []
|
||||
max_level_error = []
|
||||
max_level_warn = []
|
||||
max_level_info = []
|
||||
max_level_debug = []
|
||||
max_level_trace = []
|
||||
|
||||
release_max_level_off = []
|
||||
release_max_level_error = []
|
||||
release_max_level_warn = []
|
||||
release_max_level_info = []
|
||||
release_max_level_debug = []
|
||||
release_max_level_trace = []
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
# tokio-trace
|
||||
|
||||
A scoped, structured logging and diagnostics system.
|
||||
|
||||
[Documentation](https://docs.rs/tokio-trace/0.1.0/tokio_trace/index.html)
|
||||
|
||||
## Overview
|
||||
|
||||
`tokio-trace` is a framework for instrumenting Rust programs to collect
|
||||
structured, event-based diagnostic information.
|
||||
|
||||
In asynchronous systems like Tokio, interpreting traditional log messages can
|
||||
often be quite challenging. Since individual tasks are multiplexed on the same
|
||||
thread, associated events and log lines are intermixed making it difficult to
|
||||
trace the logic flow. `tokio-trace` expands upon logging-style diagnostics by
|
||||
allowing libraries and applications to record structured events with additional
|
||||
information about *temporality* and *causality* — unlike a log message, a span
|
||||
in `tokio-trace` has a beginning and end time, may be entered and exited by the
|
||||
flow of execution, and may exist within a nested tree of similar spans. In
|
||||
addition, `tokio-trace` spans are *structured*, with the ability to record typed
|
||||
data as well as textual messages.
|
||||
|
||||
The `tokio-trace` crate provides the APIs necessary for instrumenting libraries
|
||||
and applications to emit trace data.
|
||||
|
||||
## Usage
|
||||
|
||||
First, add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio-trace = "0.1"
|
||||
```
|
||||
|
||||
Next, add this to your crate:
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
```
|
||||
|
||||
This crate provides macros for creating `Span`s and `Event`s, which represent
|
||||
periods of time and momentary events within the execution of a program,
|
||||
respectively.
|
||||
|
||||
As a rule of thumb, _spans_ should be used to represent discrete units of work
|
||||
(e.g., a given request's lifetime in a server) or periods of time spent in a
|
||||
given context (e.g., time spent interacting with an instance of an external
|
||||
system, such as a database). In contrast, _events_ should be used to represent
|
||||
points in time within a span — a request returned with a given status code,
|
||||
_n_ new items were taken from a queue, and so on.
|
||||
|
||||
`Span`s are constructed using the `span!` macro, and then _entered_
|
||||
to indicate that some code takes place within the context of that `Span`:
|
||||
|
||||
```rust
|
||||
// Construct a new span named "my span".
|
||||
let mut span = span!("my span");
|
||||
span.enter(|| {
|
||||
// Any trace events in this closure or code called by it will occur within
|
||||
// the span.
|
||||
});
|
||||
// Dropping the span will close it, indicating that it has ended.
|
||||
```
|
||||
|
||||
The `Event` type represent an event that occurs instantaneously, and is
|
||||
essentially a `Span` that cannot be entered. They are created using the `event!`
|
||||
macro:
|
||||
|
||||
```rust
|
||||
use tokio_trace::Level;
|
||||
event!(Level::INFO, "something has happened!");
|
||||
```
|
||||
|
||||
Users of the [`log`] crate should note that `tokio-trace` exposes a set of macros for
|
||||
creating `Event`s (`trace!`, `debug!`, `info!`, `warn!`, and `error!`) which may
|
||||
be invoked with the same syntax as the similarly-named macros from the `log`
|
||||
crate. Often, the process of converting a project to use `tokio-trace` can begin
|
||||
with a simple drop-in replacement.
|
||||
|
||||
Let's consider the `log` crate's yak-shaving example:
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
use tokio_trace::field;
|
||||
|
||||
pub fn shave_the_yak(yak: &mut Yak) {
|
||||
// Create a new span for this invocation of `shave_the_yak`, annotated
|
||||
// with the yak being shaved as a *field* on the span.
|
||||
span!("shave_the_yak", yak = field::debug(&yak)).enter(|| {
|
||||
// Since the span is annotated with the yak, it is part of the context
|
||||
// for everything happening inside the span. Therefore, we don't need
|
||||
// to add it to the message for this event, as the `log` crate does.
|
||||
info!(target: "yak_events", "Commencing yak shaving");
|
||||
|
||||
loop {
|
||||
match find_a_razor() {
|
||||
Ok(razor) => {
|
||||
// We can add the razor as a field rather than formatting it
|
||||
// as part of the message, allowing subscribers to consume it
|
||||
// in a more structured manner:
|
||||
info!({ razor = field::display(razor) }, "Razor located");
|
||||
yak.shave(razor);
|
||||
break;
|
||||
}
|
||||
Err(err) => {
|
||||
// However, we can also create events with formatted messages,
|
||||
// just as we would for log records.
|
||||
warn!("Unable to locate a razor: {}, retrying", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
You can find examples showing how to use this crate in the examples directory.
|
||||
|
||||
### In libraries
|
||||
|
||||
Libraries should link only to the `tokio-trace` crate, and use the provided
|
||||
macros to record whatever information will be useful to downstream consumers.
|
||||
|
||||
### In executables
|
||||
|
||||
In order to record trace events, executables have to use a `Subscriber`
|
||||
implementation compatible with `tokio-trace`. A `Subscriber` implements a way of
|
||||
collecting trace data, such as by logging it to standard output.
|
||||
|
||||
Unlike the `log` crate, `tokio-trace` does *not* use a global `Subscriber` which
|
||||
is initialized once. Instead, it follows the `tokio` pattern of executing code
|
||||
in a context. For example:
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
|
||||
let my_subscriber = FooSubscriber::new();
|
||||
|
||||
tokio_trace::subscriber::with_default(subscriber, || {
|
||||
// Any trace events generated in this closure or by functions it calls
|
||||
// will be collected by `my_subscriber`.
|
||||
})
|
||||
```
|
||||
|
||||
This approach allows trace data to be collected by multiple subscribers within
|
||||
different contexts in the program. Alternatively, a single subscriber may be
|
||||
constructed by the `main` function and all subsequent code executed with that
|
||||
subscriber as the default. Any trace events generated outside the context of a
|
||||
subscriber will not be collected.
|
||||
|
||||
The executable itself may use the `tokio-trace` crate to instrument itself as
|
||||
well.
|
||||
|
||||
The [`tokio-trace-nursery`] repository contains less stable crates designed to
|
||||
be used with the `tokio-trace` ecosystem. It includes a collection of
|
||||
`Subscriber` implementations, as well as utility and adapter crates.
|
||||
|
||||
[`log`]: https://docs.rs/log/0.4.6/log/
|
||||
[`tokio-trace-nursery`]: https://github.com/tokio-rs/tokio-trace-nursery
|
||||
|
||||
## 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,46 +0,0 @@
|
||||
#![feature(test)]
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate test;
|
||||
use test::Bencher;
|
||||
use tokio_trace::Level;
|
||||
|
||||
#[bench]
|
||||
fn bench_span_no_subscriber(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
span!(Level::TRACE, "span");
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_log_no_logger(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
log!(log::Level::Info, "log");
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_costly_field_no_subscriber(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
span!(
|
||||
Level::TRACE,
|
||||
"span",
|
||||
foo = tokio_trace::field::display(format!("bar {:?}", 2))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_no_span_no_subscriber(b: &mut Bencher) {
|
||||
b.iter(|| {});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_1_atomic_load(b: &mut Bencher) {
|
||||
// This is just included as a baseline.
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
let foo = AtomicUsize::new(1);
|
||||
b.iter(|| foo.load(Ordering::Relaxed));
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
#![feature(test)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
extern crate test;
|
||||
use test::Bencher;
|
||||
use tokio_trace::Level;
|
||||
|
||||
use std::{
|
||||
fmt,
|
||||
sync::{Mutex, MutexGuard},
|
||||
};
|
||||
use tokio_trace::{field, span, Event, Id, Metadata};
|
||||
|
||||
/// A subscriber that is enabled but otherwise does nothing.
|
||||
struct EnabledSubscriber;
|
||||
|
||||
impl tokio_trace::Subscriber for EnabledSubscriber {
|
||||
fn new_span(&self, span: &span::Attributes) -> Id {
|
||||
let _ = span;
|
||||
Id::from_u64(0xDEADFACE)
|
||||
}
|
||||
|
||||
fn event(&self, event: &Event) {
|
||||
let _ = event;
|
||||
}
|
||||
|
||||
fn record(&self, span: &Id, values: &span::Record) {
|
||||
let _ = (span, values);
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, span: &Id, follows: &Id) {
|
||||
let _ = (span, follows);
|
||||
}
|
||||
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
let _ = metadata;
|
||||
true
|
||||
}
|
||||
|
||||
fn enter(&self, span: &Id) {
|
||||
let _ = span;
|
||||
}
|
||||
|
||||
fn exit(&self, span: &Id) {
|
||||
let _ = span;
|
||||
}
|
||||
}
|
||||
|
||||
/// Simulates a subscriber that records span data.
|
||||
struct VisitingSubscriber(Mutex<String>);
|
||||
|
||||
struct Visitor<'a>(MutexGuard<'a, String>);
|
||||
|
||||
impl<'a> field::Visit for Visitor<'a> {
|
||||
fn record_debug(&mut self, _field: &field::Field, value: &fmt::Debug) {
|
||||
use std::fmt::Write;
|
||||
let _ = write!(&mut *self.0, "{:?}", value);
|
||||
}
|
||||
}
|
||||
|
||||
impl tokio_trace::Subscriber for VisitingSubscriber {
|
||||
fn new_span(&self, span: &span::Attributes) -> Id {
|
||||
let mut visitor = Visitor(self.0.lock().unwrap());
|
||||
span.record(&mut visitor);
|
||||
Id::from_u64(0xDEADFACE)
|
||||
}
|
||||
|
||||
fn record(&self, _span: &Id, values: &span::Record) {
|
||||
let mut visitor = Visitor(self.0.lock().unwrap());
|
||||
values.record(&mut visitor);
|
||||
}
|
||||
|
||||
fn event(&self, event: &Event) {
|
||||
let mut visitor = Visitor(self.0.lock().unwrap());
|
||||
event.record(&mut visitor);
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, span: &Id, follows: &Id) {
|
||||
let _ = (span, follows);
|
||||
}
|
||||
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
let _ = metadata;
|
||||
true
|
||||
}
|
||||
|
||||
fn enter(&self, span: &Id) {
|
||||
let _ = span;
|
||||
}
|
||||
|
||||
fn exit(&self, span: &Id) {
|
||||
let _ = span;
|
||||
}
|
||||
}
|
||||
|
||||
const N_SPANS: usize = 100;
|
||||
|
||||
#[bench]
|
||||
fn span_no_fields(b: &mut Bencher) {
|
||||
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
|
||||
b.iter(|| span!(Level::TRACE, "span"))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn enter_span(b: &mut Bencher) {
|
||||
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
|
||||
let span = span!(Level::TRACE, "span");
|
||||
b.iter(|| test::black_box(span.enter(|| {})))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn span_repeatedly(b: &mut Bencher) {
|
||||
#[inline]
|
||||
fn mk_span(i: u64) -> tokio_trace::Span {
|
||||
span!(Level::TRACE, "span", i = i)
|
||||
}
|
||||
|
||||
let n = test::black_box(N_SPANS);
|
||||
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
|
||||
b.iter(|| (0..n).fold(mk_span(0), |_, i| mk_span(i as u64)))
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn span_with_fields(b: &mut Bencher) {
|
||||
tokio_trace::subscriber::with_default(EnabledSubscriber, || {
|
||||
b.iter(|| {
|
||||
span!(
|
||||
Level::TRACE,
|
||||
"span",
|
||||
foo = "foo",
|
||||
bar = "bar",
|
||||
baz = 3,
|
||||
quuux = tokio_trace::field::debug(0.99)
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn span_with_fields_record(b: &mut Bencher) {
|
||||
let subscriber = VisitingSubscriber(Mutex::new(String::from("")));
|
||||
tokio_trace::subscriber::with_default(subscriber, || {
|
||||
b.iter(|| {
|
||||
span!(
|
||||
Level::TRACE,
|
||||
"span",
|
||||
foo = "foo",
|
||||
bar = "bar",
|
||||
baz = 3,
|
||||
quuux = tokio_trace::field::debug(0.99)
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -1,144 +0,0 @@
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
|
||||
use tokio_trace::{
|
||||
field::{Field, Visit},
|
||||
span,
|
||||
subscriber::{self, Subscriber},
|
||||
Event, Id, Level, Metadata,
|
||||
};
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Arc, RwLock, RwLockReadGuard,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Counters(Arc<RwLock<HashMap<String, AtomicUsize>>>);
|
||||
|
||||
struct CounterSubscriber {
|
||||
ids: AtomicUsize,
|
||||
counters: Counters,
|
||||
}
|
||||
|
||||
struct Count<'a> {
|
||||
counters: RwLockReadGuard<'a, HashMap<String, AtomicUsize>>,
|
||||
}
|
||||
|
||||
impl<'a> Visit for Count<'a> {
|
||||
fn record_i64(&mut self, field: &Field, value: i64) {
|
||||
if let Some(counter) = self.counters.get(field.name()) {
|
||||
if value > 0 {
|
||||
counter.fetch_add(value as usize, Ordering::Release);
|
||||
} else {
|
||||
counter.fetch_sub((value * -1) as usize, Ordering::Release);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn record_u64(&mut self, field: &Field, value: u64) {
|
||||
if let Some(counter) = self.counters.get(field.name()) {
|
||||
counter.fetch_add(value as usize, Ordering::Release);
|
||||
};
|
||||
}
|
||||
|
||||
fn record_bool(&mut self, _: &Field, _: bool) {}
|
||||
fn record_str(&mut self, _: &Field, _: &str) {}
|
||||
fn record_debug(&mut self, _: &Field, _: &fmt::Debug) {}
|
||||
}
|
||||
|
||||
impl CounterSubscriber {
|
||||
fn visitor(&self) -> Count {
|
||||
Count {
|
||||
counters: self.counters.0.read().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Subscriber for CounterSubscriber {
|
||||
fn register_callsite(&self, meta: &Metadata) -> subscriber::Interest {
|
||||
let mut interest = subscriber::Interest::never();
|
||||
for key in meta.fields() {
|
||||
let name = key.name();
|
||||
if name.contains("count") {
|
||||
self.counters
|
||||
.0
|
||||
.write()
|
||||
.unwrap()
|
||||
.entry(name.to_owned())
|
||||
.or_insert_with(|| AtomicUsize::new(0));
|
||||
interest = subscriber::Interest::always();
|
||||
}
|
||||
}
|
||||
interest
|
||||
}
|
||||
|
||||
fn new_span(&self, new_span: &span::Attributes) -> Id {
|
||||
new_span.record(&mut self.visitor());
|
||||
let id = self.ids.fetch_add(1, Ordering::SeqCst);
|
||||
Id::from_u64(id as u64)
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, _span: &Id, _follows: &Id) {
|
||||
// unimplemented
|
||||
}
|
||||
|
||||
fn record(&self, _: &Id, values: &span::Record) {
|
||||
values.record(&mut self.visitor())
|
||||
}
|
||||
|
||||
fn event(&self, event: &Event) {
|
||||
event.record(&mut self.visitor())
|
||||
}
|
||||
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
metadata.fields().iter().any(|f| f.name().contains("count"))
|
||||
}
|
||||
|
||||
fn enter(&self, _span: &Id) {}
|
||||
fn exit(&self, _span: &Id) {}
|
||||
}
|
||||
|
||||
impl Counters {
|
||||
fn print_counters(&self) {
|
||||
for (k, v) in self.0.read().unwrap().iter() {
|
||||
println!("{}: {}", k, v.load(Ordering::Acquire));
|
||||
}
|
||||
}
|
||||
|
||||
fn new() -> (Self, CounterSubscriber) {
|
||||
let counters = Counters(Arc::new(RwLock::new(HashMap::new())));
|
||||
let subscriber = CounterSubscriber {
|
||||
ids: AtomicUsize::new(1),
|
||||
counters: counters.clone(),
|
||||
};
|
||||
(counters, subscriber)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (counters, subscriber) = Counters::new();
|
||||
|
||||
tokio_trace::subscriber::with_default(subscriber, || {
|
||||
let mut foo: u64 = 2;
|
||||
span!(Level::TRACE, "my_great_span", foo_count = &foo).enter(|| {
|
||||
foo += 1;
|
||||
info!({ yak_shaved = true, yak_count = 1 }, "hi from inside my span");
|
||||
span!(
|
||||
Level::TRACE,
|
||||
"my other span",
|
||||
foo_count = &foo,
|
||||
baz_count = 5
|
||||
)
|
||||
.enter(|| {
|
||||
warn!({ yak_shaved = false, yak_count = -1 }, "failed to shave yak");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
counters.print_counters();
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
//! A simple example demonstrating how one might implement a custom
|
||||
//! subscriber.
|
||||
//!
|
||||
//! This subscriber implements a tree-structured logger similar to
|
||||
//! the "compact" formatter in [`slog-term`]. The demo mimicks the
|
||||
//! example output in the screenshot in the [`slog` README].
|
||||
//!
|
||||
//! Note that this logger isn't ready for actual production use.
|
||||
//! Several corners were cut to make the example simple.
|
||||
//!
|
||||
//! [`slog-term`]: https://docs.rs/slog-term/2.4.0/slog_term/
|
||||
//! [`slog` README]: https://github.com/slog-rs/slog#terminal-output-example
|
||||
#[macro_use]
|
||||
extern crate tokio_trace;
|
||||
|
||||
use tokio_trace::{field, Level};
|
||||
|
||||
mod sloggish_subscriber;
|
||||
use self::sloggish_subscriber::SloggishSubscriber;
|
||||
|
||||
fn main() {
|
||||
let subscriber = SloggishSubscriber::new(2);
|
||||
|
||||
tokio_trace::subscriber::with_default(subscriber, || {
|
||||
span!(Level::TRACE, "", version = &field::display(5.0)).enter(|| {
|
||||
span!(Level::TRACE, "server", host = "localhost", port = 8080).enter(|| {
|
||||
info!("starting");
|
||||
info!("listening");
|
||||
let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381);
|
||||
peer1.enter(|| {
|
||||
debug!("connected");
|
||||
debug!({ length = 2 }, "message received");
|
||||
});
|
||||
let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230);
|
||||
peer2.enter(|| {
|
||||
debug!("connected");
|
||||
});
|
||||
peer1.enter(|| {
|
||||
warn!({ algo = "xor" }, "weak encryption requested");
|
||||
debug!({ length = 8 }, "response sent");
|
||||
debug!("disconnected");
|
||||
});
|
||||
peer2.enter(|| {
|
||||
debug!({ length = 5 }, "message received");
|
||||
debug!({ length = 8 }, "response sent");
|
||||
debug!("disconnected");
|
||||
});
|
||||
warn!("internal error");
|
||||
info!("exit");
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1,285 +0,0 @@
|
||||
//! A simple example demonstrating how one might implement a custom
|
||||
//! subscriber.
|
||||
//!
|
||||
//! This subscriber implements a tree-structured logger similar to
|
||||
//! the "compact" formatter in [`slog-term`]. The demo mimicks the
|
||||
//! example output in the screenshot in the [`slog` README].
|
||||
//!
|
||||
//! Note that this logger isn't ready for actual production use.
|
||||
//! Several corners were cut to make the example simple.
|
||||
//!
|
||||
//! [`slog-term`]: https://docs.rs/slog-term/2.4.0/slog_term/
|
||||
//! [`slog` README]: https://github.com/slog-rs/slog#terminal-output-example
|
||||
extern crate ansi_term;
|
||||
extern crate humantime;
|
||||
use self::ansi_term::{Color, Style};
|
||||
use super::tokio_trace::{
|
||||
self,
|
||||
field::{Field, Visit},
|
||||
Id, Level, Subscriber,
|
||||
};
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
io::{self, Write},
|
||||
sync::{
|
||||
atomic::{AtomicUsize, Ordering},
|
||||
Mutex,
|
||||
},
|
||||
thread,
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
/// Tracks the currently executing span on a per-thread basis.
|
||||
#[derive(Clone)]
|
||||
pub struct CurrentSpanPerThread {
|
||||
current: &'static thread::LocalKey<RefCell<Vec<Id>>>,
|
||||
}
|
||||
|
||||
impl CurrentSpanPerThread {
|
||||
pub fn new() -> Self {
|
||||
thread_local! {
|
||||
static CURRENT: RefCell<Vec<Id>> = RefCell::new(vec![]);
|
||||
};
|
||||
Self { current: &CURRENT }
|
||||
}
|
||||
|
||||
/// Returns the [`Id`](::Id) of the span in which the current thread is
|
||||
/// executing, or `None` if it is not inside of a span.
|
||||
pub fn id(&self) -> Option<Id> {
|
||||
self.current
|
||||
.with(|current| current.borrow().last().cloned())
|
||||
}
|
||||
|
||||
pub fn enter(&self, span: Id) {
|
||||
self.current.with(|current| {
|
||||
current.borrow_mut().push(span);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn exit(&self) {
|
||||
self.current.with(|current| {
|
||||
let _ = current.borrow_mut().pop();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SloggishSubscriber {
|
||||
// TODO: this can probably be unified with the "stack" that's used for
|
||||
// printing?
|
||||
current: CurrentSpanPerThread,
|
||||
indent_amount: usize,
|
||||
stderr: io::Stderr,
|
||||
stack: Mutex<Vec<Id>>,
|
||||
spans: Mutex<HashMap<Id, Span>>,
|
||||
ids: AtomicUsize,
|
||||
}
|
||||
|
||||
struct Span {
|
||||
parent: Option<Id>,
|
||||
kvs: Vec<(&'static str, String)>,
|
||||
}
|
||||
|
||||
struct Event<'a> {
|
||||
stderr: io::StderrLock<'a>,
|
||||
comma: bool,
|
||||
}
|
||||
|
||||
struct ColorLevel<'a>(&'a Level);
|
||||
|
||||
impl<'a> fmt::Display for ColorLevel<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
&Level::TRACE => Color::Purple.paint("TRACE"),
|
||||
&Level::DEBUG => Color::Blue.paint("DEBUG"),
|
||||
&Level::INFO => Color::Green.paint("INFO "),
|
||||
&Level::WARN => Color::Yellow.paint("WARN "),
|
||||
&Level::ERROR => Color::Red.paint("ERROR"),
|
||||
}
|
||||
.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
fn new(parent: Option<Id>, attrs: &tokio_trace::span::Attributes) -> Self {
|
||||
let mut span = Self {
|
||||
parent,
|
||||
kvs: Vec::new(),
|
||||
};
|
||||
attrs.record(&mut span);
|
||||
span
|
||||
}
|
||||
}
|
||||
|
||||
impl Visit for Span {
|
||||
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
|
||||
self.kvs.push((field.name(), format!("{:?}", value)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visit for Event<'a> {
|
||||
fn record_debug(&mut self, field: &Field, value: &fmt::Debug) {
|
||||
write!(
|
||||
&mut self.stderr,
|
||||
"{comma} ",
|
||||
comma = if self.comma { "," } else { "" },
|
||||
)
|
||||
.unwrap();
|
||||
let name = field.name();
|
||||
if name == "message" {
|
||||
write!(
|
||||
&mut self.stderr,
|
||||
"{}",
|
||||
// Have to alloc here due to `ansi_term`'s API...
|
||||
Style::new().bold().paint(format!("{:?}", value))
|
||||
)
|
||||
.unwrap();
|
||||
self.comma = true;
|
||||
} else {
|
||||
write!(
|
||||
&mut self.stderr,
|
||||
"{}: {:?}",
|
||||
Style::new().bold().paint(name),
|
||||
value
|
||||
)
|
||||
.unwrap();
|
||||
self.comma = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SloggishSubscriber {
|
||||
pub fn new(indent_amount: usize) -> Self {
|
||||
Self {
|
||||
current: CurrentSpanPerThread::new(),
|
||||
indent_amount,
|
||||
stderr: io::stderr(),
|
||||
stack: Mutex::new(vec![]),
|
||||
spans: Mutex::new(HashMap::new()),
|
||||
ids: AtomicUsize::new(1),
|
||||
}
|
||||
}
|
||||
|
||||
fn print_kvs<'a, I, K, V>(
|
||||
&self,
|
||||
writer: &mut impl Write,
|
||||
kvs: I,
|
||||
leading: &str,
|
||||
) -> io::Result<()>
|
||||
where
|
||||
I: IntoIterator<Item = (K, V)>,
|
||||
K: AsRef<str> + 'a,
|
||||
V: fmt::Display + 'a,
|
||||
{
|
||||
let mut kvs = kvs.into_iter();
|
||||
if let Some((k, v)) = kvs.next() {
|
||||
write!(
|
||||
writer,
|
||||
"{}{}: {}",
|
||||
leading,
|
||||
Style::new().bold().paint(k.as_ref()),
|
||||
v
|
||||
)?;
|
||||
}
|
||||
for (k, v) in kvs {
|
||||
write!(writer, ", {}: {}", Style::new().bold().paint(k.as_ref()), v)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_indent(&self, writer: &mut impl Write, indent: usize) -> io::Result<()> {
|
||||
for _ in 0..(indent * self.indent_amount) {
|
||||
write!(writer, " ")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Subscriber for SloggishSubscriber {
|
||||
fn enabled(&self, _metadata: &tokio_trace::Metadata) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn new_span(&self, span: &tokio_trace::span::Attributes) -> tokio_trace::Id {
|
||||
let next = self.ids.fetch_add(1, Ordering::SeqCst) as u64;
|
||||
let id = tokio_trace::Id::from_u64(next);
|
||||
let span = Span::new(self.current.id(), span);
|
||||
self.spans.lock().unwrap().insert(id.clone(), span);
|
||||
id
|
||||
}
|
||||
|
||||
fn record(&self, span: &tokio_trace::Id, values: &tokio_trace::span::Record) {
|
||||
let mut spans = self.spans.lock().expect("mutex poisoned!");
|
||||
if let Some(span) = spans.get_mut(span) {
|
||||
values.record(span);
|
||||
}
|
||||
}
|
||||
|
||||
fn record_follows_from(&self, _span: &tokio_trace::Id, _follows: &tokio_trace::Id) {
|
||||
// unimplemented
|
||||
}
|
||||
|
||||
fn enter(&self, span_id: &tokio_trace::Id) {
|
||||
self.current.enter(span_id.clone());
|
||||
let mut stderr = self.stderr.lock();
|
||||
let mut stack = self.stack.lock().unwrap();
|
||||
let spans = self.spans.lock().unwrap();
|
||||
let data = spans.get(span_id);
|
||||
let parent = data.and_then(|span| span.parent.as_ref());
|
||||
if stack.iter().any(|id| id == span_id) {
|
||||
// We are already in this span, do nothing.
|
||||
return;
|
||||
} else {
|
||||
let indent = if let Some(idx) = stack
|
||||
.iter()
|
||||
.position(|id| parent.map(|p| id == p).unwrap_or(false))
|
||||
{
|
||||
let idx = idx + 1;
|
||||
stack.truncate(idx);
|
||||
idx
|
||||
} else {
|
||||
stack.clear();
|
||||
0
|
||||
};
|
||||
self.print_indent(&mut stderr, indent).unwrap();
|
||||
stack.push(span_id.clone());
|
||||
if let Some(data) = data {
|
||||
self.print_kvs(&mut stderr, data.kvs.iter().map(|(k, v)| (k, v)), "")
|
||||
.unwrap();
|
||||
}
|
||||
write!(&mut stderr, "\n").unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn event(&self, event: &tokio_trace::Event) {
|
||||
let mut stderr = self.stderr.lock();
|
||||
let indent = self.stack.lock().unwrap().len();
|
||||
self.print_indent(&mut stderr, indent).unwrap();
|
||||
write!(
|
||||
&mut stderr,
|
||||
"{timestamp} {level} {target}",
|
||||
timestamp = humantime::format_rfc3339_seconds(SystemTime::now()),
|
||||
level = ColorLevel(event.metadata().level()),
|
||||
target = &event.metadata().target(),
|
||||
)
|
||||
.unwrap();
|
||||
let mut visitor = Event {
|
||||
stderr,
|
||||
comma: false,
|
||||
};
|
||||
event.record(&mut visitor);
|
||||
write!(&mut visitor.stderr, "\n").unwrap();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn exit(&self, _span: &tokio_trace::Id) {
|
||||
// TODO: unify stack with current span
|
||||
self.current.exit();
|
||||
}
|
||||
|
||||
fn drop_span(&self, _id: tokio_trace::Id) {
|
||||
// TODO: GC unneeded spans.
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
//! Structured data associated with `Span`s and `Event`s.
|
||||
pub use tokio_trace_core::field::*;
|
||||
|
||||
use Metadata;
|
||||
|
||||
/// Trait implemented to allow a type to be used as a field key.
|
||||
///
|
||||
/// **Note**: Although this is implemented for both the [`Field`] type *and* any
|
||||
/// type that can be borrowed as an `&str`, only `Field` allows _O_(1) access.
|
||||
/// Indexing a field with a string results in an iterative search that performs
|
||||
/// string comparisons. Thus, if possible, once the key for a field is known, it
|
||||
/// should be used whenever possible.
|
||||
///
|
||||
/// [`Field`]: ../struct.Field.html
|
||||
pub trait AsField: ::sealed::Sealed {
|
||||
/// Attempts to convert `&self` into a `Field` with the specified `metadata`.
|
||||
///
|
||||
/// If `metadata` defines this field, then the field is returned. Otherwise,
|
||||
/// this returns `None`.
|
||||
fn as_field(&self, metadata: &Metadata) -> Option<Field>;
|
||||
}
|
||||
|
||||
// ===== impl AsField =====
|
||||
|
||||
impl AsField for Field {
|
||||
#[inline]
|
||||
fn as_field(&self, metadata: &Metadata) -> Option<Field> {
|
||||
if self.callsite() == metadata.callsite() {
|
||||
Some(self.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsField for &'a Field {
|
||||
#[inline]
|
||||
fn as_field(&self, metadata: &Metadata) -> Option<Field> {
|
||||
if self.callsite() == metadata.callsite() {
|
||||
Some((*self).clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsField for str {
|
||||
#[inline]
|
||||
fn as_field(&self, metadata: &Metadata) -> Option<Field> {
|
||||
metadata.fields().field(&self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ::sealed::Sealed for Field {}
|
||||
impl<'a> ::sealed::Sealed for &'a Field {}
|
||||
impl ::sealed::Sealed for str {}
|
||||
@@ -1,137 +0,0 @@
|
||||
//! Trace verbosity level filtering.
|
||||
//!
|
||||
//! # Compile time filters
|
||||
//!
|
||||
//! Trace verbosity levels can be statically disabled at compile time via Cargo
|
||||
//! features, similar to the [`log` crate]. Trace instrumentation at disabled
|
||||
//! levels will be skipped and will not even be present in the resulting binary
|
||||
//! unless the verbosity level is specified dynamically. This level is
|
||||
//! configured separately for release and debug builds. The features are:
|
||||
//!
|
||||
//! * `max_level_off`
|
||||
//! * `max_level_error`
|
||||
//! * `max_level_warn`
|
||||
//! * `max_level_info`
|
||||
//! * `max_level_debug`
|
||||
//! * `max_level_trace`
|
||||
//! * `release_max_level_off`
|
||||
//! * `release_max_level_error`
|
||||
//! * `release_max_level_warn`
|
||||
//! * `release_max_level_info`
|
||||
//! * `release_max_level_debug`
|
||||
//! * `release_max_level_trace`
|
||||
//!
|
||||
//! These features control the value of the `STATIC_MAX_LEVEL` constant. The
|
||||
//! instrumentation macros macros check this value before recording an event or
|
||||
//! constructing a span. By default, no levels are disabled.
|
||||
//!
|
||||
//! For example, a crate can disable trace level instrumentation in debug builds
|
||||
//! and trace, debug, and info level instrumentation in release builds with the
|
||||
//! following configuration:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! tokio-trace = { version = "0.1", features = ["max_level_debug", "release_max_level_warn"] }
|
||||
//! ```
|
||||
//!
|
||||
//! [`log` crate]: https://docs.rs/log/0.4.6/log/#compile-time-filters
|
||||
use std::cmp::Ordering;
|
||||
use tokio_trace_core::Level;
|
||||
|
||||
/// A filter comparable to trace verbosity `Level`.
|
||||
///
|
||||
/// If a `Level` is considered less than a `LevelFilter`, it should be
|
||||
/// considered disabled; if greater than or equal to the `LevelFilter`, that
|
||||
/// level is enabled.
|
||||
///
|
||||
/// Note that this is essentially identical to the `Level` type, but with the
|
||||
/// addition of an `OFF` level that completely disables all trace
|
||||
/// instrumentation.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||
pub struct LevelFilter(Option<Level>);
|
||||
|
||||
impl LevelFilter {
|
||||
/// The "off" level.
|
||||
///
|
||||
/// Designates that trace instrumentation should be completely disabled.
|
||||
pub const OFF: LevelFilter = LevelFilter(None);
|
||||
/// The "error" level.
|
||||
///
|
||||
/// Designates very serious errors.
|
||||
pub const ERROR: LevelFilter = LevelFilter(Some(Level::ERROR));
|
||||
/// The "warn" level.
|
||||
///
|
||||
/// Designates hazardous situations.
|
||||
pub const WARN: LevelFilter = LevelFilter(Some(Level::WARN));
|
||||
/// The "info" level.
|
||||
///
|
||||
/// Designates useful information.
|
||||
pub const INFO: LevelFilter = LevelFilter(Some(Level::INFO));
|
||||
/// The "debug" level.
|
||||
///
|
||||
/// Designates lower priority information.
|
||||
pub const DEBUG: LevelFilter = LevelFilter(Some(Level::DEBUG));
|
||||
/// The "trace" level.
|
||||
///
|
||||
/// Designates very low priority, often extremely verbose, information.
|
||||
pub const TRACE: LevelFilter = LevelFilter(Some(Level::TRACE));
|
||||
}
|
||||
|
||||
impl PartialEq<LevelFilter> for Level {
|
||||
fn eq(&self, other: &LevelFilter) -> bool {
|
||||
match other.0 {
|
||||
None => false,
|
||||
Some(ref level) => self.eq(level),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<LevelFilter> for Level {
|
||||
fn partial_cmp(&self, other: &LevelFilter) -> Option<Ordering> {
|
||||
match other.0 {
|
||||
None => Some(Ordering::Less),
|
||||
Some(ref level) => self.partial_cmp(level),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The statically configured maximum trace level.
|
||||
///
|
||||
/// See the [module-level documentation] for information on how to configure
|
||||
/// this.
|
||||
///
|
||||
/// This value is checked by the `event!` and `span!` macros. Code that
|
||||
/// manually constructs events or spans via the `Event::record` function or
|
||||
/// `Span` constructors should compare the level against this value to
|
||||
/// determine if those spans or events are enabled.
|
||||
///
|
||||
/// [module-level documentation]: ../index.html#compile-time-filters
|
||||
pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::OFF;
|
||||
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::ERROR;
|
||||
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::WARN;
|
||||
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::INFO;
|
||||
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG;
|
||||
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::TRACE;
|
||||
} else if #[cfg(feature = "max_level_off")] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::OFF;
|
||||
} else if #[cfg(feature = "max_level_error")] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::ERROR;
|
||||
} else if #[cfg(feature = "max_level_warn")] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::WARN;
|
||||
} else if #[cfg(feature = "max_level_info")] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::INFO;
|
||||
} else if #[cfg(feature = "max_level_debug")] {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG;
|
||||
} else {
|
||||
const MAX_LEVEL: LevelFilter = LevelFilter::TRACE;
|
||||
}
|
||||
}
|
||||
@@ -1,437 +0,0 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-trace/0.1.0")]
|
||||
#![deny(missing_debug_implementations, missing_docs, unreachable_pub)]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
//! A scoped, structured logging and diagnostics system.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! `tokio-trace` is a framework for instrumenting Rust programs to collect
|
||||
//! structured, event-based diagnostic information.
|
||||
//!
|
||||
//! In asynchronous systems like Tokio, interpreting traditional log messages can
|
||||
//! often be quite challenging. Since individual tasks are multiplexed on the same
|
||||
//! thread, associated events and log lines are intermixed making it difficult to
|
||||
//! trace the logic flow. `tokio-trace` expands upon logging-style diagnostics by
|
||||
//! allowing libraries and applications to record structured events with additional
|
||||
//! information about *temporality* and *causality* — unlike a log message, a span
|
||||
//! in `tokio-trace` has a beginning and end time, may be entered and exited by the
|
||||
//! flow of execution, and may exist within a nested tree of similar spans. In
|
||||
//! addition, `tokio-trace` spans are *structured*, with the ability to record typed
|
||||
//! data as well as textual messages.
|
||||
//!
|
||||
//! The `tokio-trace` crate provides the APIs necessary for instrumenting libraries
|
||||
//! and applications to emit trace data.
|
||||
//!
|
||||
//! # Core Concepts
|
||||
//!
|
||||
//! The core of `tokio-trace`'s API is composed of `Event`s, `Span`s, and
|
||||
//! `Subscriber`s. We'll cover these in turn.
|
||||
//!
|
||||
//! ## `Span`s
|
||||
//!
|
||||
//! A [`Span`] represents a _period of time_ during which a program was executing
|
||||
//! in some context. A thread of execution is said to _enter_ a span when it
|
||||
//! begins executing in that context, and to _exit_ the span when switching to
|
||||
//! another context. The span in which a thread is currently executing is
|
||||
//! referred to as the _current_ span.
|
||||
//!
|
||||
//! For example:
|
||||
//! ```
|
||||
//! #[macro_use]
|
||||
//! extern crate tokio_trace;
|
||||
//!
|
||||
//! use tokio_trace::Level;
|
||||
//!
|
||||
//! # fn main() {
|
||||
//! span!(Level::TRACE, "my_span").enter(|| {
|
||||
//! // perform some work in the context of `my_span`...
|
||||
//! });
|
||||
//! # }
|
||||
//!```
|
||||
//!
|
||||
//! Spans form a tree structure — unless it is a root span, all spans have a
|
||||
//! _parent_, and may have one or more _children_. When a new span is created,
|
||||
//! the current span becomes the new span's parent. The total execution time of
|
||||
//! a span consists of the time spent in that span and in the entire subtree
|
||||
//! represented by its children. Thus, a parent span always lasts for at least
|
||||
//! as long as the longest-executing span in its subtree.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate tokio_trace;
|
||||
//! # use tokio_trace::Level;
|
||||
//! # fn main() {
|
||||
//! // this span is considered the "root" of a new trace tree:
|
||||
//! span!(Level::INFO, "root").enter(|| {
|
||||
//! // since we are now inside "root", this span is considered a child
|
||||
//! // of "root":
|
||||
//! span!(Level::DEBUG, "outer_child").enter(|| {
|
||||
//! // this span is a child of "outer_child", which is in turn a
|
||||
//! // child of "root":
|
||||
//! span!(Level::TRACE, "inner_child").enter(|| {
|
||||
//! // and so on...
|
||||
//! });
|
||||
//! });
|
||||
//! });
|
||||
//! # }
|
||||
//!```
|
||||
//!
|
||||
//! In addition, data may be associated with spans. A span may have _fields_ —
|
||||
//! a set of key-value pairs describing the state of the program during that
|
||||
//! span; an optional name, and metadata describing the source code location
|
||||
//! where the span was originally entered.
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate tokio_trace;
|
||||
//! # use tokio_trace::Level;
|
||||
//! # fn main() {
|
||||
//! // construct a new span with three fields:
|
||||
//! // - "foo", with a value of 42,
|
||||
//! // - "bar", with the value "false"
|
||||
//! // - "baz", with no initial value
|
||||
//! let my_span = span!(Level::INFO, "my_span", foo = 42, bar = false, baz);
|
||||
//!
|
||||
//! // record a value for the field "baz" declared above:
|
||||
//! my_span.record("baz", &"hello world");
|
||||
//! # }
|
||||
//!```
|
||||
//!
|
||||
//! ### When to use spans
|
||||
//!
|
||||
//! As a rule of thumb, spans should be used to represent discrete units of work
|
||||
//! (e.g., a given request's lifetime in a server) or periods of time spent in a
|
||||
//! given context (e.g., time spent interacting with an instance of an external
|
||||
//! system, such as a database).
|
||||
//!
|
||||
//! Which scopes in a program correspond to new spans depend somewhat on user
|
||||
//! intent. For example, consider the case of a loop in a program. Should we
|
||||
//! construct one span and perform the entire loop inside of that span, like:
|
||||
//! ```rust
|
||||
//! # #[macro_use] extern crate tokio_trace;
|
||||
//! # use tokio_trace::Level;
|
||||
//! # fn main() {
|
||||
//! # let n = 1;
|
||||
//! span!(Level::TRACE, "my loop").enter(|| {
|
||||
//! for i in 0..n {
|
||||
//! # let _ = i;
|
||||
//! // ...
|
||||
//! }
|
||||
//! })
|
||||
//! # }
|
||||
//! ```
|
||||
//! Or, should we create a new span for each iteration of the loop, as in:
|
||||
//! ```rust
|
||||
//! # #[macro_use] extern crate tokio_trace;
|
||||
//! # use tokio_trace::Level;
|
||||
//! # fn main() {
|
||||
//! # let n = 1u64;
|
||||
//! for i in 0..n {
|
||||
//! # let _ = i;
|
||||
//! span!(Level::TRACE, "my loop", iteration = i).enter(|| {
|
||||
//! // ...
|
||||
//! })
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Depending on the circumstances, we might want to do either, or both. For
|
||||
//! example, if we want to know how long was spent in the loop overall, we would
|
||||
//! create a single span around the entire loop; whereas if we wanted to know how
|
||||
//! much time was spent in each individual iteration, we would enter a new span
|
||||
//! on every iteration.
|
||||
//!
|
||||
//! ## Events
|
||||
//!
|
||||
//! An [`Event`] represents a _point_ in time. It signifies something that
|
||||
//! happened while the trace was executing. `Event`s are comparable to the log
|
||||
//! records emitted by unstructured logging code, but unlike a typical log line,
|
||||
//! an `Event` may occur within the context of a `Span`. Like a `Span`, it
|
||||
//! may have fields, and implicitly inherits any of the fields present on its
|
||||
//! parent span.
|
||||
//!
|
||||
//! For example:
|
||||
//! ```
|
||||
//! # #[macro_use] extern crate tokio_trace;
|
||||
//! # use tokio_trace::Level;
|
||||
//! # fn main() {
|
||||
//! // records an event outside of any span context:
|
||||
//! event!(Level::INFO, "something happened");
|
||||
//!
|
||||
//! span!(Level::INFO, "my_span").enter(|| {
|
||||
//! // records an event within "my_span".
|
||||
//! event!(Level::DEBUG, "something happened inside my_span");
|
||||
//! });
|
||||
//! # }
|
||||
//!```
|
||||
//!
|
||||
//! Essentially, `Event`s exist to bridge the gap between traditional
|
||||
//! unstructured logging and span-based tracing. Similar to log records, they
|
||||
//! may be recorded at a number of levels, and can have unstructured,
|
||||
//! human-readable messages; however, they also carry key-value data and exist
|
||||
//! within the context of the tree of spans that comprise a trace. Thus,
|
||||
//! individual log record-like events can be pinpointed not only in time, but
|
||||
//! in the logical execution flow of the system.
|
||||
//!
|
||||
//! Events are represented as a special case of spans — they are created, they
|
||||
//! may have fields added, and then they close immediately, without being
|
||||
//! entered.
|
||||
//!
|
||||
//! In general, events should be used to represent points in time _within_ a
|
||||
//! span — a request returned with a given status code, _n_ new items were
|
||||
//! taken from a queue, and so on.
|
||||
//!
|
||||
//! ## `Subscriber`s
|
||||
//!
|
||||
//! As `Span`s and `Event`s occur, they are recorded or aggregated by
|
||||
//! implementations of the [`Subscriber`] trait. `Subscriber`s are notified
|
||||
//! when an `Event` takes place and when a `Span` is entered or exited. These
|
||||
//! notifications are represented by the following `Subscriber` trait methods:
|
||||
//! + [`observe_event`], called when an `Event` takes place,
|
||||
//! + [`enter`], called when execution enters a `Span`,
|
||||
//! + [`exit`], called when execution exits a `Span`
|
||||
//!
|
||||
//! In addition, subscribers may implement the [`enabled`] function to _filter_
|
||||
//! the notifications they receive based on [metadata] describing each `Span`
|
||||
//! or `Event`. If a call to `Subscriber::enabled` returns `false` for a given
|
||||
//! set of metadata, that `Subscriber` will *not* be notified about the
|
||||
//! corresponding `Span` or `Event`. For performance reasons, if no currently
|
||||
//! active subscribers express interest in a given set of metadata by returning
|
||||
//! `true`, then the corresponding `Span` or `Event` will never be constructed.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! First, add this to your `Cargo.toml`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! tokio-trace = "0.1"
|
||||
//! ```
|
||||
//!
|
||||
//! Next, add this to your crate:
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use]
|
||||
//! extern crate tokio_trace;
|
||||
//! # fn main() {}
|
||||
//! ```
|
||||
//!
|
||||
//! `Span`s are constructed using the `span!` macro, and then _entered_
|
||||
//! to indicate that some code takes place within the context of that `Span`:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use]
|
||||
//! # extern crate tokio_trace;
|
||||
//! # use tokio_trace::Level;
|
||||
//! # fn main() {
|
||||
//! // Construct a new span named "my span" with trace log level.
|
||||
//! let span = span!(Level::TRACE, "my span");
|
||||
//! span.enter(|| {
|
||||
//! // Any trace events in this closure or code called by it will occur within
|
||||
//! // the span.
|
||||
//! });
|
||||
//! // Dropping the span will close it, indicating that it has ended.
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! `Event`s are created using the `event!` macro, and are recorded when the
|
||||
//! event is dropped:
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use]
|
||||
//! # extern crate tokio_trace;
|
||||
//! # fn main() {
|
||||
//! use tokio_trace::Level;
|
||||
//! event!(Level::INFO, "something has happened!");
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Users of the [`log`] crate should note that `tokio-trace` exposes a set of
|
||||
//! macros for creating `Event`s (`trace!`, `debug!`, `info!`, `warn!`, and
|
||||
//! `error!`) which may be invoked with the same syntax as the similarly-named
|
||||
//! macros from the `log` crate. Often, the process of converting a project to
|
||||
//! use `tokio-trace` can begin with a simple drop-in replacement.
|
||||
//!
|
||||
//! Let's consider the `log` crate's yak-shaving example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use]
|
||||
//! extern crate tokio_trace;
|
||||
//! use tokio_trace::{field, Level};
|
||||
//! # #[derive(Debug)] pub struct Yak(String);
|
||||
//! # impl Yak { fn shave(&mut self, _: u32) {} }
|
||||
//! # fn find_a_razor() -> Result<u32, u32> { Ok(1) }
|
||||
//! # fn main() {
|
||||
//! pub fn shave_the_yak(yak: &mut Yak) {
|
||||
//! // Create a new span for this invocation of `shave_the_yak`, annotated
|
||||
//! // with the yak being shaved as a *field* on the span.
|
||||
//! span!(Level::TRACE, "shave_the_yak", yak = field::debug(&yak)).enter(|| {
|
||||
//! // Since the span is annotated with the yak, it is part of the context
|
||||
//! // for everything happening inside the span. Therefore, we don't need
|
||||
//! // to add it to the message for this event, as the `log` crate does.
|
||||
//! info!(target: "yak_events", "Commencing yak shaving");
|
||||
//!
|
||||
//! loop {
|
||||
//! match find_a_razor() {
|
||||
//! Ok(razor) => {
|
||||
//! // We can add the razor as a field rather than formatting it
|
||||
//! // as part of the message, allowing subscribers to consume it
|
||||
//! // in a more structured manner:
|
||||
//! info!({ razor = field::display(razor) }, "Razor located");
|
||||
//! yak.shave(razor);
|
||||
//! break;
|
||||
//! }
|
||||
//! Err(err) => {
|
||||
//! // However, we can also create events with formatted messages,
|
||||
//! // just as we would for log records.
|
||||
//! warn!("Unable to locate a razor: {}, retrying", err);
|
||||
//! }
|
||||
//! }
|
||||
//! }
|
||||
//! })
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! You can find examples showing how to use this crate in the examples
|
||||
//! directory.
|
||||
//!
|
||||
//! ## In libraries
|
||||
//!
|
||||
//! Libraries should link only to the `tokio-trace` crate, and use the provided
|
||||
//! macros to record whatever information will be useful to downstream
|
||||
//! consumers.
|
||||
//!
|
||||
//! ## In executables
|
||||
//!
|
||||
//! In order to record trace events, executables have to use a `Subscriber`
|
||||
//! implementation compatible with `tokio-trace`. A `Subscriber` implements a
|
||||
//! way of collecting trace data, such as by logging it to standard output.
|
||||
//!
|
||||
//! Unlike the `log` crate, `tokio-trace` does *not* use a global `Subscriber`
|
||||
//! which is initialized once. Instead, it follows the `tokio` pattern of
|
||||
//! executing code in a context. For example:
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use]
|
||||
//! extern crate tokio_trace;
|
||||
//! # pub struct FooSubscriber;
|
||||
//! # use tokio_trace::{span::{Id, Attributes, Record}, Metadata};
|
||||
//! # impl tokio_trace::Subscriber for FooSubscriber {
|
||||
//! # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(0) }
|
||||
//! # fn record(&self, _: &Id, _: &Record) {}
|
||||
//! # fn event(&self, _: &tokio_trace::Event) {}
|
||||
//! # fn record_follows_from(&self, _: &Id, _: &Id) {}
|
||||
//! # fn enabled(&self, _: &Metadata) -> bool { false }
|
||||
//! # fn enter(&self, _: &Id) {}
|
||||
//! # fn exit(&self, _: &Id) {}
|
||||
//! # }
|
||||
//! # impl FooSubscriber {
|
||||
//! # fn new() -> Self { FooSubscriber }
|
||||
//! # }
|
||||
//! # fn main() {
|
||||
//!
|
||||
//! let my_subscriber = FooSubscriber::new();
|
||||
//!
|
||||
//! tokio_trace::subscriber::with_default(my_subscriber, || {
|
||||
//! // Any trace events generated in this closure or by functions it calls
|
||||
//! // will be collected by `my_subscriber`.
|
||||
//! })
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! This approach allows trace data to be collected by multiple subscribers
|
||||
//! within different contexts in the program. Alternatively, a single subscriber
|
||||
//! may be constructed by the `main` function and all subsequent code executed
|
||||
//! with that subscriber as the default. Any trace events generated outside the
|
||||
//! context of a subscriber will not be collected.
|
||||
//!
|
||||
//! The executable itself may use the `tokio-trace` crate to instrument itself
|
||||
//! as well.
|
||||
//!
|
||||
//! The [`tokio-trace-nursery`] repository contains less stable crates designed
|
||||
//! to be used with the `tokio-trace` ecosystem. It includes a collection of
|
||||
//! `Subscriber` implementations, as well as utility and adapter crates.
|
||||
//!
|
||||
//! In particular, the following `tokio-trace-nursery` crates are likely to be
|
||||
//! of interest:
|
||||
//!
|
||||
//! - [`tokio-trace-futures`] provides a compatibility layer with the `futures`
|
||||
//! crate, allowing spans to be attached to `Future`s, `Stream`s, and `Executor`s.
|
||||
//! - [`tokio-trace-fmt`] provides a `Subscriber` implementation for
|
||||
//! logging formatted trace data to stdout, with similar filtering and
|
||||
//! formatting to the `env-logger` crate.
|
||||
//! - [`tokio-trace-log`] provides a compatibility layer with the `log` crate,
|
||||
//! allowing log `Record`s to be recorded as `tokio-trace` `Event`s within the
|
||||
//! trace tree. This is useful when a project using `tokio-trace` have
|
||||
//! dependencies which use `log`.
|
||||
//!
|
||||
//!
|
||||
//! ## Crate Feature Flags
|
||||
//!
|
||||
//! The following crate feature flags are available:
|
||||
//!
|
||||
//! * A set of features controlling the [static verbosity level].
|
||||
//! * `log` causes trace instrumentation points to emit [`log`] records as well
|
||||
//! as trace events. This is inteded for use in libraries whose users may be
|
||||
//! using either `tokio-trace` or `log`.
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! tokio-trace = { version = "0.1", features = ["log"] }
|
||||
//! ```
|
||||
//!
|
||||
//! [`log`]: https://docs.rs/log/0.4.6/log/
|
||||
//! [`Span`]: span/struct.Span
|
||||
//! [`Event`]: struct.Event.html
|
||||
//! [`Subscriber`]: subscriber/trait.Subscriber.html
|
||||
//! [`observe_event`]: subscriber/trait.Subscriber.html#tymethod.observe_event
|
||||
//! [`enter`]: subscriber/trait.Subscriber.html#tymethod.enter
|
||||
//! [`exit`]: subscriber/trait.Subscriber.html#tymethod.exit
|
||||
//! [`enabled`]: subscriber/trait.Subscriber.html#tymethod.enabled
|
||||
//! [metadata]: struct.Metadata.html
|
||||
//! [`tokio-trace-nursery`]: https://github.com/tokio-rs/tokio-trace-nursery
|
||||
//! [`tokio-trace-futures`]: https://github.com/tokio-rs/tokio-trace-nursery/tree/master/tokio-trace-futures
|
||||
//! [`tokio-trace-fmt`]: https://github.com/tokio-rs/tokio-trace-nursery/tree/master/tokio-trace-fmt
|
||||
//! [`tokio-trace-log`]: https://github.com/tokio-rs/tokio-trace-nursery/tree/master/tokio-trace-log
|
||||
//! [static verbosity level]: level_filters/index.html#compile-time-filters
|
||||
#[macro_use]
|
||||
extern crate cfg_if;
|
||||
extern crate tokio_trace_core;
|
||||
|
||||
#[cfg(feature = "log")]
|
||||
#[doc(hidden)]
|
||||
pub extern crate log;
|
||||
|
||||
// Somehow this `use` statement is necessary for us to re-export the `core`
|
||||
// macros on Rust 1.26.0. I'm not sure how this makes it work, but it does.
|
||||
#[allow(unused_imports)]
|
||||
#[doc(hidden)]
|
||||
use tokio_trace_core::*;
|
||||
|
||||
pub use self::{
|
||||
dispatcher::Dispatch,
|
||||
event::Event,
|
||||
field::Value,
|
||||
span::Span,
|
||||
subscriber::Subscriber,
|
||||
tokio_trace_core::{dispatcher, event, Level, Metadata},
|
||||
};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use self::{
|
||||
span::Id,
|
||||
tokio_trace_core::{
|
||||
callsite::{self, Callsite},
|
||||
metadata,
|
||||
},
|
||||
};
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod field;
|
||||
pub mod level_filters;
|
||||
pub mod span;
|
||||
pub mod subscriber;
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user