Merge branch 'charlesmchen/profileDeadlock2'

pull/1/head
Matthew Chen 8 years ago
commit c38fd51fbc

@ -16,9 +16,9 @@ extern NSString *const kNSNotificationKey_ProfileGroupId;
extern const NSUInteger kOWSProfileManager_NameDataLength; extern const NSUInteger kOWSProfileManager_NameDataLength;
extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter; extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter;
@class TSThread;
@class OWSAES256Key; @class OWSAES256Key;
@class OWSMessageSender; @class OWSMessageSender;
@class TSThread;
// This class can be safely accessed and used from any thread. // This class can be safely accessed and used from any thread.
@interface OWSProfileManager : NSObject <ProfileManagerProtocol> @interface OWSProfileManager : NSObject <ProfileManagerProtocol>
@ -63,10 +63,6 @@ extern const NSUInteger kOWSProfileManager_MaxAvatarDiameter;
- (void)addThreadToProfileWhitelist:(TSThread *)thread; - (void)addThreadToProfileWhitelist:(TSThread *)thread;
- (BOOL)isThreadInProfileWhitelist:(TSThread *)thread;
- (BOOL)isUserInProfileWhitelist:(NSString *)recipientId;
- (void)setContactRecipientIds:(NSArray<NSString *> *)contactRecipientIds; - (void)setContactRecipientIds:(NSArray<NSString *> *)contactRecipientIds;
#pragma mark - Other User's Profiles #pragma mark - Other User's Profiles

@ -30,16 +30,16 @@ NS_ASSUME_NONNULL_BEGIN
@property (atomic, readonly) NSString *recipientId; @property (atomic, readonly) NSString *recipientId;
@property (atomic, nullable) OWSAES256Key *profileKey; @property (atomic, nullable) OWSAES256Key *profileKey;
@property (nonatomic, nullable) NSString *profileName; @property (atomic, nullable) NSString *profileName;
@property (nonatomic, nullable) NSString *avatarUrlPath; @property (atomic, nullable) NSString *avatarUrlPath;
// This filename is relative to OWSProfileManager.profileAvatarsDirPath. // This filename is relative to OWSProfileManager.profileAvatarsDirPath.
@property (nonatomic, nullable) NSString *avatarFileName; @property (atomic, nullable) NSString *avatarFileName;
// This should reflect when either: // This should reflect when either:
// //
// * The last successful update finished. // * The last successful update finished.
// * The current in-flight update began. // * The current in-flight update began.
@property (nonatomic, nullable) NSDate *lastUpdateDate; @property (atomic, nullable) NSDate *lastUpdateDate;
- (instancetype)init NS_UNAVAILABLE; - (instancetype)init NS_UNAVAILABLE;
@ -49,6 +49,8 @@ NS_ASSUME_NONNULL_BEGIN
@implementation UserProfile @implementation UserProfile
@synthesize profileName = _profileName;
- (instancetype)initWithRecipientId:(NSString *)recipientId - (instancetype)initWithRecipientId:(NSString *)recipientId
{ {
self = [super initWithUniqueId:recipientId]; self = [super initWithUniqueId:recipientId];
@ -63,23 +65,20 @@ NS_ASSUME_NONNULL_BEGIN
return self; return self;
} }
- (void)setProfileName:(nullable NSString *)profileName - (nullable NSString *)profileName
{
_profileName = [profileName ows_stripped];
}
#pragma mark - NSObject
- (BOOL)isEqual:(UserProfile *)other
{ {
return ([other isKindOfClass:[UserProfile class]] && [self.recipientId isEqualToString:other.recipientId] && @synchronized(self)
[self.profileName isEqualToString:other.profileName] && [self.avatarUrlPath isEqualToString:other.avatarUrlPath] && {
[self.avatarFileName isEqualToString:other.avatarFileName]); return _profileName;
}
} }
- (NSUInteger)hash - (void)setProfileName:(nullable NSString *)profileName
{ {
return self.recipientId.hash ^ self.profileName.hash ^ self.avatarUrlPath.hash ^ self.avatarFileName.hash; @synchronized(self)
{
_profileName = [profileName ows_stripped];
}
} }
@end @end
@ -234,42 +233,48 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
{ {
OWSAssert(userProfile); OWSAssert(userProfile);
// Make a copy to use inside the transaction.
// To avoid deadlock, we want to avoid creating a new transaction while sync'd on self.
UserProfile *userProfileCopy;
@synchronized(self) @synchronized(self)
{ {
// Make sure to save on the local db connection for consistency. userProfileCopy = [userProfile copy];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { // Other threads may modify this profile's properties
[userProfile saveWithTransaction:transaction]; OWSAssert([userProfile isEqual:userProfileCopy]);
}]; }
BOOL isLocalUserProfile = userProfile == self.localUserProfile; // Make sure to save on the local db connection for consistency.
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[userProfileCopy saveWithTransaction:transaction];
}];
dispatch_async(dispatch_get_main_queue(), ^{ BOOL isLocalUserProfile = userProfile == self.localUserProfile;
if (isLocalUserProfile) {
[MultiDeviceProfileKeyUpdateJob runWithProfileKey:userProfile.profileKey
identityManager:self.identityManager
messageSender:self.messageSender
profileManager:self];
[[NSNotificationCenter defaultCenter] dispatch_async(dispatch_get_main_queue(), ^{
postNotificationNameAsync:kNSNotificationName_LocalProfileDidChange if (isLocalUserProfile) {
object:nil [MultiDeviceProfileKeyUpdateJob runWithProfileKey:userProfile.profileKey
userInfo:nil]; identityManager:self.identityManager
} else { messageSender:self.messageSender
[[NSNotificationCenter defaultCenter] profileManager:self];
postNotificationNameAsync:kNSNotificationName_OtherUsersProfileWillChange
object:nil [[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_LocalProfileDidChange
userInfo:@{ object:nil
kNSNotificationKey_ProfileRecipientId : userProfile.recipientId, userInfo:nil];
}]; } else {
[[NSNotificationCenter defaultCenter] [[NSNotificationCenter defaultCenter]
postNotificationNameAsync:kNSNotificationName_OtherUsersProfileDidChange postNotificationNameAsync:kNSNotificationName_OtherUsersProfileWillChange
object:nil object:nil
userInfo:@{ userInfo:@{
kNSNotificationKey_ProfileRecipientId : userProfile.recipientId, kNSNotificationKey_ProfileRecipientId : userProfile.recipientId,
}]; }];
} [[NSNotificationCenter defaultCenter]
}); postNotificationNameAsync:kNSNotificationName_OtherUsersProfileDidChange
} object:nil
userInfo:@{
kNSNotificationKey_ProfileRecipientId : userProfile.recipientId,
}];
}
});
} }
- (void)ensureLocalProfileCached - (void)ensureLocalProfileCached
@ -295,10 +300,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
DDLogInfo(@"%@ Building local profile.", self.tag); DDLogInfo(@"%@ Building local profile.", self.tag);
_localUserProfile = [[UserProfile alloc] initWithRecipientId:kLocalProfileUniqueId]; _localUserProfile = [[UserProfile alloc] initWithRecipientId:kLocalProfileUniqueId];
_localUserProfile.profileKey = [OWSAES256Key generateRandomKey]; _localUserProfile.profileKey = [OWSAES256Key generateRandomKey];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[_localUserProfile saveWithTransaction:transaction];
}];
// Sync local profile to any linked device
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self) @synchronized(self)
{ {
@ -764,23 +766,7 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
{ {
OWSAssert(recipientId.length > 0); OWSAssert(recipientId.length > 0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ [self addUsersToProfileWhitelist:@[ recipientId ]];
@synchronized(self)
{
if ([self isUserInProfileWhitelist:recipientId]) {
return;
}
self.userProfileWhitelistCache[recipientId] = @(YES);
[self.dbConnection setBool:YES forKey:recipientId inCollection:kOWSProfileManager_UserWhitelistCollection];
}
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_ProfileWhitelistDidChange
object:nil
userInfo:@{
kNSNotificationKey_ProfileRecipientId : recipientId,
}];
});
} }
- (void)addUsersToProfileWhitelist:(NSArray<NSString *> *)recipientIds - (void)addUsersToProfileWhitelist:(NSArray<NSString *> *)recipientIds
@ -802,24 +788,27 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
return; return;
} }
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) { for (NSString *recipientId in recipientIds) {
for (NSString *recipientId in recipientIds) { self.userProfileWhitelistCache[recipientId] = @(YES);
[transaction setObject:@(YES) }
forKey:recipientId
inCollection:kOWSProfileManager_UserWhitelistCollection];
self.userProfileWhitelistCache[recipientId] = @(YES);
}
}];
} }
for (NSString *recipientId in newRecipientIds) { [self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[NSNotificationCenter defaultCenter] for (NSString *recipientId in recipientIds) {
postNotificationNameAsync:kNSNotificationName_ProfileWhitelistDidChange [transaction setObject:@(YES)
object:nil forKey:recipientId
userInfo:@{ inCollection:kOWSProfileManager_UserWhitelistCollection];
kNSNotificationKey_ProfileRecipientId : recipientId,
}];
} }
}];
for (NSString *recipientId in newRecipientIds) {
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:kNSNotificationName_ProfileWhitelistDidChange
object:nil
userInfo:@{
kNSNotificationKey_ProfileRecipientId : recipientId,
}];
}
}); });
} }
@ -846,24 +835,24 @@ const NSUInteger kOWSProfileManager_MaxAvatarDiameter = 640;
OWSAssert(groupId.length > 0); OWSAssert(groupId.length > 0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString *groupIdKey = [groupId hexadecimalString];
@synchronized(self) @synchronized(self)
{ {
NSString *groupIdKey = [groupId hexadecimalString];
if ([self isGroupIdInProfileWhitelist:groupId]) { if ([self isGroupIdInProfileWhitelist:groupId]) {
return; return;
} }
[self.dbConnection setBool:YES forKey:groupIdKey inCollection:kOWSProfileManager_GroupWhitelistCollection];
self.groupProfileWhitelistCache[groupIdKey] = @(YES); self.groupProfileWhitelistCache[groupIdKey] = @(YES);
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:kNSNotificationName_ProfileWhitelistDidChange
object:nil
userInfo:@{
kNSNotificationKey_ProfileGroupId : groupId,
}];
} }
[self.dbConnection setBool:YES forKey:groupIdKey inCollection:kOWSProfileManager_GroupWhitelistCollection];
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:kNSNotificationName_ProfileWhitelistDidChange
object:nil
userInfo:@{
kNSNotificationKey_ProfileGroupId : groupId,
}];
}); });
} }

Loading…
Cancel
Save