Open D0ntPanic opened 3 years ago
@D0ntPanic You might want to give the dev branch a shot, though you'll need some extra patches on top of it (See #161). That's been far more stable on macOS for me. I'm hoping we can get this released sometime soon, we're just blocked on some linux issues right now.
I wrote a minimized repro using the dev channel with the extra patches, and the bug still exists in latest.
Minimal repro code:
use btleplug::api::{BDAddr, Central, Peripheral, Manager as _};
use btleplug::platform::{Adapter, Manager};
use futures::StreamExt;
use std::time::Duration;
use uuid::Uuid;
use std::str::FromStr;
#[tokio::main]
async fn main() {
let manager = Manager::new().await.unwrap();
let adapter = manager.adapters().await.unwrap();
let central = adapter
.into_iter()
.nth(0)
.unwrap();
central.start_scan().await.unwrap();
loop {
// Enumerate devices
for device in central.peripherals().await.unwrap() {
if let Some(name) = device.properties().await.unwrap().local_name {
if name.starts_with("GoCube") {
println!("Found GoCube, connecting");
device.connect().await.unwrap();
let characteristics = device.discover_characteristics().await.unwrap();
let mut read = None;
for characteristic in characteristics {
if characteristic.uuid
== Uuid::from_str("6e400003-b5a3-f393-e0a9-e50e24dcca9e").unwrap()
{
read = Some(characteristic);
}
}
let read = read.unwrap();
println!("Subscribing");
device.subscribe(&read).await.unwrap();
let mut notifications = device.notifications().await.unwrap();
println!("Watching for events");
while let Some(value) = notifications.next().await {
println!("{:?}", value);
}
return;
}
}
}
// This is technically wrong in async but this is just a test
std::thread::sleep(Duration::from_millis(100));
}
}
Output:
Found GoCube, connecting
Subscribing
thread '<unnamed>' panicked at 'called `Option::unwrap()` on a `None` value', /Users/rwagner/projects/btleplug/src/corebluetooth/internal.rs:410:65
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
The line of code at fault is the same, so it is likely the same root cause.
Applying the same patch as in original post to latest code gives the expected results:
Found GoCube, connecting
Subscribing
Watching for events
ValueNotification { uuid: 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: [42, 6, 1, 4, 9, 62, 13, 10] }
ValueNotification { uuid: 6e400003-b5a3-f393-e0a9-e50e24dcca9e, value: [42, 6, 1, 5, 6, 60, 13, 10] }
Unfortunately the repro requires access to a GoCube or a Rubik's Connected cube.
Describe the bug I'm getting panics after calling
subscribe
on a characteristic on CoreBluetooth for a very specific device (the GoCube smart cube).It appears that what is happening is that extra characteristic discovery events are firing after
connect
completes, which causes race conditions in the subscription code. If you attempt to subscribe to the data characteristic on the GoCube, often an extra characteristic discovery event fires before the subscription completes and wipes out the existing characteristic data. When the subscription request completes, an attempt is made to complete the subscription request in thesubscribe_future_state
field of the characteristic. Unfortunately the extra discovery event has cleared this field, so a panic results.Expected behavior The subscription completes and starts sending notification events.
Actual behavior A panic crashes the thread and all Bluetooth connections stop working.
The panic happens on line 417 in
src/corebluetooth/internal.rs
:Additional context I have only seen this on the GoCube smart cube and it's sister product the Rubik's Connected smart cube.
I made a simple patch to fix it from panicking, but this is likely not a real solution to the underlying issue. I don't know the CoreBluetooth API well enough to know the true root cause. The commit which fixes the issue for me (but is likely not good enough to integrate) is here:
https://github.com/D0ntPanic/btleplug/commit/1bfb34b514688908aaaf13273dcd23724d10a30a