fix the deadlock issue using the same transaction to write to database in multiple threads

pull/194/head
ryanzhao 5 years ago
parent cc461baab5
commit 9ac7159a2f

@ -24,8 +24,8 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
/// Gets the device links associated with the given hex encoded public key from the /// Gets the device links associated with the given hex encoded public key from the
/// server and stores and returns the valid ones. /// server and stores and returns the valid ones.
public static func getDeviceLinks(associatedWith hexEncodedPublicKey: String) -> Promise<Set<DeviceLink>> { public static func getDeviceLinks(associatedWith hexEncodedPublicKey: String, in transaction: YapDatabaseReadWriteTransaction? = nil) -> Promise<Set<DeviceLink>> {
return getDeviceLinks(associatedWith: [ hexEncodedPublicKey ]) return getDeviceLinks(associatedWith: [ hexEncodedPublicKey ], in: transaction)
} }
@objc(getDeviceLinksAssociatedWithHexEncodedPublicKeys:) @objc(getDeviceLinksAssociatedWithHexEncodedPublicKeys:)
@ -35,7 +35,7 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
/// Gets the device links associated with the given hex encoded public keys from the /// Gets the device links associated with the given hex encoded public keys from the
/// server and stores and returns the valid ones. /// server and stores and returns the valid ones.
public static func getDeviceLinks(associatedWith hexEncodedPublicKeys: Set<String>) -> Promise<Set<DeviceLink>> { public static func getDeviceLinks(associatedWith hexEncodedPublicKeys: Set<String>, in transaction: YapDatabaseReadWriteTransaction? = nil) -> Promise<Set<DeviceLink>> {
let hexEncodedPublicKeysDescription = "[ \(hexEncodedPublicKeys.joined(separator: ", ")) ]" let hexEncodedPublicKeysDescription = "[ \(hexEncodedPublicKeys.joined(separator: ", ")) ]"
print("[Loki] Getting device links for: \(hexEncodedPublicKeysDescription).") print("[Loki] Getting device links for: \(hexEncodedPublicKeysDescription).")
return getAuthToken(for: server).then(on: DispatchQueue.global()) { token -> Promise<Set<DeviceLink>> in return getAuthToken(for: server).then(on: DispatchQueue.global()) { token -> Promise<Set<DeviceLink>> in
@ -88,8 +88,12 @@ public final class LokiFileServerAPI : LokiDotNetAPI {
let (promise, seal) = Promise<Set<DeviceLink>>.pending() let (promise, seal) = Promise<Set<DeviceLink>>.pending()
// Dispatch async on the main queue to avoid nested write transactions // Dispatch async on the main queue to avoid nested write transactions
DispatchQueue.main.async { DispatchQueue.main.async {
storage.dbReadWriteConnection.readWrite { transaction in if let trans = transaction {
storage.setDeviceLinks(deviceLinks, in: transaction) storage.setDeviceLinks(deviceLinks, in: trans)
} else {
storage.dbReadWriteConnection.readWrite { transaction in
storage.setDeviceLinks(deviceLinks, in: transaction)
}
} }
// We have to wait for the device links to be stored because a lot of our logic relies // We have to wait for the device links to be stored because a lot of our logic relies
// on them being in the database // on them being in the database

@ -306,7 +306,7 @@ public extension MultiDeviceProtocol {
} }
if timeSinceLastUpdate > deviceLinkUpdateInterval { if timeSinceLastUpdate > deviceLinkUpdateInterval {
let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey let masterHexEncodedPublicKey = storage.getMasterHexEncodedPublicKey(for: hexEncodedPublicKey, in: transaction) ?? hexEncodedPublicKey
LokiFileServerAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey).done(on: DispatchQueue.global()) { _ in LokiFileServerAPI.getDeviceLinks(associatedWith: masterHexEncodedPublicKey, in: transaction as! YapDatabaseReadWriteTransaction).done(on: DispatchQueue.global()) { _ in
getDestinations() getDestinations()
lastDeviceLinkUpdate[hexEncodedPublicKey] = Date() lastDeviceLinkUpdate[hexEncodedPublicKey] = Date()
}.catch(on: DispatchQueue.global()) { error in }.catch(on: DispatchQueue.global()) { error in

@ -1266,12 +1266,14 @@ NS_ASSUME_NONNULL_BEGIN
// Loki: Update device links in a blocking way // Loki: Update device links in a blocking way
// FIXME: This is horrible for performance // FIXME: This is horrible for performance
// FIXME: ======== // FIXME: ========
// FIX: Using the same transaction for write to avoid deadlock
// The envelope source is set during UD decryption // The envelope source is set during UD decryption
if ([ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source] && dataMessage.publicChatInfo == nil) { // Handled in LokiPublicChatPoller for open group messages if ([ECKeyPair isValidHexEncodedPublicKeyWithCandidate:envelope.source] && dataMessage.publicChatInfo == nil) { // Handled in LokiPublicChatPoller for open group messages
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[[LKMultiDeviceProtocol updateDeviceLinksIfNeededForHexEncodedPublicKey:envelope.source in:transaction].ensureOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^() { dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[[LKMultiDeviceProtocol updateDeviceLinksIfNeededForHexEncodedPublicKey:envelope.source in:transaction].ensureOn(queue, ^() {
dispatch_semaphore_signal(semaphore); dispatch_semaphore_signal(semaphore);
}).catchOn(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(NSError *error) { }).catchOn(queue, ^(NSError *error) {
dispatch_semaphore_signal(semaphore); dispatch_semaphore_signal(semaphore);
}) retainUntilComplete]; }) retainUntilComplete];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC)); dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC));

Loading…
Cancel
Save