Skip to content

Commit 358c3e9

Browse files
committed
[Concurrency] a few missing overloads for immediate tasks
1 parent cdbbe7a commit 358c3e9

File tree

6 files changed

+251
-33
lines changed

6 files changed

+251
-33
lines changed

stdlib/public/Concurrency/Task+immediate.swift.gyb

Lines changed: 148 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ extension Task where Failure == ${FAILURE_TYPE} {
7272
/// - name: The high-level human-readable name given for this task
7373
/// - priority: The priority of the task.
7474
/// Pass `nil` to use the ``Task/basePriority`` of the current task (if there is one).
75+
/// - taskExecutor: The task executor that the child task should be started on and keep using.
76+
/// Explicitly passing `nil` as the executor preference is equivalent to no preference,
77+
/// and effectively means to inherit the outer context's executor preference.
78+
/// You can also pass the ``globalConcurrentExecutor`` global executor explicitly.
7579
/// - operation: the operation to be run immediately upon entering the task.
7680
/// - Returns: A reference to the unstructured task which may be awaited on.
7781
@available(SwiftStdlib 6.2, *)
@@ -80,6 +84,7 @@ extension Task where Failure == ${FAILURE_TYPE} {
8084
public static func immediate(
8185
name: String? = nil,
8286
priority: TaskPriority? = nil,
87+
executorPreference taskExecutor: consuming (any TaskExecutor)? = nil,
8388
@_implicitSelfCapture @_inheritActorContext(always) operation: sending @isolated(any) @escaping () async ${THROWS} -> Success
8489
) -> Task<Success, ${FAILURE_TYPE}> {
8590

@@ -110,20 +115,56 @@ extension Task where Failure == ${FAILURE_TYPE} {
110115
var task: Builtin.NativeObject?
111116
#if $BuiltinCreateAsyncTaskName
112117
if let name {
118+
#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
113119
task =
114120
unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in
115121
Builtin.createTask(
116122
flags: flags,
117123
initialSerialExecutor: builtinSerialExecutor,
124+
initialTaskExecutorConsuming: taskExecutor,
118125
taskName: nameBytes.baseAddress!._rawValue,
119126
operation: operation).0
120127
}
128+
#else // no $BuiltinCreateAsyncTaskOwnedTaskExecutor
129+
task =
130+
unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in
131+
Builtin.createTask(
132+
flags: flags,
133+
initialSerialExecutor: builtinSerialExecutor,
134+
taskName: nameBytes.baseAddress!._rawValue,
135+
operation: operation).0
136+
}
137+
#endif // $BuiltinCreateAsyncTaskOwnedTaskExecutor
138+
} // let name
139+
#endif // $BuiltinCreateAsyncTaskName
140+
141+
// Task name was not set, or task name createTask is unavailable
142+
if task == nil {
143+
assert(name == nil)
144+
#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
145+
task = Builtin.createTask(
146+
flags: flags,
147+
initialSerialExecutor: builtinSerialExecutor,
148+
initialTaskExecutorConsuming: taskExecutor,
149+
operation: operation).0
150+
#else
151+
// legacy branch for the non-consuming task executor
152+
let executorBuiltin: Builtin.Executor =
153+
taskExecutor.asUnownedTaskExecutor().executor
154+
155+
task = Builtin.createTask(
156+
flags: flags,
157+
initialSerialExecutor: builtinSerialExecutor,
158+
initialTaskExecutor: executorBuiltin,
159+
operation: operation).0
160+
#endif
121161
}
122-
#endif
162+
123163
if task == nil {
124164
// either no task name was set, or names are unsupported
125165
task = Builtin.createTask(
126166
flags: flags,
167+
initialSerialExecutor: builtinSerialExecutor,
127168
operation: operation).0
128169
}
129170

@@ -177,10 +218,22 @@ GROUP_AND_OP_INFO = [
177218
}%
178219
% for (GROUP_TYPE, METHOD_NAMES, THROWS, RESULT_TYPE) in GROUP_AND_OP_INFO:
179220
% for METHOD_NAME in METHOD_NAMES:
221+
%
222+
% IS_DISCARDING = 'Discarding' in GROUP_TYPE
223+
% IS_ADD_UNLESS_CANCELLED = METHOD_NAME == "addImmediateTaskUnlessCancelled"
224+
%
225+
% ARROW_RETURN_TYPE = "-> Bool " if IS_ADD_UNLESS_CANCELLED else ""
226+
%
227+
% if IS_DISCARDING:
228+
% TASK_CREATE_FN = 'Builtin.createDiscardingTask'
229+
% else:
230+
% TASK_CREATE_FN = 'Builtin.createTask'
231+
% end
232+
180233
@available(SwiftStdlib 6.2, *)
181234
extension ${GROUP_TYPE} {
182235

183-
/// Create and immediately start running a new child task in the context of the calling thread/task.
236+
/// Add a child task to the group and immediately start running it in the context of the calling thread/task.
184237
///
185238
/// This function _starts_ the created task on the calling context.
186239
/// The task will continue executing on the caller's context until it suspends,
@@ -195,36 +248,116 @@ extension ${GROUP_TYPE} {
195248
/// Other than the execution semantics discussed above, the created task
196249
/// is semantically equivalent to its basic version which can be
197250
/// created using ``${GROUP_TYPE}/addTask``.
251+
///
252+
/// - Parameters:
253+
/// - name: Human readable name of this task.
254+
/// - priority: The priority of the operation task.
255+
/// Omit this parameter or pass `nil` to inherit the task group's base priority.
256+
/// - taskExecutor: The task executor that the child task should be started on and keep using.
257+
/// Explicitly passing `nil` as the executor preference is equivalent to
258+
/// calling the `${METHOD_NAME}` method without a preference, and effectively
259+
/// means to inherit the outer context's executor preference.
260+
/// You can also pass the ``globalConcurrentExecutor`` global executor explicitly.
261+
/// - operation: The operation to execute as part of the task group.
262+
% if IS_ADD_UNLESS_CANCELLED:
263+
/// - Returns: `true` if the child task was added to the group;
264+
/// otherwise `false`.
265+
% end
198266
@available(SwiftStdlib 6.2, *)
199267
@_alwaysEmitIntoClient
200-
public func ${METHOD_NAME}( // in ${GROUP_TYPE}
268+
public mutating func ${METHOD_NAME}( // in ${GROUP_TYPE}
201269
name: String? = nil,
202270
priority: TaskPriority? = nil,
271+
executorPreference taskExecutor: consuming (any TaskExecutor)? = nil,
203272
@_inheritActorContext @_implicitSelfCapture operation: sending @isolated(any) @escaping () async ${THROWS}-> ${RESULT_TYPE}
204-
) {
273+
) ${ARROW_RETURN_TYPE}{
274+
275+
% if IS_ADD_UNLESS_CANCELLED:
276+
let canAdd = _taskGroupAddPendingTask(group: _group, unconditionally: false)
277+
278+
guard canAdd else {
279+
// the group is cancelled and is not accepting any new work
280+
return false
281+
}
282+
% end # IS_ADD_UNLESS_CANCELLED
283+
205284
let flags = taskCreateFlags(
206285
priority: priority,
207286
isChildTask: true,
208287
copyTaskLocals: false,
209288
inheritContext: false,
210289
enqueueJob: false, // don't enqueue, we'll run it manually
290+
% if IS_ADD_UNLESS_CANCELLED:
291+
% # In this case, we already added the pending task count before we create the task
292+
% # so we must not add to the pending counter again.
293+
addPendingGroupTaskUnconditionally: false,
294+
% else:
211295
addPendingGroupTaskUnconditionally: true,
212-
isDiscardingTask: ${'true' if 'Discarding' in GROUP_TYPE else 'false'},
296+
% end
297+
isDiscardingTask: ${str(IS_DISCARDING).lower()},
213298
isSynchronousStart: true
214299
)
215300

216-
// Create the asynchronous task.
217301
let builtinSerialExecutor =
218302
unsafe Builtin.extractFunctionIsolation(operation)?.unownedExecutor.executor
219303

220-
// Create the task in this group.
221-
let (task, _) = Builtin.createTask(
222-
flags: flags,
223-
initialSerialExecutor: builtinSerialExecutor,
224-
taskGroup: self._group,
225-
operation: operation
226-
)
227-
_startTaskImmediately(task, targetExecutor: builtinSerialExecutor)
304+
var task: Builtin.NativeObject?
305+
306+
#if $BuiltinCreateAsyncTaskName
307+
if let name {
308+
task =
309+
unsafe name.utf8CString.withUnsafeBufferPointer { nameBytes in
310+
${TASK_CREATE_FN}(
311+
flags: flags,
312+
initialSerialExecutor: builtinSerialExecutor,
313+
taskGroup: _group,
314+
initialTaskExecutorConsuming: taskExecutor,
315+
taskName: nameBytes.baseAddress!._rawValue,
316+
operation: operation).0
317+
}
318+
}
319+
#endif // $BuiltinCreateAsyncTaskName
320+
321+
// Task name was not set, or task name createTask is unavailable
322+
if task == nil, let taskExecutor {
323+
#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
324+
task = ${TASK_CREATE_FN}(
325+
flags: flags,
326+
initialSerialExecutor: builtinSerialExecutor,
327+
taskGroup: _group,
328+
initialTaskExecutorConsuming: taskExecutor,
329+
operation: operation).0
330+
#else
331+
// legacy branch for the non-consuming task executor
332+
let executorBuiltin: Builtin.Executor =
333+
taskExecutor.asUnownedTaskExecutor().executor
334+
335+
task = ${TASK_CREATE_FN}(
336+
flags: flags,
337+
initialSerialExecutor: builtinSerialExecutor,
338+
taskGroup: _group,
339+
initialTaskExecutor: executorBuiltin,
340+
operation: operation).0
341+
#endif
342+
}
343+
344+
if task == nil {
345+
task = ${TASK_CREATE_FN}(
346+
flags: flags,
347+
initialSerialExecutor: builtinSerialExecutor,
348+
taskGroup: _group,
349+
operation: operation).0
350+
}
351+
352+
// Assert that we did create the task, but there's no need to store it,
353+
// as it was added to the group itself.
354+
assert(task != nil, "Expected task to be created!")
355+
356+
_startTaskImmediately(task!, targetExecutor: builtinSerialExecutor)
357+
358+
% if IS_ADD_UNLESS_CANCELLED:
359+
return true // task successfully enqueued
360+
% end
228361
}
229362
}
230363
% end # METHOD_NAMES
@@ -255,6 +388,7 @@ extension Task where Failure == ${FAILURE_TYPE} {
255388
@MainActor
256389
@available(SwiftStdlib 5.9, *)
257390
@discardableResult
391+
@available(*, deprecated, renamed: "immediate")
258392
public static func startOnMainActor(
259393
priority: TaskPriority? = nil,
260394
@_inheritActorContext @_implicitSelfCapture _ operation: __owned @Sendable @escaping @MainActor () async ${THROWS} -> Success

stdlib/public/Concurrency/Task.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,7 @@ swift_task_create_commonImpl(size_t rawTaskCreateFlags,
864864

865865
} else if (taskIsUnstructured(taskCreateFlags, jobFlags)) {
866866
SWIFT_TASK_DEBUG_LOG("Creating an unstructured task from %p%s", currentTask,
867-
taskCreateFlags.isSynchronousStartTask() ? " [start synchronously]" : "");
867+
taskCreateFlags.isImmediateTask() ? " [immediate]" : "");
868868

869869
if (isUnspecified(basePriority)) {
870870
// Case 1: No priority specified

stdlib/public/Concurrency/TaskGroup+addTask.swift.gyb

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,6 @@ extension ${TYPE} {
232232
/// - name: Human readable name of this task.
233233
% end
234234
% if HAS_TASK_EXECUTOR:
235-
/// - taskExecutor:
236235
/// - taskExecutor: The task executor that the child task should be started on and keep using.
237236
/// Explicitly passing `nil` as the executor preference is equivalent to
238237
/// calling the `${METHOD_NAME}` method without a preference, and effectively
@@ -243,8 +242,6 @@ extension ${TYPE} {
243242
/// - priority: The priority of the operation task.
244243
/// Omit this parameter or pass `nil` to inherit the task group's base priority.
245244
% end
246-
/// Omit this parameter or pass `.unspecified`
247-
/// to set the child task's priority to the priority of the group.
248245
/// - operation: The operation to execute as part of the task group.
249246
% if IS_ADD_UNLESS_CANCELLED:
250247
/// - Returns: `true` if the child task was added to the group;

stdlib/public/Concurrency/TaskStatus.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -521,8 +521,8 @@ swift_task_pushTaskExecutorPreferenceImpl(TaskExecutorRef taskExecutor) {
521521
// the executor.
522522
/*retainedExecutor=*/false);
523523
SWIFT_TASK_DEBUG_LOG("[TaskExecutorPreference] Create task executor "
524-
"preference record %p for task:%p",
525-
allocation, task);
524+
"preference record:%p taskExecutor:%p for task:%p",
525+
allocation, taskExecutor.getIdentity(), task);
526526

527527

528528
addStatusRecord(task, record,
@@ -589,8 +589,8 @@ void AsyncTask::pushInitialTaskExecutorPreference(
589589
::new (allocation) TaskExecutorPreferenceStatusRecord(
590590
preferredExecutor, /*ownsExecutor=*/owned);
591591
SWIFT_TASK_DEBUG_LOG("[InitialTaskExecutorPreference] Create a task "
592-
"preference record %p for task:%p",
593-
record, this);
592+
"preference record:%p taskExecutor:%p for task:%p",
593+
record, preferredExecutor.getIdentity(), this);
594594

595595
addStatusRecord(this, record,
596596
[&](ActiveTaskStatus oldStatus, ActiveTaskStatus &newStatus) {

0 commit comments

Comments
 (0)