mirror of https://github.com/oxen-io/session-ios
Merge branch 'charlesmchen/attachmentDownloadsVsBackground'
commit
40ec869072
@ -0,0 +1,43 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
typedef NS_ENUM(NSUInteger, BackgroundTaskState) {
|
||||
BackgroundTaskState_Success,
|
||||
BackgroundTaskState_CouldNotStart,
|
||||
BackgroundTaskState_Expired,
|
||||
};
|
||||
|
||||
typedef void (^BackgroundTaskCompletionBlock)(BackgroundTaskState backgroundTaskState);
|
||||
|
||||
// This class makes it easier and safer to use background tasks.
|
||||
//
|
||||
// * Uses RAII (Resource Acquisition Is Initialization) pattern.
|
||||
// * Ensures completion block is called exactly once and on main thread,
|
||||
// to facilitate handling "background task timed out" case, for example.
|
||||
// * Ensures we properly handle the "background task could not be created"
|
||||
// case.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// * Use factory method to start a background task.
|
||||
// * Retain a strong reference to the OWSBackgroundTask during the "work".
|
||||
// * Clear all references to the OWSBackgroundTask when the work is done,
|
||||
// if possible.
|
||||
@interface OWSBackgroundTask : NSObject
|
||||
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
+ (OWSBackgroundTask *)backgroundTaskWithLabelStr:(const char *)labelStr;
|
||||
|
||||
// completionBlock will be called exactly once on the main thread.
|
||||
+ (OWSBackgroundTask *)backgroundTaskWithLabelStr:(const char *)labelStr
|
||||
completionBlock:(BackgroundTaskCompletionBlock)completionBlock;
|
||||
|
||||
+ (OWSBackgroundTask *)backgroundTaskWithLabel:(NSString *)label;
|
||||
|
||||
// completionBlock will be called exactly once on the main thread.
|
||||
+ (OWSBackgroundTask *)backgroundTaskWithLabel:(NSString *)label
|
||||
completionBlock:(BackgroundTaskCompletionBlock)completionBlock;
|
||||
|
||||
@end
|
@ -0,0 +1,164 @@
|
||||
//
|
||||
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSBackgroundTask.h"
|
||||
#import "AppContext.h"
|
||||
#import "Threading.h"
|
||||
|
||||
@interface OWSBackgroundTask ()
|
||||
|
||||
@property (nonatomic, readonly) NSString *label;
|
||||
|
||||
// This property should only be accessed while synchronized on this instance.
|
||||
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTaskId;
|
||||
|
||||
// This property should only be accessed while synchronized on this instance.
|
||||
@property (nonatomic, nullable) BackgroundTaskCompletionBlock completionBlock;
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation OWSBackgroundTask
|
||||
|
||||
+ (OWSBackgroundTask *)backgroundTaskWithLabelStr:(const char *)labelStr
|
||||
{
|
||||
OWSAssert(labelStr);
|
||||
|
||||
NSString *label = [NSString stringWithFormat:@"%s", labelStr];
|
||||
return [[OWSBackgroundTask alloc] initWithLabel:label completionBlock:nil];
|
||||
}
|
||||
|
||||
+ (OWSBackgroundTask *)backgroundTaskWithLabelStr:(const char *)labelStr
|
||||
completionBlock:(BackgroundTaskCompletionBlock)completionBlock
|
||||
{
|
||||
|
||||
OWSAssert(labelStr);
|
||||
|
||||
NSString *label = [NSString stringWithFormat:@"%s", labelStr];
|
||||
return [[OWSBackgroundTask alloc] initWithLabel:label completionBlock:completionBlock];
|
||||
}
|
||||
|
||||
+ (OWSBackgroundTask *)backgroundTaskWithLabel:(NSString *)label
|
||||
{
|
||||
return [[OWSBackgroundTask alloc] initWithLabel:label completionBlock:nil];
|
||||
}
|
||||
|
||||
+ (OWSBackgroundTask *)backgroundTaskWithLabel:(NSString *)label
|
||||
completionBlock:(BackgroundTaskCompletionBlock)completionBlock
|
||||
{
|
||||
return [[OWSBackgroundTask alloc] initWithLabel:label completionBlock:completionBlock];
|
||||
}
|
||||
|
||||
- (instancetype)initWithLabel:(NSString *)label completionBlock:(BackgroundTaskCompletionBlock _Nullable)completionBlock
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
OWSAssert(label.length > 0);
|
||||
|
||||
_label = label;
|
||||
self.completionBlock = completionBlock;
|
||||
|
||||
[self startBackgroundTask];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self endBackgroundTask];
|
||||
}
|
||||
|
||||
- (void)startBackgroundTask
|
||||
{
|
||||
// beginBackgroundTaskWithExpirationHandler must be called on the main thread.
|
||||
DispatchMainThreadSafe(^{
|
||||
__weak typeof(self) weakSelf = self;
|
||||
self.backgroundTaskId = [CurrentAppContext() beginBackgroundTaskWithExpirationHandler:^{
|
||||
// Note the usage of OWSCAssert() to avoid capturing a reference to self.
|
||||
OWSCAssert([NSThread isMainThread]);
|
||||
|
||||
OWSBackgroundTask *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// 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
|
||||
{
|
||||
// Make a local copy of this state, since this method is called by `dealloc`.
|
||||
UIBackgroundTaskIdentifier backgroundTaskId;
|
||||
BackgroundTaskCompletionBlock _Nullable completionBlock;
|
||||
NSString *logTag = self.logTag;
|
||||
NSString *label = self.label;
|
||||
|
||||
@synchronized(self)
|
||||
{
|
||||
backgroundTaskId = self.backgroundTaskId;
|
||||
completionBlock = self.completionBlock;
|
||||
self.completionBlock = nil;
|
||||
}
|
||||
|
||||
if (backgroundTaskId == UIBackgroundTaskInvalid) {
|
||||
OWSAssert(!completionBlock);
|
||||
return;
|
||||
}
|
||||
|
||||
// endBackgroundTask must be called on the main thread.
|
||||
DispatchMainThreadSafe(^{
|
||||
DDLogVerbose(@"%@ %@ background task completed.", logTag, label);
|
||||
|
||||
if (completionBlock) {
|
||||
completionBlock(BackgroundTaskState_Success);
|
||||
}
|
||||
|
||||
[CurrentAppContext() endBackgroundTask:backgroundTaskId];
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue