OSSCancellationToken.m 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * Copyright (c) 2014, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. *
  9. */
  10. #import "OSSCancellationToken.h"
  11. #import "OSSCancellationTokenRegistration.h"
  12. NS_ASSUME_NONNULL_BEGIN
  13. @interface OSSCancellationToken ()
  14. @property (nullable, nonatomic, strong) NSMutableArray *registrations;
  15. @property (nonatomic, strong) NSObject *lock;
  16. @property (nonatomic) BOOL disposed;
  17. @end
  18. @interface OSSCancellationTokenRegistration (OSSCancellationToken)
  19. + (instancetype)registrationWithToken:(OSSCancellationToken *)token delegate:(OSSCancellationBlock)delegate;
  20. - (void)notifyDelegate;
  21. @end
  22. @implementation OSSCancellationToken
  23. @synthesize cancellationRequested = _cancellationRequested;
  24. #pragma mark - Initializer
  25. - (instancetype)init {
  26. self = [super init];
  27. if (!self) return self;
  28. _registrations = [NSMutableArray array];
  29. _lock = [NSObject new];
  30. return self;
  31. }
  32. #pragma mark - Custom Setters/Getters
  33. - (BOOL)isCancellationRequested {
  34. @synchronized(self.lock) {
  35. [self throwIfDisposed];
  36. return _cancellationRequested;
  37. }
  38. }
  39. - (void)cancel {
  40. NSArray *registrations;
  41. @synchronized(self.lock) {
  42. [self throwIfDisposed];
  43. if (_cancellationRequested) {
  44. return;
  45. }
  46. [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancelPrivate) object:nil];
  47. _cancellationRequested = YES;
  48. registrations = [self.registrations copy];
  49. }
  50. [self notifyCancellation:registrations];
  51. }
  52. - (void)notifyCancellation:(NSArray *)registrations {
  53. for (OSSCancellationTokenRegistration *registration in registrations) {
  54. [registration notifyDelegate];
  55. }
  56. }
  57. - (OSSCancellationTokenRegistration *)registerCancellationObserverWithBlock:(OSSCancellationBlock)block {
  58. @synchronized(self.lock) {
  59. OSSCancellationTokenRegistration *registration = [OSSCancellationTokenRegistration registrationWithToken:self delegate:[block copy]];
  60. [self.registrations addObject:registration];
  61. return registration;
  62. }
  63. }
  64. - (void)unregisterRegistration:(OSSCancellationTokenRegistration *)registration {
  65. @synchronized(self.lock) {
  66. [self throwIfDisposed];
  67. [self.registrations removeObject:registration];
  68. }
  69. }
  70. // Delay on a non-public method to prevent interference with a user calling performSelector or
  71. // cancelPreviousPerformRequestsWithTarget on the public method
  72. - (void)cancelPrivate {
  73. [self cancel];
  74. }
  75. - (void)cancelAfterDelay:(int)millis {
  76. [self throwIfDisposed];
  77. if (millis < -1) {
  78. [NSException raise:NSInvalidArgumentException format:@"Delay must be >= -1"];
  79. }
  80. if (millis == 0) {
  81. [self cancel];
  82. return;
  83. }
  84. @synchronized(self.lock) {
  85. [self throwIfDisposed];
  86. [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(cancelPrivate) object:nil];
  87. if (self.cancellationRequested) {
  88. return;
  89. }
  90. if (millis != -1) {
  91. double delay = (double)millis / 1000;
  92. [self performSelector:@selector(cancelPrivate) withObject:nil afterDelay:delay];
  93. }
  94. }
  95. }
  96. - (void)dispose {
  97. @synchronized(self.lock) {
  98. if (self.disposed) {
  99. return;
  100. }
  101. [self.registrations makeObjectsPerformSelector:@selector(dispose)];
  102. self.registrations = nil;
  103. self.disposed = YES;
  104. }
  105. }
  106. - (void)throwIfDisposed {
  107. if (self.disposed) {
  108. [NSException raise:NSInternalInconsistencyException format:@"Object already disposed"];
  109. }
  110. }
  111. @end
  112. NS_ASSUME_NONNULL_END