| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288 |
- //
- // Atomic.swift
- // ReactiveSwift
- //
- // Created by Justin Spahr-Summers on 2014-06-10.
- // Copyright (c) 2014 GitHub. All rights reserved.
- //
- import Foundation
- #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
- import MachO
- #endif
- /// Represents a finite state machine that can transit from one state to
- /// another.
- internal protocol AtomicStateProtocol {
- associatedtype State: RawRepresentable
- /// Try to transit from the expected current state to the specified next
- /// state.
- ///
- /// - parameters:
- /// - expected: The expected state.
- ///
- /// - returns:
- /// `true` if the transition succeeds. `false` otherwise.
- func tryTransiting(from expected: State, to next: State) -> Bool
- }
- /// A simple, generic lock-free finite state machine.
- ///
- /// - warning: `deinitialize` must be called to dispose of the consumed memory.
- internal struct UnsafeAtomicState<State: RawRepresentable>: AtomicStateProtocol where State.RawValue == Int32 {
- internal typealias Transition = (expected: State, next: State)
- #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
- private let value: UnsafeMutablePointer<Int32>
- /// Create a finite state machine with the specified initial state.
- ///
- /// - parameters:
- /// - initial: The desired initial state.
- internal init(_ initial: State) {
- value = UnsafeMutablePointer<Int32>.allocate(capacity: 1)
- value.initialize(to: initial.rawValue)
- }
- /// Deinitialize the finite state machine.
- internal func deinitialize() {
- value.deinitialize()
- value.deallocate(capacity: 1)
- }
- /// Compare the current state with the specified state.
- ///
- /// - parameters:
- /// - expected: The expected state.
- ///
- /// - returns:
- /// `true` if the current state matches the expected state. `false`
- /// otherwise.
- @inline(__always)
- internal func `is`(_ expected: State) -> Bool {
- return OSAtomicCompareAndSwap32Barrier(expected.rawValue,
- expected.rawValue,
- value)
- }
- /// Try to transit from the expected current state to the specified next
- /// state.
- ///
- /// - parameters:
- /// - expected: The expected state.
- ///
- /// - returns:
- /// `true` if the transition succeeds. `false` otherwise.
- @inline(__always)
- internal func tryTransiting(from expected: State, to next: State) -> Bool {
- return OSAtomicCompareAndSwap32Barrier(expected.rawValue,
- next.rawValue,
- value)
- }
- #else
- private let value: Atomic<Int32>
- /// Create a finite state machine with the specified initial state.
- ///
- /// - parameters:
- /// - initial: The desired initial state.
- internal init(_ initial: State) {
- value = Atomic(initial.rawValue)
- }
- /// Deinitialize the finite state machine.
- internal func deinitialize() {}
- /// Compare the current state with the specified state.
- ///
- /// - parameters:
- /// - expected: The expected state.
- ///
- /// - returns:
- /// `true` if the current state matches the expected state. `false`
- /// otherwise.
- internal func `is`(_ expected: State) -> Bool {
- return value.modify { $0 == expected.rawValue }
- }
- /// Try to transit from the expected current state to the specified next
- /// state.
- ///
- /// - parameters:
- /// - expected: The expected state.
- ///
- /// - returns:
- /// `true` if the transition succeeds. `false` otherwise.
- internal func tryTransiting(from expected: State, to next: State) -> Bool {
- return value.modify { value in
- if value == expected.rawValue {
- value = next.rawValue
- return true
- }
- return false
- }
- }
- #endif
- }
- final class PosixThreadMutex: NSLocking {
- private var mutex = pthread_mutex_t()
- init() {
- let result = pthread_mutex_init(&mutex, nil)
- precondition(result == 0, "Failed to initialize mutex with error \(result).")
- }
- deinit {
- let result = pthread_mutex_destroy(&mutex)
- precondition(result == 0, "Failed to destroy mutex with error \(result).")
- }
- func lock() {
- let result = pthread_mutex_lock(&mutex)
- precondition(result == 0, "Failed to lock \(self) with error \(result).")
- }
- func unlock() {
- let result = pthread_mutex_unlock(&mutex)
- precondition(result == 0, "Failed to unlock \(self) with error \(result).")
- }
- }
- /// An atomic variable.
- public final class Atomic<Value>: AtomicProtocol {
- private let lock: PosixThreadMutex
- private var _value: Value
- /// Initialize the variable with the given initial value.
- ///
- /// - parameters:
- /// - value: Initial value for `self`.
- public init(_ value: Value) {
- _value = value
- lock = PosixThreadMutex()
- }
- /// Atomically modifies the variable.
- ///
- /// - parameters:
- /// - action: A closure that takes the current value.
- ///
- /// - returns: The result of the action.
- @discardableResult
- public func modify<Result>(_ action: (inout Value) throws -> Result) rethrows -> Result {
- lock.lock()
- defer { lock.unlock() }
- return try action(&_value)
- }
-
- /// Atomically perform an arbitrary action using the current value of the
- /// variable.
- ///
- /// - parameters:
- /// - action: A closure that takes the current value.
- ///
- /// - returns: The result of the action.
- @discardableResult
- public func withValue<Result>(_ action: (Value) throws -> Result) rethrows -> Result {
- lock.lock()
- defer { lock.unlock() }
- return try action(_value)
- }
- }
- /// An atomic variable which uses a recursive lock.
- internal final class RecursiveAtomic<Value>: AtomicProtocol {
- private let lock: NSRecursiveLock
- private var _value: Value
- private let didSetObserver: ((Value) -> Void)?
- /// Initialize the variable with the given initial value.
- ///
- /// - parameters:
- /// - value: Initial value for `self`.
- /// - name: An optional name used to create the recursive lock.
- /// - action: An optional closure which would be invoked every time the
- /// value of `self` is mutated.
- internal init(_ value: Value, name: StaticString? = nil, didSet action: ((Value) -> Void)? = nil) {
- _value = value
- lock = NSRecursiveLock()
- lock.name = name.map(String.init(describing:))
- didSetObserver = action
- }
- /// Atomically modifies the variable.
- ///
- /// - parameters:
- /// - action: A closure that takes the current value.
- ///
- /// - returns: The result of the action.
- @discardableResult
- func modify<Result>(_ action: (inout Value) throws -> Result) rethrows -> Result {
- lock.lock()
- defer {
- didSetObserver?(_value)
- lock.unlock()
- }
- return try action(&_value)
- }
-
- /// Atomically perform an arbitrary action using the current value of the
- /// variable.
- ///
- /// - parameters:
- /// - action: A closure that takes the current value.
- ///
- /// - returns: The result of the action.
- @discardableResult
- func withValue<Result>(_ action: (Value) throws -> Result) rethrows -> Result {
- lock.lock()
- defer { lock.unlock() }
- return try action(_value)
- }
- }
- /// A protocol used to constraint convenience `Atomic` methods and properties.
- public protocol AtomicProtocol: class {
- associatedtype Value
- @discardableResult
- func withValue<Result>(_ action: (Value) throws -> Result) rethrows -> Result
- @discardableResult
- func modify<Result>(_ action: (inout Value) throws -> Result) rethrows -> Result
- }
- extension AtomicProtocol {
- /// Atomically get or set the value of the variable.
- public var value: Value {
- get {
- return withValue { $0 }
- }
-
- set(newValue) {
- swap(newValue)
- }
- }
- /// Atomically replace the contents of the variable.
- ///
- /// - parameters:
- /// - newValue: A new value for the variable.
- ///
- /// - returns: The old value.
- @discardableResult
- public func swap(_ newValue: Value) -> Value {
- return modify { (value: inout Value) in
- let oldValue = value
- value = newValue
- return oldValue
- }
- }
- }
|