Skip to content

Commit

Permalink
1.0.5 - Observer Locking Bug Fixed
Browse files Browse the repository at this point in the history
Fixe for `ObservableThread` and `ObservableThreadSafeClass` whereby items in the pending queues would never be removed, and would never be added due to a foolish mistake of mine.
  • Loading branch information
LK-Simon committed Jul 13, 2022
1 parent 69e9f20 commit 4c981de
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 16 deletions.
31 changes: 23 additions & 8 deletions Sources/Observable/ObservableThread.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,17 @@ open class ObservableThread: Thread, Observable, ObservableObject {
private var observerRemoveLock = DispatchSemaphore(value: 1)

public func addObserver<TObservationProtocol: AnyObject>(_ observer: TObservationProtocol) {
let lock = observerLock.wait(timeout: DispatchTime.now().advanced(by: DispatchTimeInterval.milliseconds(10))) == .success ? observerLock : observerAddLock
var collection = ObjectIdentifier(lock) == ObjectIdentifier(observerLock) ? observers : observersAddQueue
collection[ObjectIdentifier(observer)] = ObserverContainer(observer: observer, dispatchQueue: OperationQueue.current?.underlyingQueue)
lock.signal()
let observation = ObserverContainer(observer: observer, dispatchQueue: OperationQueue.current?.underlyingQueue)

if observerLock.wait(timeout: DispatchTime.now().advanced(by: DispatchTimeInterval.milliseconds(10))) == .success {
observers[ObjectIdentifier(observer)] = observation
observerLock.signal()
}
else {
observerAddLock.wait()
observersAddQueue[ObjectIdentifier(observer)] = observation
observerAddLock.signal()
}
}

public func removeObserver<TObservationProtocol: AnyObject>(_ observer: TObservationProtocol) {
Expand Down Expand Up @@ -105,15 +112,23 @@ open class ObservableThread: Thread, Observable, ObservableObject {
}

self.observerAddLock.wait() // Lock the Add Queue
let observersToAdd = self.observersAddQueue
observersAddQueue.removeAll()
self.observerAddLock.signal() // Release the Add Queue Lock

self.observerRemoveLock.wait() // Lock the Remove Queue
for (id, observation) in observersAddQueue { // Add all of the Queued Observers
let observersToRemove = self.observersRemoveQueue
observersRemoveQueue.removeAll()
self.observerRemoveLock.signal() // Release the Remove Queue Lock

// Add all of the Queued Observers
for (id, observation) in observersToAdd {
observers[id] = observation
}
for (id) in observersRemoveQueue.keys { // Remove all of the Queued Observers
// Remove all of the Queued Observers
for (id) in observersToRemove.keys {
observers.removeValue(forKey: id)
}
self.observerAddLock.signal() // Release the Add Queue Lock
self.observerRemoveLock.signal() // Release the Remove Queue Lock
self.observerLock.signal()
}

Expand Down
31 changes: 23 additions & 8 deletions Sources/Observable/ObservableThreadSafeClass.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,17 @@ open class ObservableThreadSafeClass: Observable, ObservableObject {
private var observerRemoveLock = DispatchSemaphore(value: 1)

public func addObserver<TObservationProtocol: AnyObject>(_ observer: TObservationProtocol) {
let lock = observerLock.wait(timeout: DispatchTime.now().advanced(by: DispatchTimeInterval.milliseconds(10))) == .success ? observerLock : observerAddLock
var collection = ObjectIdentifier(lock) == ObjectIdentifier(observerLock) ? observers : observersAddQueue
collection[ObjectIdentifier(observer)] = ObserverContainer(observer: observer, dispatchQueue: OperationQueue.current?.underlyingQueue)
lock.signal()
let observation = ObserverContainer(observer: observer, dispatchQueue: OperationQueue.current?.underlyingQueue)

if observerLock.wait(timeout: DispatchTime.now().advanced(by: DispatchTimeInterval.milliseconds(10))) == .success {
observers[ObjectIdentifier(observer)] = observation
observerLock.signal()
}
else {
observerAddLock.wait()
observersAddQueue[ObjectIdentifier(observer)] = observation
observerAddLock.signal()
}
}

public func removeObserver<TObservationProtocol: AnyObject>(_ observer: TObservationProtocol) {
Expand Down Expand Up @@ -105,15 +112,23 @@ open class ObservableThreadSafeClass: Observable, ObservableObject {
}

self.observerAddLock.wait() // Lock the Add Queue
let observersToAdd = self.observersAddQueue
observersAddQueue.removeAll()
self.observerAddLock.signal() // Release the Add Queue Lock

self.observerRemoveLock.wait() // Lock the Remove Queue
for (id, observation) in observersAddQueue { // Add all of the Queued Observers
let observersToRemove = self.observersRemoveQueue
observersRemoveQueue.removeAll()
self.observerRemoveLock.signal() // Release the Remove Queue Lock

// Add all of the Queued Observers
for (id, observation) in observersToAdd {
observers[id] = observation
}
for (id) in observersRemoveQueue.keys { // Remove all of the Queued Observers
// Remove all of the Queued Observers
for (id) in observersToRemove.keys {
observers.removeValue(forKey: id)
}
self.observerAddLock.signal() // Release the Add Queue Lock
self.observerRemoveLock.signal() // Release the Remove Queue Lock
self.observerLock.signal()
}

Expand Down

0 comments on commit 4c981de

Please sign in to comment.