1use std::{
12 future::Future,
13 pin::Pin,
14 sync::Arc,
15 task::{Context, Poll},
16};
17
18use pin_project_lite::pin_project;
19
20use crate::{
21 observer::{Observer, with_observer_task_sync},
22 scope::{ScopeFrame, finish_scope_frame, pop_frame, push_frame},
23};
24
25pin_project! {
26 #[must_use = "Instrumented<F> is a future; await it to drive the inner future"]
30 pub struct Instrumented<F> {
31 #[pin]
32 inner: F,
33 scope_frame: Option<ScopeFrame>,
34 observer: Option<Arc<dyn Observer>>,
35 }
36}
37
38impl<F> std::fmt::Debug for Instrumented<F> {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.debug_struct("Instrumented")
41 .field("has_scope", &self.scope_frame.is_some())
42 .field("has_observer", &self.observer.is_some())
43 .finish()
44 }
45}
46
47impl<F: Future> Future for Instrumented<F> {
48 type Output = F::Output;
49
50 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<F::Output> {
51 let this = self.project();
52 let mut scope_guard = PollScopeGuard::enter(this.scope_frame);
53 let result = match this.observer.as_ref() {
54 Some(o) => with_observer_task_sync(o.clone(), || this.inner.poll(cx)),
55 None => this.inner.poll(cx),
56 };
57 if result.is_ready() {
58 scope_guard.finish_on_drop();
59 }
60 result
61 }
62}
63
64struct PollScopeGuard<'a> {
69 slot: &'a mut Option<ScopeFrame>,
70 active: bool,
71 finished: bool,
72}
73
74impl<'a> PollScopeGuard<'a> {
75 fn enter(slot: &'a mut Option<ScopeFrame>) -> Self {
76 let Some(frame) = slot.take() else {
77 return Self {
78 slot,
79 active: false,
80 finished: false,
81 };
82 };
83 let _ = push_frame(frame);
84 Self {
85 slot,
86 active: true,
87 finished: false,
88 }
89 }
90
91 fn finish_on_drop(&mut self) {
92 self.finished = true;
93 }
94}
95
96impl Drop for PollScopeGuard<'_> {
97 fn drop(&mut self) {
98 if self.active
99 && let Some(frame) = pop_frame()
100 {
101 if self.finished {
102 finish_scope_frame(frame);
103 } else {
104 *self.slot = Some(frame);
105 }
106 }
107 }
108}
109
110pub trait Instrument: Future + Sized {
113 fn instrument(self, scope: ScopeFrame) -> Instrumented<Self> {
117 Instrumented {
118 inner: self,
119 scope_frame: Some(scope),
120 observer: None,
121 }
122 }
123}
124
125impl<F: Future> Instrument for F {}
126
127pub trait WithObserver: Future + Sized {
131 fn with_observer(self, observer: Arc<dyn Observer>) -> Instrumented<Self> {
133 Instrumented {
134 inner: self,
135 scope_frame: None,
136 observer: Some(observer),
137 }
138 }
139}
140
141impl<F: Future> WithObserver for F {}
142
143impl<F: Future> Instrumented<F> {
144 pub fn instrument(mut self, scope: ScopeFrame) -> Self {
147 self.scope_frame = Some(scope);
148 self
149 }
150
151 pub fn with_observer(mut self, observer: Arc<dyn Observer>) -> Self {
154 self.observer = Some(observer);
155 self
156 }
157}