Skip to content

Commit 02fed0c

Browse files
committed
ios: put peripheral context in a struct for clarity
1 parent 75b52b0 commit 02fed0c

File tree

1 file changed

+41
-13
lines changed

1 file changed

+41
-13
lines changed

frontends/ios/BitBoxApp/BitBoxApp/Bluetooth.swift

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ var pairedDeviceIdentifiers: Set<String> {
5151
}
5252
}
5353

54+
class BLEConnectionContext {
55+
let semaphore = DispatchSemaphore(value: 0)
56+
var readBuffer = Data()
57+
var readBufferLock = NSLock()
58+
}
59+
5460
class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CBPeripheralDelegate {
5561
private var state: State = State(
5662
bluetoothAvailable: false,
@@ -70,9 +76,11 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
7076
// This is for failed connections to not enter an infinite connect loop.
7177
private var dontAutoConnectSet: Set<UUID> = []
7278

73-
private var readBuffer = Data()
74-
private let readBufferLock = NSLock() // Ensure thread-safe buffer access
75-
private let semaphore = DispatchSemaphore(value: 0)
79+
private var currentContext: BLEConnectionContext?
80+
// Locks access to the `currentContext` var only, not to its contents. This is important, as
81+
// one can't keep the context locked while waiting for the semaphore, which would lead to a
82+
// deadlock.
83+
private let currentContextLock = NSLock()
7684

7785
override init() {
7886
super.init()
@@ -93,6 +101,9 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
93101
state.discoveredPeripherals[peripheralID] = metadata
94102
state.scanning = false
95103
updateBackendState()
104+
currentContextLock.lock()
105+
currentContext = BLEConnectionContext()
106+
currentContextLock.unlock()
96107
centralManager.connect(metadata.peripheral, options: nil)
97108
}
98109

@@ -246,17 +257,24 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
246257
return
247258
}
248259

260+
currentContextLock.lock()
261+
guard let ctx = currentContext else {
262+
currentContextLock.unlock()
263+
return
264+
}
265+
currentContextLock.unlock()
266+
249267
if characteristic == pReader, let data = characteristic.value {
250268
if data.count != 64 {
251269
print("BLE: ERROR, expected 64 bytes")
252270
}
253271
print("BLE: received data: \(data.hexEncodedString())")
254-
readBufferLock.lock()
255-
readBuffer.append(data)
256-
readBufferLock.unlock()
272+
ctx.readBufferLock.lock()
273+
ctx.readBuffer.append(data)
274+
ctx.readBufferLock.unlock()
257275

258276
// Signal the semaphore to unblock `readBlocking`
259-
semaphore.signal()
277+
ctx.semaphore.signal()
260278
}
261279
if characteristic == pProduct {
262280
print("BLE: product changed: \(String(describing: parseProduct()))")
@@ -276,12 +294,15 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
276294
}
277295

278296
func handleDisconnect() {
297+
currentContextLock.lock()
298+
currentContext = nil
299+
currentContextLock.unlock()
279300
connectedPeripheral = nil
280301
pReader = nil
281302
pWriter = nil
282303
pProduct = nil
283304
state.discoveredPeripherals.removeAll()
284-
isPaired = false
305+
isPaired = false
285306
updateBackendState()
286307

287308
// Have the backend scan right away, which will make it detect that we disconnected.
@@ -303,16 +324,23 @@ class BluetoothManager: NSObject, ObservableObject, CBCentralManagerDelegate, CB
303324
}
304325
print("BLE: wants to read \(length)")
305326

327+
currentContextLock.lock()
328+
guard let ctx = currentContext else {
329+
currentContextLock.unlock()
330+
return nil
331+
}
332+
currentContextLock.unlock()
333+
306334
var data = Data()
307335

308336
// Loop until we've read the required amount of data
309337
while data.count < length {
310338
// Block until BLE reader callback notifies us
311-
semaphore.wait()
312-
readBufferLock.lock()
313-
data.append(readBuffer.prefix(64))
314-
readBuffer = readBuffer.advanced(by: 64)
315-
readBufferLock.unlock()
339+
ctx.semaphore.wait()
340+
ctx.readBufferLock.lock()
341+
data.append(ctx.readBuffer.prefix(64))
342+
ctx.readBuffer = ctx.readBuffer.advanced(by: 64)
343+
ctx.readBufferLock.unlock()
316344
}
317345
print("BLE: got \(data.count)")
318346

0 commit comments

Comments
 (0)