Skip to main content

task_control/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! A simple asynchronous task model for execution that needs to be started,
5//! stopped, mutated, and inspected.
6//!
7//! # Worker cancellation
8//!
9//! [`TaskControl`] requests cancellation for stop, inspection, and updates.
10//! Return `Err(Cancelled)` to hand control back with unfinished work; `Ok(())`
11//! means the worker is permanently finished until removed and reinserted.
12//!
13//! An `await` need not return `Pending`. If a wrapped loop keeps making progress
14//! in one poll, [`StopTask::until_stopped`] cannot interrupt it from outside.
15//! Choose the wrapping style below to match your work loop. In both examples,
16//! `process` is a bounded synchronous step.
17//!
18//! ## Per-operation waits
19//!
20//! Wrap each interruptible wait when its poll and the work between waits are
21//! bounded. The wrapper checks stop even when every wait is immediately ready.
22//!
23//! ```rust
24//! use futures::{Stream, StreamExt};
25//! use task_control::{Cancelled, StopTask};
26//!
27//! async fn run<T>(
28//!     stop: &mut StopTask<'_>,
29//!     items: &mut (impl Stream<Item = T> + Unpin),
30//!     mut process: impl FnMut(T),
31//! ) -> Result<(), Cancelled> {
32//!     loop {
33//!         let Some(item) = stop.until_stopped(items.next()).await? else {
34//!             return Ok(());
35//!         };
36//!         process(item);
37//!     }
38//! }
39//! ```
40//!
41//! ## Whole-loop waits
42//!
43//! When wrapping the whole loop, obtain a [`StopCheck`] before borrowing `stop`
44//! for the wait. Check it before fetching each new unit of work. The wrapper
45//! interrupts blocked waits; the checks interrupt continuously ready work.
46//!
47//! ```rust
48//! use futures::{Stream, StreamExt};
49//! use task_control::{Cancelled, StopTask};
50//!
51//! async fn run<T>(
52//!     stop: &mut StopTask<'_>,
53//!     items: &mut (impl Stream<Item = T> + Unpin),
54//!     mut process: impl FnMut(T),
55//! ) -> Result<(), Cancelled> {
56//!     let check = stop.check_handle()?;
57//!     stop.until_stopped(async {
58//!         loop {
59//!             check.check()?;
60//!             let Some(item) = items.next().await else {
61//!                 return Ok(());
62//!             };
63//!             process(item);
64//!         }
65//!     })
66//!     .await?
67//! }
68//! ```
69//!
70//! ## Choosing check boundaries
71//!
72//! - Check between bounded batches rather than inside every item. Queue capacity
73//!   is not a bound if buffers or credits can be recycled while draining.
74//! - Cover nested loops and each concurrent branch, not just worker entry. A
75//!   shared helper can instead return after one bounded step to a checked caller.
76//! - Keep accepted I/O and partial progress in the task or its state across
77//!   awaits. Finish required queue publication and coordinator handoffs before
78//!   returning cancellation; do not put a check between consuming work and
79//!   recording the state needed to resume it.
80//!
81//! Keep cancellation separate from operational failures. Do not catch and log
82//! `Cancelled` and then return `Ok(())`, which prevents the worker from resuming.
83//! A synchronous check does not interrupt an outstanding wait or yield the
84//! executor; use `until_stopped` for waits that must be interruptible.
85
86#![forbid(unsafe_code)]
87
88use inspect::Inspect;
89use inspect::InspectMut;
90use pal_async::task::Spawn;
91use pal_async::task::Task;
92use parking_lot::Mutex;
93use std::future::Future;
94use std::future::poll_fn;
95use std::pin::Pin;
96use std::pin::pin;
97use std::sync::Arc;
98use std::sync::atomic::AtomicBool;
99use std::sync::atomic::Ordering;
100use std::task::Context;
101use std::task::Poll;
102use std::task::Waker;
103
104/// A method implemented by a task that can be run and stopped, storing
105/// transient state in `S`.
106///
107/// See the [worker cancellation guide](crate#worker-cancellation) for wrapping
108/// examples and check placement.
109pub trait AsyncRun<S>: 'static + Send {
110    /// Runs the task.
111    ///
112    /// The task should stop when `stop` becomes ready. This can be determined
113    /// either by awaiting on `stop`, or by calling [`StopTask::until_stopped`]
114    /// with a future to run.
115    /// A future passed to `until_stopped` must return from each poll after
116    /// bounded work. Otherwise, use [`StopTask::check`] at cancel-safe work
117    /// boundaries within that future.
118    ///
119    /// The function should return `Ok(())` if the task is complete, in which
120    /// case it will only run again after being removed and reinserted.
121    ///
122    /// If the function instead returns `Err(Cancelled)`, this indicates that
123    /// the task's work is not complete, and it should be restarted after
124    /// handling any incoming events. Implementations that choose to use
125    /// [`StopTask::until_stopped`] must be cancel-tolerant: because
126    /// [`StopTask::until_stopped`] drops the inner future when a stop
127    /// is signaled, implementations must ensure no in-flight work is silently
128    /// lost across a cancellation.
129    fn run(
130        &mut self,
131        stop: &mut StopTask<'_>,
132        _: &mut S,
133    ) -> impl Send + Future<Output = Result<(), Cancelled>>;
134}
135
136/// The return error from [`AsyncRun::run`] indicating the task has not yet
137/// finished executing.
138///
139/// This is control flow, not an operational error. It deliberately does not
140/// implement [`std::error::Error`], preventing implicit error boxing.
141///
142/// ```compile_fail
143/// let error: Box<dyn std::error::Error> = Box::new(task_control::Cancelled);
144/// ```
145#[derive(Debug)]
146pub struct Cancelled;
147
148/// A future indicating that the task should return for event processing or
149/// because the task was stopped.
150///
151/// See the [worker cancellation guide](crate#worker-cancellation).
152pub struct StopTask<'a> {
153    inner: &'a mut (dyn 'a + Send + Future<Output = ()> + Unpin),
154    requested: &'a AtomicBool,
155    outer_waker: Option<Waker>,
156}
157
158/// A control-request check that can be used while [`StopTask`] is borrowed by a wait.
159///
160/// The handle borrows the backing signal, not the `StopTask`, and must only be
161/// used within the current worker activation.
162#[derive(Clone, Copy)]
163pub struct StopCheck<'a>(&'a AtomicBool);
164
165impl StopCheck<'_> {
166    /// Checks for a control request with only a relaxed atomic load.
167    #[inline]
168    pub fn check(&self) -> Result<(), Cancelled> {
169        if self.0.load(Ordering::Relaxed) {
170            Err(Cancelled)
171        } else {
172            Ok(())
173        }
174    }
175}
176
177/// The inner polling implementation, which polls for an incoming request from
178/// `TaskControl`.
179///
180/// This is separate from `StopTask` so that the types can be erased.
181struct StopTaskInner<'a, T, S> {
182    shared: &'a Shared<T, S>,
183}
184
185impl<T: AsyncRun<S>, S> Future for StopTaskInner<'_, T, S> {
186    type Output = ();
187
188    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
189        let mut shared = self.get_mut().shared.lock();
190        if !shared.calls.is_empty() || shared.stop {
191            return Poll::Ready(());
192        }
193        if shared
194            .inner_waker
195            .as_ref()
196            .is_none_or(|waker| !cx.waker().will_wake(waker))
197        {
198            shared.inner_waker = Some(cx.waker().clone());
199        }
200        Poll::Pending
201    }
202}
203
204impl<'a> StopTask<'a> {
205    /// Runs `f` with a task that never receives control requests.
206    ///
207    /// Intended for test helpers that run a device without a `TaskControl`.
208    pub async fn run_unstoppable<R>(f: impl AsyncFnOnce(&mut StopTask<'_>) -> R) -> R {
209        let requested = AtomicBool::new(false);
210        let mut pending = std::future::pending();
211        let mut stop = StopTask {
212            inner: &mut pending,
213            requested: &requested,
214            outer_waker: None,
215        };
216        f(&mut stop).await
217    }
218
219    /// Checks whether the task should return for stop or event processing.
220    ///
221    /// Call at cancel-safe boundaries in loops that can keep making progress
222    /// without yielding. This does not yield or cancel an outstanding wait;
223    /// use [`Self::until_stopped`] for waits that must be interruptible.
224    ///
225    /// This only performs a relaxed atomic load. Request producers publish
226    /// the signal directly; checking does not register or poll a waker.
227    #[inline]
228    pub fn check(&self) -> Result<(), Cancelled> {
229        StopCheck(self.requested).check()
230    }
231
232    /// Checks for a request and returns a handle for this worker activation.
233    ///
234    /// Use this inside a future passed to [`Self::until_stopped`], or when
235    /// several processing loops need to check the same control request.
236    pub fn check_handle(&self) -> Result<StopCheck<'a>, Cancelled> {
237        self.check()?;
238        Ok(StopCheck(self.requested))
239    }
240
241    /// Runs `fut` until the task is requested to stop.
242    ///
243    /// If `fut` completes, then `Ok(_)` is returned.
244    ///
245    /// If the task is requested to stop before `fut` completes, then `fut` is
246    /// dropped and `Err(Cancelled)` is returned.
247    /// Stop takes priority when both stop and work are ready.
248    pub async fn until_stopped<F: Future>(&mut self, fut: F) -> Result<F::Output, Cancelled> {
249        let mut fut = pin!(fut);
250
251        // Check stop, poll work, then handle wake registration only if pending.
252        // Always-ready loops avoid waker comparisons entirely. Frequently
253        // pending workers reuse registration without a second stop-flag load.
254        poll_fn(|cx| {
255            if let Err(cancelled) = self.check() {
256                Poll::Ready(Err(cancelled))
257            } else if let Poll::Ready(r) = fut.as_mut().poll(cx) {
258                Poll::Ready(Ok(r))
259            } else if self.poll_wait(cx).is_ready() {
260                Poll::Ready(Err(Cancelled))
261            } else {
262                Poll::Pending
263            }
264        })
265        .await
266    }
267
268    #[inline]
269    fn poll_wait(&mut self, cx: &mut Context<'_>) -> Poll<()> {
270        // The caller already checked stop. On a cache hit, a request arriving
271        // during work polling wakes this task for another poll.
272        if self
273            .outer_waker
274            .as_ref()
275            .is_some_and(|waker| cx.waker().will_wake(waker))
276        {
277            return Poll::Pending;
278        }
279        if Pin::new(&mut self.inner).poll(cx).is_ready() {
280            return Poll::Ready(());
281        }
282        self.outer_waker = Some(cx.waker().clone());
283        // Recheck after registering the waker so a concurrent request
284        // cannot leave an interruptible wait asleep.
285        if self.check().is_err() {
286            Poll::Ready(())
287        } else {
288            Poll::Pending
289        }
290    }
291}
292
293impl Future for StopTask<'_> {
294    type Output = ();
295
296    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
297        if self.check().is_err() {
298            Poll::Ready(())
299        } else {
300            self.poll_wait(cx)
301        }
302    }
303}
304
305/// A task wrapper that runs a task asynchronously and provides access to its
306/// state and control over its execution (start/stop).
307///
308/// Pairs a task implementation `T: AsyncRun<S>` with transient state `S`.
309/// Execution is cancelled when [`stop`](Self::stop) is invoked.
310///
311/// Outside of an actual [`stop`](Self::stop), `TaskControl` also raises the
312/// stop signal to fulfill [`update_with`](Self::update_with) and
313/// [`Inspect`]/[`InspectMut`] calls, after which `run` is re-invoked with
314/// the (possibly updated) state. See [`AsyncRun`] for the cancel-tolerance
315/// requirements this places on implementations.
316pub struct TaskControl<T, S> {
317    inner: Inner<T, S>,
318}
319
320/// A trait for inspecting a task and its associated state.
321pub trait InspectTask<S>: AsyncRun<S> {
322    /// Inspects the task and its state.
323    ///
324    /// The state may be missing if it has not yet been inserted into the
325    /// [`TaskControl`].
326    fn inspect(&self, req: inspect::Request<'_>, state: Option<&S>);
327}
328
329impl<T: InspectTask<S>, S> Inspect for TaskAndState<T, S> {
330    fn inspect(&self, req: inspect::Request<'_>) {
331        self.task.inspect(req, self.state.as_ref());
332    }
333}
334
335impl<T: InspectTask<S>, S> Inspect for TaskControl<T, S> {
336    fn inspect(&self, req: inspect::Request<'_>) {
337        match &self.inner {
338            Inner::NoState(task_and_state) => task_and_state.inspect(req),
339            Inner::WithState {
340                activity, shared, ..
341            } => match activity {
342                Activity::Stopped(task_and_state) => task_and_state.inspect(req),
343                Activity::Running => {
344                    let deferred = req.defer();
345                    Shared::push_call(
346                        shared,
347                        Box::new(|task_and_state| {
348                            deferred.inspect(&task_and_state);
349                        }),
350                    )
351                }
352            },
353            Inner::Invalid => unreachable!(),
354        }
355    }
356}
357
358/// A trait for mutably inspecting a task and its associated state.
359pub trait InspectTaskMut<T>: AsyncRun<T> {
360    /// Inspects the task and its state.
361    ///
362    /// The state may be missing if it has not yet been inserted into the
363    /// [`TaskControl`].
364    fn inspect_mut(&mut self, req: inspect::Request<'_>, state: Option<&mut T>);
365}
366
367impl<T: InspectTaskMut<S>, S> InspectMut for TaskAndState<T, S> {
368    fn inspect_mut(&mut self, req: inspect::Request<'_>) {
369        self.task.inspect_mut(req, self.state.as_mut());
370    }
371}
372
373impl<T: InspectTaskMut<U>, U> InspectMut for TaskControl<T, U> {
374    fn inspect_mut(&mut self, req: inspect::Request<'_>) {
375        match &mut self.inner {
376            Inner::NoState(task_and_state) => task_and_state.inspect_mut(req),
377            Inner::WithState {
378                activity, shared, ..
379            } => match activity {
380                Activity::Stopped(task_and_state) => task_and_state.inspect_mut(req),
381                Activity::Running => {
382                    let deferred = req.defer();
383                    Shared::push_call(
384                        shared,
385                        Box::new(|task_and_state| {
386                            deferred.inspect(task_and_state);
387                        }),
388                    );
389                }
390            },
391            Inner::Invalid => unreachable!(),
392        }
393    }
394}
395
396type CallFn<T, S> = Box<dyn FnOnce(&mut TaskAndState<T, S>) + Send>;
397
398enum Inner<T, S> {
399    NoState(Box<TaskAndState<T, S>>),
400    WithState {
401        activity: Activity<T, S>,
402        _backing_task: Task<()>,
403        shared: Arc<Shared<T, S>>,
404    },
405    Invalid,
406}
407
408struct TaskAndState<T, S> {
409    task: T,
410    state: Option<S>,
411    done: bool,
412}
413
414struct Shared<T, S> {
415    // Mirrors stop or queued calls. Writers hold the state lock; readers use
416    // this only to return control, not to acquire access to the protected state.
417    requested: AtomicBool,
418    inner: Mutex<SharedState<T, S>>,
419}
420
421struct SharedState<T, S> {
422    task_and_state: Option<Box<TaskAndState<T, S>>>,
423    calls: Vec<CallFn<T, S>>,
424    stop: bool,
425    outer_waker: Option<Waker>,
426    inner_waker: Option<Waker>,
427}
428
429impl<T, S> Shared<T, S> {
430    fn lock(&self) -> parking_lot::MutexGuard<'_, SharedState<T, S>> {
431        self.inner.lock()
432    }
433
434    fn push_call(this: &Self, f: CallFn<T, S>) {
435        let waker = {
436            let mut shared = this.lock();
437            shared.calls.push(f);
438            this.requested.store(true, Ordering::Relaxed);
439            shared.inner_waker.take()
440        };
441        if let Some(waker) = waker {
442            waker.wake();
443        }
444    }
445}
446
447enum Activity<T, S> {
448    Stopped(Box<TaskAndState<T, S>>),
449    Running,
450}
451
452impl<T: AsyncRun<S>, S: 'static + Send> TaskControl<T, S> {
453    /// Creates the task control, taking the state for the task but not yet
454    /// creating or starting it.
455    pub fn new(task: T) -> Self {
456        Self {
457            inner: Inner::NoState(Box::new(TaskAndState {
458                task,
459                state: None,
460                done: false,
461            })),
462        }
463    }
464
465    /// Returns true if a task has been inserted.
466    pub fn has_state(&self) -> bool {
467        match &self.inner {
468            Inner::NoState(_) => false,
469            Inner::WithState { .. } => true,
470            Inner::Invalid => unreachable!(),
471        }
472    }
473
474    /// Returns true if a task is running.
475    pub fn is_running(&self) -> bool {
476        match &self.inner {
477            Inner::NoState(_)
478            | Inner::WithState {
479                activity: Activity::Stopped { .. },
480                ..
481            } => false,
482            Inner::WithState {
483                activity: Activity::Running,
484                ..
485            } => true,
486            Inner::Invalid => unreachable!(),
487        }
488    }
489
490    /// Gets the task.
491    ///
492    /// Panics if the task is running.
493    #[track_caller]
494    pub fn task(&self) -> &T {
495        self.get().0
496    }
497
498    /// Gets the task.
499    ///
500    /// Panics if the task is running.
501    #[track_caller]
502    pub fn task_mut(&mut self) -> &mut T {
503        self.get_mut().0
504    }
505
506    /// Gets the transient task state.
507    ///
508    /// Panics if the task is running.
509    #[track_caller]
510    pub fn state(&self) -> Option<&S> {
511        self.get().1
512    }
513
514    /// Gets the transient task state.
515    ///
516    /// Panics if the task is running.
517    #[track_caller]
518    pub fn state_mut(&mut self) -> Option<&mut S> {
519        self.get_mut().1
520    }
521
522    /// Gets the task and its state.
523    ///
524    /// Panics if the task is running.
525    #[track_caller]
526    pub fn get(&self) -> (&T, Option<&S>) {
527        let task_and_state = match &self.inner {
528            Inner::NoState(task_and_state) => task_and_state,
529            Inner::WithState {
530                activity: Activity::Stopped(task_and_state),
531                ..
532            } => task_and_state,
533            Inner::WithState {
534                activity: Activity::Running,
535                ..
536            } => panic!("attempt to access running task"),
537            Inner::Invalid => unreachable!(),
538        };
539        (&task_and_state.task, task_and_state.state.as_ref())
540    }
541
542    /// Gets the state and the task.
543    ///
544    /// Panics if the task is running.
545    #[track_caller]
546    pub fn get_mut(&mut self) -> (&mut T, Option<&mut S>) {
547        let task_and_state = match &mut self.inner {
548            Inner::NoState(task_and_state) => task_and_state,
549            Inner::WithState {
550                activity: Activity::Stopped(task_and_state),
551                ..
552            } => task_and_state,
553            Inner::WithState {
554                activity: Activity::Running,
555                ..
556            } => panic!("attempt to access running task"),
557            Inner::Invalid => unreachable!(),
558        };
559        (&mut task_and_state.task, task_and_state.state.as_mut())
560    }
561
562    /// Retrieves the task and its state.
563    ///
564    /// Panics if the task is running.
565    #[track_caller]
566    pub fn into_inner(self) -> (T, Option<S>) {
567        let task_and_state = match self.inner {
568            Inner::NoState(task_and_state) => task_and_state,
569            Inner::WithState {
570                activity: Activity::Stopped(task_and_state),
571                ..
572            } => task_and_state,
573            Inner::WithState {
574                activity: Activity::Running,
575                ..
576            } => panic!("attempt to extract running task"),
577            Inner::Invalid => unreachable!(),
578        };
579        (task_and_state.task, task_and_state.state)
580    }
581
582    /// Calls `f` against the task and its state.
583    ///
584    /// If the task is running, then `f` will run remotely and will not
585    /// necessarily finish before this routine returns.
586    pub fn update_with(&mut self, f: impl 'static + Send + FnOnce(&mut T, Option<&mut S>)) {
587        let f = |task_and_state: &mut TaskAndState<T, S>| {
588            f(&mut task_and_state.task, task_and_state.state.as_mut())
589        };
590        match &mut self.inner {
591            Inner::NoState(task_and_state) => f(task_and_state),
592            Inner::WithState {
593                activity, shared, ..
594            } => match activity {
595                Activity::Stopped(task_and_state) => f(task_and_state),
596                Activity::Running => Shared::push_call(shared, Box::new(f)),
597            },
598            Inner::Invalid => unreachable!(),
599        }
600    }
601
602    /// Inserts the state the task object will use to run and starts the backing
603    /// task, but does not start running it.
604    #[track_caller]
605    pub fn insert(&mut self, spawn: impl Spawn, name: impl Into<Arc<str>>, state: S) -> &mut S {
606        self.inner = match std::mem::replace(&mut self.inner, Inner::Invalid) {
607            Inner::NoState(mut task_and_state) => {
608                task_and_state.state = Some(state);
609                task_and_state.done = false;
610                let shared = Arc::new(Shared {
611                    requested: AtomicBool::new(true),
612                    inner: Mutex::new(SharedState {
613                        task_and_state: None,
614                        calls: Vec::new(),
615                        stop: true,
616                        outer_waker: None,
617                        inner_waker: None,
618                    }),
619                });
620                let backing_task = spawn.spawn(name, Self::run(shared.clone()));
621                Inner::WithState {
622                    activity: Activity::Stopped(task_and_state),
623                    _backing_task: backing_task,
624                    shared,
625                }
626            }
627            Inner::WithState { .. } => panic!("attempt to insert already-present state"),
628            Inner::Invalid => unreachable!(),
629        };
630        self.state_mut().unwrap()
631    }
632
633    /// Starts the task if it is not already running.
634    ///
635    /// Returns true if the task is now running (even if it was previously
636    /// running). Returns false if the task is not running (either because its
637    /// state has not been inserted, or because it has already completed).
638    pub fn start(&mut self) -> bool {
639        match &mut self.inner {
640            Inner::WithState {
641                activity, shared, ..
642            } => match std::mem::replace(activity, Activity::Running) {
643                Activity::Stopped(task_and_state) => {
644                    if task_and_state.done {
645                        *activity = Activity::Stopped(task_and_state);
646                        return false;
647                    }
648                    let waker = {
649                        let mut state = shared.lock();
650                        state.task_and_state = Some(task_and_state);
651                        state.stop = false;
652                        shared
653                            .requested
654                            .store(!state.calls.is_empty(), Ordering::Relaxed);
655                        state.inner_waker.take()
656                    };
657                    if let Some(waker) = waker {
658                        waker.wake();
659                    }
660                    true
661                }
662                Activity::Running => true,
663            },
664            Inner::NoState(_) => false,
665            Inner::Invalid => {
666                unreachable!()
667            }
668        }
669    }
670
671    async fn run(shared: Arc<Shared<T, S>>) {
672        let mut stop_source = StopTaskInner { shared: &shared };
673        let mut stop_task = StopTask {
674            inner: &mut stop_source,
675            requested: &shared.requested,
676            outer_waker: None,
677        };
678        let mut calls = Vec::new();
679        loop {
680            let (mut task_and_state, stop) = poll_fn(|cx| {
681                let mut state = shared.lock();
682                let has_work = state
683                    .task_and_state
684                    .as_ref()
685                    .is_some_and(|ts| !state.calls.is_empty() || (!state.stop && !ts.done));
686                if !has_work {
687                    state.inner_waker = Some(cx.waker().clone());
688                    return Poll::Pending;
689                }
690                calls.append(&mut state.calls);
691                // Drain and reset under the producer lock. Requests arriving
692                // while these callbacks run must remain visible to the worker.
693                shared.requested.store(state.stop, Ordering::Relaxed);
694                Poll::Ready((state.task_and_state.take().unwrap(), state.stop))
695            })
696            .await;
697
698            for call in calls.drain(..) {
699                call(&mut task_and_state);
700            }
701
702            if !stop && !task_and_state.done {
703                // The driver may have consumed or replaced the registered
704                // worker waker since the preceding activation.
705                stop_task.outer_waker = None;
706                if stop_task.check().is_ok() {
707                    task_and_state.done = task_and_state
708                        .task
709                        .run(&mut stop_task, task_and_state.state.as_mut().unwrap())
710                        .await
711                        .is_ok();
712                }
713            }
714
715            let waker = {
716                let mut shared = shared.lock();
717                shared.task_and_state = Some(task_and_state);
718                shared.outer_waker.take()
719            };
720            if let Some(waker) = waker {
721                waker.wake();
722            }
723        }
724    }
725
726    /// Poll variant of [`stop`](Self::stop). Signals the task to stop and polls
727    /// for completion.
728    ///
729    /// Returns `Poll::Ready(true)` if the task was previously running and has
730    /// now stopped. Returns `Poll::Ready(false)` if the task was not running,
731    /// not inserted, or had already completed. Returns `Poll::Pending` if the
732    /// task has not yet stopped.
733    pub fn poll_stop(&mut self, cx: &mut Context<'_>) -> Poll<bool> {
734        match &mut self.inner {
735            Inner::WithState {
736                activity, shared, ..
737            } => match activity {
738                Activity::Running => {
739                    let mut state = shared.lock();
740                    state.stop = true;
741                    shared.requested.store(true, Ordering::Relaxed);
742                    if state.task_and_state.is_none() || !state.calls.is_empty() {
743                        state.outer_waker = Some(cx.waker().clone());
744                        let waker = state.inner_waker.take();
745                        drop(state);
746                        if let Some(waker) = waker {
747                            waker.wake();
748                        }
749                        return Poll::Pending;
750                    }
751                    let task_and_state = state.task_and_state.take().unwrap();
752                    drop(state);
753                    let done = task_and_state.done;
754                    *activity = Activity::Stopped(task_and_state);
755                    Poll::Ready(!done)
756                }
757                _ => Poll::Ready(false),
758            },
759            Inner::NoState(_) => Poll::Ready(false),
760            Inner::Invalid => unreachable!(),
761        }
762    }
763
764    /// Stops the task, waiting for it to be cancelled.
765    ///
766    /// Returns true if the task was previously running. Returns false if the
767    /// task was not running, not inserted, or had already completed.
768    pub async fn stop(&mut self) -> bool {
769        poll_fn(|cx| self.poll_stop(cx)).await
770    }
771
772    /// Removes the task state.
773    ///
774    /// Panics if the task is not stopped.
775    #[track_caller]
776    pub fn remove(&mut self) -> S {
777        match std::mem::replace(&mut self.inner, Inner::Invalid) {
778            Inner::WithState {
779                activity: Activity::Stopped(mut task_and_state),
780                ..
781            } => {
782                let state = task_and_state.state.take().unwrap();
783                self.inner = Inner::NoState(task_and_state);
784                state
785            }
786            Inner::NoState(_) => panic!("attempt to remove missing state"),
787            Inner::WithState { .. } => panic!("attempt to remove state from running task"),
788            Inner::Invalid => {
789                unreachable!()
790            }
791        }
792    }
793}
794
795#[cfg(test)]
796mod tests {
797    use super::AsyncRun;
798    use crate::Cancelled;
799    use crate::StopTask;
800    use crate::TaskControl;
801    use futures::FutureExt;
802    use pal_async::DefaultDriver;
803    use pal_async::async_test;
804    use parking_lot::Mutex;
805    use std::future::Future;
806    use std::pin::Pin;
807    use std::pin::pin;
808    use std::sync::Arc;
809    use std::sync::atomic::AtomicBool;
810    use std::sync::atomic::AtomicUsize;
811    use std::sync::atomic::Ordering;
812    use std::task::Context;
813    use std::task::Poll;
814    use std::task::Wake;
815    use std::task::Waker;
816
817    #[derive(Default)]
818    struct CheckSignal {
819        requested: AtomicBool,
820        polls: AtomicUsize,
821        waker: Mutex<Option<Waker>>,
822        request_during_registration: AtomicBool,
823    }
824
825    #[derive(Default)]
826    struct CountWake(AtomicUsize);
827
828    impl Wake for CountWake {
829        fn wake(self: Arc<Self>) {
830            self.0.fetch_add(1, Ordering::Relaxed);
831        }
832    }
833
834    impl CheckSignal {
835        fn notify(&self) {
836            let waker = self.waker.lock().clone();
837            if let Some(waker) = waker {
838                waker.wake();
839            }
840        }
841    }
842
843    struct CheckStop<'a>(&'a CheckSignal);
844
845    impl Future for CheckStop<'_> {
846        type Output = ();
847
848        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
849            self.0.polls.fetch_add(1, Ordering::Relaxed);
850            let old = self.0.waker.lock().replace(cx.waker().clone());
851            drop(old);
852            let requested = self.0.requested.load(Ordering::Relaxed);
853            if self
854                .0
855                .request_during_registration
856                .swap(false, Ordering::Relaxed)
857            {
858                self.0.requested.store(true, Ordering::Relaxed);
859            }
860            if requested {
861                Poll::Ready(())
862            } else {
863                Poll::Pending
864            }
865        }
866    }
867
868    async fn run_with_signal<R>(
869        signal: &CheckSignal,
870        f: impl AsyncFnOnce(&mut StopTask<'_>) -> R,
871    ) -> R {
872        let mut source = CheckStop(signal);
873        let mut stop = StopTask {
874            inner: &mut source,
875            requested: &signal.requested,
876            outer_waker: None,
877        };
878        f(&mut stop).await
879    }
880
881    #[test]
882    fn checks_observe_requests_without_polling_or_waker_registration() {
883        let signal = Arc::new(CheckSignal::default());
884        let result = run_with_signal(&signal, async |stop| {
885            let check = stop.check_handle().unwrap();
886            for _ in 0..1000 {
887                stop.check().unwrap();
888                check.check().unwrap();
889                stop.until_stopped(std::future::ready(())).await.unwrap();
890            }
891            assert_eq!(signal.polls.load(Ordering::Relaxed), 0);
892            assert!(signal.waker.lock().is_none());
893            assert!(stop.outer_waker.is_none());
894
895            signal.notify();
896            check.check().unwrap();
897            stop.check().unwrap();
898
899            signal.requested.store(true, Ordering::Relaxed);
900            assert!(check.check().is_err());
901            assert!(stop.check().is_err());
902            assert!(stop.check().is_err());
903            assert_eq!(signal.polls.load(Ordering::Relaxed), 0);
904            signal.requested.store(false, Ordering::Relaxed);
905            stop.check().unwrap();
906            signal.requested.store(true, Ordering::Relaxed);
907            assert!(stop.check().is_err());
908        })
909        .now_or_never();
910        assert!(result.is_some());
911    }
912
913    #[test]
914    fn interruptible_wait_prioritizes_stop_and_handles_noop_wakers() {
915        let signal = Arc::new(CheckSignal::default());
916        let result = run_with_signal(&signal, async |stop| {
917            {
918                let mut wait = pin!(stop.until_stopped(std::future::pending::<()>()));
919                assert!(
920                    wait.as_mut()
921                        .poll(&mut Context::from_waker(Waker::noop()))
922                        .is_pending()
923                );
924                signal.requested.store(true, Ordering::Relaxed);
925                signal.notify();
926                assert!(matches!(
927                    wait.as_mut().poll(&mut Context::from_waker(Waker::noop())),
928                    Poll::Ready(Err(_))
929                ));
930            }
931            let work = std::future::poll_fn(|_| -> Poll<u32> {
932                panic!("ready stop must prevent polling work");
933            });
934            assert!(stop.until_stopped(work).await.is_err());
935            assert!(stop.check().is_err());
936        })
937        .now_or_never();
938        assert!(result.is_some());
939    }
940
941    #[test]
942    fn wait_caches_registration_and_tracks_executor_waker_changes() {
943        let signal = Arc::new(CheckSignal::default());
944        let count = Arc::new(CountWake::default());
945        let waker = Waker::from(count.clone());
946        let result = run_with_signal(&signal, async |stop| {
947            let mut cx = Context::from_waker(&waker);
948            assert!(Pin::new(&mut *stop).poll(&mut cx).is_pending());
949            signal.notify();
950            stop.check().unwrap();
951            assert!(Pin::new(&mut *stop).poll(&mut cx).is_pending());
952            assert_eq!(signal.polls.load(Ordering::Relaxed), 1);
953            let new_count = Arc::new(CountWake::default());
954            let new_waker = Waker::from(new_count.clone());
955            let mut cx = Context::from_waker(&new_waker);
956            assert!(Pin::new(&mut *stop).poll(&mut cx).is_pending());
957            assert_eq!(signal.polls.load(Ordering::Relaxed), 2);
958            signal.requested.store(true, Ordering::Relaxed);
959            signal.notify();
960            assert_eq!(count.0.load(Ordering::Relaxed), 1);
961            assert_eq!(new_count.0.load(Ordering::Relaxed), 1);
962            assert!(Pin::new(&mut *stop).poll(&mut cx).is_ready());
963            signal.requested.store(false, Ordering::Relaxed);
964            signal.waker.lock().take();
965            stop.outer_waker = None;
966            assert!(Pin::new(&mut *stop).poll(&mut cx).is_pending());
967            assert_eq!(signal.polls.load(Ordering::Relaxed), 3);
968        })
969        .now_or_never();
970        assert!(result.is_some());
971    }
972
973    #[test]
974    fn cached_wait_observes_stop_on_repoll_when_requested_during_work() {
975        let signal = CheckSignal::default();
976        let count = Arc::new(CountWake::default());
977        let waker = Waker::from(count.clone());
978        let result = run_with_signal(&signal, async |stop| {
979            let mut cx = Context::from_waker(&waker);
980            assert!(Pin::new(&mut *stop).poll(&mut cx).is_pending());
981            let mut polled = false;
982            let work = std::future::poll_fn(|_| -> Poll<()> {
983                assert!(!polled, "stop must prevent polling work again");
984                polled = true;
985                signal.requested.store(true, Ordering::Relaxed);
986                signal.waker.lock().take().unwrap().wake();
987                Poll::Pending
988            });
989            let mut wait = pin!(stop.until_stopped(work));
990            assert!(wait.as_mut().poll(&mut cx).is_pending());
991            assert_eq!(count.0.load(Ordering::Relaxed), 1);
992            assert!(matches!(wait.as_mut().poll(&mut cx), Poll::Ready(Err(_))));
993            assert_eq!(signal.polls.load(Ordering::Relaxed), 1);
994        })
995        .now_or_never();
996        assert!(result.is_some());
997    }
998
999    #[test]
1000    fn wait_observes_request_arriving_during_registration() {
1001        let signal = CheckSignal {
1002            request_during_registration: AtomicBool::new(true),
1003            ..Default::default()
1004        };
1005        let result = run_with_signal(&signal, async |stop| {
1006            let mut wait = pin!(stop.until_stopped(std::future::pending::<()>()));
1007            assert!(matches!(
1008                wait.as_mut().poll(&mut Context::from_waker(Waker::noop())),
1009                Poll::Ready(Err(_))
1010            ));
1011        })
1012        .now_or_never();
1013        assert!(result.is_some());
1014    }
1015
1016    #[async_test]
1017    async fn checks_interrupt_busy_workers_for_update_stop_and_restart(driver: DefaultDriver) {
1018        struct Busy {
1019            started: std::sync::mpsc::Sender<usize>,
1020            updates: usize,
1021        }
1022        impl AsyncRun<()> for Busy {
1023            async fn run(&mut self, stop: &mut StopTask<'_>, _: &mut ()) -> Result<(), Cancelled> {
1024                self.started.send(self.updates).unwrap();
1025                loop {
1026                    stop.check()?;
1027                    std::future::ready(()).await;
1028                }
1029            }
1030        }
1031        async fn stop_with_timeout(task: &mut TaskControl<Busy, ()>, driver: &DefaultDriver) {
1032            let mut timer = pal_async::timer::PolledTimer::new(driver);
1033            match futures::future::select(
1034                pin!(task.stop()),
1035                pin!(timer.sleep(std::time::Duration::from_secs(5))),
1036            )
1037            .await
1038            {
1039                futures::future::Either::Left((stopped, _)) => assert!(stopped),
1040                futures::future::Either::Right(_) => panic!("busy worker did not stop"),
1041            }
1042        }
1043
1044        let (_thread, worker_driver) = pal_async::DefaultPool::spawn_on_thread("busy-check-test");
1045        let (started, recv_started) = std::sync::mpsc::channel();
1046        let mut task = TaskControl::new(Busy {
1047            started,
1048            updates: 0,
1049        });
1050        task.insert(&worker_driver, "busy", ());
1051        assert!(task.start());
1052        recv_started
1053            .recv_timeout(std::time::Duration::from_secs(5))
1054            .unwrap();
1055        let (entered, recv_entered) = std::sync::mpsc::channel();
1056        let (release, recv_release) = std::sync::mpsc::channel();
1057        task.update_with(move |worker, _| {
1058            worker.updates += 1;
1059            entered.send(()).unwrap();
1060            recv_release
1061                .recv_timeout(std::time::Duration::from_secs(5))
1062                .unwrap();
1063        });
1064        recv_entered
1065            .recv_timeout(std::time::Duration::from_secs(5))
1066            .unwrap();
1067        task.update_with(|worker, _| worker.updates += 1);
1068        release.send(()).unwrap();
1069        let updates_at_restart = recv_started
1070            .recv_timeout(std::time::Duration::from_secs(5))
1071            .unwrap();
1072        stop_with_timeout(&mut task, &driver).await;
1073        assert_eq!(updates_at_restart, 2);
1074        assert_eq!(task.task().updates, 2);
1075        assert!(task.start());
1076        recv_started
1077            .recv_timeout(std::time::Duration::from_secs(5))
1078            .unwrap();
1079        stop_with_timeout(&mut task, &driver).await;
1080        assert_eq!(task.task().updates, 2);
1081    }
1082
1083    struct Foo(u32);
1084
1085    impl AsyncRun<bool> for Foo {
1086        async fn run(
1087            &mut self,
1088            stop: &mut StopTask<'_>,
1089            state: &mut bool,
1090        ) -> Result<(), Cancelled> {
1091            stop.until_stopped(async {
1092                self.0 += 1;
1093                if !*state {
1094                    std::future::pending::<()>().await;
1095                }
1096            })
1097            .await
1098        }
1099    }
1100
1101    async fn yield_once() {
1102        let mut yielded = false;
1103        std::future::poll_fn(|cx| {
1104            if yielded {
1105                Poll::Ready(())
1106            } else {
1107                yielded = true;
1108                cx.waker().wake_by_ref();
1109                Poll::Pending
1110            }
1111        })
1112        .await
1113    }
1114
1115    #[async_test]
1116    async fn test(driver: DefaultDriver) {
1117        let mut t = TaskControl::new(Foo(5));
1118        t.insert(&driver, "test", false);
1119        t.remove();
1120        t.insert(&driver, "test", false);
1121        assert_eq!(t.task().0, 5);
1122        assert!(t.start());
1123        yield_once().await;
1124        assert!(t.stop().await);
1125        assert_eq!(t.task().0, 6);
1126        *t.state_mut().unwrap() = true;
1127        assert!(t.start());
1128        yield_once().await;
1129        assert!(!t.stop().await);
1130        assert_eq!(t.task().0, 7);
1131        // The task has completed, so starting it again will not increment the counter.
1132        assert!(!t.start());
1133        yield_once().await;
1134        assert!(!t.stop().await);
1135        assert_eq!(t.task().0, 7);
1136    }
1137
1138    #[async_test]
1139    async fn test_cancelled_stop(driver: DefaultDriver) {
1140        let mut t = TaskControl::new(Foo(5));
1141        t.insert(&driver, "test", false);
1142        assert!(t.start());
1143        yield_once().await;
1144        t.update_with(|t, _| t.0 += 1);
1145        assert!(t.stop().now_or_never().is_none());
1146        t.update_with(|t, _| t.0 += 1);
1147        assert!(t.stop().await);
1148        assert_eq!(t.task_mut().0, 8);
1149    }
1150
1151    #[async_test]
1152    async fn test_poll_stop(driver: DefaultDriver) {
1153        let mut t = TaskControl::new(Foo(5));
1154
1155        // poll_stop on a task without state returns Ready(false).
1156        assert_eq!(
1157            std::future::poll_fn(|cx| Poll::Ready(t.poll_stop(cx))).await,
1158            Poll::Ready(false)
1159        );
1160
1161        t.insert(&driver, "test", false);
1162
1163        // poll_stop on a stopped (not started) task returns Ready(false).
1164        assert_eq!(
1165            std::future::poll_fn(|cx| Poll::Ready(t.poll_stop(cx))).await,
1166            Poll::Ready(false)
1167        );
1168
1169        assert!(t.start());
1170        yield_once().await;
1171
1172        // poll_stop drives the task to stop, equivalent to stop().await.
1173        let result = std::future::poll_fn(|cx| t.poll_stop(cx)).await;
1174        assert!(result); // was running
1175        assert_eq!(t.task().0, 6);
1176
1177        // poll_stop after already stopped returns Ready(false).
1178        assert_eq!(
1179            std::future::poll_fn(|cx| Poll::Ready(t.poll_stop(cx))).await,
1180            Poll::Ready(false)
1181        );
1182    }
1183}