Use explicit transactions.

- Start expiration within scope of existing transaction when we're
  already in a transaction

// FREEBIE
pull/1/head
Michael Kirk 7 years ago
parent b7625689cb
commit dfb2a034af

@ -3278,7 +3278,7 @@ NS_ASSUME_NONNULL_BEGIN
attachmentIds:@[]
expiresInSeconds:0
quotedMessage:nil];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
break;
}
case 1: {
@ -3316,7 +3316,7 @@ NS_ASSUME_NONNULL_BEGIN
]
expiresInSeconds:0
quotedMessage:nil];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
break;
}
case 3: {
@ -3767,7 +3767,7 @@ NS_ASSUME_NONNULL_BEGIN
attachmentIds:[NSMutableArray new]
expiresInSeconds:0
quotedMessage:nil];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
}
{
TSOutgoingMessage *message =
@ -4105,7 +4105,7 @@ NS_ASSUME_NONNULL_BEGIN
attachmentIds:attachmentIds
expiresInSeconds:0
quotedMessage:quotedMessage];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:NO];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
return message;
}

@ -8,6 +8,8 @@ NS_ASSUME_NONNULL_BEGIN
#define OWSDisappearingMessagesConfigurationDefaultExpirationDuration kDayInterval
@class YapDatabaseReadTransaction;
@interface OWSDisappearingMessagesConfiguration : TSYapDatabaseObject
- (instancetype)initDefaultWithThreadId:(NSString *)threadId;
@ -21,7 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) BOOL dictionaryValueDidChange;
@property (readonly, getter=isNewRecord) BOOL newRecord;
+ (instancetype)fetchOrCreateDefaultWithThreadId:(NSString *)threadId;
+ (instancetype)fetchOrCreateDefaultWithThreadId:(NSString *)threadId
transaction:(YapDatabaseReadTransaction *)transaction;
+ (NSArray<NSNumber *> *)validDurationsSeconds;

@ -51,8 +51,10 @@ NS_ASSUME_NONNULL_BEGIN
}
+ (instancetype)fetchOrCreateDefaultWithThreadId:(NSString *)threadId
transaction:(YapDatabaseReadTransaction *)transaction
{
OWSDisappearingMessagesConfiguration *savedConfiguration = [self fetchObjectWithUniqueID:threadId];
OWSDisappearingMessagesConfiguration *savedConfiguration =
[self fetchObjectWithUniqueID:threadId transaction:transaction];
if (savedConfiguration) {
return savedConfiguration;
} else {

@ -230,7 +230,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)markAllAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
{
for (id<OWSReadTracking> message in [self unseenMessagesWithTransaction:transaction]) {
[message markAsReadWithTransaction:transaction sendReadReceipt:YES updateExpiration:YES];
[message markAsReadWithTransaction:transaction sendReadReceipt:YES];
}
// Just to be defensive, we'll also check for unread messages.

@ -134,9 +134,13 @@ NS_ASSUME_NONNULL_BEGIN
}
if (transcript.isExpirationTimerUpdate) {
[OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:outgoingMessage
contactsManager:self.contactsManager];
[[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:outgoingMessage
contactsManager:self.contactsManager
transaction:transaction];
// early return to avoid saving an empty incoming message.
OWSAssert(transcript.body.length == 0);
OWSAssert(outgoingMessage.attachmentIds.count == 0);
return;
}
@ -147,10 +151,12 @@ NS_ASSUME_NONNULL_BEGIN
[outgoingMessage saveWithTransaction:transaction];
[outgoingMessage updateWithWasSentFromLinkedDeviceWithTransaction:transaction];
[OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:outgoingMessage
contactsManager:self.contactsManager];
[OWSDisappearingMessagesJob setExpirationForMessage:outgoingMessage
expirationStartedAt:transcript.expirationStartedAt];
[[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:outgoingMessage
contactsManager:self.contactsManager
transaction:transaction];
[[OWSDisappearingMessagesJob sharedJob] setExpirationForMessage:outgoingMessage
expirationStartedAt:transcript.expirationStartedAt
transaction:transaction];
[self.readReceiptManager applyEarlyReadReceiptsForOutgoingMessageFromLinkedDevice:outgoingMessage
transaction:transaction];

@ -186,7 +186,6 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
{
OWSAssert(transaction);
@ -200,7 +199,7 @@ NSUInteger TSErrorMessageSchemaVersion = 1;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
// Ignore sendReadReceipt and updateExpiration; they don't apply to error messages.
// Ignore sendReadReceipt - it doesn't apply to error messages.
}
@end

@ -136,7 +136,6 @@ NS_ASSUME_NONNULL_BEGIN
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
{
OWSAssert(transaction);
@ -149,10 +148,10 @@ NS_ASSUME_NONNULL_BEGIN
_read = YES;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
if (updateExpiration) {
[OWSDisappearingMessagesJob setExpirationForMessage:self];
}
[[OWSDisappearingMessagesJob sharedJob] startExpirationForMessage:self
transaction:transaction];
if (sendReadReceipt) {
[OWSReadReceiptManager.sharedManager messageWasReadLocally:self];

@ -131,7 +131,6 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
{
OWSAssert(transaction);
@ -145,7 +144,7 @@ NSUInteger TSInfoMessageSchemaVersion = 1;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
// Ignore sendReadReceipt and updateExpiration; they don't apply to info messages.
// Ignore sendReadReceipt, it doesn't apply to info messages.
}
@end

@ -7,6 +7,7 @@ NS_ASSUME_NONNULL_BEGIN
@class OWSPrimaryStorage;
@class TSMessage;
@class TSThread;
@class YapDatabaseReadWriteTransaction;
@protocol ContactsManagerProtocol;
@ -16,9 +17,13 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)init NS_UNAVAILABLE;
+ (void)setExpirationsForThread:(TSThread *)thread;
+ (void)setExpirationForMessage:(TSMessage *)message;
+ (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt;
//+ (void)setExpirationsForThread:(TSThread *)thread;
- (void)startExpirationForMessage:(TSMessage *)message
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction;
- (void)setExpirationForMessage:(TSMessage *)message
expirationStartedAt:(uint64_t)expirationStartedAt
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction;
/**
* Synchronize our disappearing messages settings with that of the given message. Useful so we can
@ -31,8 +36,9 @@ NS_ASSUME_NONNULL_BEGIN
* @param contactsManager
* Provides the contact name responsible for any configuration changes in an info message.
*/
+ (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message
contactsManager:(id<ContactsManagerProtocol>)contactsManager;
- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message
contactsManager:(id<ContactsManagerProtocol>)contactsManager
transaction:(YapDatabaseReadWriteTransaction *)transaction;
// Clean up any messages that expired since last launch immediately
// and continue cleaning in the background.

@ -166,42 +166,23 @@ void AssertIsOnDisappearingMessagesQueue()
return deletedCount;
}
+ (void)setExpirationForMessage:(TSMessage *)message
{
dispatch_async(self.serialQueue, ^{
[[self sharedJob] setExpirationForMessage:message];
});
}
- (void)setExpirationForMessage:(TSMessage *)message
- (void)startExpirationForMessage:(TSMessage *)message
transaction:(YapDatabaseReadWriteTransaction *_Nonnull)transaction
{
if (!message.isExpiringMessage) {
return;
}
OWSDisappearingMessagesConfiguration *disappearingConfig =
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:message.uniqueThreadId];
[OWSDisappearingMessagesConfiguration fetchObjectWithUniqueID:message.uniqueThreadId transaction:transaction];
if (!disappearingConfig.isEnabled) {
return;
}
[self setExpirationForMessage:message expirationStartedAt:[NSDate ows_millisecondTimeStamp]];
}
+ (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt
{
dispatch_async(self.serialQueue, ^{
[[self sharedJob] setExpirationForMessage:message expirationStartedAt:expirationStartedAt];
});
}
// This method should only be called on the serialQueue.
- (void)setExpirationForMessage:(TSMessage *)message expirationStartedAt:(uint64_t)expirationStartedAt
{
[self.databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self setExpirationForMessage:message expirationStartedAt:expirationStartedAt transaction:transaction];
}];
[self setExpirationForMessage:message
expirationStartedAt:[NSDate ows_millisecondTimeStamp]
transaction:transaction];
}
- (void)setExpirationForMessage:(TSMessage *)message
@ -222,106 +203,93 @@ void AssertIsOnDisappearingMessagesQueue()
[message updateWithExpireStartedAt:expirationStartedAt transaction:transaction];
}
// Necessary that the async expiration run happens *after* the message is saved with expiration configuration.
[self scheduleRunByDate:[NSDate ows_dateWithMillisecondsSince1970:message.expiresAt]];
}
+ (void)setExpirationsForThread:(TSThread *)thread
{
dispatch_async(self.serialQueue, ^{
[[self sharedJob] setExpirationsForThread:thread];
});
[transaction addCompletionQueue:nil
completionBlock:^{
// Necessary that the async expiration run happens *after* the message is saved with it's new
// expiration configuration.
[self scheduleRunByDate:[NSDate ows_dateWithMillisecondsSince1970:message.expiresAt]];
}];
}
// This method should only be called on the serialQueue.
- (void)setExpirationsForThread:(TSThread *)thread
- (void)setExpirationsForThread:(TSThread *)thread transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
uint64_t now = [NSDate ows_millisecondTimeStamp];
[self.databaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
[self.disappearingMessagesFinder
enumerateUnstartedExpiringMessagesInThread:thread
block:^(TSMessage *_Nonnull message) {
DDLogWarn(
@"%@ Starting expiring message which should have already "
@"been started.",
self.logTag);
// specify "now" in case D.M. have since been disabled, but we have
// existing unstarted expiring messages that still need to expire.
[self setExpirationForMessage:message
expirationStartedAt:now
transaction:transaction];
}
transaction:transaction];
}];
[self.disappearingMessagesFinder
enumerateUnstartedExpiringMessagesInThread:thread
block:^(TSMessage *_Nonnull message) {
DDLogWarn(@"%@ Starting expiring message which should have already "
@"been started.",
self.logTag);
// specify "now" in case D.M. have since been disabled, but we have
// existing unstarted expiring messages that still need to expire.
[self setExpirationForMessage:message
expirationStartedAt:now
transaction:transaction];
}
transaction:transaction];
backgroundTask = nil;
}
+ (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message
contactsManager:(id<ContactsManagerProtocol>)contactsManager
{
[[self sharedJob] becomeConsistentWithConfigurationForMessage:message contactsManager:contactsManager];
}
- (void)becomeConsistentWithConfigurationForMessage:(TSMessage *)message
contactsManager:(id<ContactsManagerProtocol>)contactsManager
transaction:(YapDatabaseReadWriteTransaction *)transaction
{
OWSAssert(message);
OWSAssert(contactsManager);
__block OWSBackgroundTask *_Nullable backgroundTask = [OWSBackgroundTask backgroundTaskWithLabelStr:__PRETTY_FUNCTION__];
dispatch_async(OWSDisappearingMessagesJob.serialQueue, ^{
// Become eventually consistent in the case that the remote changed their settings at the same time.
// Also in case remote doesn't support expiring messages
OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
[OWSDisappearingMessagesConfiguration fetchOrCreateDefaultWithThreadId:message.uniqueThreadId];
BOOL changed = NO;
if (message.expiresInSeconds == 0) {
if (disappearingMessagesConfiguration.isEnabled) {
changed = YES;
DDLogWarn(@"%@ Received remote message which had no expiration set, disabling our expiration to become "
@"consistent.",
self.logTag);
disappearingMessagesConfiguration.enabled = NO;
[disappearingMessagesConfiguration save];
}
} else if (message.expiresInSeconds != disappearingMessagesConfiguration.durationSeconds) {
// Become eventually consistent in the case that the remote changed their settings at the same time.
// Also in case remote doesn't support expiring messages
OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration =
[OWSDisappearingMessagesConfiguration fetchOrCreateDefaultWithThreadId:message.uniqueThreadId
transaction:transaction];
BOOL changed = NO;
if (message.expiresInSeconds == 0) {
if (disappearingMessagesConfiguration.isEnabled) {
changed = YES;
DDLogInfo(@"%@ Received remote message with different expiration set, updating our expiration to become "
DDLogWarn(@"%@ Received remote message which had no expiration set, disabling our expiration to become "
@"consistent.",
self.logTag);
disappearingMessagesConfiguration.enabled = YES;
disappearingMessagesConfiguration.durationSeconds = message.expiresInSeconds;
[disappearingMessagesConfiguration save];
}
if (!changed) {
return;
}
if ([message isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message;
NSString *contactName = [contactsManager displayNameForPhoneIdentifier:incomingMessage.messageAuthorId];
// We want the info message to appear _before_ the message.
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp - 1
thread:message.thread
configuration:disappearingMessagesConfiguration
createdByRemoteName:contactName] save];
} else {
// We want the info message to appear _before_ the message.
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp - 1
thread:message.thread
configuration:disappearingMessagesConfiguration]
save];
self.logTag);
disappearingMessagesConfiguration.enabled = NO;
[disappearingMessagesConfiguration saveWithTransaction:transaction];
}
backgroundTask = nil;
});
} else if (message.expiresInSeconds != disappearingMessagesConfiguration.durationSeconds) {
changed = YES;
DDLogInfo(@"%@ Received remote message with different expiration set, updating our expiration to become "
@"consistent.",
self.logTag);
disappearingMessagesConfiguration.enabled = YES;
disappearingMessagesConfiguration.durationSeconds = message.expiresInSeconds;
[disappearingMessagesConfiguration saveWithTransaction:transaction];
}
if (!changed) {
return;
}
if ([message isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)message;
NSString *contactName = [contactsManager displayNameForPhoneIdentifier:incomingMessage.messageAuthorId];
// We want the info message to appear _before_ the message.
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp - 1
thread:message.thread
configuration:disappearingMessagesConfiguration
createdByRemoteName:contactName] saveWithTransaction:transaction];
} else {
// We want the info message to appear _before_ the message.
[[[OWSDisappearingConfigurationUpdateInfoMessage alloc] initWithTimestamp:message.timestamp - 1
thread:message.thread
configuration:disappearingMessagesConfiguration]
saveWithTransaction:transaction];
}
backgroundTask = nil;
}
- (void)startIfNecessary

@ -1092,7 +1092,7 @@ NS_ASSUME_NONNULL_BEGIN
BOOL shouldMarkMessageAsRead = [envelope.source isEqualToString:localNumber];
if (shouldMarkMessageAsRead) {
// Don't send a read receipt for messages sent by ourselves.
[incomingMessage markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:YES];
[incomingMessage markAsReadWithTransaction:transaction sendReadReceipt:NO];
}
TSQuotedMessage *_Nullable quotedMessage = incomingMessage.quotedMessage;
@ -1130,8 +1130,9 @@ NS_ASSUME_NONNULL_BEGIN
[OWSReadReceiptManager.sharedManager applyEarlyReadReceiptsForIncomingMessage:incomingMessage
transaction:transaction];
[OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:incomingMessage
contactsManager:self.contactsManager];
[[OWSDisappearingMessagesJob sharedJob] becomeConsistentWithConfigurationForMessage:incomingMessage
contactsManager:self.contactsManager
transaction:transaction];
// Update thread preview in inbox
[thread touchWithTransaction:transaction];

@ -84,14 +84,6 @@ NS_SWIFT_NAME(MessageSender)
success:(void (^)(void))successHandler
failure:(void (^)(NSError *error))failureHandler;
/**
* Set local configuration to match that of the of `outgoingMessage`'s sender
*
* We do this because messages and async message latency make it possible for thread participants disappearing messags
* configuration to get out of sync.
*/
- (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSOutgoingMessage *)outgoingMessage;
@end
NS_ASSUME_NONNULL_END

@ -1052,13 +1052,10 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[self sendSyncTranscriptForMessage:message];
}
[OWSDisappearingMessagesJob setExpirationForMessage:message];
}
- (void)becomeConsistentWithDisappearingConfigurationForMessage:(TSOutgoingMessage *)outgoingMessage
{
[OWSDisappearingMessagesJob becomeConsistentWithConfigurationForMessage:outgoingMessage
contactsManager:self.contactsManager];
[self.dbConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
[[OWSDisappearingMessagesJob sharedJob] startExpirationForMessage:message
transaction:transaction];
}];
}
- (void)handleSendToMyself:(TSOutgoingMessage *)outgoingMessage

@ -431,11 +431,15 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
if (!readReceipt) {
return;
}
[message markAsReadWithTransaction:transaction sendReadReceipt:NO updateExpiration:YES];
[message markAsReadWithTransaction:transaction sendReadReceipt:NO];
[readReceipt removeWithTransaction:transaction];
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:kIncomingMessageMarkedAsReadNotification
object:message];
[transaction addCompletionQueue:nil
completionBlock:^{
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:kIncomingMessageMarkedAsReadNotification
object:message];
}];
}
- (void)processReadReceiptsFromLinkedDevice:(NSArray<OWSSignalServiceProtosSyncMessageRead *> *)readReceiptProtos
@ -538,13 +542,18 @@ NSString *const OWSReadReceiptManagerAreReadReceiptsEnabled = @"areReadReceiptsE
} else {
DDLogError(@"Marking %zd messages as read by linked device.", interactions.count);
}
for (id<OWSReadTracking> possiblyRead in interactions) {
[possiblyRead markAsReadWithTransaction:transaction sendReadReceipt:wasLocal updateExpiration:YES];
[possiblyRead markAsReadWithTransaction:transaction sendReadReceipt:wasLocal];
if ([possiblyRead isKindOfClass:[TSIncomingMessage class]]) {
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)possiblyRead;
[[NSNotificationCenter defaultCenter] postNotificationNameAsync:kIncomingMessageMarkedAsReadNotification
object:incomingMessage];
[transaction addCompletionQueue:nil
completionBlock:^{
[[NSNotificationCenter defaultCenter]
postNotificationNameAsync:kIncomingMessageMarkedAsReadNotification
object:incomingMessage];
}];
}
}
}

@ -1,5 +1,5 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
//
@class YapDatabaseReadWriteTransaction;
@ -21,10 +21,9 @@
- (BOOL)shouldAffectUnreadCounts;
/**
* Used for *responding* to a remote read receipt or in response to user activity.
* Used both for *responding* to a remote read receipt and in response to the local user's activity.
*/
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration;
sendReadReceipt:(BOOL)sendReadReceipt;
@end

@ -96,7 +96,6 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
- (void)markAsReadWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
sendReadReceipt:(BOOL)sendReadReceipt
updateExpiration:(BOOL)updateExpiration
{
OWSAssert(transaction);
@ -110,7 +109,7 @@ NSUInteger TSCallCurrentSchemaVersion = 1;
[self saveWithTransaction:transaction];
[self touchThreadWithTransaction:transaction];
// Ignore sendReadReceipt and updateExpiration; they don't apply to calls.
// Ignore sendReadReceipt - it doesn't apply to calls.
}
#pragma mark - Methods

Loading…
Cancel
Save