1 //! Stack on top of the Bluetooth interface shim
2 //!
3 //! Helpers for dealing with the stack on top of the Bluetooth interface.
4
5 use std::any::{Any, TypeId};
6 use std::collections::HashMap;
7 use std::sync::{Arc, LazyLock, Mutex};
8 use tokio::runtime::{Builder, Runtime};
9
10 // Shared runtime for topshim handlers. All async tasks will get run by this
11 // runtime and this will properly serialize all spawned tasks.
12 pub static RUNTIME: LazyLock<Arc<Runtime>> = LazyLock::new(|| {
13 Arc::new(
14 Builder::new_multi_thread()
15 .worker_threads(1)
16 .max_blocking_threads(1)
17 .enable_all()
18 .build()
19 .unwrap(),
20 )
21 });
22
get_runtime() -> Arc<Runtime>23 pub fn get_runtime() -> Arc<Runtime> {
24 RUNTIME.clone()
25 }
26
27 static CB_DISPATCHER: LazyLock<Arc<Mutex<DispatchContainer>>> =
28 LazyLock::new(|| Arc::new(Mutex::new(DispatchContainer { instances: HashMap::new() })));
29
30 /// A Box-ed struct that implements a `dispatch` fn.
31 ///
32 /// Example:
33 /// ```
34 /// use std::sync::Arc;
35 /// use std::sync::Mutex;
36 ///
37 /// #[derive(Debug)]
38 /// enum Foo {
39 /// First(i16),
40 /// Second(i32),
41 /// }
42 ///
43 /// struct FooDispatcher {
44 /// dispatch: Box<dyn Fn(Foo) + Send>,
45 /// }
46 ///
47 /// fn main() {
48 /// let foo_dispatcher = FooDispatcher {
49 /// dispatch: Box::new(move |value| {
50 /// println!("Dispatch {:?}", value);
51 /// })
52 /// };
53 /// let value = Arc::new(Mutex::new(foo_dispatcher));
54 /// let instance_box = Box::new(value);
55 /// }
56 /// ```
57 pub type InstanceBox = Box<dyn Any + Send + Sync>;
58
59 /// Manage enum dispatches for emulating callbacks.
60 ///
61 /// Libbluetooth is highly callback based but our Rust code prefers using
62 /// channels. To reconcile these two systems, we pass static callbacks to
63 /// libbluetooth that convert callback args into an enum variant and call the
64 /// dispatcher for that enum. The dispatcher will then queue that enum into the
65 /// channel (using a captured channel tx in the closure).
66 pub struct DispatchContainer {
67 instances: HashMap<TypeId, InstanceBox>,
68 }
69
70 impl DispatchContainer {
71 /// Find registered dispatcher for enum specialization.
72 ///
73 /// # Return
74 ///
75 /// Returns an Option with a dispatcher object (the contents of
76 /// [`InstanceBox`]).
get<T: 'static + Clone + Send + Sync>(&self) -> Option<T>77 pub fn get<T: 'static + Clone + Send + Sync>(&self) -> Option<T> {
78 let typeid = TypeId::of::<T>();
79
80 if let Some(value) = self.instances.get(&typeid) {
81 return Some(value.downcast_ref::<T>().unwrap().clone());
82 }
83
84 None
85 }
86
87 /// Set dispatcher for an enum specialization.
88 ///
89 /// # Arguments
90 ///
91 /// * `obj` - The contents of [`InstanceBox`], usually `Arc<Mutex<U>>`. See
92 /// the [`InstanceBox`] documentation for examples.
93 ///
94 /// # Returns
95 ///
96 /// True if this is replacing an existing enum dispatcher.
set<T: 'static + Clone + Send + Sync>(&mut self, obj: T) -> bool97 pub fn set<T: 'static + Clone + Send + Sync>(&mut self, obj: T) -> bool {
98 self.instances.insert(TypeId::of::<T>(), Box::new(obj)).is_some()
99 }
100 }
101
102 /// Take a clone of the static dispatcher container.
get_dispatchers() -> Arc<Mutex<DispatchContainer>>103 pub fn get_dispatchers() -> Arc<Mutex<DispatchContainer>> {
104 CB_DISPATCHER.clone()
105 }
106