Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bc3004ee authored by Hsin-chen Chuang's avatar Hsin-chen Chuang
Browse files

floss: Guarantee the execution order of GATT client callbacks

Originally, the callbacks are sent in an async thread, which means it
could be interrupted by the other tasks, and thus their order after
sent is not guaranteed to be the same as before.
This patch pushes the callbacks into a queue and makes sure the pop+send
operation is not interrupted, this shall guarantee the order after sent.

Bug: 348270691
Tag: #floss
Test: mmm packages/modules/Bluetooth
Test: manual test with an LE mouse and BAS works
Flag: EXEMPT, Floss-only changes
Change-Id: I5eeec16197b4d3b41e4feba9e76cd44181adefb7
parent 1853c14e
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ use num_derive::{FromPrimitive, ToPrimitive};
use num_traits::cast::{FromPrimitive, ToPrimitive};
use rand::rngs::SmallRng;
use rand::{RngCore, SeedableRng};
use std::collections::{HashMap, HashSet};
use std::collections::{HashMap, HashSet, VecDeque};
use std::convert::{TryFrom, TryInto};
use std::sync::{Arc, Mutex, MutexGuard};
use tokio::sync::mpsc::Sender;
@@ -1486,11 +1486,23 @@ impl BluetoothGatt {
    pub fn init_profiles(&mut self, tx: Sender<Message>, api_tx: Sender<APIMessage>) {
        self.gatt = Gatt::new(&self.intf.lock().unwrap()).map(|gatt| Arc::new(Mutex::new(gatt)));

        // TODO(b/353643607): Make this dispatch_queue design more general for all profiles.
        let tx_clone = tx.clone();
        let async_mutex = Arc::new(tokio::sync::Mutex::new(()));
        let dispatch_queue = Arc::new(Mutex::new(VecDeque::new()));
        let gatt_client_callbacks_dispatcher = GattClientCallbacksDispatcher {
            dispatch: Box::new(move |cb| {
                let tx_clone = tx_clone.clone();
                let async_mutex = async_mutex.clone();
                let dispatch_queue = dispatch_queue.clone();
                // Enqueue the callbacks at the synchronized block to ensure the order.
                dispatch_queue.lock().unwrap().push_back(cb);
                topstack::get_runtime().spawn(async move {
                    // Acquire the lock first to ensure |pop_front| and |tx_clone.send| not
                    // interrupted by the other async threads.
                    let _guard = async_mutex.lock().await;
                    // Consume exactly one callback.
                    let cb = dispatch_queue.lock().unwrap().pop_front().unwrap();
                    let _ = tx_clone.send(Message::GattClient(cb)).await;
                });
            }),