async_inspect/runtime/
async_std.rs

1//! async-std runtime integration
2//!
3//! This module provides integration with the async-std runtime,
4//! allowing automatic task tracking for async-std tasks.
5
6use crate::inspector::Inspector;
7use crate::instrument::{clear_current_task_id, set_current_task_id};
8use crate::task::TaskId;
9use std::future::Future;
10use std::pin::Pin;
11use std::task::{Context, Poll};
12use std::time::Instant;
13
14/// Spawn a tracked task on the async-std runtime
15///
16/// This function spawns a task and automatically tracks it in the global Inspector.
17///
18/// # Examples
19///
20/// ```no_run
21/// use async_inspect::runtime::async_std::spawn_tracked;
22///
23/// async_std::task::block_on(async {
24///     let handle = spawn_tracked("my_task", async {
25///         // Your async code here
26///         42
27///     });
28///
29///     let result = handle.await;
30///     println!("Result: {}", result);
31/// });
32/// ```
33pub fn spawn_tracked<F, T>(name: T, future: F) -> async_std::task::JoinHandle<F::Output>
34where
35    F: Future + Send + 'static,
36    F::Output: Send + 'static,
37    T: Into<String>,
38{
39    use crate::instrument::current_task_id;
40
41    let task_name = name.into();
42
43    // Check if there's a parent task
44    let task_id = if let Some(parent_id) = current_task_id() {
45        Inspector::global().register_child_task(task_name, parent_id)
46    } else {
47        Inspector::global().register_task(task_name)
48    };
49
50    async_std::task::spawn(async move {
51        // Set task context for this task
52        set_current_task_id(task_id);
53
54        // Wrap execution to track completion
55        let result = future.await;
56
57        // Mark as completed
58        Inspector::global().task_completed(task_id);
59
60        // Clear context
61        clear_current_task_id();
62
63        result
64    })
65}
66
67/// Spawn a local tracked task on the async-std runtime
68///
69/// Similar to `spawn_tracked` but for !Send futures.
70/// Note: This requires the `unstable` feature of async-std.
71///
72/// # Examples
73///
74/// ```no_run
75/// # #[cfg(feature = "unstable")]
76/// use async_inspect::runtime::async_std::spawn_local_tracked;
77///
78/// # #[cfg(feature = "unstable")]
79/// async_std::task::block_on(async {
80///     let handle = spawn_local_tracked("my_local_task", async {
81///         // Your !Send async code here
82///         42
83///     });
84///
85///     let result = handle.await;
86///     println!("Result: {}", result);
87/// });
88/// ```
89#[cfg(feature = "unstable")]
90pub fn spawn_local_tracked<F, T>(name: T, future: F) -> async_std::task::JoinHandle<F::Output>
91where
92    F: Future + 'static,
93    F::Output: 'static,
94    T: Into<String>,
95{
96    let task_name = name.into();
97    let task_id = Inspector::global().register_task(task_name);
98
99    async_std::task::spawn_local(async move {
100        set_current_task_id(task_id);
101
102        let result = future.await;
103
104        Inspector::global().task_completed(task_id);
105        clear_current_task_id();
106
107        result
108    })
109}
110
111/// A future wrapper that automatically tracks execution
112pub struct TrackedFuture<F> {
113    future: F,
114    task_id: TaskId,
115    started: bool,
116    poll_start: Option<Instant>,
117}
118
119impl<F> TrackedFuture<F> {
120    /// Create a new tracked future
121    pub fn new(future: F, name: String) -> Self {
122        let task_id = Inspector::global().register_task(name);
123
124        Self {
125            future,
126            task_id,
127            started: false,
128            poll_start: None,
129        }
130    }
131
132    /// Get the task ID
133    pub fn task_id(&self) -> TaskId {
134        self.task_id
135    }
136}
137
138impl<F: Future> Future for TrackedFuture<F> {
139    type Output = F::Output;
140
141    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
142        // SAFETY: We don't move the future
143        let this = unsafe { self.get_unchecked_mut() };
144
145        // Set task context
146        set_current_task_id(this.task_id);
147
148        // Record poll start
149        if !this.started {
150            this.started = true;
151        }
152
153        let poll_start = Instant::now();
154        this.poll_start = Some(poll_start);
155
156        Inspector::global().poll_started(this.task_id);
157
158        // Poll the inner future
159        // SAFETY: We're pinning the projection
160        let result = unsafe { Pin::new_unchecked(&mut this.future).poll(cx) };
161
162        // Record poll end
163        let poll_duration = poll_start.elapsed();
164        Inspector::global().poll_ended(this.task_id, poll_duration);
165
166        match result {
167            Poll::Ready(output) => {
168                // Task completed
169                Inspector::global().task_completed(this.task_id);
170                clear_current_task_id();
171                Poll::Ready(output)
172            }
173            Poll::Pending => {
174                // Still pending
175                Poll::Pending
176            }
177        }
178    }
179}
180
181/// Extension trait for async-std futures to add tracking
182///
183/// # Examples
184///
185/// ```no_run
186/// use async_inspect::runtime::async_std::InspectExt;
187///
188/// async_std::task::block_on(async {
189///     let result = async {
190///         // Your async code
191///         42
192///     }
193///     .inspect("my_operation")
194///     .await;
195/// });
196/// ```
197pub trait InspectExt: Future + Sized {
198    /// Wrap this future with automatic tracking
199    fn inspect(self, name: impl Into<String>) -> TrackedFuture<Self> {
200        TrackedFuture::new(self, name.into())
201    }
202
203    /// Spawn this future on async-std with tracking
204    fn spawn_tracked(self, name: impl Into<String>) -> async_std::task::JoinHandle<Self::Output>
205    where
206        Self: Send + 'static,
207        Self::Output: Send + 'static,
208    {
209        spawn_tracked(name, self)
210    }
211}
212
213// Implement for all futures
214impl<F: Future> InspectExt for F {}
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219
220    #[test]
221    fn test_spawn_tracked() {
222        async_std::task::block_on(async {
223            let handle = spawn_tracked("test_task", async { 42 });
224            let result = handle.await;
225            assert_eq!(result, 42);
226        });
227    }
228
229    #[test]
230    fn test_inspect_ext() {
231        async_std::task::block_on(async {
232            let result = async { 42 }.inspect("test_operation").await;
233            assert_eq!(result, 42);
234        });
235    }
236}