Skip to content

Commit 5cc451c

Browse files
authored
Merge pull request #37159 from DougGregor/concurrency-priority-cleanup
2 parents b92f058 + cdc6dbe commit 5cc451c

File tree

3 files changed

+154
-45
lines changed

3 files changed

+154
-45
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,6 +2015,7 @@ class JobFlags : public FlagSet<size_t> {
20152015
Task_IsChildTask = 24,
20162016
Task_IsFuture = 25,
20172017
Task_IsGroupChildTask = 26,
2018+
Task_IsContinuingAsyncTask = 27,
20182019
};
20192020

20202021
explicit JobFlags(size_t bits) : FlagSet(bits) {}
@@ -2044,6 +2045,9 @@ class JobFlags : public FlagSet<size_t> {
20442045
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsGroupChildTask,
20452046
task_isGroupChildTask,
20462047
task_setIsGroupChildTask)
2048+
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsContinuingAsyncTask,
2049+
task_isContinuingAsyncTask,
2050+
task_setIsContinuingAsyncTask)
20472051
};
20482052

20492053
/// Kinds of task status record.

stdlib/public/Concurrency/Task.swift

Lines changed: 106 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,21 @@ extension Task {
4747

4848
/// Returns the `current` task's priority.
4949
///
50-
/// If no current `Task` is available, returns `Priority.default`.
50+
/// If no current `Task` is available, queries the system to determine the
51+
/// priority at which the current function is running. If the system cannot
52+
/// provide an appropriate priority, returns `Priority.default`.
5153
///
5254
/// - SeeAlso: `Task.Priority`
5355
/// - SeeAlso: `Task.priority`
5456
public static var currentPriority: Priority {
5557
withUnsafeCurrentTask { task in
56-
guard let task = task else {
57-
return Priority.default
58+
// If we are running on behalf of a task, use that task's priority.
59+
if let task = task {
60+
return task.priority
5861
}
5962

60-
return getJobFlags(task._task).priority
63+
// Otherwise, query the system.
64+
return Task.Priority(rawValue: _getCurrentThreadPriority()) ?? .default
6165
}
6266
}
6367

@@ -100,6 +104,8 @@ extension Task {
100104
case `default` = 0x15
101105
case utility = 0x11
102106
case background = 0x09
107+
108+
@available(*, deprecated, message: "unspecified priority will be removed; use nil")
103109
case unspecified = 0x00
104110

105111
public static func < (lhs: Priority, rhs: Priority) -> Bool {
@@ -108,6 +114,18 @@ extension Task {
108114
}
109115
}
110116

117+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
118+
extension Task.Priority {
119+
/// Downgrade user-interactive to user-initiated.
120+
var _downgradeUserInteractive: Task.Priority {
121+
if self == .userInteractive {
122+
return .userInitiated
123+
}
124+
125+
return self
126+
}
127+
}
128+
111129
// ==== Task Handle ------------------------------------------------------------
112130

113131
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
@@ -257,13 +275,13 @@ extension Task {
257275
var isAsyncTask: Bool { kind == .task }
258276

259277
/// The priority given to the job.
260-
var priority: Priority {
278+
var priority: Priority? {
261279
get {
262-
Priority(rawValue: (bits & 0xFF00) >> 8)!
280+
Priority(rawValue: (bits & 0xFF00) >> 8)
263281
}
264282

265283
set {
266-
bits = (bits & ~0xFF00) | (newValue.rawValue << 8)
284+
bits = (bits & ~0xFF00) | ((newValue?.rawValue ?? 0) << 8)
267285
}
268286
}
269287

@@ -312,6 +330,22 @@ extension Task {
312330
}
313331
}
314332

333+
/// Whether this is a task created by the 'async' operation, which
334+
/// conceptually continues the work of the synchronous code that invokes
335+
/// it.
336+
var isContinuingAsyncTask: Bool {
337+
get {
338+
(bits & (1 << 27)) != 0
339+
}
340+
341+
set {
342+
if newValue {
343+
bits = bits | 1 << 27
344+
} else {
345+
bits = (bits & ~(1 << 27))
346+
}
347+
}
348+
}
315349
}
316350
}
317351

@@ -438,23 +472,37 @@ public func detach<T>(
438472
}
439473

440474
@discardableResult
441-
@_alwaysEmitIntoClient
442475
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
443476
public func asyncDetached<T>(
444-
priority: Task.Priority = .unspecified,
477+
priority: Task.Priority? = nil,
445478
@_implicitSelfCapture operation: __owned @Sendable @escaping () async -> T
446479
) -> Task.Handle<T, Never> {
447-
return detach(priority: priority, operation: operation)
480+
return detach(priority: priority ?? .unspecified, operation: operation)
448481
}
449482

450483
@discardableResult
451-
@_alwaysEmitIntoClient
452484
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
453485
public func asyncDetached<T>(
454-
priority: Task.Priority = .unspecified,
486+
priority: Task.Priority? = nil,
455487
@_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> T
456488
) -> Task.Handle<T, Error> {
457-
return detach(priority: priority, operation: operation)
489+
return detach(priority: priority ?? .unspecified, operation: operation)
490+
}
491+
492+
/// ABI stub while we stage in the new signatures
493+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
494+
@usableFromInline
495+
func async(
496+
priority: Task.Priority,
497+
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> Void
498+
) {
499+
let adjustedPriority: Task.Priority?
500+
if priority == .unspecified {
501+
adjustedPriority = nil
502+
} else {
503+
adjustedPriority = priority
504+
}
505+
let _: Task.Handle = async(priority: adjustedPriority, operation: operation)
458506
}
459507

460508
/// Run given `operation` as asynchronously in its own top-level task.
@@ -468,46 +516,65 @@ public func asyncDetached<T>(
468516
/// does not return a handle to refer to the task.
469517
///
470518
/// - Parameters:
471-
/// - priority: priority of the task. If unspecified, the priority will
472-
/// be inherited from the task that is currently executing
473-
/// or, if there is none, from the platform's understanding of
474-
/// which thread is executing.
519+
/// - priority: priority of the task. If nil, the priority will come from
520+
/// Task.currentPriority.
475521
/// - operation: the operation to execute
476522
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
477-
public func async(
478-
priority: Task.Priority = .unspecified,
479-
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> Void
480-
) {
481-
// Determine the priority at which we should create this task
482-
let actualPriority: Task.Priority
483-
if priority == .unspecified {
484-
actualPriority = withUnsafeCurrentTask { task in
485-
// If we are running on behalf of a task,
486-
if let task = task {
487-
return task.priority
488-
}
523+
@discardableResult
524+
public func async<T>(
525+
priority: Task.Priority? = nil,
526+
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async -> T
527+
) -> Task.Handle<T, Never> {
528+
// Set up the job flags for a new task.
529+
var flags = Task.JobFlags()
530+
flags.kind = .task
531+
flags.priority = priority ?? Task.currentPriority._downgradeUserInteractive
532+
flags.isFuture = true
533+
flags.isContinuingAsyncTask = true
489534

490-
return Task.Priority(rawValue: _getCurrentThreadPriority()) ?? .unspecified
491-
}
492-
} else {
493-
actualPriority = priority
494-
}
535+
// Create the asynchronous task future.
536+
let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation)
495537

496-
let adjustedPriority = actualPriority == .userInteractive
497-
? .userInitiated
498-
: actualPriority
538+
// Enqueue the resulting job.
539+
_enqueueJobGlobal(Builtin.convertTaskToJob(task))
540+
541+
return Task.Handle(task)
542+
}
499543

544+
/// Run given `operation` as asynchronously in its own top-level task.
545+
///
546+
/// The `async` function should be used when creating asynchronous work
547+
/// that operates on behalf of the synchronous function that calls it.
548+
/// Like `detach`, the async function creates a separate, top-level task.
549+
/// Unlike `detach`, the task creating by `async` inherits the priority and
550+
/// actor context of the caller, so the `operation` is treated more like an
551+
/// asynchronous extension to the synchronous operation. Additionally, `async`
552+
/// does not return a handle to refer to the task.
553+
///
554+
/// - Parameters:
555+
/// - priority: priority of the task. If nil, the priority will come from
556+
/// Task.currentPriority.
557+
/// - operation: the operation to execute
558+
@available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
559+
@discardableResult
560+
public func async<T>(
561+
priority: Task.Priority? = nil,
562+
@_inheritActorContext @_implicitSelfCapture operation: __owned @Sendable @escaping () async throws -> T
563+
) -> Task.Handle<T, Error> {
500564
// Set up the job flags for a new task.
501565
var flags = Task.JobFlags()
502566
flags.kind = .task
503-
flags.priority = actualPriority
567+
flags.priority = priority ?? Task.currentPriority._downgradeUserInteractive
504568
flags.isFuture = true
569+
flags.isContinuingAsyncTask = true
505570

506571
// Create the asynchronous task future.
507572
let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, operation)
508573

509574
// Enqueue the resulting job.
510575
_enqueueJobGlobal(Builtin.convertTaskToJob(task))
576+
577+
return Task.Handle(task)
511578
}
512579

513580
// ==== Async Handler ----------------------------------------------------------
@@ -656,12 +723,10 @@ public struct UnsafeCurrentTask {
656723

657724
/// Returns the `current` task's priority.
658725
///
659-
/// If no current `Task` is available, returns `Priority.default`.
660-
///
661726
/// - SeeAlso: `Task.Priority`
662727
/// - SeeAlso: `Task.currentPriority`
663728
public var priority: Task.Priority {
664-
getJobFlags(_task).priority
729+
getJobFlags(_task).priority ?? .default
665730
}
666731

667732
}

stdlib/public/Concurrency/TaskGroup.swift

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,16 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
220220
}
221221
}
222222

223+
// Historical entry point, maintained for ABI compatibility
224+
@usableFromInline
225+
mutating func spawn(
226+
priority: Task.Priority,
227+
operation: __owned @Sendable @escaping () async -> ChildTaskResult
228+
) {
229+
let optPriority: Task.Priority? = priority
230+
spawn(priority: optPriority, operation: operation)
231+
}
232+
223233
/// Add a child task to the group.
224234
///
225235
/// ### Error handling
@@ -236,7 +246,7 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
236246
/// - `true` if the operation was added to the group successfully,
237247
/// `false` otherwise (e.g. because the group `isCancelled`)
238248
public mutating func spawn(
239-
priority: Task.Priority = .unspecified,
249+
priority: Task.Priority? = nil,
240250
operation: __owned @Sendable @escaping () async -> ChildTaskResult
241251
) {
242252
_ = _taskGroupAddPendingTask(group: _group, unconditionally: true)
@@ -260,6 +270,16 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
260270
_enqueueJobGlobal(Builtin.convertTaskToJob(childTask))
261271
}
262272

273+
// Historical entry point, maintained for ABI compatibility
274+
@usableFromInline
275+
mutating func spawnUnlessCancelled(
276+
priority: Task.Priority = .unspecified,
277+
operation: __owned @Sendable @escaping () async -> ChildTaskResult
278+
) -> Bool {
279+
let optPriority: Task.Priority? = priority
280+
return spawnUnlessCancelled(priority: optPriority, operation: operation)
281+
}
282+
263283
/// Add a child task to the group.
264284
///
265285
/// ### Error handling
@@ -276,7 +296,7 @@ public struct TaskGroup<ChildTaskResult: Sendable> {
276296
/// - `true` if the operation was added to the group successfully,
277297
/// `false` otherwise (e.g. because the group `isCancelled`)
278298
public mutating func spawnUnlessCancelled(
279-
priority: Task.Priority = .unspecified,
299+
priority: Task.Priority? = nil,
280300
operation: __owned @Sendable @escaping () async -> ChildTaskResult
281301
) -> Bool {
282302
let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false)
@@ -472,6 +492,16 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
472492
}
473493
}
474494

495+
// Historical entry point for ABI reasons
496+
@usableFromInline
497+
mutating func spawn(
498+
priority: Task.Priority = .unspecified,
499+
operation: __owned @Sendable @escaping () async throws -> ChildTaskResult
500+
) {
501+
let optPriority: Task.Priority? = priority
502+
return spawn(priority: optPriority, operation: operation)
503+
}
504+
475505
/// Spawn, unconditionally, a child task in the group.
476506
///
477507
/// ### Error handling
@@ -488,7 +518,7 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
488518
/// - `true` if the operation was added to the group successfully,
489519
/// `false` otherwise (e.g. because the group `isCancelled`)
490520
public mutating func spawn(
491-
priority: Task.Priority = .unspecified,
521+
priority: Task.Priority? = nil,
492522
operation: __owned @Sendable @escaping () async throws -> ChildTaskResult
493523
) {
494524
// we always add, so no need to check if group was cancelled
@@ -513,6 +543,16 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
513543
_enqueueJobGlobal(Builtin.convertTaskToJob(childTask))
514544
}
515545

546+
// Historical entry point for ABI reasons
547+
@usableFromInline
548+
mutating func spawnUnlessCancelled(
549+
priority: Task.Priority,
550+
operation: __owned @Sendable @escaping () async throws -> ChildTaskResult
551+
) -> Bool {
552+
let optPriority: Task.Priority? = priority
553+
return spawnUnlessCancelled(priority: optPriority, operation: operation)
554+
}
555+
516556
/// Add a child task to the group.
517557
///
518558
/// ### Error handling
@@ -529,7 +569,7 @@ public struct ThrowingTaskGroup<ChildTaskResult: Sendable, Failure: Error> {
529569
/// - `true` if the operation was added to the group successfully,
530570
/// `false` otherwise (e.g. because the group `isCancelled`)
531571
public mutating func spawnUnlessCancelled(
532-
priority: Task.Priority = .unspecified,
572+
priority: Task.Priority? = nil,
533573
operation: __owned @Sendable @escaping () async throws -> ChildTaskResult
534574
) -> Bool {
535575
let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false)

0 commit comments

Comments
 (0)