From 3598cc18fa3138ab2469f2c9c1d38d0267add9e2 Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Tue, 13 Jun 2017 15:08:27 -0400 Subject: [PATCH] Ensure message sends only succeed or fail once. // FREEBIE --- src/Messages/OWSMessageSender.m | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Messages/OWSMessageSender.m b/src/Messages/OWSMessageSender.m index 6c812b460..3e3ae93df 100644 --- a/src/Messages/OWSMessageSender.m +++ b/src/Messages/OWSMessageSender.m @@ -159,6 +159,7 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4; @property (nonatomic, readonly) void (^failureHandler)(NSError *_Nonnull error); @property (nonatomic) OWSSendMessageOperationState operationState; @property (nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier; +@property (nonatomic) BOOL hasCompleted; @end @@ -190,6 +191,13 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4; return; } + // Ensure we call the success or failure handler exactly once. + @synchronized(strongSelf) + { + OWSCAssert(!strongSelf.hasCompleted); + strongSelf.hasCompleted = YES; + } + [message updateWithMessageState:TSOutgoingMessageStateSentToService]; DDLogDebug(@"%@ succeeded.", strongSelf.tag); @@ -204,6 +212,13 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4; return; } + // Ensure we call the success or failure handler exactly once. + @synchronized(strongSelf) + { + OWSCAssert(!strongSelf.hasCompleted); + strongSelf.hasCompleted = YES; + } + [strongSelf.message updateWithSendingError:error]; DDLogDebug(@"%@ failed with error: %@", strongSelf.tag, error); @@ -281,9 +296,14 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4; { DDLogDebug(@"%@ remainingRetries: %lu", self.tag, (unsigned long)remainingRetries); + // Use this flag to ensure a given operation only succeeds or fails once. + __block BOOL onceFlag = NO; RetryableFailureHandler retryableFailureHandler = ^(NSError *_Nonnull error) { DDLogInfo(@"%@ Sending failed.", self.tag); + OWSAssert(!onceFlag); + onceFlag = YES; + if (![error isRetryable] || [error isFatal]) { DDLogInfo(@"%@ Skipping retry due to terminal error: %@", self.tag, error); self.failureHandler(error); @@ -299,7 +319,14 @@ NSUInteger const OWSSendMessageOperationMaxRetries = 4; } }; - [self.messageSender attemptToSendMessage:self.message success:self.successHandler failure:retryableFailureHandler]; + [self.messageSender attemptToSendMessage:self.message + success:^{ + OWSAssert(!onceFlag); + onceFlag = YES; + + self.successHandler(); + } + failure:retryableFailureHandler]; } - (void)markAsComplete @@ -762,6 +789,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException"; // retryable errors. if ([error isFatal]) { failureHandler(error); + return; } if ([error isRetryable] && !firstRetryableError) {