From 5cf89a0f3d0fd8c93fa73753f1f7423dbbed83cb Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Fri, 26 Jan 2018 15:53:26 -0500 Subject: [PATCH] Register all database views asynchronously. --- Podfile | 4 +- Podfile.lock | 10 +- Signal/src/AppDelegate.m | 25 ++++- Signal/test/util/OWSDatabaseConverterTest.m | 69 +++++++++++- .../OWS104CreateRecipientIdentities.m | 64 +++++------ .../migrations/OWSDatabaseMigration.m | 36 +++++++ .../migrations/OWSDatabaseMigrationRunner.m | 6 +- .../src/Messages/OWSBatchMessageProcessor.h | 2 +- .../src/Messages/OWSBatchMessageProcessor.m | 8 +- .../src/Messages/OWSBlockingManager.h | 3 +- .../Messages/OWSDisappearingMessagesFinder.h | 2 + .../Messages/OWSDisappearingMessagesFinder.m | 12 +-- .../OWSFailedAttachmentDownloadsJob.h | 2 + .../OWSFailedAttachmentDownloadsJob.m | 11 +- .../src/Messages/OWSFailedMessagesJob.h | 2 + .../src/Messages/OWSFailedMessagesJob.m | 12 +-- .../src/Messages/OWSIdentityManager.m | 13 +-- .../src/Messages/OWSMessageReceiver.h | 2 +- .../src/Messages/OWSMessageReceiver.m | 8 +- .../src/Storage/OWSIncomingMessageFinder.m | 6 +- SignalServiceKit/src/Storage/OWSStorage.h | 32 +++++- SignalServiceKit/src/Storage/OWSStorage.m | 92 ++++++++++------ SignalServiceKit/src/Storage/TSDatabaseView.h | 8 +- SignalServiceKit/src/Storage/TSDatabaseView.m | 30 ++---- .../src/Storage/TSStorageManager.m | 30 ++++-- SignalServiceKit/src/Util/AppReadiness.h | 21 ++++ SignalServiceKit/src/Util/AppReadiness.m | 100 ++++++++++++++++++ SignalServiceKit/src/Util/NSDate+OWS.h | 8 +- SignalServiceKit/src/Util/NSDate+OWS.mm | 29 ++++- SignalServiceKit/src/Util/OWSFileSystem.m | 5 +- 30 files changed, 473 insertions(+), 179 deletions(-) create mode 100755 SignalServiceKit/src/Util/AppReadiness.h create mode 100755 SignalServiceKit/src/Util/AppReadiness.m diff --git a/Podfile b/Podfile index bef8dfdd0..94ae1a6a9 100644 --- a/Podfile +++ b/Podfile @@ -7,8 +7,8 @@ def shared_pods # OWS Pods # pod 'SQLCipher', path: '../sqlcipher2' pod 'SQLCipher', :git => 'https://github.com/sqlcipher/sqlcipher.git', :commit => 'd5c2bec' - # pod 'YapDatabase/SQLCipher', path: '../YapDatabase' - pod 'YapDatabase/SQLCipher', :git => 'https://github.com/WhisperSystems/YapDatabase.git', branch: 'release/unencryptedHeaders' + pod 'YapDatabase/SQLCipher', path: '../YapDatabase' + # pod 'YapDatabase/SQLCipher', :git => 'https://github.com/WhisperSystems/YapDatabase.git', branch: 'release/unencryptedHeaders' pod 'SignalServiceKit', path: '.' pod 'AxolotlKit', git: 'https://github.com/WhisperSystems/SignalProtocolKit.git', branch: 'mkirk/framework-friendly' #pod 'AxolotlKit', path: '../SignalProtocolKit' diff --git a/Podfile.lock b/Podfile.lock index e2c1142f2..a0e1699e1 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -141,7 +141,7 @@ DEPENDENCIES: - SocketRocket (from `https://github.com/facebook/SocketRocket.git`) - SQLCipher (from `https://github.com/sqlcipher/sqlcipher.git`, commit `d5c2bec`) - SSZipArchive - - YapDatabase/SQLCipher (from `https://github.com/WhisperSystems/YapDatabase.git`, branch `release/unencryptedHeaders`) + - YapDatabase/SQLCipher (from `../YapDatabase`) - YYImage EXTERNAL SOURCES: @@ -167,8 +167,7 @@ EXTERNAL SOURCES: :commit: d5c2bec :git: https://github.com/sqlcipher/sqlcipher.git YapDatabase: - :branch: release/unencryptedHeaders - :git: https://github.com/WhisperSystems/YapDatabase.git + :path: ../YapDatabase CHECKOUT OPTIONS: AxolotlKit: @@ -192,9 +191,6 @@ CHECKOUT OPTIONS: SQLCipher: :commit: d5c2bec :git: https://github.com/sqlcipher/sqlcipher.git - YapDatabase: - :commit: eaff655ebc774105e83f835ead71f8b7a02e4ac1 - :git: https://github.com/WhisperSystems/YapDatabase.git SPEC CHECKSUMS: AFNetworking: 5e0e199f73d8626b11e79750991f5d173d1f8b67 @@ -221,6 +217,6 @@ SPEC CHECKSUMS: YapDatabase: 299a32de9d350d37a9ac5b0532609d87d5d2a5de YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 -PODFILE CHECKSUM: 0d804514eb2db34b9874b653e543255d8c2f5751 +PODFILE CHECKSUM: 6da9d13ffba2c3d0ae0a0d376b8881102f41395f COCOAPODS: 1.3.1 diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index fd644f29c..8f5c44fac 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -29,6 +29,7 @@ #import #import #import +#import #import #import #import @@ -134,7 +135,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [self startupLogging]; - // If a backup restore is in progress, try to complete it. // Otherwise, cleanup backup state. [OWSBackup applicationDidFinishLaunching]; @@ -197,7 +197,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; return YES; } - /** * The user must unlock the device once after reboot before the database encryption key can be accessed. */ @@ -394,7 +393,6 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; - (void)applicationDidBecomeActive:(UIApplication *)application { DDLogWarn(@"%@ applicationDidBecomeActive.", self.logTag); - if (CurrentAppContext().isRunningTests) { return; } @@ -403,6 +401,19 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; [self ensureRootViewController]; + [AppReadiness.sharedManager runNowOrWhenAppIsReady:^{ + [self handleActivation]; + }]; + + DDLogInfo(@"%@ applicationDidBecomeActive completed.", self.logTag); +} + +- (void)handleActivation +{ + OWSAssertIsOnMainThread(); + + DDLogWarn(@"%@ handleActivation.", self.logTag); + // Always check prekeys after app launches, and sometimes check on app activation. [TSPreKeyManager checkPreKeysIfNecessary]; @@ -472,10 +483,9 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; preferences:[Environment preferences]]; } }); - } - DDLogInfo(@"%@ applicationDidBecomeActive completed.", self.logTag); + DDLogInfo(@"%@ handleActivation completed.", self.logTag); } - (void)applicationWillResignActive:(UIApplication *)application { @@ -753,10 +763,13 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; - (void)storageIsReady { + OWSAssertIsOnMainThread(); DDLogInfo(@"%@ storageIsReady", self.logTag); [OWSPreferences setIsRegistered:[TSAccountManager isRegistered]]; + [AppReadiness.sharedManager setAppIsReady]; + if ([TSAccountManager isRegistered]) { DDLogInfo(@"localNumber: %@", [TSAccountManager localNumber]); @@ -833,6 +846,8 @@ static NSString *const kURLHostVerifyPrefix = @"verify"; - (void)ensureRootViewController { + OWSAssertIsOnMainThread(); + DDLogInfo(@"%@ ensureRootViewController", self.logTag); if (![OWSStorage isStorageReady] || self.hasInitialRootViewController) { diff --git a/Signal/test/util/OWSDatabaseConverterTest.m b/Signal/test/util/OWSDatabaseConverterTest.m index ce51fd44c..75745c499 100644 --- a/Signal/test/util/OWSDatabaseConverterTest.m +++ b/Signal/test/util/OWSDatabaseConverterTest.m @@ -342,14 +342,14 @@ NS_ASSUME_NONNULL_BEGIN YapDatabaseSaltBlock saltBlock = ^(NSData *saltData) { OWSAssert(!databaseSalt); OWSAssert(saltData); - + databaseSalt = saltData; }; __block NSData *_Nullable databaseKeySpec = nil; YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) { OWSAssert(!databaseKeySpec); OWSAssert(keySpecData); - + databaseKeySpec = keySpecData; }; NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath @@ -373,6 +373,71 @@ NS_ASSUME_NONNULL_BEGIN XCTAssertTrue(isValid); } +// Verifies that legacy users with non-converted databases can convert. +- (void)testDatabaseConversionPerformance_WithKeyspec +{ + NSData *databasePassword = [self randomDatabasePassword]; + NSString *databaseFilePath = [self createTempDatabaseFilePath]; + + const int kItemCount = 50 * 1000; + + // Create an populate the unconverted database. + [self openYapDatabase:databaseFilePath + databasePassword:databasePassword + databaseSalt:nil + databaseKeySpec:nil + databaseBlock:^(YapDatabase *database) { + YapDatabaseConnection *dbConnection = database.newConnection; + [dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) { + for (int i = 0; i < kItemCount; i++) { + [transaction setObject:@(i) forKey:@"test_key_name" inCollection:@"test_collection_name"]; + } + }]; + }]; + + XCTAssertTrue([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]); + + __block NSData *_Nullable databaseSalt = nil; + YapDatabaseSaltBlock saltBlock = ^(NSData *saltData) { + OWSAssert(!databaseSalt); + OWSAssert(saltData); + + databaseSalt = saltData; + }; + __block NSData *_Nullable databaseKeySpec = nil; + YapDatabaseKeySpecBlock keySpecBlock = ^(NSData *keySpecData) { + OWSAssert(!databaseKeySpec); + OWSAssert(keySpecData); + + databaseKeySpec = keySpecData; + }; + NSError *_Nullable error = [YapDatabaseCryptoUtils convertDatabaseIfNecessary:databaseFilePath + databasePassword:databasePassword + saltBlock:saltBlock + keySpecBlock:keySpecBlock]; + if (error) { + DDLogError(@"%s error: %@", __PRETTY_FUNCTION__, error); + } + XCTAssertNil(error); + XCTAssertFalse([YapDatabaseCryptoUtils doesDatabaseNeedToBeConverted:databaseFilePath]); + XCTAssertNotNil(databaseSalt); + XCTAssertEqual(databaseSalt.length, kSQLCipherSaltLength); + XCTAssertNotNil(databaseKeySpec); + XCTAssertEqual(databaseKeySpec.length, kSQLCipherKeySpecLength); + + // Verify the contents of the unconverted database. + __block BOOL isValid = NO; + [self openYapDatabase:databaseFilePath + databasePassword:databasePassword + databaseSalt:nil + databaseKeySpec:nil + databaseBlock:^(YapDatabase *database) { + YapDatabaseConnection *dbConnection = database.newConnection; + isValid = [dbConnection numberOfKeysInCollection:@"test_collection_name"] == kItemCount; + }]; + XCTAssertTrue(isValid); +} + // Verifies new users who create new pre-converted databases. - (void)testDatabaseCreation_WithoutKeySpec { diff --git a/SignalMessaging/environment/migrations/OWS104CreateRecipientIdentities.m b/SignalMessaging/environment/migrations/OWS104CreateRecipientIdentities.m index d08e101fe..4b63f5fe5 100644 --- a/SignalMessaging/environment/migrations/OWS104CreateRecipientIdentities.m +++ b/SignalMessaging/environment/migrations/OWS104CreateRecipientIdentities.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWS104CreateRecipientIdentities.h" @@ -24,42 +24,36 @@ static NSString *const OWS104CreateRecipientIdentitiesMigrationId = @"104"; return OWS104CreateRecipientIdentitiesMigrationId; } -// Overriding runUp instead of runUpWithTransaction in order to implement a blocking migration. -- (void)runUp +- (void)runUpWithTransaction:(YapDatabaseReadWriteTransaction *)transaction { - [[OWSRecipientIdentity dbReadWriteConnection] readWriteWithBlock:^( - YapDatabaseReadWriteTransaction *_Nonnull transaction) { - NSMutableDictionary *identityKeys = [NSMutableDictionary new]; - - [transaction - enumerateKeysAndObjectsInCollection:TSStorageManagerTrustedKeysCollection - usingBlock:^( - NSString *_Nonnull recipientId, id _Nonnull object, BOOL *_Nonnull stop) { - if (![object isKindOfClass:[NSData class]]) { - OWSFail( - @"%@ Unexpected object in trusted keys collection key: %@ object: %@", - self.logTag, - recipientId, - object); - return; - } - NSData *identityKey = (NSData *)object; - [identityKeys setObject:identityKey forKey:recipientId]; - }]; - - [identityKeys enumerateKeysAndObjectsUsingBlock:^( - NSString *_Nonnull recipientId, NSData *_Nonnull identityKey, BOOL *_Nonnull stop) { - DDLogInfo(@"%@ Migrating identity key for recipient: %@", self.logTag, recipientId); - [[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId - identityKey:identityKey - isFirstKnownKey:NO - createdAt:[NSDate dateWithTimeIntervalSince1970:0] - verificationState:OWSVerificationStateDefault] - saveWithTransaction:transaction]; - }]; - - [self saveWithTransaction:transaction]; + NSMutableDictionary *identityKeys = [NSMutableDictionary new]; + + [transaction + enumerateKeysAndObjectsInCollection:TSStorageManagerTrustedKeysCollection + usingBlock:^(NSString *_Nonnull recipientId, id _Nonnull object, BOOL *_Nonnull stop) { + if (![object isKindOfClass:[NSData class]]) { + OWSFail(@"%@ Unexpected object in trusted keys collection key: %@ object: %@", + self.logTag, + recipientId, + object); + return; + } + NSData *identityKey = (NSData *)object; + [identityKeys setObject:identityKey forKey:recipientId]; + }]; + + [identityKeys enumerateKeysAndObjectsUsingBlock:^( + NSString *_Nonnull recipientId, NSData *_Nonnull identityKey, BOOL *_Nonnull stop) { + DDLogInfo(@"%@ Migrating identity key for recipient: %@", self.logTag, recipientId); + [[[OWSRecipientIdentity alloc] initWithRecipientId:recipientId + identityKey:identityKey + isFirstKnownKey:NO + createdAt:[NSDate dateWithTimeIntervalSince1970:0] + verificationState:OWSVerificationStateDefault] + saveWithTransaction:transaction]; }]; + + [self saveWithTransaction:transaction]; } @end diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigration.m b/SignalMessaging/environment/migrations/OWSDatabaseMigration.m index 9b6a6af5e..25c95be47 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigration.m +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigration.m @@ -58,6 +58,42 @@ NS_ASSUME_NONNULL_BEGIN }]; } +#pragma mark - Database Connections + +#ifdef DEBUG ++ (YapDatabaseConnection *)dbReadConnection +{ + return self.dbReadWriteConnection; +} + +// Database migrations need to occur _before_ storage is ready (by definition), +// so we need to use a connection with canWriteBeforeStorageReady set in +// debug builds. ++ (YapDatabaseConnection *)dbReadWriteConnection +{ + static dispatch_once_t onceToken; + static YapDatabaseConnection *sharedDBConnection; + dispatch_once(&onceToken, ^{ + sharedDBConnection = [TSStorageManager sharedManager].newDatabaseConnection; + + OWSAssert([sharedDBConnection isKindOfClass:[OWSDatabaseConnection class]]); + ((OWSDatabaseConnection *)sharedDBConnection).canWriteBeforeStorageReady = YES; + }); + + return sharedDBConnection; +} + +- (YapDatabaseConnection *)dbReadConnection +{ + return OWSDatabaseMigration.dbReadConnection; +} + +- (YapDatabaseConnection *)dbReadWriteConnection +{ + return OWSDatabaseMigration.dbReadWriteConnection; +} +#endif + @end NS_ASSUME_NONNULL_END diff --git a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m index 73f5ad103..909076106 100644 --- a/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m +++ b/SignalMessaging/environment/migrations/OWSDatabaseMigrationRunner.m @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "OWSDatabaseMigrationRunner.h" @@ -36,7 +36,7 @@ NS_ASSUME_NONNULL_BEGIN [[OWS100RemoveTSRecipientsMigration alloc] initWithStorageManager:storageManager], [[OWS102MoveLoggingPreferenceToUserDefaults alloc] initWithStorageManager:storageManager], [[OWS103EnableVideoCalling alloc] initWithStorageManager:storageManager], - // OWS104CreateRecipientIdentities is run separately. See runSafeBlockingMigrations. + [[OWS104CreateRecipientIdentities alloc] initWithStorageManager:storageManager], [[OWS105AttachmentFilePaths alloc] initWithStorageManager:storageManager], [[OWS106EnsureProfileComplete alloc] initWithStorageManager:storageManager] ]; @@ -51,7 +51,7 @@ NS_ASSUME_NONNULL_BEGIN { TSStorageManager *storageManager = TSStorageManager.sharedManager; return @[ - [[OWS104CreateRecipientIdentities alloc] initWithStorageManager:storageManager], + // [[OWS104CreateRecipientIdentities alloc] initWithStorageManager:storageManager], ]; } diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h index 9dc9d1737..74e4dcdf9 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.h @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSBatchMessageProcessor : NSObject + (instancetype)sharedInstance; -+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage; ++ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage; - (void)enqueueEnvelopeData:(NSData *)envelopeData plaintextData:(NSData *_Nullable)plaintextData; - (void)handleAnyUnprocessedEnvelopesAsync; diff --git a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m index 8ff5b3a41..341722f23 100644 --- a/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m +++ b/SignalServiceKit/src/Messages/OWSBatchMessageProcessor.m @@ -200,7 +200,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo } -+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage ++ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage { YapDatabaseView *existingView = [storage registeredExtension:OWSMessageContentJobFinderExtensionName]; if (existingView) { @@ -208,7 +208,7 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo // already initialized return; } - [storage registerExtension:[self databaseExtension] withName:OWSMessageContentJobFinderExtensionName]; + [storage asyncRegisterExtension:[self databaseExtension] withName:OWSMessageContentJobFinderExtensionName]; } #pragma mark Logging @@ -443,9 +443,9 @@ NSString *const OWSMessageContentJobFinderExtensionGroup = @"OWSMessageContentJo #pragma mark - class methods -+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage ++ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage { - [OWSMessageContentJobFinder syncRegisterDatabaseExtension:storage]; + [OWSMessageContentJobFinder asyncRegisterDatabaseExtension:storage]; } #pragma mark - instance methods diff --git a/SignalServiceKit/src/Messages/OWSBlockingManager.h b/SignalServiceKit/src/Messages/OWSBlockingManager.h index e600aadab..03ac96c63 100644 --- a/SignalServiceKit/src/Messages/OWSBlockingManager.h +++ b/SignalServiceKit/src/Messages/OWSBlockingManager.h @@ -1,9 +1,10 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN + extern NSString *const kNSNotificationName_BlockedPhoneNumbersDidChange; // This class can be safely accessed and used from any thread. diff --git a/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.h b/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.h index 6c0314d0a..090626730 100644 --- a/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.h +++ b/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.h @@ -30,10 +30,12 @@ NS_ASSUME_NONNULL_BEGIN */ + (void)asyncRegisterDatabaseExtensions:(OWSStorage *)storage; +#ifdef DEBUG /** * Only use the sync version for testing, generally we'll want to register extensions async */ + (void)blockingRegisterDatabaseExtensions:(OWSStorage *)storage; +#endif @end diff --git a/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.m b/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.m index db68b9182..62a482dc3 100644 --- a/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.m +++ b/SignalServiceKit/src/Messages/OWSDisappearingMessagesFinder.m @@ -188,23 +188,17 @@ static NSString *const OWSDisappearingMessageFinderExpiresAtIndex = @"index_mess return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler]; } +#ifdef DEBUG // Useful for tests, don't use in app startup path because it's slow. + (void)blockingRegisterDatabaseExtensions:(OWSStorage *)storage { [storage registerExtension:[self indexDatabaseExtension] withName:OWSDisappearingMessageFinderExpiresAtIndex]; } +#endif + (void)asyncRegisterDatabaseExtensions:(OWSStorage *)storage { - [storage asyncRegisterExtension:[self indexDatabaseExtension] - withName:OWSDisappearingMessageFinderExpiresAtIndex - completionBlock:^(BOOL ready) { - if (ready) { - DDLogDebug(@"%@ completed registering extension async.", self.logTag); - } else { - DDLogError(@"%@ failed registering extension async.", self.logTag); - } - }]; + [storage asyncRegisterExtension:[self indexDatabaseExtension] withName:OWSDisappearingMessageFinderExpiresAtIndex]; } @end diff --git a/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.h b/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.h index 5ad34d21d..1ed3968fe 100644 --- a/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.h +++ b/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.h @@ -19,10 +19,12 @@ NS_ASSUME_NONNULL_BEGIN */ + (void)asyncRegisterDatabaseExtensionsWithStorageManager:(OWSStorage *)storage; +#ifdef DEBUG /** * Only use the sync version for testing, generally we'll want to register extensions async */ - (void)blockingRegisterDatabaseExtensions; +#endif @end diff --git a/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.m b/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.m index b3c675672..3d9388f63 100644 --- a/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.m +++ b/SignalServiceKit/src/Messages/OWSFailedAttachmentDownloadsJob.m @@ -117,24 +117,19 @@ static NSString *const OWSFailedAttachmentDownloadsJobAttachmentStateIndex = @"i return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler]; } +#ifdef DEBUG // Useful for tests, don't use in app startup path because it's slow. - (void)blockingRegisterDatabaseExtensions { [self.storageManager registerExtension:[self.class indexDatabaseExtension] withName:OWSFailedAttachmentDownloadsJobAttachmentStateIndex]; } +#endif + (void)asyncRegisterDatabaseExtensionsWithStorageManager:(OWSStorage *)storage { [storage asyncRegisterExtension:[self indexDatabaseExtension] - withName:OWSFailedAttachmentDownloadsJobAttachmentStateIndex - completionBlock:^(BOOL ready) { - if (ready) { - DDLogDebug(@"%@ completed registering extension async.", self.logTag); - } else { - DDLogError(@"%@ failed registering extension async.", self.logTag); - } - }]; + withName:OWSFailedAttachmentDownloadsJobAttachmentStateIndex]; } @end diff --git a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.h b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.h index fc3ca6fca..76c290c97 100644 --- a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.h +++ b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.h @@ -19,10 +19,12 @@ NS_ASSUME_NONNULL_BEGIN */ + (void)asyncRegisterDatabaseExtensionsWithStorageManager:(OWSStorage *)storage; +#ifdef DEBUG /** * Only use the sync version for testing, generally we'll want to register extensions async */ - (void)blockingRegisterDatabaseExtensions; +#endif @end diff --git a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m index 6adf44d8a..af0a82518 100644 --- a/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m +++ b/SignalServiceKit/src/Messages/OWSFailedMessagesJob.m @@ -127,24 +127,18 @@ static NSString *const OWSFailedMessagesJobMessageStateIndex = @"index_outoing_m return [[YapDatabaseSecondaryIndex alloc] initWithSetup:setup handler:handler]; } +#ifdef DEBUG // Useful for tests, don't use in app startup path because it's slow. - (void)blockingRegisterDatabaseExtensions { [self.storageManager registerExtension:[self.class indexDatabaseExtension] withName:OWSFailedMessagesJobMessageStateIndex]; } +#endif + (void)asyncRegisterDatabaseExtensionsWithStorageManager:(OWSStorage *)storage { - [storage asyncRegisterExtension:[self indexDatabaseExtension] - withName:OWSFailedMessagesJobMessageStateIndex - completionBlock:^(BOOL ready) { - if (ready) { - DDLogDebug(@"%@ completed registering extension async.", self.logTag); - } else { - DDLogError(@"%@ failed registering extension async.", self.logTag); - } - }]; + [storage asyncRegisterExtension:[self indexDatabaseExtension] withName:OWSFailedMessagesJobMessageStateIndex]; } @end diff --git a/SignalServiceKit/src/Messages/OWSIdentityManager.m b/SignalServiceKit/src/Messages/OWSIdentityManager.m index 4e2e20cef..77ba9f229 100644 --- a/SignalServiceKit/src/Messages/OWSIdentityManager.m +++ b/SignalServiceKit/src/Messages/OWSIdentityManager.m @@ -4,6 +4,7 @@ #import "OWSIdentityManager.h" #import "AppContext.h" +#import "AppReadiness.h" #import "NSDate+OWS.h" #import "NSNotificationCenter+OWS.h" #import "NotificationsProtocol.h" @@ -448,13 +449,13 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa { OWSAssertIsOnMainThread(); - if (!CurrentAppContext().isMainAppAndActive) { - // Only try to sync if the main app is active to avoid interfering with startup. - // - // applicationDidBecomeActive: will try to sync again when the main app becomes active. - return; - } + [AppReadiness runNowOrWhenAppIsReady:^{ + [self syncQueuedVerificationStates]; + }]; +} +- (void)syncQueuedVerificationStates +{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ @synchronized(self) { diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.h b/SignalServiceKit/src/Messages/OWSMessageReceiver.h index a49ace41f..8af362d77 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.h +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.h @@ -14,7 +14,7 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSMessageReceiver : NSObject + (instancetype)sharedInstance; -+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage; ++ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage; - (void)handleReceivedEnvelope:(OWSSignalServiceProtosEnvelope *)envelope; - (void)handleAnyUnprocessedEnvelopesAsync; diff --git a/SignalServiceKit/src/Messages/OWSMessageReceiver.m b/SignalServiceKit/src/Messages/OWSMessageReceiver.m index f1aff98f4..fc1228cb9 100644 --- a/SignalServiceKit/src/Messages/OWSMessageReceiver.m +++ b/SignalServiceKit/src/Messages/OWSMessageReceiver.m @@ -190,7 +190,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin }); } -+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage ++ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage { [self registerLegacyClasses]; @@ -200,7 +200,7 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin // already initialized return; } - [storage registerExtension:[self databaseExtension] withName:OWSMessageDecryptJobFinderExtensionName]; + [storage asyncRegisterExtension:[self databaseExtension] withName:OWSMessageDecryptJobFinderExtensionName]; } @end @@ -422,9 +422,9 @@ NSString *const OWSMessageDecryptJobFinderExtensionGroup = @"OWSMessageProcessin #pragma mark - class methods -+ (void)syncRegisterDatabaseExtension:(OWSStorage *)storage ++ (void)asyncRegisterDatabaseExtension:(OWSStorage *)storage { - [OWSMessageDecryptJobFinder syncRegisterDatabaseExtension:storage]; + [OWSMessageDecryptJobFinder asyncRegisterDatabaseExtension:storage]; } #pragma mark - instance methods diff --git a/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.m b/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.m index d41afd241..85a06307a 100644 --- a/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.m +++ b/SignalServiceKit/src/Storage/OWSIncomingMessageFinder.m @@ -95,11 +95,7 @@ NSString *const OWSIncomingMessageFinderColumnSourceDeviceId = @"OWSIncomingMess + (void)asyncRegisterExtensionWithStorageManager:(OWSStorage *)storage { DDLogInfo(@"%@ registering async.", self.logTag); - [storage asyncRegisterExtension:self.indexExtension - withName:OWSIncomingMessageFinderExtensionName - completionBlock:^(BOOL ready) { - DDLogInfo(@"%@ finished registering async.", self.logTag); - }]; + [storage asyncRegisterExtension:self.indexExtension withName:OWSIncomingMessageFinderExtensionName]; } // We should not normally hit this, as we should have prefer registering async, but it is useful for testing. diff --git a/SignalServiceKit/src/Storage/OWSStorage.h b/SignalServiceKit/src/Storage/OWSStorage.h index a39a42ebe..867fd7ca7 100644 --- a/SignalServiceKit/src/Storage/OWSStorage.h +++ b/SignalServiceKit/src/Storage/OWSStorage.h @@ -10,6 +10,30 @@ extern NSString *const StorageIsReadyNotification; @class YapDatabaseExtension; +@protocol OWSDatabaseConnectionDelegate + +- (BOOL)areAllRegistrationsComplete; + +@end + +#pragma mark - + +@interface OWSDatabaseConnection : YapDatabaseConnection + +@property (atomic, weak) id delegate; + +#ifdef DEBUG +@property (atomic) BOOL canWriteBeforeStorageReady; +#endif + +- (instancetype)init NS_UNAVAILABLE; +- (instancetype)initWithDatabase:(YapDatabase *)database + delegate:(id)delegate NS_DESIGNATED_INITIALIZER; + +@end + +#pragma mark - + @interface OWSStorage : NSObject - (instancetype)init NS_UNAVAILABLE; @@ -37,14 +61,16 @@ extern NSString *const StorageIsReadyNotification; // TODO: Deprecate? - (nullable YapDatabaseConnection *)newDatabaseConnection; +#ifdef DEBUG - (BOOL)registerExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName; -- (void)asyncRegisterExtension:(YapDatabaseExtension *)extension - withName:(NSString *)extensionName - completionBlock:(nullable void (^)(BOOL ready))completionBlock; +#endif +- (void)asyncRegisterExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName; - (nullable id)registeredExtension:(NSString *)extensionName; - (unsigned long long)databaseFileSize; +- (YapDatabaseConnection *)registrationConnection; + #pragma mark - Password /** diff --git a/SignalServiceKit/src/Storage/OWSStorage.m b/SignalServiceKit/src/Storage/OWSStorage.m index 79ef4a033..4258e91bd 100644 --- a/SignalServiceKit/src/Storage/OWSStorage.m +++ b/SignalServiceKit/src/Storage/OWSStorage.m @@ -38,14 +38,6 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); #pragma mark - -@protocol OWSDatabaseConnectionDelegate - -- (BOOL)areSyncRegistrationsComplete; - -@end - -#pragma mark - - @interface YapDatabaseConnection () - (id)initWithDatabase:(YapDatabase *)database; @@ -54,18 +46,6 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); #pragma mark - -@interface OWSDatabaseConnection : YapDatabaseConnection - -@property (atomic, weak) id delegate; - -- (instancetype)init NS_UNAVAILABLE; -- (instancetype)initWithDatabase:(YapDatabase *)database - delegate:(id)delegate NS_DESIGNATED_INITIALIZER; - -@end - -#pragma mark - - @implementation OWSDatabaseConnection - (id)initWithDatabase:(YapDatabase *)database delegate:(id)delegate @@ -94,28 +74,20 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); { id delegate = self.delegate; OWSAssert(delegate); - OWSAssert(delegate.areSyncRegistrationsComplete); + OWSAssert(delegate.areAllRegistrationsComplete || self.canWriteBeforeStorageReady); [super readWriteWithBlock:block]; } - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block { - id delegate = self.delegate; - OWSAssert(delegate); - OWSAssert(delegate.areSyncRegistrationsComplete); - - [super asyncReadWriteWithBlock:block]; + [self asyncReadWriteWithBlock:block completionQueue:NULL completionBlock:NULL]; } - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block completionBlock:(nullable dispatch_block_t)completionBlock { - id delegate = self.delegate; - OWSAssert(delegate); - OWSAssert(delegate.areSyncRegistrationsComplete); - - [super asyncReadWriteWithBlock:block completionBlock:completionBlock]; + [self asyncReadWriteWithBlock:block completionQueue:NULL completionBlock:completionBlock]; } - (void)asyncReadWriteWithBlock:(void (^)(YapDatabaseReadWriteTransaction *transaction))block @@ -124,7 +96,7 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); { id delegate = self.delegate; OWSAssert(delegate); - OWSAssert(delegate.areSyncRegistrationsComplete); + OWSAssert(delegate.areAllRegistrationsComplete || self.canWriteBeforeStorageReady); [super asyncReadWriteWithBlock:block completionQueue:completionQueue completionBlock:completionBlock]; } @@ -138,6 +110,8 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); - (void)addConnection:(YapDatabaseConnection *)connection; +- (YapDatabaseConnection *)registrationConnection; + @end #pragma mark - @@ -146,6 +120,8 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); @property (atomic, weak) id delegate; +@property (atomic, nullable) YapDatabaseConnection *registrationConnectionCached; + - (instancetype)init NS_UNAVAILABLE; - (id)initWithPath:(NSString *)inPath serializer:(nullable YapDatabaseSerializer)inSerializer @@ -192,6 +168,25 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); return connection; } +- (YapDatabaseConnection *)registrationConnection +{ + @synchronized(self) + { + if (!self.registrationConnectionCached) { + YapDatabaseConnection *connection = [super registrationConnection]; + +#ifdef DEBUG + // Flag the registration connection as such. + OWSAssert([connection isKindOfClass:[OWSDatabaseConnection class]]); + ((OWSDatabaseConnection *)connection).canWriteBeforeStorageReady = YES; +#endif + + self.registrationConnectionCached = connection; + } + return self.registrationConnectionCached; + } +} + @end #pragma mark - @@ -317,6 +312,16 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); return NO; } +- (BOOL)areAllRegistrationsComplete +{ + DDLogInfo(@"%@ areAllRegistrationsComplete: %d %d = %d", + self.logTag, + self.areSyncRegistrationsComplete, + self.areAsyncRegistrationsComplete, + self.areSyncRegistrationsComplete && self.areAsyncRegistrationsComplete); + return self.areSyncRegistrationsComplete && self.areAsyncRegistrationsComplete; +} + - (void)runSyncRegistrations { OWS_ABSTRACT_METHOD(); @@ -352,11 +357,19 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); for (OWSStorage *storage in self.allStorages) { [storage runAsyncRegistrationsWithCompletion:^{ + [self postRegistrationCompleteNotificationIfPossible]; }]; + + ((OWSDatabase *)storage.database).registrationConnectionCached = nil; } } +- (YapDatabaseConnection *)registrationConnection +{ + return self.database.registrationConnection; +} + + (void)postRegistrationCompleteNotificationIfPossible { if (!self.isStorageReady) { @@ -374,7 +387,7 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); + (BOOL)isStorageReady { for (OWSStorage *storage in self.allStorages) { - if (!storage.areAsyncRegistrationsComplete) { + if (!storage.areAllRegistrationsComplete) { return NO; } } @@ -458,16 +471,25 @@ typedef NSData *_Nullable (^CreateDatabaseMetadataBlock)(void); return self.database.newConnection; } +#ifdef DEBUG - (BOOL)registerExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName { return [self.database registerExtension:extension withName:extensionName]; } +#endif - (void)asyncRegisterExtension:(YapDatabaseExtension *)extension withName:(NSString *)extensionName - completionBlock:(nullable void (^)(BOOL ready))completionBlock { - [self.database asyncRegisterExtension:extension withName:extensionName completionBlock:completionBlock]; + [self.database asyncRegisterExtension:extension + withName:extensionName + completionBlock:^(BOOL ready) { + if (!ready) { + OWSFail(@"%@ asyncRegisterExtension failed: %@", self.logTag, extensionName); + } else { + DDLogVerbose(@"%@ asyncRegisterExtension succeeded: %@", self.logTag, extensionName); + } + }]; } - (nullable id)registeredExtension:(NSString *)extensionName diff --git a/SignalServiceKit/src/Storage/TSDatabaseView.h b/SignalServiceKit/src/Storage/TSDatabaseView.h index b18f54560..27f015863 100644 --- a/SignalServiceKit/src/Storage/TSDatabaseView.h +++ b/SignalServiceKit/src/Storage/TSDatabaseView.h @@ -23,16 +23,16 @@ extern NSString *const TSSecondaryDevicesDatabaseViewExtensionName; + (void)registerCrossProcessNotifier:(OWSStorage *)storage; -// This method must be called _AFTER_ registerThreadInteractionsDatabaseView. -+ (void)registerThreadDatabaseView:(OWSStorage *)storage; +// This method must be called _AFTER_ asyncRegisterThreadInteractionsDatabaseView. ++ (void)asyncRegisterThreadDatabaseView:(OWSStorage *)storage; -+ (void)registerThreadInteractionsDatabaseView:(OWSStorage *)storage; ++ (void)asyncRegisterThreadInteractionsDatabaseView:(OWSStorage *)storage; + (void)asyncRegisterThreadOutgoingMessagesDatabaseView:(OWSStorage *)storage; // Instances of OWSReadTracking for wasRead is NO and shouldAffectUnreadCounts is YES. // // Should be used for "unread message counts". -+ (void)registerUnreadDatabaseView:(OWSStorage *)storage; ++ (void)asyncRegisterUnreadDatabaseView:(OWSStorage *)storage; // Should be used for "unread indicator". // diff --git a/SignalServiceKit/src/Storage/TSDatabaseView.m b/SignalServiceKit/src/Storage/TSDatabaseView.m index 0ceeb5c4a..65b51668b 100644 --- a/SignalServiceKit/src/Storage/TSDatabaseView.m +++ b/SignalServiceKit/src/Storage/TSDatabaseView.m @@ -72,19 +72,13 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic options:options]; if (async) { - [storage asyncRegisterExtension:view - withName:viewName - completionBlock:^(BOOL ready) { - OWSCAssert(ready); - - DDLogInfo(@"%@ asyncRegisterExtension: %@ -> %d", self.logTag, viewName, ready); - }]; + [storage asyncRegisterExtension:view withName:viewName]; } else { [storage registerExtension:view withName:viewName]; } } -+ (void)registerUnreadDatabaseView:(OWSStorage *)storage ++ (void)asyncRegisterUnreadDatabaseView:(OWSStorage *)storage { YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *( YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) { @@ -100,7 +94,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic [self registerMessageDatabaseViewWithName:TSUnreadDatabaseViewExtensionName viewGrouping:viewGrouping version:@"1" - async:NO + async:YES storage:storage]; } @@ -152,7 +146,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic storage:storage]; } -+ (void)registerThreadInteractionsDatabaseView:(OWSStorage *)storage ++ (void)asyncRegisterThreadInteractionsDatabaseView:(OWSStorage *)storage { YapDatabaseViewGrouping *viewGrouping = [YapDatabaseViewGrouping withObjectBlock:^NSString *( YapDatabaseReadTransaction *transaction, NSString *collection, NSString *key, id object) { @@ -165,7 +159,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic [self registerMessageDatabaseViewWithName:TSMessageDatabaseViewExtensionName viewGrouping:viewGrouping version:@"1" - async:NO + async:YES storage:storage]; } @@ -186,7 +180,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic storage:storage]; } -+ (void)registerThreadDatabaseView:(OWSStorage *)storage ++ (void)asyncRegisterThreadDatabaseView:(OWSStorage *)storage { YapDatabaseView *threadView = [storage registeredExtension:TSThreadDatabaseViewExtensionName]; if (threadView) { @@ -234,7 +228,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic YapDatabaseView *databaseView = [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"3" options:options]; - [storage registerExtension:databaseView withName:TSThreadDatabaseViewExtensionName]; + [storage asyncRegisterExtension:databaseView withName:TSThreadDatabaseViewExtensionName]; } /** @@ -347,15 +341,7 @@ NSString *const TSSecondaryDevicesDatabaseViewExtensionName = @"TSSecondaryDevic YapDatabaseView *view = [[YapDatabaseAutoView alloc] initWithGrouping:viewGrouping sorting:viewSorting versionTag:@"3" options:options]; - [storage asyncRegisterExtension:view - withName:TSSecondaryDevicesDatabaseViewExtensionName - completionBlock:^(BOOL ready) { - if (ready) { - DDLogDebug(@"%@ Successfully set up extension: %@", self.logTag, TSSecondaryDevicesGroup); - } else { - DDLogError(@"%@ Unable to setup extension: %@", self.logTag, TSSecondaryDevicesGroup); - } - }]; + [storage asyncRegisterExtension:view withName:TSSecondaryDevicesDatabaseViewExtensionName]; } + (id)unseenDatabaseViewExtension:(YapDatabaseReadTransaction *)transaction diff --git a/SignalServiceKit/src/Storage/TSStorageManager.m b/SignalServiceKit/src/Storage/TSStorageManager.m index 23b1c3295..4cb4d1719 100644 --- a/SignalServiceKit/src/Storage/TSStorageManager.m +++ b/SignalServiceKit/src/Storage/TSStorageManager.m @@ -29,12 +29,6 @@ void runSyncRegistrationsForStorage(OWSStorage *storage) // Synchronously register extensions which are essential for views. [TSDatabaseView registerCrossProcessNotifier:storage]; - [TSDatabaseView registerThreadInteractionsDatabaseView:storage]; - [TSDatabaseView registerThreadDatabaseView:storage]; - [TSDatabaseView registerUnreadDatabaseView:storage]; - [storage registerExtension:[TSDatabaseSecondaryIndexes registerTimeStampIndex] withName:@"idx"]; - [OWSMessageReceiver syncRegisterDatabaseExtension:storage]; - [OWSBatchMessageProcessor syncRegisterDatabaseExtension:storage]; } void runAsyncRegistrationsForStorage(OWSStorage *storage) @@ -45,6 +39,14 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage) // // All sync registrations must be done before all async registrations, // or the sync registrations will block on the async registrations. + + [TSDatabaseView asyncRegisterThreadInteractionsDatabaseView:storage]; + [TSDatabaseView asyncRegisterThreadDatabaseView:storage]; + [TSDatabaseView asyncRegisterUnreadDatabaseView:storage]; + [storage asyncRegisterExtension:[TSDatabaseSecondaryIndexes registerTimeStampIndex] withName:@"idx"]; + [OWSMessageReceiver asyncRegisterDatabaseExtension:storage]; + [OWSBatchMessageProcessor asyncRegisterDatabaseExtension:storage]; + [TSDatabaseView asyncRegisterUnseenDatabaseView:storage]; [TSDatabaseView asyncRegisterThreadOutgoingMessagesDatabaseView:storage]; [TSDatabaseView asyncRegisterThreadSpecialMessagesDatabaseView:storage]; @@ -127,12 +129,26 @@ void runAsyncRegistrationsForStorage(OWSStorage *storage) runAsyncRegistrationsForStorage(self); + DDLogVerbose(@"%@ async registrations enqueued.", self.logTag); + // Block until all async registrations are complete. - YapDatabaseConnection *dbConnection = self.newDatabaseConnection; + YapDatabaseConnection *dbConnection = self.registrationConnection; + OWSAssert(self.registrationConnection); + // [dbConnection asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) { + // OWSAssert(!self.areAsyncRegistrationsComplete); + // + // DDLogVerbose(@"%@ async registrations flushed.", self.logTag); + // + // self.areAsyncRegistrationsComplete = YES; + // + // completion(); + // }]; [dbConnection flushTransactionsWithCompletionQueue:dispatch_get_main_queue() completionBlock:^{ OWSAssert(!self.areAsyncRegistrationsComplete); + DDLogVerbose(@"%@ async registrations complete.", self.logTag); + self.areAsyncRegistrationsComplete = YES; completion(); diff --git a/SignalServiceKit/src/Util/AppReadiness.h b/SignalServiceKit/src/Util/AppReadiness.h new file mode 100755 index 000000000..03541b0e5 --- /dev/null +++ b/SignalServiceKit/src/Util/AppReadiness.h @@ -0,0 +1,21 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^AppReadyBlock)(void); + +@interface AppReadiness : NSObject + +- (instancetype)init NS_UNAVAILABLE; + ++ (BOOL)isAppReady; + ++ (void)setAppIsReady; + ++ (void)runNowOrWhenAppIsReady:(AppReadyBlock)block; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/AppReadiness.m b/SignalServiceKit/src/Util/AppReadiness.m new file mode 100755 index 000000000..42fee4440 --- /dev/null +++ b/SignalServiceKit/src/Util/AppReadiness.m @@ -0,0 +1,100 @@ +// +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. +// + +#import "AppReadiness.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface AppReadiness () + +@property (atomic) BOOL isAppReady; + +@property (nonatomic, nullable) NSMutableArray *appReadyBlocks; + +@end + +#pragma mark - + +@implementation AppReadiness + ++ (instancetype)sharedManager +{ + static AppReadiness *sharedMyManager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedMyManager = [[self alloc] initDefault]; + }); + return sharedMyManager; +} + +- (instancetype)initDefault +{ + self = [super init]; + + if (!self) { + return self; + } + + OWSSingletonAssert(); + + return self; +} + ++ (BOOL)isAppReady +{ + return self.sharedManager.isAppReady; +} + ++ (void)runNowOrWhenAppIsReady:(AppReadyBlock)block +{ + OWSAssertIsOnMainThread(); + + [self.sharedManager runNowOrWhenAppIsReady:block]; +} + +- (void)runNowOrWhenAppIsReady:(AppReadyBlock)block +{ + OWSAssertIsOnMainThread(); + OWSAssert(block); + + if (self.isAppReady) { + block(); + return; + } + + if (!self.appReadyBlocks) { + self.appReadyBlocks = [NSMutableArray new]; + } + [self.appReadyBlocks addObject:block]; +} + ++ (void)setAppIsReady +{ + [self.sharedManager setAppIsReady]; +} + +- (void)setAppIsReady +{ + OWSAssertIsOnMainThread(); + OWSAssert(!self.isAppReady); + + self.isAppReady = YES; + + [self runAppReadyBlocks]; +} + +- (void)runAppReadyBlocks +{ + OWSAssertIsOnMainThread(); + OWSAssert(self.isAppReady); + + for (AppReadyBlock block in self.appReadyBlocks) { + block(); + } + self.appReadyBlocks = nil; +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/NSDate+OWS.h b/SignalServiceKit/src/Util/NSDate+OWS.h index 104c20892..039ca12de 100755 --- a/SignalServiceKit/src/Util/NSDate+OWS.h +++ b/SignalServiceKit/src/Util/NSDate+OWS.h @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // NS_ASSUME_NONNULL_BEGIN @@ -18,12 +18,16 @@ NS_ASSUME_NONNULL_BEGIN #define kWeekInMs (kDayInMs * 7) #define kMonthInMs (kDayInMs * 30) -@interface NSDate (millisecondTimeStamp) +@interface NSDate (OWS) + (uint64_t)ows_millisecondTimeStamp; + (NSDate *)ows_dateWithMillisecondsSince1970:(uint64_t)milliseconds; + (uint64_t)ows_millisecondsSince1970ForDate:(NSDate *)date; +#pragma mark - Debugging + ++ (NSString *)debugTimestamp; + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/NSDate+OWS.mm b/SignalServiceKit/src/Util/NSDate+OWS.mm index 334dbb944..e857341f8 100644 --- a/SignalServiceKit/src/Util/NSDate+OWS.mm +++ b/SignalServiceKit/src/Util/NSDate+OWS.mm @@ -1,5 +1,5 @@ // -// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// Copyright (c) 2018 Open Whisper Systems. All rights reserved. // #import "NSDate+OWS.h" @@ -7,7 +7,7 @@ NS_ASSUME_NONNULL_BEGIN -@implementation NSDate (millisecondTimeStamp) +@implementation NSDate (OWS) + (uint64_t)ows_millisecondTimeStamp { @@ -26,6 +26,31 @@ NS_ASSUME_NONNULL_BEGIN return (uint64_t)(date.timeIntervalSince1970 * 1000); } +#pragma mark - Debugging + ++ (NSDateFormatter *)dateFormatterForDebugTimestamps +{ + static NSDateFormatter *formatter; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + formatter = [NSDateFormatter new]; + [formatter setLocale:[NSLocale currentLocale]]; + [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSS"]; + }); + return formatter; +} + ++ (NSString *)debugTimestamp +{ + // We use ows_millisecondTimeStamp for its higher precision. + return [[NSDate ows_dateWithMillisecondsSince1970:[NSDate ows_millisecondTimeStamp]] formattedAsDebugTimestamp]; +} + +- (NSString *)formattedAsDebugTimestamp +{ + return [[NSDate dateFormatterForDebugTimestamps] stringFromDate:self]; +} + @end NS_ASSUME_NONNULL_END diff --git a/SignalServiceKit/src/Util/OWSFileSystem.m b/SignalServiceKit/src/Util/OWSFileSystem.m index a95ae4395..bdb49465b 100644 --- a/SignalServiceKit/src/Util/OWSFileSystem.m +++ b/SignalServiceKit/src/Util/OWSFileSystem.m @@ -58,12 +58,13 @@ NS_ASSUME_NONNULL_BEGIN sharedDataFilePath:(NSString *)newFilePath exceptionName:(NSString *)exceptionName { - DDLogInfo(@"%@ Moving file or directory from: %@ to: %@", self.logTag, oldFilePath, newFilePath); - NSFileManager *fileManager = [NSFileManager defaultManager]; if (![fileManager fileExistsAtPath:oldFilePath]) { return; } + + DDLogInfo(@"%@ Moving file or directory from: %@ to: %@", self.logTag, oldFilePath, newFilePath); + if ([fileManager fileExistsAtPath:newFilePath]) { OWSFail(@"%@ Can't move file or directory from: %@ to: %@; destination already exists.", self.logTag,