Guard BLE link state lookups on BLE queue (#805)

Co-authored-by: jack <jackjackbits@users.noreply.github.com>
This commit is contained in:
jack
2025-10-14 21:11:07 +02:00
committed by GitHub
co-authored by jack
parent 6588861e34
commit 8a0727fcf7
+29 -12
View File
@@ -1514,6 +1514,22 @@ extension BLEService {
} }
} }
/// Safely fetch the current direct-link state for a peer using the BLE queue.
private func linkState(for peerID: PeerID) -> (hasPeripheral: Bool, hasCentral: Bool) {
let computeState = { () -> (Bool, Bool) in
let peripheralUUID = self.peerToPeripheralUUID[peerID]
let hasPeripheral = peripheralUUID.flatMap { self.peripherals[$0]?.isConnected } ?? false
let hasCentral = self.centralToPeerID.values.contains(peerID)
return (hasPeripheral, hasCentral)
}
if DispatchQueue.getSpecific(key: bleQueueKey) != nil {
return computeState()
} else {
return bleQueue.sync { computeState() }
}
}
private func configureNoiseServiceCallbacks(for service: NoiseEncryptionService) { private func configureNoiseServiceCallbacks(for service: NoiseEncryptionService) {
service.onPeerAuthenticated = { [weak self] peerID, fingerprint in service.onPeerAuthenticated = { [weak self] peerID, fingerprint in
let peerID = PeerID(str: peerID) let peerID = PeerID(str: peerID)
@@ -2458,15 +2474,15 @@ extension BLEService {
// Track if this is a new or reconnected peer // Track if this is a new or reconnected peer
var isNewPeer = false var isNewPeer = false
var isReconnectedPeer = false var isReconnectedPeer = false
let directLinkState = linkState(for: peerID)
collectionsQueue.sync(flags: .barrier) { collectionsQueue.sync(flags: .barrier) {
// Check if we have an actual BLE connection to this peer // Check if we have an actual BLE connection to this peer
let peripheralUUID = peerToPeripheralUUID[peerID] let hasPeripheralConnection = directLinkState.hasPeripheral
let hasPeripheralConnection = peripheralUUID != nil && peripherals[peripheralUUID!]?.isConnected == true
// Check if this peer is subscribed to us as a central // Check if this peer is subscribed to us as a central
// Note: We can't identify which specific central is which peer without additional mapping // Note: We can't identify which specific central is which peer without additional mapping
let hasCentralSubscription = centralToPeerID.values.contains(peerID) let hasCentralSubscription = directLinkState.hasCentral
// Direct announces arrive with full TTL (no prior hop) // Direct announces arrive with full TTL (no prior hop)
let isDirectAnnounce = (packet.ttl == messageTTL) let isDirectAnnounce = (packet.ttl == messageTTL)
@@ -2698,12 +2714,8 @@ extension BLEService {
return return
} }
// Determine if we have a direct link to the sender // Determine if we have a direct link to the sender
let hasDirectLink: Bool = collectionsQueue.sync { let directLink = linkState(for: peerID)
let perUUID = peerToPeripheralUUID[peerID] let hasDirectLink = directLink.hasPeripheral || directLink.hasCentral
let perConnected = perUUID != nil && peripherals[perUUID!]?.isConnected == true
let hasCentral = centralToPeerID.values.contains(peerID)
return perConnected || hasCentral
}
let pathTag = hasDirectLink ? "direct" : "mesh" let pathTag = hasDirectLink ? "direct" : "mesh"
SecureLogger.debug("💬 [\(senderNickname)] TTL:\(packet.ttl) (\(pathTag)): \(String(content.prefix(50)))\(content.count > 50 ? "..." : "")", category: .session) SecureLogger.debug("💬 [\(senderNickname)] TTL:\(packet.ttl) (\(pathTag)): \(String(content.prefix(50)))\(content.count > 50 ? "..." : "")", category: .session)
@@ -3046,6 +3058,11 @@ extension BLEService {
private func checkPeerConnectivity() { private func checkPeerConnectivity() {
let now = Date() let now = Date()
var disconnectedPeers: [String] = [] var disconnectedPeers: [String] = []
let peerIDsForLinkState: [PeerID] = collectionsQueue.sync { Array(peers.keys) }
var cachedLinkStates: [PeerID: (hasPeripheral: Bool, hasCentral: Bool)] = [:]
for peerID in peerIDsForLinkState {
cachedLinkStates[peerID] = linkState(for: peerID)
}
var removedOfflineCount = 0 var removedOfflineCount = 0
collectionsQueue.sync(flags: .barrier) { collectionsQueue.sync(flags: .barrier) {
@@ -3054,9 +3071,9 @@ extension BLEService {
let retention: TimeInterval = peer.isVerifiedNickname ? TransportConfig.bleReachabilityRetentionVerifiedSeconds : TransportConfig.bleReachabilityRetentionUnverifiedSeconds let retention: TimeInterval = peer.isVerifiedNickname ? TransportConfig.bleReachabilityRetentionVerifiedSeconds : TransportConfig.bleReachabilityRetentionUnverifiedSeconds
if peer.isConnected && age > TransportConfig.blePeerInactivityTimeoutSeconds { if peer.isConnected && age > TransportConfig.blePeerInactivityTimeoutSeconds {
// Check if we still have an active BLE connection to this peer // Check if we still have an active BLE connection to this peer
let hasPeripheralConnection = peerToPeripheralUUID[peerID] != nil && let state = cachedLinkStates[peerID] ?? (hasPeripheral: false, hasCentral: false)
peripherals[peerToPeripheralUUID[peerID]!]?.isConnected == true let hasPeripheralConnection = state.hasPeripheral
let hasCentralConnection = centralToPeerID.values.contains(peerID) let hasCentralConnection = state.hasCentral
// If direct link is gone, mark as not connected (retain entry for reachability) // If direct link is gone, mark as not connected (retain entry for reachability)
if !hasPeripheralConnection && !hasCentralConnection { if !hasPeripheralConnection && !hasCentralConnection {