@@ -15,7 +15,9 @@ import Swift
15
15
/// A service that can execute jobs.
16
16
@available ( SwiftStdlib 5 . 1 , * )
17
17
public protocol Executor : AnyObject , Sendable {
18
+
18
19
@available ( * , deprecated, message: " Implement 'enqueue(_: __owned Job)' instead " )
20
+ @available ( SwiftStdlib 5 . 1 , * )
19
21
func enqueue( _ job: UnownedJob )
20
22
21
23
@available ( SwiftStdlib 5 . 9 , * )
@@ -43,13 +45,14 @@ public protocol SerialExecutor: Executor {
43
45
44
46
/// Convert this executor value to the optimized form of borrowed
45
47
/// executor references.
48
+ @available ( SwiftStdlib 5 . 9 , * )
46
49
func asUnownedSerialExecutor( ) -> UnownedSerialExecutor
47
50
}
48
51
49
52
@available ( SwiftStdlib 5 . 9 , * )
50
53
extension Executor {
51
54
public func enqueue( _ job: UnownedJob ) {
52
- fatalError ( " Please implement \( Self . self) .enqueueJob(_:) " )
55
+ self . enqueue ( Job ( job ) )
53
56
}
54
57
55
58
public func enqueue( _ job: __owned Job) {
@@ -59,6 +62,7 @@ extension Executor {
59
62
60
63
@available ( SwiftStdlib 5 . 9 , * )
61
64
extension SerialExecutor {
65
+ @available ( SwiftStdlib 5 . 9 , * )
62
66
public func asUnownedSerialExecutor( ) -> UnownedSerialExecutor {
63
67
UnownedSerialExecutor ( ordinary: self )
64
68
}
@@ -107,18 +111,6 @@ public struct UnownedSerialExecutor: Sendable {
107
111
108
112
}
109
113
110
- /// Checks if the current task is running on the expected executor.
111
- ///
112
- /// Generally, Swift programs should be constructed such that it is statically
113
- /// known that a specific executor is used, for example by using global actors or
114
- /// custom executors. However, in some APIs it may be useful to provide an
115
- /// additional runtime check for this, especially when moving towards Swift
116
- /// concurrency from other runtimes which frequently use such assertions.
117
- /// - Parameter executor: The expected executor.
118
- @available ( SwiftStdlib 5 . 9 , * )
119
- @_silgen_name ( " swift_task_isOnExecutor " )
120
- public func _taskIsOnExecutor< Executor: SerialExecutor > ( _ executor: Executor ) -> Bool
121
-
122
114
/// Primarily a debug utility.
123
115
///
124
116
/// If the passed in Job is a Task, returns the complete 64bit TaskId,
@@ -135,28 +127,12 @@ internal func _getJobTaskId(_ job: UnownedJob) -> UInt64
135
127
internal func _enqueueOnExecutor< E > ( job unownedJob: UnownedJob, executor: E)
136
128
where E: SerialExecutor {
137
129
if #available( SwiftStdlib 5 . 9 , * ) {
138
- executor. enqueue ( Job ( context: unownedJob. context ) )
130
+ executor. enqueue ( Job ( context: unownedJob. _context ) )
139
131
} else {
140
132
executor. enqueue ( unownedJob)
141
133
}
142
134
}
143
135
144
- @available ( SwiftStdlib 5 . 1 , * )
145
- @_transparent
146
- public // COMPILER_INTRINSIC
147
- func _checkExpectedExecutor( _filenameStart: Builtin . RawPointer ,
148
- _filenameLength: Builtin . Word ,
149
- _filenameIsASCII: Builtin . Int1 ,
150
- _line: Builtin . Word ,
151
- _executor: Builtin . Executor ) {
152
- if _taskIsCurrentExecutor ( _executor) {
153
- return
154
- }
155
-
156
- _reportUnexpectedExecutor (
157
- _filenameStart, _filenameLength, _filenameIsASCII, _line, _executor)
158
- }
159
-
160
136
#if !SWIFT_STDLIB_SINGLE_THREADED_CONCURRENCY && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
161
137
// This must take a DispatchQueueShim, not something like AnyObject,
162
138
// or else SILGen will emit a retain/release in unoptimized builds,
@@ -185,3 +161,120 @@ internal final class DispatchQueueShim: @unchecked Sendable, SerialExecutor {
185
161
}
186
162
}
187
163
#endif
164
+
165
+ // ==== -----------------------------------------------------------------------
166
+ // - MARK: Executor assertions
167
+
168
+ /// Checks if the current task is running on the expected executor.
169
+ ///
170
+ /// Do note that if multiple actors share the same serial executor,
171
+ /// this assertion checks for the executor, not specific actor instance.
172
+ ///
173
+ /// Generally, Swift programs should be constructed such that it is statically
174
+ /// known that a specific executor is used, for example by using global actors or
175
+ /// custom executors. However, in some APIs it may be useful to provide an
176
+ /// additional runtime check for this, especially when moving towards Swift
177
+ /// concurrency from other runtimes which frequently use such assertions.
178
+ @available ( SwiftStdlib 5 . 9 , * )
179
+ public func preconditionOnSerialExecutor(
180
+ _ executor: some SerialExecutor ,
181
+ _ message: @autoclosure ( ) -> String = " " ,
182
+ file: StaticString = #fileID, line: UInt = #line) {
183
+ preconditionOnSerialExecutor ( executor. asUnownedSerialExecutor ( ) , file: file, line: line)
184
+ }
185
+
186
+ @available ( SwiftStdlib 5 . 9 , * )
187
+ public func preconditionOnSerialExecutor(
188
+ _ unowned: UnownedSerialExecutor ,
189
+ _ message: @autoclosure ( ) -> String = " " ,
190
+ file: StaticString = #fileID, line: UInt = #line) {
191
+ if _taskIsCurrentExecutor ( unowned. executor) {
192
+ return
193
+ }
194
+
195
+ // TODO: log on what executor it was instead of the expected one
196
+ let message = " Expected executor \( unowned) ; \( message ( ) ) "
197
+ preconditionFailure (
198
+ message,
199
+ file: file, line: line)
200
+ }
201
+
202
+ /// Same as ``preconditionOnSerialExecutor(_:_:file:line)`` however only in DEBUG mode.
203
+ @available ( SwiftStdlib 5 . 9 , * )
204
+ public func assertOnSerialExecutor(
205
+ _ executor: some SerialExecutor ,
206
+ _ message: @autoclosure ( ) -> String = " " ,
207
+ file: StaticString = #fileID, line: UInt = #line) {
208
+ assertOnSerialExecutor ( executor. asUnownedSerialExecutor ( ) , file: file, line: line)
209
+ }
210
+
211
+ @available ( SwiftStdlib 5 . 9 , * )
212
+ public func assertOnSerialExecutor(
213
+ _ unowned: UnownedSerialExecutor ,
214
+ _ message: @autoclosure ( ) -> String = " " ,
215
+ file: StaticString = #fileID, line: UInt = #line) {
216
+ if _isDebugAssertConfiguration ( ) {
217
+ if _taskIsCurrentExecutor ( unowned. executor) {
218
+ return
219
+ }
220
+
221
+ // TODO: log on what executor it was instead of the expected one
222
+ // TODO: fixme use assertion here?
223
+ fatalError ( " Expected executor \( unowned) ; \( message ( ) ) " , file: file, line: line)
224
+ }
225
+ }
226
+
227
+ /// Checks if the current task is running on the expected executor.
228
+ ///
229
+ /// Generally, Swift programs should be constructed such that it is statically
230
+ /// known that a specific executor is used, for example by using global actors or
231
+ /// custom executors. However, in some APIs it may be useful to provide an
232
+ /// additional runtime check for this, especially when moving towards Swift
233
+ /// concurrency from other runtimes which frequently use such assertions.
234
+ /// - Parameter executor: The expected executor.
235
+ @available ( SwiftStdlib 5 . 9 , * )
236
+ @_silgen_name ( " swift_task_isOnExecutor " )
237
+ func _taskIsOnExecutor( _ executor: some SerialExecutor ) -> Bool
238
+
239
+ @available ( SwiftStdlib 5 . 1 , * )
240
+ @_transparent
241
+ public // COMPILER_INTRINSIC
242
+ func _checkExpectedExecutor( _filenameStart: Builtin . RawPointer ,
243
+ _filenameLength: Builtin . Word ,
244
+ _filenameIsASCII: Builtin . Int1 ,
245
+ _line: Builtin . Word ,
246
+ _executor: Builtin . Executor ) {
247
+ if _taskIsCurrentExecutor ( _executor) {
248
+ return
249
+ }
250
+
251
+ _reportUnexpectedExecutor (
252
+ _filenameStart, _filenameLength, _filenameIsASCII, _line, _executor)
253
+ }
254
+
255
+ @available ( SwiftStdlib 5 . 9 , * )
256
+ @_alwaysEmitIntoClient // FIXME: use @backDeploy(before: SwiftStdlib 5.9)
257
+ func _checkExpectedExecutor(
258
+ _ _executor: Builtin . Executor ,
259
+ file: String ,
260
+ line: Int ) {
261
+ if _taskIsCurrentExecutor ( _executor) {
262
+ return
263
+ }
264
+
265
+ file. utf8CString. withUnsafeBufferPointer { ( _ bufPtr: UnsafeBufferPointer < CChar > ) in
266
+ let fileBasePtr : Builtin . RawPointer = bufPtr. baseAddress!. _rawValue
267
+
268
+ // string lengths exclude trailing \0 byte, which should be there!
269
+ let fileLength : Builtin . Word = ( bufPtr. count - 1 ) . _builtinWordValue
270
+
271
+ // we're handing it UTF-8
272
+ let falseByte : Int8 = 0
273
+ let fileIsASCII : Builtin . Int1 = Builtin . trunc_Int8_Int1 ( falseByte. _value)
274
+
275
+ _reportUnexpectedExecutor (
276
+ fileBasePtr, fileLength, fileIsASCII,
277
+ line. _builtinWordValue,
278
+ _executor)
279
+ }
280
+ }
0 commit comments