@@ -39,47 +39,45 @@ private final class NodeExecutor: SerialExecutor {
3939 }
4040
4141 func enqueue( _ job: UnownedJob ) {
42- schedulerQueue. async {
43- // We want to access `job`'s task-local storage. To do so,
44- // this temporarily swaps ResumeTask for our own function.
45- // Then, swift_job_run is called, which sets the active task to
46- // the receiver and invokes its ResumeTask. We then execute the
47- // given closure, allowing us to grab task-local values. Finally,
48- // we "suspend" the task and return ResumeTask to its old value.
49- //
50- // on Darwin we can instead replace the "current task" thread-local
51- // (key 103) temporarily, but that isn't portable.
52- //
53- // This is sort of like inserting a "work(); await Task.yield()" block
54- // at the top of the task, since when a Task awaits it similarly changes
55- // the Resume function and suspends. Note that we can assume that this
56- // is a Task and not a basic Job, because Executor.enqueue is only
57- // called from swift_task_enqueue.
58- //
59- // Regarding `schedulerQueue.async`:
60- // Pre Swift 6.0 we didn't need a scheduler queue as enqueue would always
61- // run on the global queue. However, Swift 6 introduces optimizations in
62- // Task dispatch that allow tasks to be enqueued more efficiently, including
63- // that Task.init avoids a hop when possible. This, however, interferes with
64- // our `job.asCurrent` code because `asCurrent` relies on there being no already-
65- // running task (swift_job_run doesn't play well with nesting, it's possible but
66- // requires more private APIs, cf [swift_task_startOnMainActor][1]). The simplest
67- // solution is to hop onto our own queue for scheduling.
68- //
69- // [1]: https://github.com/apple/swift/blob/876c056153554f93b89dfd134794a05426ee789a/stdlib/public/Concurrency/Task.cpp#L1739
70- let target = job. asCurrent { NodeActor . target }
71-
72- guard let q = target? . queue else {
73- nodeFatalError ( " There is no target NodeAsyncQueue associated with this Task " )
74- }
75-
76- let ref = self . asUnownedSerialExecutor ( )
77-
78- do {
79- try q. run { job. runSynchronously ( on: ref) }
80- } catch {
81- nodeFatalError ( " Could not execute job on NodeActor: \( error) " )
82- }
42+ // We want to enqueue the job on the "current" NodeActor, which is
43+ // stored in task-local storage. In the Swift 6 compiler,
44+ // NodeExecutor.enqueue is invoked with the same isolation as the caller,
45+ // which means we can simply read out the TaskLocal value to obtain
46+ // this.
47+ let target : NodeAsyncQueue . Handle ?
48+ #if compiler(>=6.0)
49+ target = NodeActor . target
50+ #else
51+ // It's a bit trickier in Swift <= 5.10 as Swift first makes a hop to the
52+ // global executor before invoking this method. So instead we have to use
53+ // some runtime spelunking to read the TaskLocal value from `job`.
54+ // To do so, we temporarily swap ResumeTask for our own function.
55+ // Then, swift_job_run is called, which sets the active task to
56+ // the receiver and invokes its ResumeTask. We then execute the
57+ // given closure, allowing us to grab task-local values. Finally,
58+ // we "suspend" the task and return ResumeTask to its old value.
59+ //
60+ // on Darwin we can instead replace the "current task" thread-local
61+ // (key 103) temporarily, but that isn't portable.
62+ //
63+ // This is sort of like inserting a "work(); await Task.yield()" block
64+ // at the top of the task, since when a Task awaits it similarly changes
65+ // the Resume function and suspends. Note that we can assume that this
66+ // is a Task and not a basic Job, because Executor.enqueue is only
67+ // called from swift_task_enqueue.
68+ target = job. asCurrent { NodeActor . target }
69+ #endif
70+
71+ guard let q = target? . queue else {
72+ nodeFatalError ( " There is no target NodeAsyncQueue associated with this Task " )
73+ }
74+
75+ let ref = self . asUnownedSerialExecutor ( )
76+
77+ do {
78+ try q. run { job. runSynchronously ( on: ref) }
79+ } catch {
80+ nodeFatalError ( " Could not execute job on NodeActor: \( error) " )
8381 }
8482 }
8583
0 commit comments