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