summaryrefslogtreecommitdiff
path: root/system/gd/rust/topshim/src/topstack.rs
blob: 18edf5d2e3f0c97ab578a40dcf51e589ad395eb0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//! Stack on top of the Bluetooth interface shim
//!
//! Helpers for dealing with the stack on top of the Bluetooth interface.

use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use tokio::runtime::{Builder, Runtime};

lazy_static! {
    // Shared runtime for topshim handlers. All async tasks will get run by this
    // runtime and this will properly serialize all spawned tasks.
    pub static ref RUNTIME: Arc<Runtime> = Arc::new(
        Builder::new_multi_thread()
            .worker_threads(1)
            .max_blocking_threads(1)
            .enable_all()
            .build()
            .unwrap()
    );
}

pub fn get_runtime() -> Arc<Runtime> {
    RUNTIME.clone()
}

lazy_static! {
    static ref CB_DISPATCHER: Arc<Mutex<DispatchContainer>> =
        Arc::new(Mutex::new(DispatchContainer { instances: HashMap::new() }));
}

/// A Box-ed struct that implements a `dispatch` fn.
///
///  Example:
///  ```
///  use std::sync::Arc;
///  use std::sync::Mutex;
///
///  #[derive(Debug)]
///  enum Foo {
///     First(i16),
///     Second(i32),
///  }
///
///  struct FooDispatcher {
///     dispatch: Box<dyn Fn(Foo) + Send>,
///  }
///
///  fn main() {
///     let foo_dispatcher = FooDispatcher {
///         dispatch: Box::new(move |value| {
///             println!("Dispatch {:?}", value);
///         })
///     };
///     let value = Arc::new(Mutex::new(foo_dispatcher));
///     let instance_box = Box::new(value);
///  }
///  ```
pub type InstanceBox = Box<dyn Any + Send + Sync>;

/// Manage enum dispatches for emulating callbacks.
///
/// Libbluetooth is highly callback based but our Rust code prefers using
/// channels. To reconcile these two systems, we pass static callbacks to
/// libbluetooth that convert callback args into an enum variant and call the
/// dispatcher for that enum. The dispatcher will then queue that enum into the
/// channel (using a captured channel tx in the closure).
pub struct DispatchContainer {
    instances: HashMap<TypeId, InstanceBox>,
}

impl DispatchContainer {
    /// Find registered dispatcher for enum specialization.
    ///
    /// # Return
    ///
    /// Returns an Option with a dispatcher object (the contents of
    /// [`InstanceBox`]).
    pub fn get<T: 'static + Clone + Send + Sync>(&self) -> Option<T> {
        let typeid = TypeId::of::<T>();

        if let Some(value) = self.instances.get(&typeid) {
            return Some(value.downcast_ref::<T>().unwrap().clone());
        }

        None
    }

    /// Set dispatcher for an enum specialization.
    ///
    /// # Arguments
    ///
    /// * `obj` - The contents of [`InstanceBox`], usually `Arc<Mutex<U>>`. See
    ///           the [`InstanceBox`] documentation for examples.
    ///
    /// # Returns
    ///
    /// True if this is replacing an existing enum dispatcher.
    pub fn set<T: 'static + Clone + Send + Sync>(&mut self, obj: T) -> bool {
        self.instances.insert(TypeId::of::<T>(), Box::new(obj)).is_some()
    }
}

/// Take a clone of the static dispatcher container.
pub fn get_dispatchers() -> Arc<Mutex<DispatchContainer>> {
    CB_DISPATCHER.clone()
}