/*
|
* 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
|