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