manolofdez / AsyncBluetooth

A small library that adds concurrency to CoreBluetooth APIs.
MIT License
168 stars 31 forks source link

Crash when chaining operations in different Tasks #37

Closed maximebianchi closed 1 year ago

maximebianchi commented 1 year ago

version: 1.8.2

Issue:

I have 2 methods: one to register to notifications on some characteristics, and the second to read init ble values. Each method uses Task { do {} catch {} } blocks to send their ble command. In that case, calling these two methods one after the other, leads to a crash in PeripheralContext.readCharacteristicValueExecutor:

Capture d’écran 2023-10-25 à 15 28 24

This crash does not occurs if I merge the two methods into one Task { do {} catch {} } block.

======================================= Here is the sample code with crash:

init() {
     setupBindings()
     setupNotifications()
     readBleValues()
}

func setupBindings() {
    peripheral?.characteristicValueUpdatedPublisher
            .receive(on: RunLoop.main)
            .filter { $0.uuid.uuidString == Uuids.battery.uuidString }
            .sink { [weak self] characteristic in
                  ....
            }.store(in :&cancellables)
}

func  setupNotifications() {
  Task {
      do {
              try await self.peripheral?.setNotifyValue(true, forCharacteristicWithUUID: Uuids.battery,  ofServiceWithUUID: mainSrv)
      } catch {
          ...
     }
}

func  readBleValues() {
     Task {
         do {
               try await peripheral?.readValue(forCharacteristicWithUUID: <another uuid>, ofServiceWithUUID: mainSrv) ==> crashes
         } catch {
            ....
        }
}

======================================= without crash:


init() {
     setupBindings()
     initNotificationsAndReadValues()
}

func setupBindings() {
      // no change, same as above
}

func  initNotificationsAndReadValues() {
  Task {
      do {
              try await self.peripheral?.setNotifyValue(true, forCharacteristicWithUUID: Uuids.battery,  ofServiceWithUUID: mainSrv)
              try await peripheral?.readValue(forCharacteristicWithUUID: <another uuid>, ofServiceWithUUID: mainSrv) ==> NO CRASHES
     } catch {
          ...
     }
}
manolofdez commented 1 year ago

Oh, interesting! While I was unable to reproduce this, I think I know why this might be happening. That flushable list is not thread safe. I'll attempt a blind fix.

maximebianchi commented 1 year ago

Yes it does not occur systematically. But occurence might be enhanced by stressing out 2 similar characteristics