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