Skip to content

Commit a3e0b17

Browse files
authored
[Concurrency] TaskExecutors may be non-swift objects; dont swift_release them (#75059)
1 parent eb51b6f commit a3e0b17

File tree

2 files changed

+54
-1
lines changed

2 files changed

+54
-1
lines changed

stdlib/public/Concurrency/TaskStatus.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -772,7 +772,11 @@ void AsyncTask::dropInitialTaskExecutorPreferenceRecord() {
772772
//
773773
// This should not be done for withTaskExecutorPreference executors,
774774
// however in that case, we would not enter this function here to clean up.
775-
swift_release(executorIdentityToRelease);
775+
//
776+
// NOTE: This MUST NOT assume that the object is a swift object (and use
777+
// swift_release), because a dispatch_queue_t conforms to TaskExecutor,
778+
// and may be passed in here; in which case swift_releasing it would be incorrect.
779+
swift_unknownObjectRelease(executorIdentityToRelease);
776780
}
777781

778782
/**************************************************************************/
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library )
2+
3+
// REQUIRES: executable_test
4+
// REQUIRES: concurrency
5+
// REQUIRES: libdispatch
6+
7+
// REQUIRES: OS=macosx
8+
// REQUIRES: objc_interop
9+
10+
// REQUIRES: concurrency_runtime
11+
// UNSUPPORTED: back_deployment_runtime
12+
13+
import Dispatch
14+
import StdlibUnittest
15+
import _Concurrency
16+
17+
import Foundation
18+
import Darwin
19+
20+
// Sneaky trick to make this type objc reference counted.
21+
//
22+
// This test specifically checks that our reference counting accounts for existence of
23+
// objective-c types as TaskExecutors -- which was a bug where we'd swift_release
24+
// obj-c excecutors by accident (rdar://131151645).
25+
final class NSQueueTaskExecutor: NSData, TaskExecutor, @unchecked Sendable {
26+
public func enqueue(_ _job: consuming ExecutorJob) {
27+
let job = UnownedJob(_job)
28+
DispatchQueue.main.async {
29+
job.runSynchronously(on: self.asUnownedTaskExecutor())
30+
}
31+
}
32+
}
33+
34+
@main struct Main {
35+
static func main() async {
36+
var taskExecutor: (any TaskExecutor)? = NSQueueTaskExecutor()
37+
38+
let task = Task(executorPreference: taskExecutor) {
39+
dispatchPrecondition(condition: .onQueue(DispatchQueue.main))
40+
try? await Task.sleep(for: .seconds(2))
41+
return 12
42+
}
43+
44+
taskExecutor = nil
45+
46+
let num = await task.value
47+
assert(num == 12)
48+
}
49+
}

0 commit comments

Comments
 (0)