AliyunOSSSDK/OSSTask/OSSCancellationToken.m (101 lines of code) (raw):
/*
* Copyright (c) 2014, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*/
#import "OSSCancellationToken.h"
#import "OSSCancellationTokenRegistration.h"
NS_ASSUME_NONNULL_BEGIN
@interface OSSCancellationToken ()
@property (nullable, nonatomic, strong) NSMutableArray *registrations;
@property (nonatomic, strong) NSObject *lock;
@property (nonatomic) BOOL disposed;
@end
@interface OSSCancellationTokenRegistration (OSSCancellationToken)
+ (instancetype)registrationWithToken:(OSSCancellationToken *)token delegate:(OSSCancellationBlock)delegate;
- (void)notifyDelegate;
@end
@implementation OSSCancellationToken
@synthesize cancellationRequested = _cancellationRequested;
#pragma mark - Initializer
- (instancetype)init {
self = [super init];
if (!self) return self;
_registrations = [NSMutableArray array];
_lock = [NSObject new];
return self;
}
#pragma mark - Custom Setters/Getters
- (BOOL)isCancellationRequested {
@synchronized(self.lock) {
[self throwIfDisposed];
return _cancellationRequested;
}
}
- (void)cancel {
NSArray *registrations;
@synchronized(self.lock) {
[self throwIfDisposed];
if (_cancellationRequested) {
return;
}
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancelPrivate) object:nil];
_cancellationRequested = YES;
registrations = [self.registrations copy];
}
[self notifyCancellation:registrations];
}
- (void)notifyCancellation:(NSArray *)registrations {
for (OSSCancellationTokenRegistration *registration in registrations) {
[registration notifyDelegate];
}
}
- (OSSCancellationTokenRegistration *)registerCancellationObserverWithBlock:(OSSCancellationBlock)block {
@synchronized(self.lock) {
OSSCancellationTokenRegistration *registration = [OSSCancellationTokenRegistration registrationWithToken:self delegate:[block copy]];
[self.registrations addObject:registration];
return registration;
}
}
- (void)unregisterRegistration:(OSSCancellationTokenRegistration *)registration {
@synchronized(self.lock) {
[self throwIfDisposed];
[self.registrations removeObject:registration];
}
}
// Delay on a non-public method to prevent interference with a user calling performSelector or
// cancelPreviousPerformRequestsWithTarget on the public method
- (void)cancelPrivate {
[self cancel];
}
- (void)cancelAfterDelay:(int)millis {
[self throwIfDisposed];
if (millis < -1) {
[NSException raise:NSInvalidArgumentException format:@"Delay must be >= -1"];
}
if (millis == 0) {
[self cancel];
return;
}
@synchronized(self.lock) {
[self throwIfDisposed];
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancelPrivate) object:nil];
if (self.cancellationRequested) {
return;
}
if (millis != -1) {
double delay = (double)millis / 1000;
[self performSelector:@selector(cancelPrivate) withObject:nil afterDelay:delay];
}
}
}
- (void)dispose {
@synchronized(self.lock) {
if (self.disposed) {
return;
}
[self.registrations makeObjectsPerformSelector:@selector(dispose)];
self.registrations = nil;
self.disposed = YES;
}
}
- (void)throwIfDisposed {
if (self.disposed) {
[NSException raise:NSInternalInconsistencyException format:@"Object already disposed"];
}
}
@end
NS_ASSUME_NONNULL_END