Merge branch 'mkirk/nsoperation-sending-queue'

pull/1/head
Michael Kirk 8 years ago
commit 15f5c078a1

@ -41,6 +41,164 @@
NS_ASSUME_NONNULL_BEGIN
/**
* OWSSendMessageOperation encapsulates all the work associated with sending a message, e.g. uploading attachments,
* getting proper keys, and retrying upon failure.
*
* Used by `OWSMessageSender` to serialize message sending, ensuring that messages are emitted in the order they
* were sent.
*/
@interface OWSSendMessageOperation : NSOperation
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithMessage:(TSOutgoingMessage *)message
messageSender:(OWSMessageSender *)messageSender
success:(void (^)())successHandler
failure:(void (^)(NSError *_Nonnull error))failureHandler NS_DESIGNATED_INITIALIZER;
@end
typedef NS_ENUM(NSInteger, OWSSendMessageOperationState) {
OWSSendMessageOperationStateNew,
OWSSendMessageOperationStateExecuting,
OWSSendMessageOperationStateFinished
};
@interface OWSMessageSender (OWSSendMessageOperation)
- (void)attemptToSendMessage:(TSOutgoingMessage *)message
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler;
@end
NSString *const OWSSendMessageOperationKeyIsExecuting = @"isExecuting";
NSString *const OWSSendMessageOperationKeyIsFinished = @"isFinished";
NSUInteger const OWSSendMessageOperationMaxRetries = 4;
@interface OWSSendMessageOperation ()
@property (nonatomic, readonly) TSOutgoingMessage *message;
@property (nonatomic, readonly) OWSMessageSender *messageSender;
@property (nonatomic, readonly) void (^successHandler)();
@property (nonatomic, readonly) void (^failureHandler)(NSError *_Nonnull error);
@property (atomic) OWSSendMessageOperationState operationState;
@end
@implementation OWSSendMessageOperation
- (instancetype)initWithMessage:(TSOutgoingMessage *)message
messageSender:(OWSMessageSender *)messageSender
success:(void (^)())aSuccessHandler
failure:(void (^)(NSError *_Nonnull error))aFailureHandler
{
self = [super init];
if (!self) {
return self;
}
_operationState = OWSSendMessageOperationStateNew;
_message = message;
_messageSender = messageSender;
__weak typeof(self) weakSelf = self;
_successHandler = ^{
typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
OWSAssert(NO);
return;
}
DDLogDebug(@"%@ succeeded.", strongSelf.tag);
aSuccessHandler();
[strongSelf markAsComplete];
};
_failureHandler = ^(NSError *_Nonnull error) {
typeof(self) strongSelf = weakSelf;
if (!strongSelf) {
OWSAssert(NO);
return;
}
DDLogDebug(@"%@ failed with error: %@", strongSelf.tag, error);
aFailureHandler(error);
[strongSelf markAsComplete];
};
return self;
}
#pragma mark - NSOperation overrides
- (BOOL)isExecuting
{
return self.operationState == OWSSendMessageOperationStateExecuting;
}
- (BOOL)isFinished
{
return self.operationState == OWSSendMessageOperationStateFinished;
}
- (void)start
{
[self willChangeValueForKey:OWSSendMessageOperationKeyIsExecuting];
self.operationState = OWSSendMessageOperationStateExecuting;
[self didChangeValueForKey:OWSSendMessageOperationKeyIsExecuting];
[self main];
}
- (void)main
{
[self tryWithRemainingRetries:OWSSendMessageOperationMaxRetries];
}
#pragma mark - methods
- (void)tryWithRemainingRetries:(NSUInteger)remainingRetries
{
DDLogDebug(@"%@ remainingRetries: %lu", self.tag, remainingRetries);
void (^retryableFailureHandler)(NSError *_Nonnull) = ^(NSError *_Nonnull error) {
DDLogInfo(@"%@ Sending failed.", self.tag);
if (remainingRetries > 0) {
[self tryWithRemainingRetries:remainingRetries - 1];
} else {
DDLogWarn(@"%@ Too many failures. Giving up sending.", self.tag);
self.failureHandler(error);
}
};
[self.messageSender attemptToSendMessage:self.message success:self.successHandler failure:retryableFailureHandler];
}
- (void)markAsComplete
{
[self willChangeValueForKey:OWSSendMessageOperationKeyIsExecuting];
[self willChangeValueForKey:OWSSendMessageOperationKeyIsFinished];
self.operationState = OWSSendMessageOperationStateFinished;
[self didChangeValueForKey:OWSSendMessageOperationKeyIsExecuting];
[self didChangeValueForKey:OWSSendMessageOperationKeyIsFinished];
}
#pragma mark - Logging
+ (NSString *)tag
{
return [NSString stringWithFormat:@"[%@]", self.class];
}
- (NSString *)tag
{
return self.class.tag;
}
@end
int const OWSMessageSenderRetryAttempts = 3;
NSString *const OWSMessageSenderInvalidDeviceException = @"InvalidDeviceException";
NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
@ -54,6 +212,7 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
@property (nonatomic, readonly) id<ContactsManagerProtocol> contactsManager;
@property (nonatomic, readonly) ContactsUpdater *contactsUpdater;
@property (nonatomic, readonly) OWSDisappearingMessagesJob *disappearingMessagesJob;
@property (nonatomic, readonly) NSOperationQueue *sendingQueue;
@end
@ -73,6 +232,9 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
_storageManager = storageManager;
_contactsManager = contactsManager;
_contactsUpdater = contactsUpdater;
_sendingQueue = [NSOperationQueue new];
_sendingQueue.qualityOfService = NSOperationQualityOfServiceUserInitiated;
_sendingQueue.maxConcurrentOperationCount = 1;
_uploadingService = [[OWSUploadingService alloc] initWithNetworkManager:networkManager];
_dbConnection = storageManager.newDatabaseConnection;
@ -84,6 +246,18 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
- (void)sendMessage:(TSOutgoingMessage *)message
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler
{
[self saveMessage:message withState:TSOutgoingMessageStateAttemptingOut];
OWSSendMessageOperation *sendMessageOperation = [[OWSSendMessageOperation alloc] initWithMessage:message
messageSender:self
success:successHandler
failure:failureHandler];
[self.sendingQueue addOperation:sendMessageOperation];
}
- (void)attemptToSendMessage:(TSOutgoingMessage *)message
success:(void (^)())successHandler
failure:(void (^)(NSError *error))failureHandler
{
DDLogDebug(@"%@ sending message: %@", self.tag, message.debugDescription);
void (^markAndFailureHandler)(NSError *error) = ^(NSError *error) {
@ -167,8 +341,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
[attachmentStream save];
[message.attachmentIds addObject:attachmentStream.uniqueId];
message.messageState = TSOutgoingMessageStateAttemptingOut;
[message save];
[self sendMessage:message success:successHandler failure:failureHandler];
@ -266,9 +438,6 @@ NSString *const OWSMessageSenderRateLimitedException = @"RateLimitedException";
|| [message isKindOfClass:[OWSOutgoingSyncMessage class]]) {
TSContactThread *contactThread = (TSContactThread *)thread;
[self saveMessage:message withState:TSOutgoingMessageStateAttemptingOut];
if ([contactThread.contactIdentifier isEqualToString:self.storageManager.localNumber]
&& ![message isKindOfClass:[OWSOutgoingSyncMessage class]]) {

@ -41,9 +41,6 @@ NSString *const kAttachmentUploadAttachmentIDKey = @"kAttachmentUploadAttachment
success:(void (^)())successHandler
failure:(void (^)(NSError *_Nonnull))failureHandler
{
outgoingMessage.messageState = TSOutgoingMessageStateAttemptingOut;
[outgoingMessage save];
if (attachmentStream.serverId) {
DDLogDebug(@"%@ Attachment previously uploaded.", self.tag);
successHandler(outgoingMessage);

Loading…
Cancel
Save