libasync_dispatcher/current_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 crate::{AsAsyncDispatcherRef, AsyncDispatcher, AsyncDispatcherRef, GetAsyncDispatcher};
6use libasync_sys::*;
7use std::ptr::{NonNull, null};
8
9/// A placeholder for the currently active dispatcher. Use
10/// [`GetAsyncDispatcher::get_async_dispatcher`] to access it when needed.
11#[derive(Clone, Copy, Debug, Default, PartialEq)]
12pub struct CurrentDispatcher;
13
14impl CurrentDispatcher {
15 /// Sets the currently active dispatcher for this thread.
16 ///
17 /// # Safety
18 ///
19 /// The caller must ensure that the dispatcher they set will be alive until it is [`Self::unset`]
20 /// or [`Self::set`] to another value. Using [`Self::with`] is a safer way to use this api as
21 /// it will use the current execution stack to ensure that it is properly unset.
22 pub unsafe fn set(dispatcher: &impl AsAsyncDispatcherRef) {
23 let dispatcher = dispatcher.as_async_dispatcher_ref();
24 // SAFETY: The caller promises that the dispatcher will live as long as it is set as the default.
25 unsafe { async_set_default_dispatcher(dispatcher.0.as_ptr()) };
26 }
27
28 /// Unsets the currently active dispatcher whatever it is.
29 pub fn unset() {
30 // SAFETY: It is always safe to unset the current dispatcher, as the worst that can happen
31 // is the set dispatcher is leaked.
32 unsafe { async_set_default_dispatcher(null()) }
33 }
34
35 /// Runs the passed function with the current dispatcher set to the one passed in, and then
36 /// re-sets the dispatcher to whatever it was before.
37 pub fn with<R>(dispatcher: &impl AsAsyncDispatcherRef, f: impl FnOnce() -> R) -> R {
38 let old = async_get_default_dispatcher();
39 // SAFETY: We will reset the dispatcher below.
40 unsafe { Self::set(dispatcher) };
41 let ret = f();
42 // SAFETY: The dispatcher being set was set prior to entering this function, so something
43 // else is responsible for ensuring that it was valid before and after this function is
44 // called. We are only returning to the status quo.
45 unsafe { async_set_default_dispatcher(old) }
46 ret
47 }
48}
49
50impl GetAsyncDispatcher for CurrentDispatcher {
51 fn try_get_async_dispatcher(&self) -> Option<AsyncDispatcher> {
52 let dispatcher = NonNull::new(async_get_default_dispatcher().cast_mut())?;
53 // SAFETY: NonNull::new will null-check that we have a current dispatcher, and the contract
54 // of async-default is that the dispatcher set should be valid.
55 Some(AsyncDispatcher::new(&unsafe { AsyncDispatcherRef::from_raw(dispatcher) }))
56 }
57}