From 2f0f2008d978cf94cb5048f8a3c0f34e1569f066 Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Wed, 30 Apr 2025 21:34:44 +0900 Subject: [PATCH 1/2] [Concurrency] Bring back ability to get a queue custom executor in executorForEnqueuedJob This was a regression and now even if we had a queue set in job->SchedulerPrivate[Job::DispatchQueueIndex]. Arguably the way we're using that storage needs to be fixed somehow, since if we're opening this storage to other executors then it means we could have OTHER things in there, not just a dispatch queueue, and therefore this cast would be bad. For now though we can bring back the existing behavior while we work out with Alastair what the right way to handle this will be -- some marker bit somewhere that indeed this is a queue stored here? resolves rdar://150213107 --- stdlib/public/Concurrency/Task.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index 8fcf079de8c89..6000f2c6c02ca 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -382,6 +382,11 @@ static SerialExecutorRef executorForEnqueuedJob(Job *job) { return swift_task_getMainExecutor(); } + if (auto identity = reinterpret_cast(jobQueue)) { + return SerialExecutorRef::forOrdinary( + identity, _swift_task_getDispatchQueueSerialExecutorWitnessTable()); + } + return SerialExecutorRef::generic(); #endif } From d51db03f9f60c2353ff704c955421c4d4e71d1aa Mon Sep 17 00:00:00 2001 From: Konrad 'ktoso' Malawski Date: Wed, 30 Apr 2025 22:02:35 +0900 Subject: [PATCH 2/2] Attempt to include reproducer as a test case --- ...cutor_tracking_custom_executor_queue.swift | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 test/Concurrency/Runtime/actor_executor_tracking_custom_executor_queue.swift diff --git a/test/Concurrency/Runtime/actor_executor_tracking_custom_executor_queue.swift b/test/Concurrency/Runtime/actor_executor_tracking_custom_executor_queue.swift new file mode 100644 index 0000000000000..8cb7096dfb44c --- /dev/null +++ b/test/Concurrency/Runtime/actor_executor_tracking_custom_executor_queue.swift @@ -0,0 +1,82 @@ +// RUN: %target-run-simple-swift( -target %target-swift-5.1-abi-triple -parse-as-library -Xfrontend -disable-availability-checking) | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: concurrency +// REQUIRES: concurrency_runtime +// REQUIRES: concurrency_runtime +// REQUIRES: libdispatch + +// REQUIRES: OS=macosx + +// Regression test for rdar://150213107 + +import Darwin +import Dispatch + +actor TestActor { + private let queue = DispatchSerialQueue(label: "MYQUEUE") + + nonisolated var unownedExecutor: UnownedSerialExecutor { + queue.asUnownedSerialExecutor() + } + + init() { + dispatchPrecondition(condition: .notOnQueue(self.queue)) + + Task.detached { + dispatchPrecondition(condition: .notOnQueue(self.queue)) + sleep(1) + await self.start() + } + } + + private var startupCompleted: Bool = false + private var startupContinuations: [CheckedContinuation] = [] + + func waitForStartup() async { + self.assertIsolated() + guard !startupCompleted else { + print("waitForStartup - completed, bailing") + return + } + + print("waitForStartup - adding continuation") + sleep(2) + print("waitForStartup - slept...") + + print("waitForStartup - suspend") + await withCheckedContinuation { continuation in + print("waitForStartup - suspend inside") + dispatchPrecondition(condition: .onQueue(self.queue)) + startupContinuations.append(continuation) + print("waitForStartup - added") + } + } + + private func start() { + dispatchPrecondition(condition: .onQueue(self.queue)) + print("start") + + startupCompleted = true + for continuation in startupContinuations { + print("start - resuming") + continuation.resume() + } + startupContinuations = [] + print("start - FINISHED") + } +} + + +@main struct Main { + static func main() async { + // CHECK: waitForStartup - adding continuation + // CHECK: waitForStartup - added + // CHECK: start + // CHECK: start - FINISHED + // CHECK: DONE + let test = TestActor() + await test.waitForStartup() + print("DONE") + } +} \ No newline at end of file