@@ -64,6 +64,23 @@ actor MyGlobalActor {
64
64
static let shared : MyGlobalActor = MyGlobalActor ( )
65
65
}
66
66
67
+ final class NaiveQueueExecutor : SerialExecutor {
68
+ let queue : DispatchQueue
69
+
70
+ init ( queue: DispatchQueue ) {
71
+ self . queue = queue
72
+ }
73
+
74
+ public func enqueue( _ job: consuming ExecutorJob ) {
75
+ let unowned = UnownedJob ( job)
76
+ print ( " NaiveQueueExecutor( \( self . queue. label) ) enqueue... [thread: \( getCurrentThreadID ( ) ) ] " )
77
+ queue. async {
78
+ print ( " NaiveQueueExecutor( \( self . queue. label) ) enqueue: run [thread: \( getCurrentThreadID ( ) ) ] " )
79
+ unowned. runSynchronously( on: self . asUnownedSerialExecutor ( ) )
80
+ }
81
+ }
82
+ }
83
+
67
84
// Test on all platforms
68
85
func syncOnMyGlobalActor( ) -> [ Task < Void , Never > ] {
69
86
MyGlobalActor . shared. preconditionIsolated ( " Should be executing on the global actor here " )
@@ -189,7 +206,7 @@ syncOnNonTaskThread(synchronousTask: behavior)
189
206
// CHECK: after startSynchronously, outside; cancel (wakeup) the synchronous task! [thread:[[CALLING_THREAD3]]]
190
207
191
208
print ( " \n \n ==== ------------------------------------------------------------------ " )
192
- print ( " callActorFromStartSynchronousTask() " )
209
+ print ( " callActorFromStartSynchronousTask() - not on specific queue " )
193
210
callActorFromStartSynchronousTask ( recipient: . recipient( Recipient ( ) ) )
194
211
195
212
// CHECK: callActorFromStartSynchronousTask()
@@ -203,11 +220,6 @@ callActorFromStartSynchronousTask(recipient: .recipient(Recipient()))
203
220
// CHECK-NOT: ERROR!
204
221
// CHECK: inside startSynchronously, call rec.sync() done
205
222
206
- // CHECK-NOT: ERROR!
207
- // CHECK: inside startSynchronously, call rec.async()
208
- // CHECK-NOT: ERROR!
209
- // CHECK: inside startSynchronously, call rec.async() done
210
-
211
223
// CHECK-NOT: ERROR!
212
224
// CHECK: inside startSynchronously, done
213
225
@@ -219,35 +231,20 @@ enum TargetActorToCall {
219
231
}
220
232
221
233
protocol RecipientProtocol where Self: Actor {
222
- func sync( syncTaskThreadID: ThreadID ) async
223
- func async ( syncTaskThreadID: ThreadID ) async
234
+ func callAndSuspend( syncTaskThreadID: ThreadID ) async
224
235
}
225
236
226
237
// default actor, must not declare an 'unownedExecutor'
227
- actor Recipient {
228
- func sync( syncTaskThreadID: ThreadID ) {
229
- self . preconditionIsolated ( )
230
-
231
- print ( " \( Recipient . self) / \( #function) Current actor thread id = \( getCurrentThreadID ( ) ) @ : \( #line) " )
232
- if compareThreadIDs ( syncTaskThreadID, . equal, getCurrentThreadID ( ) ) {
233
- print ( " NOTICE: Actor must not run on the synchronous task's thread : \( #line) " )
234
- }
235
- }
236
-
237
- func async ( syncTaskThreadID: ThreadID ) async {
238
+ actor Recipient : RecipientProtocol {
239
+ func callAndSuspend( syncTaskThreadID: ThreadID ) async {
238
240
self . preconditionIsolated ( )
239
241
240
- // Dispatch may end up reusing the thread used to service the queue so we
241
- // cannot truly assert exact thread identity in such tests.
242
- // Usually this will be on a different thread by now though.
243
242
print ( " \( Recipient . self) / \( #function) Current actor thread id = \( getCurrentThreadID ( ) ) @ : \( #line) " )
244
243
if compareThreadIDs ( syncTaskThreadID, . equal, getCurrentThreadID ( ) ) {
245
244
print ( " NOTICE: Actor must not run on the synchronous task's thread : \( #line) " )
246
245
}
247
246
248
- await Task {
249
- self . preconditionIsolated ( )
250
- } . value
247
+ try ? await Task . sleep ( for: . milliseconds( 100 ) )
251
248
}
252
249
}
253
250
@@ -274,8 +271,8 @@ func callActorFromStartSynchronousTask(recipient rec: TargetActorToCall) {
274
271
275
272
print ( " inside startSynchronously, call rec.sync() [thread: \( getCurrentThreadID ( ) ) ] @ : \( #line) " )
276
273
switch rec {
277
- case . recipient( let recipient) : await recipient. sync ( syncTaskThreadID: innerTID)
278
- case . recipientOnQueue( let recipient) : await recipient. sync ( syncTaskThreadID: innerTID)
274
+ case . recipient( let recipient) : await recipient. callAndSuspend ( syncTaskThreadID: innerTID)
275
+ case . recipientOnQueue( let recipient) : await recipient. callAndSuspend ( syncTaskThreadID: innerTID)
279
276
}
280
277
print ( " inside startSynchronously, call rec.sync() done [thread: \( getCurrentThreadID ( ) ) ] @ : \( #line) " )
281
278
@@ -290,22 +287,6 @@ func callActorFromStartSynchronousTask(recipient rec: TargetActorToCall) {
290
287
print ( " NOTICE: Task resumed on same thread as it entered the synchronous task! " )
291
288
}
292
289
293
- print ( " inside startSynchronously, call rec.async() [thread: \( getCurrentThreadID ( ) ) ] @ : \( #line) " )
294
- switch rec {
295
- case . recipient( let recipient) : await recipient. async ( syncTaskThreadID: innerTID)
296
- case . recipientOnQueue( let recipient) : await recipient. async ( syncTaskThreadID: innerTID)
297
- }
298
- print ( " inside startSynchronously, call rec.async() done [thread: \( getCurrentThreadID ( ) ) ] @ : \( #line) " )
299
-
300
- print ( " Inner thread id = \( innerTID) " )
301
- print ( " Current thread id = \( getCurrentThreadID ( ) ) " )
302
- // Dispatch may end up reusing the thread used to service the queue so we
303
- // cannot truly assert exact thread identity in such tests.
304
- // Usually this will be on a different thread by now though.
305
- if compareThreadIDs ( innerTID, . equal, getCurrentThreadID ( ) ) {
306
- print ( " NOTICE: Task resumed on same thread as it entered the synchronous task! " )
307
- }
308
-
309
290
print ( " inside startSynchronously, done [thread: \( getCurrentThreadID ( ) ) ] @ : \( #line) " )
310
291
sem1. signal ( )
311
292
}
@@ -323,6 +304,28 @@ print("callActorFromStartSynchronousTask() - actor in custom executor with its o
323
304
let actorQueue = DispatchQueue ( label: " recipient-actor-queue " )
324
305
callActorFromStartSynchronousTask ( recipient: . recipientOnQueue( RecipientOnQueue ( queue: actorQueue) ) )
325
306
307
+
308
+ // 50: callActorFromStartSynchronousTask()
309
+ // 51: before startSynchronously [thread:0x00007000054f5000] @ :366
310
+ // 52: inside startSynchronously [thread:0x00007000054f5000] @ :372
311
+ // 53: inside startSynchronously, call rec.sync() [thread:0x00007000054f5000] @ :380
312
+ // 54: Recipient/sync(syncTaskThreadID:) Current actor thread id = 0x000070000567e000 @ :336
313
+ // 55: inside startSynchronously, call rec.sync() done [thread:0x000070000567e000] @ :385
314
+ // 56: Inner thread id = 0x00007000054f5000
315
+ // 57: Current thread id = 0x000070000567e000
316
+ // 60: after startSynchronously [thread:0x00007000054f5000] @ :418
317
+ // 61: - async work on queue
318
+ // 62: - async work on queue
319
+ // 63: - async work on queue
320
+ // 64: - async work on queue
321
+ // 65: - async work on queue
322
+ // 67: - async work on queue
323
+ // 68: - async work on queue
324
+ // 69: - async work on queue
325
+ // 71: Inner thread id = 0x00007000054f5000
326
+ // 72: Current thread id = 0x000070000567e000
327
+ // 73: inside startSynchronously, done [thread:0x000070000567e000] @ :414
328
+
326
329
// CHECK-LABEL: callActorFromStartSynchronousTask() - actor in custom executor with its own queue
327
330
// No interleaving allowed between "before" and "inside":
328
331
// CHECK: before startSynchronously [thread:[[CALLING_THREAD4:.*]]]
@@ -333,38 +336,14 @@ callActorFromStartSynchronousTask(recipient: .recipientOnQueue(RecipientOnQueue(
333
336
// allowing the 'after startSynchronously' to run.
334
337
//
335
338
// CHECK-NEXT: inside startSynchronously, call rec.sync() [thread:[[CALLING_THREAD4]]]
336
- // CHECK: NaiveQueueExecutor(recipient-actor-queue) enqueue
337
339
// CHECK: after startSynchronously
338
340
// CHECK-NOT: ERROR!
339
341
// CHECK: inside startSynchronously, call rec.sync() done
340
342
341
- // CHECK-NOT: ERROR!
342
- // CHECK: inside startSynchronously, call rec.async()
343
- // CHECK: NaiveQueueExecutor(recipient-actor-queue) enqueue
344
- // CHECK-NOT: ERROR!
345
- // CHECK: inside startSynchronously, call rec.async() done
346
-
347
343
// CHECK-NOT: ERROR!
348
344
// CHECK: inside startSynchronously, done
349
345
350
- final class NaiveQueueExecutor : SerialExecutor {
351
- let queue : DispatchQueue
352
-
353
- init ( queue: DispatchQueue ) {
354
- self . queue = queue
355
- }
356
-
357
- public func enqueue( _ job: consuming ExecutorJob ) {
358
- let unowned = UnownedJob ( job)
359
- print ( " NaiveQueueExecutor( \( self . queue. label) ) enqueue... [thread: \( getCurrentThreadID ( ) ) ] " )
360
- queue. async {
361
- print ( " NaiveQueueExecutor( \( self . queue. label) ) enqueue: run [thread: \( getCurrentThreadID ( ) ) ] " )
362
- unowned. runSynchronously( on: self . asUnownedSerialExecutor ( ) )
363
- }
364
- }
365
- }
366
-
367
- actor RecipientOnQueue {
346
+ actor RecipientOnQueue : RecipientProtocol {
368
347
let executor : NaiveQueueExecutor
369
348
nonisolated let unownedExecutor : UnownedSerialExecutor
370
349
@@ -373,30 +352,15 @@ actor RecipientOnQueue {
373
352
self . unownedExecutor = executor. asUnownedSerialExecutor ( )
374
353
}
375
354
376
- func sync( syncTaskThreadID: ThreadID ) {
377
- self . preconditionIsolated ( )
378
- dispatchPrecondition ( condition: . onQueue( self . executor. queue) )
379
-
380
- print ( " \( Recipient . self) / \( #function) Current actor thread id = \( getCurrentThreadID ( ) ) @ : \( #line) " )
381
- if compareThreadIDs ( syncTaskThreadID, . equal, getCurrentThreadID ( ) ) {
382
- print ( " NOTICE: Actor must not run on the synchronous task's thread : \( #line) " )
383
- }
384
- }
385
-
386
- func async ( syncTaskThreadID: ThreadID ) async {
355
+ func callAndSuspend( syncTaskThreadID: ThreadID ) async {
387
356
self . preconditionIsolated ( )
388
357
dispatchPrecondition ( condition: . onQueue( self . executor. queue) )
389
358
390
- // Dispatch may end up reusing the thread used to service the queue so we
391
- // cannot truly assert exact thread identity in such tests.
392
- // Usually this will be on a different thread by now though.
393
359
print ( " \( Recipient . self) / \( #function) Current actor thread id = \( getCurrentThreadID ( ) ) @ : \( #line) " )
394
360
if compareThreadIDs ( syncTaskThreadID, . equal, getCurrentThreadID ( ) ) {
395
361
print ( " NOTICE: Actor must not run on the synchronous task's thread : \( #line) " )
396
362
}
397
363
398
- await Task {
399
- self . preconditionIsolated ( )
400
- } . value
364
+ try ? await Task . sleep ( for: . milliseconds( 100 ) )
401
365
}
402
366
}
0 commit comments