Skip to content

Commit 8c897e9

Browse files
committed
[Concurrency] Move-only Job, in addition to UnownedJob
1 parent 5ef0c4e commit 8c897e9

File tree

11 files changed

+156
-10
lines changed

11 files changed

+156
-10
lines changed

include/swift/ABI/Task.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@ class alignas(2 * alignof(void*)) Job :
133133
return Flags.getPriority();
134134
}
135135

136+
uint32_t getJobId() const {
137+
return Id;
138+
}
139+
136140
/// Given that we've fully established the job context in the current
137141
/// thread, actually start running this job. To establish the context
138142
/// correctly, call swift_job_run or runJobInExecutorContext.

include/swift/Runtime/Concurrency.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,11 @@ bool swift_task_isOnExecutor(
715715
const Metadata *selfType,
716716
const SerialExecutorWitnessTable *wtable);
717717

718+
/// Return the 64bit TaskID (if the job is an AsyncTask),
719+
/// or the 32bits of the job Id otherwise.
720+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
721+
uint64_t swift_task_getJobTaskId(Job *job);
722+
718723
#if SWIFT_CONCURRENCY_ENABLE_DISPATCH
719724

720725
/// Enqueue the given job on the main executor.

stdlib/cmake/modules/AddSwiftStdlib.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1807,7 +1807,7 @@ function(add_swift_target_library name)
18071807
if (SWIFTLIB_IS_STDLIB)
18081808
list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS "-warn-implicit-overrides")
18091809
list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS "-Xfrontend;-enable-ossa-modules")
1810-
list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS "-Xfrontend;-enable-lexical-lifetimes=false")
1810+
list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS "-Xfrontend;-enable-lexical-lifetimes=true") # FIXME: undo this
18111811
endif()
18121812

18131813
if(NOT SWIFT_BUILD_RUNTIME_WITH_HOST_COMPILER AND NOT BUILD_STANDALONE AND

stdlib/public/BackDeployConcurrency/Executor.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public struct UnownedSerialExecutor: Sendable {
7474
@_silgen_name("_swift_task_enqueueOnExecutor")
7575
internal func _enqueueOnExecutor<E>(job: UnownedJob, executor: E)
7676
where E: SerialExecutor {
77+
// TODO: something to do here?
7778
executor.enqueue(job)
7879
}
7980

stdlib/public/Concurrency/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
149149
${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS}
150150
-parse-stdlib
151151
-Xfrontend -enable-experimental-concurrency
152+
-Xfrontend -enable-experimental-move-only # FIXME: REMOVE THIS
152153
-diagnostic-style swift
153154
${SWIFT_RUNTIME_CONCURRENCY_SWIFT_FLAGS}
154155
${swift_concurrency_options}

stdlib/public/Concurrency/Executor.swift

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,44 @@ import Swift
1515
/// A service that can execute jobs.
1616
@available(SwiftStdlib 5.1, *)
1717
public protocol Executor: AnyObject, Sendable {
18+
// This requirement is repeated here as a non-override so that we
19+
// get a redundant witness-table entry for it. This allows us to
20+
// avoid drilling down to the base conformance just for the basic
21+
// work-scheduling operation.
22+
@available(*, deprecated, message: "Implement enqueueJob instead")
1823
func enqueue(_ job: UnownedJob)
24+
25+
@available(SwiftStdlib 5.9, *)
26+
func enqueueJob(_ job: __owned Job) // FIXME: figure out how to introduce in compatible way
1927
}
2028

2129
/// A service that executes jobs.
2230
@available(SwiftStdlib 5.1, *)
2331
public protocol SerialExecutor: Executor {
24-
// This requirement is repeated here as a non-override so that we
25-
// get a redundant witness-table entry for it. This allows us to
26-
// avoid drilling down to the base conformance just for the basic
27-
// work-scheduling operation.
32+
2833
@_nonoverride
34+
@available(*, deprecated, message: "Implement enqueueJob instead")
2935
func enqueue(_ job: UnownedJob)
3036

37+
@_nonoverride
38+
@available(SwiftStdlib 5.9, *)
39+
func enqueueJob(_ job: __owned Job) // FIXME: figure out how to introduce in compatible way
40+
3141
/// Convert this executor value to the optimized form of borrowed
3242
/// executor references.
3343
func asUnownedSerialExecutor() -> UnownedSerialExecutor
3444
}
3545

46+
@available(SwiftStdlib 5.9, *)
47+
extension Executor {
48+
public func enqueue(_ job: UnownedJob) { // FIXME: this is bad; how could we deploy this nicely
49+
fatalError("Please implement \(Self.self).enqueueJob(_:)")
50+
}
51+
public func enqueueJob(_ job: __owned Job) {
52+
self.enqueue(UnownedJob(job))
53+
}
54+
}
55+
3656
/// An unowned reference to a serial executor (a `SerialExecutor`
3757
/// value).
3858
///
@@ -81,12 +101,26 @@ public struct UnownedSerialExecutor: Sendable {
81101
@_silgen_name("swift_task_isOnExecutor")
82102
public func _taskIsOnExecutor<Executor: SerialExecutor>(_ executor: Executor) -> Bool
83103

104+
/// Primarily a debug utility.
105+
///
106+
/// If the passed in Job is a Task, returns the complete 64bit TaskId,
107+
/// otherwise returns only the job's 32bit Id.
108+
///
109+
/// - Returns: the Id stored in this Job or Task, for purposes of debug printing
110+
@available(SwiftStdlib 5.9, *)
111+
@_silgen_name("swift_task_getJobTaskId")
112+
internal func _getJobTaskId(_ job: UnownedJob) -> UInt64
113+
84114
// Used by the concurrency runtime
85115
@available(SwiftStdlib 5.1, *)
86116
@_silgen_name("_swift_task_enqueueOnExecutor")
87-
internal func _enqueueOnExecutor<E>(job: UnownedJob, executor: E)
117+
internal func _enqueueOnExecutor<E>(job unownedJob: UnownedJob, executor: E)
88118
where E: SerialExecutor {
89-
executor.enqueue(job)
119+
if #available(SwiftStdlib 5.9, *) {
120+
executor.enqueueJob(Job(context: unownedJob.context))
121+
} else {
122+
executor.enqueue(unownedJob)
123+
}
90124
}
91125

92126
@available(SwiftStdlib 5.1, *)

stdlib/public/Concurrency/GlobalExecutor.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,17 @@ bool swift::swift_task_isOnExecutor(HeapObject *executor,
150150
return swift_task_isOnExecutorImpl(executor, selfType, wtable);
151151
}
152152

153+
uint64_t swift::swift_task_getJobTaskId(Job *job) {
154+
if (auto task = dyn_cast<AsyncTask>(job)) {
155+
// TaskID is actually:
156+
// 32bits of Job's Id
157+
// + 32bits stored in the AsyncTask
158+
return task->getTaskId();
159+
} else {
160+
return job->getJobId();
161+
}
162+
}
163+
153164
/*****************************************************************************/
154165
/****************************** MAIN EXECUTOR *******************************/
155166
/*****************************************************************************/

stdlib/public/Concurrency/PartialAsyncTask.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,18 @@ internal func _swiftJobRun(_ job: UnownedJob,
2626
@available(SwiftStdlib 5.1, *)
2727
@frozen
2828
public struct UnownedJob: Sendable {
29-
private var context: Builtin.Job
29+
internal var context: Builtin.Job
30+
31+
@usableFromInline
32+
internal init(context: Builtin.Job) {
33+
self.context = context
34+
}
35+
36+
@available(SwiftStdlib 5.9, *)
37+
@usableFromInline
38+
internal init(_ job: __owned Job) {
39+
self.context = job.context
40+
}
3041

3142
/// The priority of this job.
3243
@available(SwiftStdlib 5.9, *)
@@ -41,6 +52,13 @@ public struct UnownedJob: Sendable {
4152
public func _runSynchronously(on executor: UnownedSerialExecutor) {
4253
_swiftJobRun(self, executor)
4354
}
55+
}
56+
57+
@available(SwiftStdlib 5.9, *)
58+
extension UnownedJob {
59+
public struct Priority {
60+
public typealias RawValue = UInt8
61+
public var rawValue: RawValue
4462

4563
@_alwaysEmitIntoClient
4664
@inlinable
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %target-run-simple-swift( -Xfrontend -enable-experimental-move-only -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s
2+
3+
// REQUIRES: concurrency
4+
// REQUIRES: executable_test
5+
// UNSUPPORTED: freestanding
6+
7+
// UNSUPPORTED: back_deployment_runtime
8+
// REQUIRES: concurrency_runtime
9+
10+
final class InlineExecutor: SerialExecutor, CustomStringConvertible {
11+
12+
public func enqueueJob(_ job: __owned Job) {
13+
print("\(self): enqueue (job: \(job.description))")
14+
runJobSynchronously(job)
15+
print("\(self): after run")
16+
}
17+
18+
public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
19+
return UnownedSerialExecutor(ordinary: self)
20+
}
21+
22+
var description: Swift.String {
23+
"InlineExecutor()"
24+
}
25+
}
26+
27+
let inlineExecutor = InlineExecutor()
28+
29+
actor Custom {
30+
var count = 0
31+
32+
nonisolated var unownedExecutor: UnownedSerialExecutor {
33+
print("custom unownedExecutor")
34+
return inlineExecutor.asUnownedSerialExecutor()
35+
}
36+
37+
func report() async {
38+
print("custom.count == \(count)")
39+
count += 1
40+
}
41+
}
42+
43+
@available(SwiftStdlib 5.1, *)
44+
@main struct Main {
45+
static func main() async {
46+
print("begin")
47+
let actor = Custom()
48+
await actor.report()
49+
await actor.report()
50+
await actor.report()
51+
print("end")
52+
}
53+
}
54+
55+
// CHECK: begin
56+
// CHECK-NEXT: custom unownedExecutor
57+
// CHECK-NEXT: custom.count == 0
58+
// CHECK-NEXT: custom unownedExecutor
59+
// CHECK-NEXT: custom.count == 1
60+
// CHECK-NEXT: custom unownedExecutor
61+
// CHECK-NEXT: custom.count == 2
62+
// CHECK-NEXT: end

test/Concurrency/Runtime/custom_executors_priority.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ actor Custom {
3535
}
3636
}
3737

38-
@available(SwiftStdlib 5.1, *)
3938
@main struct Main {
4039
static func main() async {
4140
print("begin")
@@ -46,6 +45,11 @@ actor Custom {
4645
await Task() {
4746
await actor.report()
4847
}.value
48+
await Task(priority: .low) {
49+
print("P: \(Task.currentPriority)")
50+
print("B: \(Task.currentBasePriority)")
51+
await actor.report()
52+
}.value
4953
print("end")
5054
}
5155
}

test/Concurrency/Runtime/custom_executors_protocol.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-run-simple-swift( -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s
1+
// RUN: %target-run-simple-swift( -Xfrontend -enable-experimental-move-only -Xfrontend -disable-availability-checking %import-libdispatch -parse-as-library) | %FileCheck %s
22

33
// REQUIRES: concurrency
44
// REQUIRES: executable_test
@@ -42,6 +42,12 @@ final class InlineExecutor: SpecifiedExecutor, Swift.CustomStringConvertible {
4242
print("\(self): after run")
4343
}
4444

45+
public func enqueueJob(_ job: __owned Job) {
46+
print("\(self): enqueue")
47+
runJobSynchronously(job)
48+
print("\(self): after run")
49+
}
50+
4551
public func asUnownedSerialExecutor() -> UnownedSerialExecutor {
4652
return UnownedSerialExecutor(ordinary: self)
4753
}

0 commit comments

Comments
 (0)