From 18a79c26eff3851c47fd1d5a73a58c53175e231e Mon Sep 17 00:00:00 2001 From: Anders Date: Wed, 23 Nov 2016 06:43:44 +0100 Subject: [PATCH] Lock-free SerialDisposable. --- Sources/Disposable.swift | 63 +++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/Sources/Disposable.swift b/Sources/Disposable.swift index f4909e277..97dcce8ee 100644 --- a/Sources/Disposable.swift +++ b/Sources/Disposable.swift @@ -281,7 +281,51 @@ extension ScopedDisposable where InnerDisposable: AnyDisposable { /// A disposable that will optionally dispose of another disposable. public final class SerialDisposable: Disposable { - private let _innerDisposable: Atomic + private struct Storage { +#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + private var pointer: UnsafeMutableRawPointer? = nil + + var disposable: Disposable? { + mutating get { + return access(set: false) + .map { Unmanaged.fromOpaque($0).takeUnretainedValue() as! Disposable } + } + } + + mutating func access(set: Bool, newValue: UnsafeMutableRawPointer? = nil) -> UnsafeMutableRawPointer? { + var current = pointer + while !OSAtomicCompareAndSwapPtrBarrier(current, set ? newValue : current, &pointer) { + current = pointer + } + return current + } + + mutating func swap(_ disposable: Disposable?) -> Disposable? { + let newPointer = disposable.map { Unmanaged.passRetained($0).toOpaque() } + let oldPointer = access(set: true, newValue: newPointer) + + return oldPointer.map { old in + let reference = Unmanaged.fromOpaque(old) + defer { reference.release() } + return reference.takeUnretainedValue() as! Disposable + } + } +#else + private let _disposable = Atomic(nil) + + var disposable: Disposable? { + mutating get { + return _disposable.value + } + } + + mutating func swap(_ disposable: Disposable?) -> Disposable? { + return _disposable.swap(disposable) + } +#endif + } + + private var storage: Storage private var state: DisposableState public var isDisposed: Bool { @@ -294,14 +338,13 @@ public final class SerialDisposable: Disposable { /// disposable is automatically disposed. public var innerDisposable: Disposable? { get { - return _innerDisposable.value + // `Storage.disposable` is weakly ordered. + return storage.disposable } set(d) { - _innerDisposable.swap(d)?.dispose() - if let d = d, isDisposed { - d.dispose() - } + storage.swap(d)?.dispose() + d.flatMap { isDisposed ? $0 : nil }?.dispose() } } @@ -311,15 +354,19 @@ public final class SerialDisposable: Disposable { /// - parameters: /// - disposable: Optional disposable. public init(_ disposable: Disposable? = nil) { - self._innerDisposable = Atomic(disposable) + self.storage = Storage() self.state = DisposableState() } public func dispose() { if state.dispose() { - _innerDisposable.swap(nil)?.dispose() + storage.swap(nil)?.dispose() } } + + deinit { + storage.swap(nil)?.dispose() + } } /// Adds the right-hand-side disposable to the left-hand-side