Skip to content

Commit

Permalink
Lock-free SerialDisposable.
Browse files Browse the repository at this point in the history
  • Loading branch information
andersio committed Nov 23, 2016
1 parent 71b2d27 commit 18a79c2
Showing 1 changed file with 55 additions and 8 deletions.
63 changes: 55 additions & 8 deletions Sources/Disposable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Disposable?>
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<AnyObject>.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<AnyObject>.passRetained($0).toOpaque() }
let oldPointer = access(set: true, newValue: newPointer)

return oldPointer.map { old in
let reference = Unmanaged<AnyObject>.fromOpaque(old)
defer { reference.release() }
return reference.takeUnretainedValue() as! Disposable
}
}
#else
private let _disposable = Atomic<Disposable?>(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 {
Expand All @@ -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()
}
}

Expand All @@ -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
Expand Down

0 comments on commit 18a79c2

Please sign in to comment.