capacitor-community / bluetooth-le

Capacitor plugin for Bluetooth Low Energy
MIT License
274 stars 79 forks source link

iOS - Unsafe threading resulting in `EXC_BAD_ACCESS` being thrown with high write and notify rates #630

Closed manncr closed 5 months ago

manncr commented 6 months ago

Describe the bug When I am receiving messages via notifications from a single characteristic at a consistent rate (for the app I'm developing, average around 50/sec, but can potentially be more), and am also writing messages to the same device but through a separate write-only characteristic at a set interval, I will get a EXC_BAD_ACCESS error message after a seemingly random amount of time, and the application will terminate. How quickly I hit that exception goes up with how short that write interval is set to. When set at 1 second for example, I've seen it take as long as 45 minutes, and as quickly as 2 minutes.

To Reproduce

  1. Successfully connect to a BLE device that is capable of updating a NOTIFY characteristic at a high but inconsistent rate (80-120 times per second should trigger this I think)
  2. Using some kind of iteration like JS's setInterval(), start writing to another characteristic on the same service on that same device.
  3. Let the script run for upwards of an hour until the app crashes and spits out an error/exception in Xcode's console.

Expected behavior The application should be able to read (via notifications) and write messages at high rates without error.

Screenshots If applicable, add screenshots to help explain your problem.

Plugin version:

Desktop (please complete the following information):

Smartphone (please complete the following information):

Additional context I've spent a lot of time combing forums and can't find a solution to this. I was able to let them app run for 4 days straight no problems until I added the write interval. I've tried with and without an async/await for every call to the BLE package and am fairly confident that the error doesn't reside within my JS code. From what I've been able to tell, that EXC_BAD_ACCESS and associated errors that might appear (-[__NSCFNumber objectForKey:]: unrecognized selector sent to instance 0x8000000000000000 for example), all have to do with thread safety issues. The only difference between the errors is the __NS string, with it always being followed by "objectForKey".

From combing through the stack traces, it looks like it get triggered pretty consistently while waiting for a call to write() (I think on its own thread? Still very new to Xcode so been learning all this as I go), as indicated by the last normal printout in the Xcode console being "⚡️ To Native -> BluetoothLe write XXXXXXXX", with the exception being thrown at line 209 in Device.swift, or sometimes a couple lines above inside the call to self.resolve(). From what I can understand, this function is where notifications get processed by the BLE package.

So, my theory is that at the same time the BLE package is attempting to pull the callback from self. callbackMap[] for write and either read or notify handling, essentially accessing the same dictionary asynchronously, which is what triggers the exception.

While I am using this package through Quasar/Vue, I think the stack tracing shows that the problem should be agnostic of those frameworks.

Let me know if you need more information, want a skeleton of code that could reproduce the issue, or anything else.

manncr commented 5 months ago

Hey BLE team! It's been about month, just wanted to see if there was any update or acknowledgement, and that this is a fixable and reproducible bug?

pwespi commented 5 months ago

Thank you for the issue and the detailed description. This should be fixed in version 3.1.4.

Since the bug wasn't very easy to reproduce, it would be great if you could test the new version for your use case and report back if it is indeed fixed. Thank you.