13
13
#if !$Embedded
14
14
15
15
import Swift
16
+ import Synchronization
16
17
17
18
// We can't import Dispatch from here, sadly, because it apparently has a
18
19
// transitive dependency on Combine (which in turn depends on _Concurrency).
@@ -24,11 +25,28 @@ import Swift
24
25
// .. Main Executor ............................................................
25
26
26
27
@available ( SwiftStdlib 6 . 2 , * )
28
+ @safe
27
29
public class DispatchMainExecutor : RunLoopExecutor , @unchecked Sendable {
28
30
var threaded = false
29
31
32
+ @unsafe
33
+ struct EventTable {
34
+ var nextEvent : Int = 0
35
+ var events : [ Int : OpaquePointer ] = unsafe [ : ]
36
+ }
37
+
38
+ let eventTable = unsafe Mutex< EventTable > ( EventTable ( ) )
39
+
30
40
public init ( ) { }
31
41
42
+ deinit {
43
+ eventTable. withLock { table in
44
+ for (ident, source) in table. events {
45
+ _destroyDispatchEvent ( source)
46
+ }
47
+ }
48
+ }
49
+
32
50
public func run( ) throws {
33
51
if threaded {
34
52
fatalError ( " DispatchMainExecutor does not support recursion " )
@@ -88,23 +106,35 @@ extension DispatchMainExecutor: EventableExecutor {
88
106
handler: @escaping @Sendable ( ) -> ( )
89
107
) -> ExecutorEvent {
90
108
let source = unsafe _createDispatchEvent ( handler: handler)
109
+ let eventId = unsafe eventTable. withLock { table in
110
+ let eventId = table. nextEvent
111
+ table. nextEvent += 1
112
+ table. events [ eventId] = unsafe source
113
+ return eventId
114
+ }
91
115
92
- // Stash the pointer in the id of the ExecutorEvent struct
93
- let eventId = unsafe unsafeBitCast( source, to: Int . self)
94
116
return ExecutorEvent ( id: eventId)
95
117
}
96
118
97
119
/// Deregister the given event.
98
120
public func deregister( event: ExecutorEvent ) {
99
- // Extract the source and cancel it
100
- let source = unsafe unsafeBitCast( event. id, to: OpaquePointer . self)
121
+ guard let source = unsafe eventTable. withLock (
122
+ { unsafe $0. events . removeValue ( forKey: event. id) }
123
+ ) else {
124
+ return
125
+ }
126
+
101
127
unsafe _destroyDispatchEvent ( source)
102
128
}
103
129
104
130
/// Notify the executor of an event.
105
131
public func notify( event: ExecutorEvent ) {
106
- // Extract the source, but don't release it
107
- let source = unsafe unsafeBitCast( event. id, to: OpaquePointer . self)
132
+ guard let source = unsafe eventTable. withLock (
133
+ { unsafe $0. events [ event. id] }
134
+ ) else {
135
+ return
136
+ }
137
+
108
138
unsafe _signalDispatchEvent ( source)
109
139
}
110
140
0 commit comments