Skip to content

Commit 7eef5d3

Browse files
committed
tracing: make Entered !Send
The `Entered` guard returned by `Span::enter` represents entering and exiting a span _on the current thread_. Calling `Span::enter` enters the span, returning an `Entered`, and dropping the `Entered` ensures that the span is exited. This ensures that all spans, once entered, are eventually exited. However, `Entered` has an auto-impl of `Send`, because it doesn't contain any `!Send` types. This means that the `Entered` guard _could_ be sent to another thread and dropped. This would cause the original thread to never exit the span,. and the thread to which the `Entered` guard was sent would exit a span that it never observed an enter for. This is incorrect. This PR adds a `*mut ()` field to `Entered` so that it no longer implements `Send`. There's now a manual `Sync` impl so structs holding an `Entered` can still be `Sync`. Fixes #698 Signed-off-by: Eliza Weisman <[email protected]>
1 parent 09a3657 commit 7eef5d3

File tree

2 files changed

+96
-67
lines changed

2 files changed

+96
-67
lines changed

examples/examples/hyper-echo.rs

Lines changed: 61 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -16,78 +16,75 @@ async fn echo(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
1616
uri = ?req.uri(),
1717
headers = ?req.headers()
1818
);
19-
let _enter = span.enter();
20-
info!("received request");
21-
let mut response = Response::new(Body::empty());
19+
async move {
20+
info!("received request");
21+
let mut response = Response::new(Body::empty());
2222

23-
let (rsp_span, resp) = match (req.method(), req.uri().path()) {
24-
// Serve some instructions at /
25-
(&Method::GET, "/") => {
26-
const BODY: &str = "Try POSTing data to /echo";
27-
*response.body_mut() = Body::from(BODY);
28-
(span!(Level::INFO, "response", body = %(&BODY)), response)
29-
}
23+
match (req.method(), req.uri().path()) {
24+
// Serve some instructions at /
25+
(&Method::GET, "/") => {
26+
const BODY: &str = "Try POSTing data to /echo";
27+
*response.body_mut() = Body::from(BODY);
28+
info!(body = %(&BODY), "response",);
29+
Ok(response)
30+
}
3031

31-
// Simply echo the body back to the client.
32-
(&Method::POST, "/echo") => {
33-
let span = span!(Level::INFO, "response", response_kind = %"echo");
34-
*response.body_mut() = req.into_body();
35-
(span, response)
36-
}
32+
// Simply echo the body back to the client.
33+
(&Method::POST, "/echo") => {
34+
info!(response_kind = %"echo", "response");
35+
*response.body_mut() = req.into_body();
36+
Ok(response)
37+
}
3738

38-
// Convert to uppercase before sending back to client.
39-
(&Method::POST, "/echo/uppercase") => {
40-
let body = hyper::body::to_bytes(req).await?;
41-
let upper = body
42-
.iter()
43-
.map(|byte| byte.to_ascii_uppercase())
44-
.collect::<Vec<u8>>();
45-
debug!(
46-
body = ?str::from_utf8(&body[..]),
47-
uppercased = ?str::from_utf8(&upper[..]),
48-
"uppercased request body"
49-
);
39+
// Convert to uppercase before sending back to client.
40+
(&Method::POST, "/echo/uppercase") => {
41+
let body = hyper::body::to_bytes(req).await?;
42+
let upper = body
43+
.iter()
44+
.map(|byte| byte.to_ascii_uppercase())
45+
.collect::<Vec<u8>>();
46+
debug!(
47+
body = ?str::from_utf8(&body[..]),
48+
uppercased = ?str::from_utf8(&upper[..]),
49+
"uppercased request body"
50+
);
5051

51-
*response.body_mut() = Body::from(upper);
52-
(
53-
span!(Level::INFO, "response", response_kind = %"uppercase"),
54-
response,
55-
)
56-
}
52+
info!(response_kind = %"uppercase", "response");
53+
*response.body_mut() = Body::from(upper);
54+
Ok(response)
55+
}
5756

58-
// Reverse the entire body before sending back to the client.
59-
(&Method::POST, "/echo/reversed") => {
60-
let span = span!(Level::TRACE, "response", response_kind = %"reversed");
61-
let _enter = span.enter();
62-
let body = hyper::body::to_bytes(req).await?;
63-
let reversed = body.iter().rev().cloned().collect::<Vec<u8>>();
64-
debug!(
65-
body = ?str::from_utf8(&body[..]),
66-
"reversed request body"
67-
);
68-
*response.body_mut() = Body::from(reversed);
69-
(
70-
span!(Level::INFO, "reversed", body = ?(&response.body())),
71-
response,
72-
)
73-
}
57+
// Reverse the entire body before sending back to the client.
58+
(&Method::POST, "/echo/reversed") => {
59+
async move {
60+
let body = hyper::body::to_bytes(req).await?;
61+
let reversed = body.iter().rev().cloned().collect::<Vec<u8>>();
62+
debug!(
63+
body = ?str::from_utf8(&body[..]),
64+
"reversed request body"
65+
);
66+
*response.body_mut() = Body::from(reversed);
67+
info!( body = ?(&response.body()), "response");
68+
Ok(response)
69+
}
70+
.instrument(span!(Level::TRACE, "response", response_kind = %"reversed"))
71+
.await
72+
}
7473

75-
// The 404 Not Found route...
76-
_ => {
77-
*response.status_mut() = StatusCode::NOT_FOUND;
78-
(
79-
span!(
80-
Level::TRACE,
81-
"response",
74+
// The 404 Not Found route...
75+
_ => {
76+
*response.status_mut() = StatusCode::NOT_FOUND;
77+
info!(
8278
body = ?(),
8379
status = ?StatusCode::NOT_FOUND,
84-
),
85-
response,
86-
)
80+
"response",
81+
);
82+
Ok(response)
83+
}
8784
}
88-
};
89-
let f = async { resp }.instrument(rsp_span);
90-
Ok(f.await)
85+
}
86+
.instrument(span)
87+
.await
9188
}
9289

9390
#[tokio::main]
@@ -107,14 +104,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
107104

108105
let local_addr: std::net::SocketAddr = ([127, 0, 0, 1], 3000).into();
109106
let server_span = span!(Level::TRACE, "server", %local_addr);
110-
let _enter = server_span.enter();
111107

112108
let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(echo)) });
113109
let server = Server::bind(&local_addr)
114110
.serve(service)
115111
.instrument(server_span.clone());
116112

117-
info!("listening...");
113+
info!(parent: &server_span, "listening...");
118114
server.await?;
119115

120116
Ok(())

tracing/src/span.rs

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,7 @@ pub use tracing_core::span::{Attributes, Id, Record};
318318
use crate::stdlib::{
319319
cmp, fmt,
320320
hash::{Hash, Hasher},
321+
marker::PhantomData,
321322
};
322323
use crate::{
323324
dispatcher::{self, Dispatch},
@@ -379,6 +380,7 @@ pub(crate) struct Inner {
379380
#[must_use = "once a span has been entered, it should be exited"]
380381
pub struct Entered<'a> {
381382
span: &'a Span,
383+
_not_send: PhantomData<*mut ()>,
382384
}
383385

384386
/// `log` target for all span lifecycle (creation/enter/exit/close) records.
@@ -562,7 +564,17 @@ impl Span {
562564
/// will call [`Subscriber::exit`]. If the span is disabled, this does
563565
/// nothing.
564566
///
565-
/// # In Asynchronous Code
567+
/// <div class="information">
568+
/// <div class="tooltip ignore" style="">ⓘ<span class="tooltiptext">Note</span></div>
569+
/// </div>
570+
/// <div class="example-wrap" style="display:inline-block">
571+
/// <pre class="ignore" style="white-space:normal;font:inherit;">
572+
/// <strong>Note</strong>: The returned
573+
/// <a href="../struct.Entered.html"><code>Entered</code></a> guard does not
574+
/// implement <code>Send</code>. Dropping the guard will exit <em>this</em> span,
575+
/// and if the guard is sent to another thread and dropped there, that thread may
576+
/// never have entered this span. Thus, <code>Entered</code> should not be sent
577+
/// between threads.</pre></div>
566578
///
567579
/// **Warning**: in asynchronous code that uses [async/await syntax][syntax],
568580
/// `Span::enter` should be used very carefully or avoided entirely. Holding
@@ -755,7 +767,10 @@ impl Span {
755767
}
756768
}}
757769

758-
Entered { span: self }
770+
Entered {
771+
span: self,
772+
_not_send: PhantomData,
773+
}
759774
}
760775

761776
/// Executes the given function in the context of this span.
@@ -1220,6 +1235,24 @@ impl Clone for Inner {
12201235

12211236
// ===== impl Entered =====
12221237

1238+
/// # Safety
1239+
///
1240+
/// Technically, `Entered` _can_ implement both `Send` *and* `Sync` safely. It
1241+
/// doesn't, because it has a `PhantomData<*mut ()>` field, specifically added
1242+
/// in order to make it `!Send`.
1243+
///
1244+
/// Sending an `Entered` guard between threads cannot cause memory unsafety.
1245+
/// However, it *would* result in incorrect behavior, so we add a
1246+
/// `PhantomData<*mut ()>` to prevent it from being sent between threads. This
1247+
/// is because it must be *dropped* on the same thread that it was created;
1248+
/// otherwise, the span will never be exited on the thread where it was entered,
1249+
/// and it will attempt to exit the span on a thread that may never have entered
1250+
/// it. However, we still want them to be `Sync` so that a struct holding an
1251+
/// `Entered` guard can be `Sync`.
1252+
///
1253+
/// Thus, this is totally safe.
1254+
unsafe impl<'a> Sync for Entered<'a> {}
1255+
12231256
impl<'a> Drop for Entered<'a> {
12241257
#[inline]
12251258
fn drop(&mut self) {

0 commit comments

Comments
 (0)