From 582ff0997af27e7600a94f3fc49582ee3a4703ff Mon Sep 17 00:00:00 2001 From: Morgan Pretty Date: Tue, 22 Feb 2022 15:51:17 +1100 Subject: [PATCH] Fixed a couple of crashes and added some mock data generation logic Possibly fixed a crash due to a database deadlock Fixed a crash when the first message requests gets added if there are no other threads Added code to generate a bunch of random thread data (Needs some testing to ensure no data leaves the device) --- Session.xcodeproj/project.pbxproj | 4 + Session/Home/HomeVC.swift | 2 +- Session/Utilities/MockDataGenerator.swift | 255 ++++++++++++++++++ SessionMessagingKit/Database/TSDatabaseView.m | 2 +- .../MessageReceiver+Handling.swift | 8 +- SessionMessagingKit/Threads/TSContactThread.m | 13 + SessionMessagingKit/Threads/TSThread.h | 1 + SessionMessagingKit/Threads/TSThread.m | 5 + 8 files changed, 285 insertions(+), 5 deletions(-) create mode 100644 Session/Utilities/MockDataGenerator.swift diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index dd0c4ad0d..d66006e48 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -783,6 +783,7 @@ FD705A92278D051200F16121 /* ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A91278D051200F16121 /* ReusableView.swift */; }; FD705A94278D052B00F16121 /* UITableView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */; }; FD705A98278E9F4D00F16121 /* UIColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */; }; + FD859F0027C4691300510D0C /* MockDataGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD859EFF27C4691300510D0C /* MockDataGenerator.swift */; }; FD88BAD927A7439C00BBC442 /* MessageRequestsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */; }; FD88BADB27A750F200BBC442 /* MessageRequestsMigration.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */; }; FDC4389E27BA2B8A00C60D73 /* SessionUtilitiesKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3C2A679255388CC00C340D1 /* SessionUtilitiesKit.framework */; platformFilter = ios; }; @@ -1836,6 +1837,7 @@ FD705A91278D051200F16121 /* ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusableView.swift; sourceTree = ""; }; FD705A93278D052B00F16121 /* UITableView+ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+ReusableView.swift"; sourceTree = ""; }; FD705A97278E9F4D00F16121 /* UIColor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Extensions.swift"; sourceTree = ""; }; + FD859EFF27C4691300510D0C /* MockDataGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDataGenerator.swift; sourceTree = ""; }; FD88BAD827A7439C00BBC442 /* MessageRequestsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsCell.swift; sourceTree = ""; }; FD88BADA27A750F200BBC442 /* MessageRequestsMigration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRequestsMigration.swift; sourceTree = ""; }; FD9039443F7CB729CF71350E /* Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension/Pods-GlobalDependencies-FrameworkAndExtensionDependencies-SessionNotificationServiceExtension.debug.xcconfig"; sourceTree = ""; }; @@ -2075,6 +2077,7 @@ B83F2B87240CB75A000A54AB /* UIImage+Scaling.swift */, C31A6C59247F214E001123EF /* UIView+Glow.swift */, C3548F0724456AB6009433A8 /* UIView+Wrapping.swift */, + FD859EFF27C4691300510D0C /* MockDataGenerator.swift */, ); path = Utilities; sourceTree = ""; @@ -4978,6 +4981,7 @@ 34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */, B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */, 34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */, + FD859F0027C4691300510D0C /* MockDataGenerator.swift in Sources */, C328250F25CA06020062D0A7 /* VoiceMessageView.swift in Sources */, B82B4090239DD75000A248E7 /* RestoreVC.swift in Sources */, 3488F9362191CC4000E524CC /* MediaView.swift in Sources */, diff --git a/Session/Home/HomeVC.swift b/Session/Home/HomeVC.swift index 99d285a71..9693dc8d7 100644 --- a/Session/Home/HomeVC.swift +++ b/Session/Home/HomeVC.swift @@ -284,7 +284,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, NewConv tableView.beginUpdates() // If we need to unhide the message request row and then re-insert it - if !messageRequestInserts.isEmpty && CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] { + if !messageRequestInserts.isEmpty && (CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] || tableView.numberOfRows(inSection: 0) == 0) { CurrentAppContext().appUserDefaults()[.hasHiddenMessageRequests] = false tableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic) } diff --git a/Session/Utilities/MockDataGenerator.swift b/Session/Utilities/MockDataGenerator.swift new file mode 100644 index 000000000..489a87efb --- /dev/null +++ b/Session/Utilities/MockDataGenerator.swift @@ -0,0 +1,255 @@ +// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. + +import Foundation +import Sodium +import SessionMessagingKit + +enum MockDataGenerator { + // Note: This was taken from TensorFlow's Random (https://github.com/apple/swift/blob/bc8f9e61d333b8f7a625f74d48ef0b554726e349/stdlib/public/TensorFlow/Random.swift) + // the complex approach is needed due to an issue with Swift's randomElement(using:) + // generation (see https://stackoverflow.com/a/64897775 for more info) + struct ARC4RandomNumberGenerator: RandomNumberGenerator { + var state: [UInt8] = Array(0...255) + var iPos: UInt8 = 0 + var jPos: UInt8 = 0 + + init(seed: T) { + self.init( + seed: (0..<(UInt64.bitWidth / UInt64.bitWidth)).map { index in + UInt8(truncatingIfNeeded: seed >> (UInt8.bitWidth * index)) + } + ) + } + + init(seed: [UInt8]) { + precondition(seed.count > 0, "Length of seed must be positive") + precondition(seed.count <= 256, "Length of seed must be at most 256") + + // Note: Have to use a for loop instead of a 'forEach' otherwise + // it doesn't work properly (not sure why...) + var j: UInt8 = 0 + for i: UInt8 in 0...255 { + j &+= S(i) &+ seed[Int(i) % seed.count] + swapAt(i, j) + } + } + + /// Produce the next random UInt64 from the stream, and advance the internal state + mutating func next() -> UInt64 { + // Note: Have to use a for loop instead of a 'forEach' otherwise + // it doesn't work properly (not sure why...) + var result: UInt64 = 0 + for _ in 0.. UInt8 { + return state[Int(index)] + } + + /// Helper to swap elements of the state + private mutating func swapAt(_ i: UInt8, _ j: UInt8) { + state.swapAt(Int(i), Int(j)) + } + + /// Generates the next byte in the keystream. + private mutating func nextByte() -> UInt8 { + iPos &+= 1 + jPos &+= S(iPos) + swapAt(iPos, jPos) + return S(S(iPos) &+ S(jPos)) + } + } + + static func generateMockData() { + // Don't re-generate the mock data if it already exists + var existingMockDataThread: TSContactThread? + + Storage.read { transaction in + existingMockDataThread = TSContactThread.getWithContactSessionID("MockDatabaseThread", transaction: transaction) + } + + guard existingMockDataThread == nil else { return } + + /// The mock data generation is quite slow, there are 3 parts which take a decent amount of time (deleting the account afterwards will also take a long time): + /// Generating the threads & content - ~3s per 100 + /// Writing to the database - ~10s per 1000 + /// Updating the UI - ~10s per 1000 (crashing) + let dmThreadCount: Int = 100 + let closedGroupThreadCount: Int = 0 + let openGroupThreadCount: Int = 0 + let maxMessagesPerThread: Int = 50 + let dmRandomSeed: Int = 1111 + let cgRandomSeed: Int = 2222 + let ogRandomSeed: Int = 3333 + + // TODO: Make sure this data doesn't go off device somehow? + Storage.shared.write { anyTransaction in + guard let transaction: YapDatabaseReadWriteTransaction = anyTransaction as? YapDatabaseReadWriteTransaction else { return } + + // First create the thread used to indicate that the mock data has been generated + _ = TSContactThread.getOrCreateThread(withContactSessionID: "MockDatabaseThread", transaction: transaction) + + // Multiple spaces to make it look more like words + let stringContent: [String] = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 ".map { String($0) } + let timestampNow: TimeInterval = Date().timeIntervalSince1970 + let userSessionId: String = getUserHexEncodedPublicKey() + + // MARK: - -- DM Thread + var dmThreadRandomGenerator: ARC4RandomNumberGenerator = ARC4RandomNumberGenerator(seed: dmRandomSeed) + + (0..