Skip to content

Commit ff72a1d

Browse files
committed
Eliminated possibility of Deadlocks everywhere
Queues, Stacks, and Listeners are now immune from Deadlocking. A local copy of each of the aforementioned are taken in the operative methods that could potentially cause a deadlocked reference to these collections. I've also streamlined the housekeeping for `nil` listeners. It's now performed casually each time an Event is being processed against each Listener.
1 parent 6f2eb86 commit ff72a1d

File tree

4 files changed

+30
-31
lines changed

4 files changed

+30
-31
lines changed

Sources/EventDrivenSwift/Central/EventCentral.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ final public class EventCentral: EventDispatcher, EventCentralable {
2828
- Author: Simon J. Stuart
2929
- Version: 1.0.0
3030
*/
31-
public static var shared: EventDispatchable {
31+
@inline(__always) public static var shared: EventDispatchable {
3232
get {
3333
return _shared
3434
}
3535
}
3636

37-
public static subscript() -> EventDispatchable {
37+
@inline(__always) public static subscript() -> EventDispatchable {
3838
get {
3939
return _shared
4040
}

Sources/EventDrivenSwift/EventDispatcher/EventDispatcher.swift

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ open class EventDispatcher: EventHandler, EventDispatchable {
2626
- Author: Simon J. Stuart
2727
- Version: 1.0.0
2828
*/
29-
@ThreadSafeSemaphore private var listeners = [String:[ListenerContainer]]() //TODO: Make this a Revolving Door collection!
29+
@ThreadSafeSemaphore private var listeners = [String:[ListenerContainer]]()
3030

3131
public func addListener(_ listener: any EventReceivable, forEventType: Eventable.Type) {
3232
let eventTypeName = String(reflecting: forEventType)
@@ -83,32 +83,31 @@ open class EventDispatcher: EventHandler, EventDispatchable {
8383
override internal func processEvent(_ event: any Eventable, dispatchMethod: EventDispatchMethod, priority: EventPriority) {
8484
let eventTypeName = String(reflecting: type(of: event))
8585

86+
var snapListeners = [String:[ListenerContainer]]()
87+
8688
_listeners.withLock { listeners in
87-
let bucket = listeners[eventTypeName]
88-
if bucket == nil { return } /// No Listeners, so nothing more to do!
89-
90-
var newBucket: [ListenerContainer]? = nil
89+
// We should take this opportunity to remove any nil listeners
90+
listeners[eventTypeName]?.removeAll(where: { listenerContainer in
91+
listenerContainer.listener == nil
92+
})
93+
snapListeners = listeners
94+
}
95+
96+
let bucket = snapListeners[eventTypeName]
97+
if bucket == nil { return } /// No Listeners, so nothing more to do!
98+
99+
for listener in bucket! {
100+
if listener.listener == nil { /// If the Listener is `nil`...
101+
continue
102+
}
91103

92-
for listener in bucket! {
93-
if listener.listener == nil { /// If the Listener is `nil`...
94-
if newBucket != nil { continue } /// ...If we've already removed all `nil` values, move on
95-
///... otherwise, remove the Listener from this bucket
96-
newBucket = bucket
97-
newBucket?.removeAll(where: { listenerContainer in
98-
listenerContainer.listener == nil
99-
})
100-
continue
101-
}
102-
103-
// so, we have a listener... let's deal with it!
104-
switch dispatchMethod {
105-
case .stack:
106-
listener.listener!.stackEvent(event, priority: priority)
107-
case .queue:
108-
listener.listener!.queueEvent(event, priority: priority)
109-
}
104+
// so, we have a listener... let's deal with it!
105+
switch dispatchMethod {
106+
case .stack:
107+
listener.listener!.stackEvent(event, priority: priority)
108+
case .queue:
109+
listener.listener!.queueEvent(event, priority: priority)
110110
}
111-
if newBucket != nil { listeners[eventTypeName] = newBucket }
112111
}
113112
}
114113
}

Sources/EventDrivenSwift/EventHandler/EventHandler.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ open class EventHandler: ObservableThread, EventHandlable {
107107
- Parameters:
108108
- events: The `Array` of `Eventable` objects
109109
*/
110-
private func processEvents(_ events: [any Eventable], dispatchMethod: EventDispatchMethod, priority: EventPriority) {
110+
@inline(__always) private func processEvents(_ events: [any Eventable], dispatchMethod: EventDispatchMethod, priority: EventPriority) {
111111
for event in events {
112112
processEvent(event, dispatchMethod: dispatchMethod, priority: priority)
113113
}
@@ -158,7 +158,7 @@ open class EventHandler: ObservableThread, EventHandlable {
158158
- Author: Simon J. Stuart
159159
- Version: 1.0.0
160160
*/
161-
internal func processAllEvents() {
161+
@inline(__always) internal func processAllEvents() {
162162
processEventStacks() // we process Stacks first
163163
processEventQueues() // we process Queues next
164164
}
@@ -170,8 +170,8 @@ open class EventHandler: ObservableThread, EventHandlable {
170170
*/
171171
public override func main() {
172172
while isExecuting {
173-
eventsPending.wait()
174-
processAllEvents()
173+
eventsPending.wait() // This will make the Thread effectively "sleep" until there are Events pending
174+
processAllEvents() // Once there's at least one Event waiting, we will Process it/them.
175175
}
176176
}
177177

Sources/EventDrivenSwift/EventReceiver/EventReceiver.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ open class EventReceiver: EventHandler, EventReceivable {
3737
- Version: 1.0.0
3838
- Note: We use the Qualified Type Name as the Key because Types are not Hashable in Swift
3939
*/
40-
@ThreadSafeSemaphore private var eventCallbacks = [String:EventCallback]() //TODO: Make this a Revolving Door collection!
40+
@ThreadSafeSemaphore private var eventCallbacks = [String:EventCallback]()
4141

4242
/**
4343
Invoke the appropriate Callback for the given Event

0 commit comments

Comments
 (0)