Skip to content

Monitoring a characteristic and then cancelling has a race condition #487

@cbreezier

Description

@cbreezier

When using Peripheral.monitorCharacteristic(...) we get a Stream.

When we cancel that stream, the monitoring should be fully cancelled (ie, native resources cleaned up) after we await on the cancelling of the stream. However, the actual cleanup of resources is performed asynchronously.

    final notification = device.monitorCharacteristic(
      SERVICE,
      CHARACTERISTIC,
    )
        .first; // This turns the Stream into a Future. Upon completion of the future, the stream subscription is canceled.

    final result = await notification;
    
    print('after cancel');

Log output:

D/com.polidea.flutter_ble_lib.FlutterBleLibPlugin(29627): on native side observed method: monitorCharacteristicForDevice
D/BluetoothGatt(29627): setCharacteristicNotification() - uuid: 0000ac49-53a6-4f72-96c8-fd6e6964bae3 enable: true
I/flutter (29627): after cancel
D/com.polidea.flutter_ble_lib.FlutterBleLibPlugin(29627): on native side observed method: cancelTransaction

Note that the after cancel message is logged before the cancelTransaction was observed, whereas it should be logged after.


The root cause is that the stream returned from monitorCharacteristic is a broadcast stream, not a single subscription stream.

characteristics_mixin.dart - _createMonitoringStream:

    StreamController<CharacteristicWithValue> streamController =
        StreamController.broadcast(
      onListen: onListen,
      onCancel: () => cancelTransaction(transactionId),
    );

This means that the onCancel callback does not return a Future - it just asynchronously tries to clean up resources.

I believe that the monitoring stream should be a single subscription stream. If consumers wish to have multiple subscribers, they can always convert the single subscription stream into a broadcast stream in their application logic.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions