Respond to CR.

This commit is contained in:
Matthew Chen 2018-02-02 14:07:13 -05:00
parent 38950ae2ed
commit bd0f601792
16 changed files with 250 additions and 170 deletions

View File

@ -926,14 +926,10 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
continue;
}
[self.editingDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[OWSIdentityManager.sharedManager setVerificationState:OWSVerificationStateDefault
identityKey:identityKey
recipientId:recipientId
isUserInitiatedChange:YES
protocolContext:transaction];
}];
[OWSIdentityManager.sharedManager setVerificationState:OWSVerificationStateDefault
identityKey:identityKey
recipientId:recipientId
isUserInitiatedChange:YES];
}
}

View File

@ -99,20 +99,29 @@ NS_ASSUME_NONNULL_BEGIN
#if DEBUG
+ (void)clearSessionAndIdentityStore
{
[[TSStorageManager sharedManager] resetSessionStore];
[[OWSIdentityManager sharedManager] clearIdentityState];
[TSStorageManager.sharedManager.newDatabaseConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[TSStorageManager sharedManager] resetSessionStore:transaction];
[[OWSIdentityManager sharedManager] clearIdentityState:transaction];
}];
}
+ (void)snapshotSessionAndIdentityStore
{
[[TSStorageManager sharedManager] snapshotSessionStore];
[[OWSIdentityManager sharedManager] snapshotIdentityState];
[TSStorageManager.sharedManager.newDatabaseConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[TSStorageManager sharedManager] snapshotSessionStore:transaction];
[[OWSIdentityManager sharedManager] snapshotIdentityState:transaction];
}];
}
+ (void)restoreSessionAndIdentityStore
{
[[TSStorageManager sharedManager] restoreSessionStore];
[[OWSIdentityManager sharedManager] restoreIdentityState];
[TSStorageManager.sharedManager.newDatabaseConnection
readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[TSStorageManager sharedManager] restoreSessionStore:transaction];
[[OWSIdentityManager sharedManager] restoreIdentityState:transaction];
}];
}
#endif

View File

@ -200,15 +200,10 @@ NS_ASSUME_NONNULL_BEGIN
@"Button that marks user as verified after a successful fingerprint scan.")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[TSStorageManager.sharedManager.newDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[OWSIdentityManager.sharedManager
setVerificationState:OWSVerificationStateVerified
identityKey:identityKey
recipientId:recipientId
isUserInitiatedChange:YES
protocolContext:transaction];
}];
[OWSIdentityManager.sharedManager setVerificationState:OWSVerificationStateVerified
identityKey:identityKey
recipientId:recipientId
isUserInitiatedChange:YES];
[viewController dismissViewControllerAnimated:true completion:nil];
}]];
UIAlertAction *dismissAction =

View File

@ -251,14 +251,10 @@ NS_ASSUME_NONNULL_BEGIN
OWSFail(@"Missing identity key for: %@", recipientId);
continue;
}
[TSStorageManager.sharedManager.newDatabaseConnection
asyncReadWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[identityManger setVerificationState:OWSVerificationStateDefault
identityKey:identityKey
recipientId:recipientId
isUserInitiatedChange:YES
protocolContext:transaction];
}];
[identityManger setVerificationState:OWSVerificationStateDefault
identityKey:identityKey
recipientId:recipientId
isUserInitiatedChange:YES];
}
}

View File

@ -104,9 +104,10 @@ NSString *const TSAccountManager_ServerSignalingKey = @"TSStorageServerSignaling
_phoneNumberAwaitingVerification = nil;
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[transaction removeAllObjectsInCollection:TSAccountManager_UserAccountCollection];
[[TSStorageManager sharedManager] resetSessionStore:transaction];
}];
}
[[TSStorageManager sharedManager] resetSessionStore];
}
+ (BOOL)isRegistered

View File

@ -19,7 +19,7 @@ extern const NSUInteger kIdentityKeyLength;
@class OWSRecipientIdentity;
@class OWSSignalServiceProtosVerified;
@class OWSStorage;
@class OWSStorage;
@class YapDatabaseReadWriteTransaction;
// This class can be safely accessed and used from any thread.
@interface OWSIdentityManager : NSObject <IdentityKeyStore>
@ -44,6 +44,11 @@ extern const NSUInteger kIdentityKeyLength;
- (OWSVerificationState)verificationStateForRecipientId:(NSString *)recipientId
transaction:(YapDatabaseReadTransaction *)transaction;
- (void)setVerificationState:(OWSVerificationState)verificationState
identityKey:(NSData *)identityKey
recipientId:(NSString *)recipientId
isUserInitiatedChange:(BOOL)isUserInitiatedChange;
- (nullable OWSRecipientIdentity *)recipientIdentityForRecipientId:(NSString *)recipientId;
/**
@ -64,10 +69,10 @@ extern const NSUInteger kIdentityKeyLength;
#if DEBUG
// Clears everything except the local identity key.
- (void)clearIdentityState;
- (void)clearIdentityState:(YapDatabaseReadWriteTransaction *)transaction;
- (void)snapshotIdentityState;
- (void)restoreIdentityState;
- (void)snapshotIdentityState:(YapDatabaseReadWriteTransaction *)transaction;
- (void)restoreIdentityState:(YapDatabaseReadWriteTransaction *)transaction;
#endif
@end

View File

@ -23,7 +23,7 @@
#import "TSStorageManager.h"
#import "TextSecureKitEnv.h"
#import "YapDatabaseConnection+OWS.h"
#import "YapDatabaseReadTransaction+OWS.h"
#import "YapDatabaseTransaction+OWS.h"
#import <AxolotlKit/NSData+keyVersionByte.h>
#import <Curve25519Kit/Curve25519.h>
#import <YapDatabase/YapDatabase.h>
@ -271,6 +271,23 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa
return NO;
}
- (void)setVerificationState:(OWSVerificationState)verificationState
identityKey:(NSData *)identityKey
recipientId:(NSString *)recipientId
isUserInitiatedChange:(BOOL)isUserInitiatedChange
{
OWSAssert(identityKey.length == kStoredIdentityKeyLength);
OWSAssert(recipientId.length > 0);
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self setVerificationState:verificationState
identityKey:identityKey
recipientId:recipientId
isUserInitiatedChange:isUserInitiatedChange
transaction:transaction];
}];
}
- (void)setVerificationState:(OWSVerificationState)verificationState
identityKey:(NSData *)identityKey
recipientId:(NSString *)recipientId
@ -283,10 +300,27 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa
YapDatabaseReadWriteTransaction *transaction = protocolContext;
[self setVerificationState:verificationState
identityKey:identityKey
recipientId:recipientId
isUserInitiatedChange:isUserInitiatedChange
transaction:transaction];
}
- (void)setVerificationState:(OWSVerificationState)verificationState
identityKey:(NSData *)identityKey
recipientId:(NSString *)recipientId
isUserInitiatedChange:(BOOL)isUserInitiatedChange
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(identityKey.length == kStoredIdentityKeyLength);
OWSAssert(recipientId.length > 0);
OWSAssert(transaction);
// TODO: Remove all @synchronized
// Ensure a remote identity exists for this key. We may be learning about
// it for the first time.
[self saveRemoteIdentity:identityKey recipientId:recipientId protocolContext:protocolContext];
[self saveRemoteIdentity:identityKey recipientId:recipientId protocolContext:transaction];
OWSRecipientIdentity *recipientIdentity =
[OWSRecipientIdentity fetchObjectWithUniqueID:recipientId transaction:transaction];
@ -845,23 +879,23 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa
#pragma mark - Debug
#if DEBUG
- (void)clearIdentityState
- (void)clearIdentityState:(YapDatabaseReadWriteTransaction *)transaction
{
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
NSMutableArray<NSString *> *identityKeysToRemove = [NSMutableArray new];
[transaction enumerateKeysInCollection:TSStorageManagerIdentityKeyStoreCollection
usingBlock:^(NSString *_Nonnull key, BOOL *_Nonnull stop) {
if ([key isEqualToString:TSStorageManagerIdentityKeyStoreIdentityKey]) {
// Don't delete our own key.
return;
}
[identityKeysToRemove addObject:key];
}];
for (NSString *key in identityKeysToRemove) {
[transaction removeObjectForKey:key inCollection:TSStorageManagerIdentityKeyStoreCollection];
}
[transaction removeAllObjectsInCollection:TSStorageManagerTrustedKeysCollection];
}];
OWSAssert(transaction);
NSMutableArray<NSString *> *identityKeysToRemove = [NSMutableArray new];
[transaction enumerateKeysInCollection:TSStorageManagerIdentityKeyStoreCollection
usingBlock:^(NSString *_Nonnull key, BOOL *_Nonnull stop) {
if ([key isEqualToString:TSStorageManagerIdentityKeyStoreIdentityKey]) {
// Don't delete our own key.
return;
}
[identityKeysToRemove addObject:key];
}];
for (NSString *key in identityKeysToRemove) {
[transaction removeObjectForKey:key inCollection:TSStorageManagerIdentityKeyStoreCollection];
}
[transaction removeAllObjectsInCollection:TSStorageManagerTrustedKeysCollection];
}
- (NSString *)identityKeySnapshotFilePath
@ -878,20 +912,24 @@ NSString *const kNSNotificationName_IdentityStateDidChange = @"kNSNotificationNa
return [dirPath stringByAppendingPathComponent:@".trusted-key-snapshot"];
}
- (void)snapshotIdentityState
- (void)snapshotIdentityState:(YapDatabaseReadWriteTransaction *)transaction
{
[self.dbConnection snapshotCollection:TSStorageManagerIdentityKeyStoreCollection
snapshotFilePath:self.identityKeySnapshotFilePath];
[self.dbConnection snapshotCollection:TSStorageManagerTrustedKeysCollection
snapshotFilePath:self.trustedKeySnapshotFilePath];
OWSAssert(transaction);
[transaction snapshotCollection:TSStorageManagerIdentityKeyStoreCollection
snapshotFilePath:self.identityKeySnapshotFilePath];
[transaction snapshotCollection:TSStorageManagerTrustedKeysCollection
snapshotFilePath:self.trustedKeySnapshotFilePath];
}
- (void)restoreIdentityState
- (void)restoreIdentityState:(YapDatabaseReadWriteTransaction *)transaction
{
[self.dbConnection restoreSnapshotOfCollection:TSStorageManagerIdentityKeyStoreCollection
snapshotFilePath:self.identityKeySnapshotFilePath];
[self.dbConnection restoreSnapshotOfCollection:TSStorageManagerTrustedKeysCollection
snapshotFilePath:self.trustedKeySnapshotFilePath];
OWSAssert(transaction);
[transaction restoreSnapshotOfCollection:TSStorageManagerIdentityKeyStoreCollection
snapshotFilePath:self.identityKeySnapshotFilePath];
[transaction restoreSnapshotOfCollection:TSStorageManagerTrustedKeysCollection
snapshotFilePath:self.trustedKeySnapshotFilePath];
}
#endif

View File

@ -11,13 +11,15 @@ NS_ASSUME_NONNULL_BEGIN
- (void)archiveAllSessionsForContact:(NSString *)contactIdentifier protocolContext:(nullable id)protocolContext;
#pragma mark - debug
#pragma mark - Debug
- (void)resetSessionStore:(YapDatabaseReadWriteTransaction *)transaction;
- (void)resetSessionStore;
#if DEBUG
- (void)snapshotSessionStore;
- (void)restoreSessionStore;
- (void)snapshotSessionStore:(YapDatabaseReadWriteTransaction *)transaction;
- (void)restoreSessionStore:(YapDatabaseReadWriteTransaction *)transaction;
#endif
- (void)printAllSessions;
@end

View File

@ -5,6 +5,7 @@
#import "OWSFileSystem.h"
#import "TSStorageManager+SessionStore.h"
#import "YapDatabaseConnection+OWS.h"
#import "YapDatabaseTransaction+OWS.h"
#import <AxolotlKit/SessionRecord.h>
#import <YapDatabase/YapDatabase.h>
@ -195,12 +196,13 @@ NSString *const kSessionStoreDBConnectionKey = @"kSessionStoreDBConnectionKey";
#pragma mark - debug
- (void)resetSessionStore
- (void)resetSessionStore:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(transaction);
DDLogWarn(@"%@ resetting session store", self.logTag);
[self.sessionStoreDBConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[transaction removeAllObjectsInCollection:TSStorageManagerSessionStoreCollection];
}];
[transaction removeAllObjectsInCollection:TSStorageManagerSessionStoreCollection];
}
- (void)printAllSessions
@ -253,16 +255,19 @@ NSString *const kSessionStoreDBConnectionKey = @"kSessionStoreDBConnectionKey";
return [dirPath stringByAppendingPathComponent:@".session-snapshot"];
}
- (void)snapshotSessionStore
- (void)snapshotSessionStore:(YapDatabaseReadWriteTransaction *)transaction
{
[self.sessionStoreDBConnection snapshotCollection:TSStorageManagerSessionStoreCollection
snapshotFilePath:self.snapshotFilePath];
OWSAssert(transaction);
[transaction snapshotCollection:TSStorageManagerSessionStoreCollection snapshotFilePath:self.snapshotFilePath];
}
- (void)restoreSessionStore
- (void)restoreSessionStore:(YapDatabaseReadWriteTransaction *)transaction
{
[self.sessionStoreDBConnection restoreSnapshotOfCollection:TSStorageManagerSessionStoreCollection
snapshotFilePath:self.snapshotFilePath];
OWSAssert(transaction);
[transaction restoreSnapshotOfCollection:TSStorageManagerSessionStoreCollection
snapshotFilePath:self.snapshotFilePath];
}
#endif

View File

@ -38,13 +38,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)purgeCollection:(NSString *)collection;
#pragma mark - Debug
#if DEBUG
- (void)snapshotCollection:(NSString *)collection snapshotFilePath:(NSString *)snapshotFilePath;
- (void)restoreSnapshotOfCollection:(NSString *)collection snapshotFilePath:(NSString *)snapshotFilePath;
#endif
@end
NS_ASSUME_NONNULL_END

View File

@ -168,47 +168,6 @@ NS_ASSUME_NONNULL_BEGIN
[self setObject:@(value.timeIntervalSince1970) forKey:key inCollection:collection];
}
#pragma mark - Debug
#if DEBUG
- (void)snapshotCollection:(NSString *)collection snapshotFilePath:(NSString *)snapshotFilePath
{
OWSAssert(collection.length > 0);
OWSAssert(snapshotFilePath.length > 0);
NSMutableDictionary<NSString *, id> *snapshot = [NSMutableDictionary new];
[self readWithBlock:^(YapDatabaseReadTransaction *_Nonnull transaction) {
[transaction
enumerateKeysAndObjectsInCollection:collection
usingBlock:^(NSString *_Nonnull key, id _Nonnull value, BOOL *_Nonnull stop) {
snapshot[key] = value;
}];
}];
NSData *_Nullable data = [NSKeyedArchiver archivedDataWithRootObject:snapshot];
OWSAssert(data);
BOOL success = [data writeToFile:snapshotFilePath atomically:YES];
OWSAssert(success);
}
- (void)restoreSnapshotOfCollection:(NSString *)collection snapshotFilePath:(NSString *)snapshotFilePath
{
OWSAssert(collection.length > 0);
OWSAssert(snapshotFilePath.length > 0);
NSData *_Nullable data = [NSData dataWithContentsOfFile:snapshotFilePath];
OWSAssert(data);
NSMutableDictionary<NSString *, id> *_Nullable snapshot = [NSKeyedUnarchiver unarchiveObjectWithData:data];
OWSAssert(snapshot);
[self readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[transaction removeAllObjectsInCollection:collection];
[snapshot enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, id _Nonnull value, BOOL *_Nonnull stop) {
[transaction setObject:value forKey:key inCollection:collection];
}];
}];
}
#endif
@end
NS_ASSUME_NONNULL_END

View File

@ -25,4 +25,17 @@ NS_ASSUME_NONNULL_BEGIN
@end
#pragma mark -
@interface YapDatabaseReadWriteTransaction (OWS)
#pragma mark - Debug
#if DEBUG
- (void)snapshotCollection:(NSString *)collection snapshotFilePath:(NSString *)snapshotFilePath;
- (void)restoreSnapshotOfCollection:(NSString *)collection snapshotFilePath:(NSString *)snapshotFilePath;
#endif
@end
NS_ASSUME_NONNULL_END

View File

@ -2,7 +2,7 @@
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "YapDatabaseReadTransaction+OWS.h"
#import "YapDatabaseTransaction+OWS.h"
#import <AxolotlKit/PreKeyRecord.h>
#import <AxolotlKit/SignedPrekeyRecord.h>
#import <Curve25519Kit/Curve25519.h>
@ -109,4 +109,46 @@ NS_ASSUME_NONNULL_BEGIN
@end
#pragma mark -
@implementation YapDatabaseReadWriteTransaction (OWS)
#pragma mark - Debug
#if DEBUG
- (void)snapshotCollection:(NSString *)collection snapshotFilePath:(NSString *)snapshotFilePath
{
OWSAssert(collection.length > 0);
OWSAssert(snapshotFilePath.length > 0);
NSMutableDictionary<NSString *, id> *snapshot = [NSMutableDictionary new];
[self enumerateKeysAndObjectsInCollection:collection
usingBlock:^(NSString *_Nonnull key, id _Nonnull value, BOOL *_Nonnull stop) {
snapshot[key] = value;
}];
NSData *_Nullable data = [NSKeyedArchiver archivedDataWithRootObject:snapshot];
OWSAssert(data);
BOOL success = [data writeToFile:snapshotFilePath atomically:YES];
OWSAssert(success);
}
- (void)restoreSnapshotOfCollection:(NSString *)collection snapshotFilePath:(NSString *)snapshotFilePath
{
OWSAssert(collection.length > 0);
OWSAssert(snapshotFilePath.length > 0);
NSData *_Nullable data = [NSData dataWithContentsOfFile:snapshotFilePath];
OWSAssert(data);
NSMutableDictionary<NSString *, id> *_Nullable snapshot = [NSKeyedUnarchiver unarchiveObjectWithData:data];
OWSAssert(snapshot);
[self removeAllObjectsInCollection:collection];
[snapshot enumerateKeysAndObjectsUsingBlock:^(NSString *_Nonnull key, id _Nonnull value, BOOL *_Nonnull stop) {
[self setObject:value forKey:key inCollection:collection];
}];
}
#endif
@end
NS_ASSUME_NONNULL_END

View File

@ -77,55 +77,63 @@
- (void)startBackgroundTask
{
// beginBackgroundTaskWithExpirationHandler must be called on the main thread.
DispatchMainThreadSafe(^{
__weak typeof(self) weakSelf = self;
self.backgroundTaskId = [CurrentAppContext() beginBackgroundTaskWithExpirationHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
OWSBackgroundTask *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
__weak typeof(self) weakSelf = self;
self.backgroundTaskId = [CurrentAppContext() beginBackgroundTaskWithExpirationHandler:^{
// Supposedly [UIApplication beginBackgroundTaskWithExpirationHandler]'s handler
// will always be called on the main thread, but in practice we've observed
// otherwise. We use DispatchSyncMainThreadSafe() (note the sync) to ensure that
// this work is done on the main thread.
//
// See: https://developer.apple.com/documentation/uikit/uiapplication/1623031-beginbackgroundtaskwithexpiratio)
//
// Note the usage of OWSCAssert() to avoid capturing a reference to self.
OWSCAssert([NSThread isMainThread]);
// Make a local copy of completionBlock to ensure that it is called
// exactly once.
BackgroundTaskCompletionBlock _Nullable completionBlock = nil;
@synchronized(strongSelf)
{
if (strongSelf.backgroundTaskId == UIBackgroundTaskInvalid) {
return;
}
DDLogInfo(@"%@ %@ background task expired.", strongSelf.logTag, strongSelf.label);
strongSelf.backgroundTaskId = UIBackgroundTaskInvalid;
completionBlock = strongSelf.completionBlock;
strongSelf.completionBlock = nil;
}
if (completionBlock) {
completionBlock(BackgroundTaskState_Expired);
}
});
}];
// If a background task could not be begun, call the completion block.
if (self.backgroundTaskId == UIBackgroundTaskInvalid) {
DDLogInfo(@"%@ %@ background task could not be started.", self.logTag, self.label);
DispatchSyncMainThreadSafe(^{
OWSBackgroundTask *strongSelf = weakSelf;
if (!strongSelf) {
return;
}
// Make a local copy of completionBlock to ensure that it is called
// exactly once.
BackgroundTaskCompletionBlock _Nullable completionBlock;
@synchronized(self)
BackgroundTaskCompletionBlock _Nullable completionBlock = nil;
@synchronized(strongSelf)
{
completionBlock = self.completionBlock;
self.completionBlock = nil;
if (strongSelf.backgroundTaskId == UIBackgroundTaskInvalid) {
return;
}
DDLogInfo(@"%@ %@ background task expired.", strongSelf.logTag, strongSelf.label);
strongSelf.backgroundTaskId = UIBackgroundTaskInvalid;
completionBlock = strongSelf.completionBlock;
strongSelf.completionBlock = nil;
}
if (completionBlock) {
completionBlock(BackgroundTaskState_CouldNotStart);
completionBlock(BackgroundTaskState_Expired);
}
});
}];
// If a background task could not be begun, call the completion block.
if (self.backgroundTaskId == UIBackgroundTaskInvalid) {
DDLogInfo(@"%@ %@ background task could not be started.", self.logTag, self.label);
// Make a local copy of completionBlock to ensure that it is called
// exactly once.
BackgroundTaskCompletionBlock _Nullable completionBlock;
@synchronized(self)
{
completionBlock = self.completionBlock;
self.completionBlock = nil;
}
});
if (completionBlock) {
completionBlock(BackgroundTaskState_CouldNotStart);
}
}
}
- (void)endBackgroundTask

View File

@ -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
@ -7,8 +7,13 @@ NS_ASSUME_NONNULL_BEGIN
typedef void (^SimpleBlock)(void);
// The block is executed immediately if called from the
// main thread; otherwise it is disptached async to the
// main thread; otherwise it is dispatched async to the
// main thread.
void DispatchMainThreadSafe(SimpleBlock block);
// The block is executed immediately if called from the
// main thread; otherwise it is dispatched sync to the
// main thread.
void DispatchSyncMainThreadSafe(SimpleBlock block);
NS_ASSUME_NONNULL_END

View File

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
#import "Threading.h"
@ -19,4 +19,17 @@ void DispatchMainThreadSafe(SimpleBlock block)
}
}
void DispatchSyncMainThreadSafe(SimpleBlock block)
{
OWSCAssert(block);
if ([NSThread isMainThread]) {
block();
} else {
dispatch_sync(dispatch_get_main_queue(), ^{
block();
});
}
}
NS_ASSUME_NONNULL_END