mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 03:45:20 +00:00
Fix deadlock in DeliveryTracker causing macOS app freeze
- Fixed nested lock acquisition in trackMessage/scheduleTimeout - Properly handle lock release before calling methods that need locks - Ensure retryDelivery and handleTimeout don't cause deadlocks - Release locks before calling external methods
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objectVersion = 63;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -86,7 +86,7 @@
|
||||
763E0DBA9492A654FC0CDCB9 /* AppInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoView.swift; sourceTree = "<group>"; };
|
||||
7EEBDA723E1CFD88758DA4AC /* bitchat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8DE9CDF66D4E52D268851048 /* MessagePaddingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePaddingTests.swift; sourceTree = "<group>"; };
|
||||
997D512074C64904D75DDD40 /* bitchat.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
997D512074C64904D75DDD40 /* bitchat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
A08E03AA0C63E97C91749AEC /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
A2136C3E22D02D4A8DBE7EAB /* BinaryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryProtocol.swift; sourceTree = "<group>"; };
|
||||
AA4D7595A613F7ED3B386132 /* MessageRetryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRetryService.swift; sourceTree = "<group>"; };
|
||||
@@ -302,7 +302,6 @@
|
||||
);
|
||||
mainGroup = 18198ED912AAF495D8AF7763;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
preferredProjectObjectVersion = 54;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
@@ -486,6 +485,7 @@
|
||||
CODE_SIGNING_REQUIRED = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = L3N5LHJD5Y;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = bitchat/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
@@ -531,6 +531,7 @@
|
||||
CODE_SIGNING_REQUIRED = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = L3N5LHJD5Y;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
INFOPLIST_FILE = bitchat/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
|
||||
+14
-2
@@ -26,7 +26,19 @@
|
||||
<string>bitchat uses Bluetooth to create a secure mesh network for chatting with nearby users.</string>
|
||||
<key>NSBluetoothPeripheralUsageDescription</key>
|
||||
<string>bitchat uses Bluetooth to discover and connect with other bitchat users nearby.</string>
|
||||
<key>NSUserNotificationAlertStyle</key>
|
||||
<string>alert</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>bluetooth-central</string>
|
||||
<string>bluetooth-peripheral</string>
|
||||
</array>
|
||||
<key>UILaunchScreen</key>
|
||||
<dict>
|
||||
<key>UIColorName</key>
|
||||
<string>Black</string>
|
||||
</dict>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -68,9 +68,6 @@ class DeliveryTracker {
|
||||
// MARK: - Public Methods
|
||||
|
||||
func trackMessage(_ message: BitchatMessage, recipientID: String, recipientNickname: String, isFavorite: Bool = false, expectedRecipients: Int = 1) {
|
||||
pendingLock.lock()
|
||||
defer { pendingLock.unlock() }
|
||||
|
||||
// Don't track broadcasts or certain message types
|
||||
guard message.isPrivate || message.room != nil else { return }
|
||||
|
||||
@@ -86,14 +83,17 @@ class DeliveryTracker {
|
||||
timeoutTimer: nil
|
||||
)
|
||||
|
||||
// Store the delivery with lock
|
||||
pendingLock.lock()
|
||||
pendingDeliveries[message.id] = delivery
|
||||
pendingLock.unlock()
|
||||
|
||||
// Update status to sent
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
|
||||
self?.updateDeliveryStatus(message.id, status: .sent)
|
||||
}
|
||||
|
||||
// Schedule timeout
|
||||
// Schedule timeout (outside of lock)
|
||||
scheduleTimeout(for: message.id)
|
||||
}
|
||||
|
||||
@@ -175,10 +175,18 @@ class DeliveryTracker {
|
||||
}
|
||||
|
||||
private func scheduleTimeout(for messageID: String) {
|
||||
guard let delivery = pendingDeliveries[messageID] else { return }
|
||||
// Get delivery info with lock
|
||||
pendingLock.lock()
|
||||
guard let delivery = pendingDeliveries[messageID] else {
|
||||
pendingLock.unlock()
|
||||
return
|
||||
}
|
||||
let isFavorite = delivery.isFavorite
|
||||
let isRoomMessage = delivery.isRoomMessage
|
||||
pendingLock.unlock()
|
||||
|
||||
let timeout = delivery.isFavorite ? favoriteTimeout :
|
||||
(delivery.isRoomMessage ? roomMessageTimeout : privateMessageTimeout)
|
||||
let timeout = isFavorite ? favoriteTimeout :
|
||||
(isRoomMessage ? roomMessageTimeout : privateMessageTimeout)
|
||||
|
||||
let timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { [weak self] _ in
|
||||
self?.handleTimeout(messageID: messageID)
|
||||
@@ -194,23 +202,33 @@ class DeliveryTracker {
|
||||
|
||||
private func handleTimeout(messageID: String) {
|
||||
pendingLock.lock()
|
||||
defer { pendingLock.unlock() }
|
||||
guard let delivery = pendingDeliveries[messageID] else {
|
||||
pendingLock.unlock()
|
||||
return
|
||||
}
|
||||
|
||||
guard let delivery = pendingDeliveries[messageID] else { return }
|
||||
let shouldRetry = delivery.shouldRetry
|
||||
let isRoomMessage = delivery.isRoomMessage
|
||||
|
||||
if delivery.shouldRetry {
|
||||
// Retry for favorites
|
||||
if shouldRetry {
|
||||
pendingLock.unlock()
|
||||
// Retry for favorites (outside of lock)
|
||||
retryDelivery(messageID: messageID)
|
||||
} else {
|
||||
// Mark as failed
|
||||
let reason = delivery.isRoomMessage ? "No response from room members" : "Message not delivered"
|
||||
updateDeliveryStatus(messageID, status: .failed(reason: reason))
|
||||
let reason = isRoomMessage ? "No response from room members" : "Message not delivered"
|
||||
pendingDeliveries.removeValue(forKey: messageID)
|
||||
pendingLock.unlock()
|
||||
updateDeliveryStatus(messageID, status: .failed(reason: reason))
|
||||
}
|
||||
}
|
||||
|
||||
private func retryDelivery(messageID: String) {
|
||||
guard let delivery = pendingDeliveries[messageID] else { return }
|
||||
pendingLock.lock()
|
||||
guard let delivery = pendingDeliveries[messageID] else {
|
||||
pendingLock.unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Increment retry count
|
||||
let newDelivery = PendingDelivery(
|
||||
@@ -227,9 +245,11 @@ class DeliveryTracker {
|
||||
)
|
||||
|
||||
pendingDeliveries[messageID] = newDelivery
|
||||
let retryCount = delivery.retryCount
|
||||
pendingLock.unlock()
|
||||
|
||||
// Exponential backoff for retry
|
||||
let delay = retryDelay * pow(2, Double(delivery.retryCount))
|
||||
let delay = retryDelay * pow(2, Double(retryCount))
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
|
||||
// Trigger resend through delegate or notification
|
||||
|
||||
Reference in New Issue
Block a user