Last active
November 15, 2018 19:23
-
-
Save kean/f5ff6412c42398b2bea0d468d3925dc0 to your computer and use it in GitHub Desktop.
CancellationToken
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MARK: - CancellationTokenSource | |
/// Manages cancellation tokens and signals them when cancellation is requested. | |
/// | |
/// All `CancellationTokenSource` methods are thread safe. | |
final class CancellationTokenSource { | |
/// Returns `true` if cancellation has been requested. | |
var isCancelling: Bool { | |
_lock.lock(); defer { _lock.unlock() } | |
return _observers == nil | |
} | |
/// Creates a new token associated with the source. | |
var token: CancellationToken { | |
return CancellationToken(source: self) | |
} | |
private var _observers: ContiguousArray<() -> Void>? = [] | |
/// Initializes the `CancellationTokenSource` instance. | |
init() {} | |
fileprivate func register(_ closure: @escaping () -> Void) { | |
if !_register(closure) { | |
closure() | |
} | |
} | |
private func _register(_ closure: @escaping () -> Void) -> Bool { | |
_lock.lock(); defer { _lock.unlock() } | |
_observers?.append(closure) | |
return _observers != nil | |
} | |
/// Communicates a request for cancellation to the managed tokens. | |
func cancel() { | |
if let observers = _cancel() { | |
observers.forEach { $0() } | |
} | |
} | |
private func _cancel() -> ContiguousArray<() -> Void>? { | |
_lock.lock(); defer { _lock.unlock() } | |
let observers = _observers | |
_observers = nil // transition to `isCancelling` state | |
return observers | |
} | |
} | |
// We use the same lock across different tokens because the design of CTS | |
// prevents potential issues. For example, closures registered with a token | |
// are never executed inside a lock. | |
private let _lock = NSLock() | |
/// Enables cooperative cancellation of operations. | |
/// | |
/// You create a cancellation token by instantiating a `CancellationTokenSource` | |
/// object and calling its `token` property. You then pass the token to any | |
/// number of threads, tasks, or operations that should receive notice of | |
/// cancellation. When the owning object calls `cancel()`, the `isCancelling` | |
/// property on every copy of the cancellation token is set to `true`. | |
/// The registered objects can respond in whatever manner is appropriate. | |
/// | |
/// All `CancellationToken` methods are thread safe. | |
struct CancellationToken { | |
fileprivate let source: CancellationTokenSource? // no-op when `nil` | |
/// Returns `true` if cancellation has been requested for this token. | |
/// Returns `false` if the source was deallocated. | |
var isCancelling: Bool { | |
return source?.isCancelling ?? false | |
} | |
/// Registers the closure that will be called when the token is canceled. | |
/// If this token is already cancelled, the closure will be run immediately | |
/// and synchronously. | |
func register(_ closure: @escaping () -> Void) { | |
source?.register(closure) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment