micromegas_tracing/spans/
instrumented_future.rs1use crate::dispatch::{on_begin_async_scope, on_end_async_scope};
4use crate::spans::{DynSpanName, SpanLocation, SpanMetadata, SpanName, StaticSpanName};
5use pin_project::pin_project;
6use std::cell::UnsafeCell;
7use std::future::Future;
8use std::pin::Pin;
9use std::task::{Context, Poll};
10
11thread_local! {
12 static ASYNC_CALL_STACK: UnsafeCell<Vec<u64>> = UnsafeCell ::new(vec![0]);
13}
14
15#[inline]
18pub fn current_span_id() -> u64 {
19 ASYNC_CALL_STACK.with(|stack_cell| {
20 let stack = unsafe { &*stack_cell.get() };
21 stack.last().copied().unwrap_or(0)
22 })
23}
24
25#[pin_project]
36pub struct SpanContextFuture<F> {
37 #[pin]
38 future: F,
39 parent_span: u64,
40 parent_depth: u32,
41}
42
43impl<F: Future> Future for SpanContextFuture<F> {
44 type Output = F::Output;
45
46 #[inline]
47 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
48 let this = self.project();
49 ASYNC_CALL_STACK.with(|stack_cell| {
50 let stack = unsafe { &mut *stack_cell.get() };
51 let saved_len = stack.len();
52 let target_len = *this.parent_depth as usize + 1;
56 while stack.len() < target_len.saturating_sub(1) {
57 stack.push(0);
58 }
59 stack.push(*this.parent_span);
60 let res = this.future.poll(cx);
61 stack.truncate(saved_len);
62 res
63 })
64 }
65}
66
67#[cfg(feature = "tokio")]
94pub fn spawn_with_context<F>(future: F) -> tokio::task::JoinHandle<F::Output>
95where
96 F: Future + Send + 'static,
97 F::Output: Send + 'static,
98{
99 let (parent_span, parent_depth) = ASYNC_CALL_STACK.with(|stack_cell| {
100 let stack = unsafe { &*stack_cell.get() };
101 (
102 stack.last().copied().unwrap_or(0),
103 (stack.len().saturating_sub(1)) as u32,
104 )
105 });
106 tokio::spawn(SpanContextFuture {
107 future,
108 parent_span,
109 parent_depth,
110 })
111}
112
113pub trait InstrumentFuture: Future + Sized {
115 fn instrument(self, span_desc: &'static SpanMetadata) -> InstrumentedFuture<Self> {
117 InstrumentedFuture::new(self, span_desc)
118 }
119
120 #[doc(hidden)]
123 fn __instrument_named_internal(
124 self,
125 span_location: &'static SpanLocation,
126 name: &'static str,
127 ) -> InstrumentedNamedFuture<Self> {
128 InstrumentedNamedFuture::new(self, span_location, name)
129 }
130
131 #[doc(hidden)]
134 fn __instrument_named_dyn_internal<N: AsRef<str>>(
135 self,
136 span_location: &'static SpanLocation,
137 name: N,
138 ) -> InstrumentedNamedFuture<Self, DynSpanName<N>> {
139 InstrumentedNamedFuture::with_name(self, span_location, DynSpanName(name))
140 }
141}
142
143impl<F: Future> InstrumentFuture for F {}
144
145#[pin_project]
147pub struct InstrumentedFuture<F> {
148 #[pin]
149 future: F,
150 desc: &'static SpanMetadata,
151 span_id: Option<u64>,
152 parent: u64,
154 depth: u32,
156}
157
158impl<F> InstrumentedFuture<F> {
159 pub fn new(future: F, desc: &'static SpanMetadata) -> Self {
161 let (parent, depth) = ASYNC_CALL_STACK.with(|stack_cell| {
162 let stack = unsafe { &*stack_cell.get() };
163 assert!(!stack.is_empty());
164 (
165 stack[stack.len() - 1],
166 (stack.len().saturating_sub(1)) as u32,
167 )
168 });
169 Self {
170 future,
171 desc,
172 span_id: None,
173 parent,
174 depth,
175 }
176 }
177}
178
179impl<F> Future for InstrumentedFuture<F>
180where
181 F: Future,
182{
183 type Output = F::Output;
184
185 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
186 let this = self.project();
187 let parent = *this.parent;
188 let depth = *this.depth;
189 ASYNC_CALL_STACK.with(|stack_cell| {
190 let stack = unsafe { &mut *stack_cell.get() };
191 assert!(!stack.is_empty());
192 match this.span_id {
193 Some(span_id) => {
194 stack.push(*span_id);
195 }
196 None => {
197 let span_id = on_begin_async_scope(this.desc, parent, depth);
199 stack.push(span_id);
200 *this.span_id = Some(span_id);
201 }
202 }
203 let res = match this.future.poll(cx) {
204 Poll::Ready(output) => {
205 if let Some(span_id) = *this.span_id {
207 on_end_async_scope(span_id, parent, this.desc, depth);
208 }
209 Poll::Ready(output)
210 }
211 Poll::Pending => Poll::Pending,
212 };
213 stack.pop();
214 res
215 })
216 }
217}
218
219#[pin_project]
221pub struct InstrumentedNamedFuture<F, N = StaticSpanName> {
222 #[pin]
223 future: F,
224 span_location: &'static SpanLocation,
225 name: N,
226 span_id: Option<u64>,
227 parent: u64,
229 depth: u32,
231}
232
233impl<F> InstrumentedNamedFuture<F> {
234 pub fn new(future: F, span_location: &'static SpanLocation, name: &'static str) -> Self {
236 Self::with_name(future, span_location, StaticSpanName(name))
237 }
238}
239
240impl<F, N: SpanName> InstrumentedNamedFuture<F, N> {
241 pub fn with_name(future: F, span_location: &'static SpanLocation, name: N) -> Self {
243 let (parent, depth) = ASYNC_CALL_STACK.with(|stack_cell| {
244 let stack = unsafe { &*stack_cell.get() };
245 assert!(!stack.is_empty());
246 (
247 stack[stack.len() - 1],
248 (stack.len().saturating_sub(1)) as u32,
249 )
250 });
251 Self {
252 future,
253 span_location,
254 name,
255 span_id: None,
256 parent,
257 depth,
258 }
259 }
260}
261
262impl<F, N: SpanName> Future for InstrumentedNamedFuture<F, N>
263where
264 F: Future,
265{
266 type Output = F::Output;
267
268 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
269 let this = self.project();
270 let parent = *this.parent;
271 let depth = *this.depth;
272 ASYNC_CALL_STACK.with(|stack_cell| {
273 let stack = unsafe { &mut *stack_cell.get() };
274 assert!(!stack.is_empty());
275 match this.span_id {
276 Some(span_id) => {
277 stack.push(*span_id);
278 }
279 None => {
280 let span_id = this.name.begin_async(this.span_location, parent, depth);
282 stack.push(span_id);
283 *this.span_id = Some(span_id);
284 }
285 }
286 let res = match this.future.poll(cx) {
287 Poll::Ready(output) => {
288 if let Some(span_id) = *this.span_id {
290 this.name
291 .end_async(span_id, parent, this.span_location, depth);
292 }
293 Poll::Ready(output)
294 }
295 Poll::Pending => Poll::Pending,
296 };
297 stack.pop();
298 res
299 })
300 }
301}