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