Skip to main content

libasync_dispatcher/
detect_dispatcher.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::cell::OnceCell;
6
7use zx_status::Status;
8
9use crate::{AsAsyncDispatcherRef, AsyncDispatcher, CurrentDispatcher, GetAsyncDispatcher};
10
11/// Implements a dispatcher that is either set at construction or detected and cached on first use.
12///
13/// This is intended to be used inside futures to implement detecting the current dispatcher on
14/// first poll if the instigator of the future did not specify a dispatcher to run on.
15#[derive(Default, Debug)]
16pub struct DetectDispatcher {
17    dispatcher: OnceCell<Option<AsyncDispatcher>>,
18}
19
20impl DetectDispatcher {
21    /// Use this to pre-set the dispatcher so that future calls to [`GetAsyncDispatcher`] will
22    /// return is dispatcher instead of trying to get the default one.
23    pub fn new(with_dispatcher: impl AsAsyncDispatcherRef) -> Self {
24        let dispatcher = OnceCell::new();
25        // unwrap because this cannot fail on a freshly constructed OnceCell
26        dispatcher.set(Some(AsyncDispatcher::new(&with_dispatcher))).unwrap();
27        Self { dispatcher }
28    }
29
30    /// Gets the dispatcher if set in the constructor, or attempts to retrieve the current
31    /// dispatcher as set by [`crate::CurrentDispatcher::set`] (or the underlying async-default
32    /// api).
33    ///
34    /// Use this in a [`std::task::Poll`] implementation to defer finding the current dispatcher until the
35    /// future is being run on the dispatcher.
36    ///
37    /// Once a dispatcher has been set or detected, that dispatcher will be held and returned for
38    /// all future calls.
39    ///
40    /// Returns [`Status::BAD_STATE`] if there is no dispatcher set or detected.
41    pub fn get_or_detect(&self) -> Result<&AsyncDispatcher, Status> {
42        self.dispatcher
43            .get_or_init(|| CurrentDispatcher.try_get_async_dispatcher())
44            .as_ref()
45            .ok_or(Status::BAD_STATE)
46    }
47
48    /// Gets the dispatcher if it had been previously set or detected. If it hasn't, this will
49    /// return None.
50    pub fn get(&self) -> Option<&AsyncDispatcher> {
51        self.dispatcher.get()?.as_ref()
52    }
53}