Skip to content

Commit efe6973

Browse files
drexinktoso
andauthored
[Concurrency] Reduce overhead of Task.yield and Task.sleep (#37090)
* [Concurrency] Reduce overhead of Task.yield and Task.sleep Instead of creating a new task, we create a simple job that wraps a Builtin.RawUnsafeContinuation and resumes the continuation when it is executed. The job instance is allocated on the task local allocator, meaning we don't malloc anything. * Update stdlib/public/Concurrency/Task.swift Co-authored-by: Konrad `ktoso` Malawski <[email protected]> Co-authored-by: Konrad `ktoso` Malawski <[email protected]>
1 parent 7487c12 commit efe6973

File tree

6 files changed

+84
-29
lines changed

6 files changed

+84
-29
lines changed

include/swift/ABI/MetadataValues.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1983,7 +1983,8 @@ enum class JobKind : size_t {
19831983

19841984
DefaultActorInline = First_Reserved,
19851985
DefaultActorSeparate,
1986-
DefaultActorOverride
1986+
DefaultActorOverride,
1987+
NullaryContinuation
19871988
};
19881989

19891990
/// The priority of a job. Higher priorities are larger values.

include/swift/ABI/Task.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,25 @@ class ActiveTaskStatus {
166166
}
167167
};
168168

169+
class NullaryContinuationJob : public Job {
170+
171+
private:
172+
AsyncTask* Task;
173+
AsyncTask* Continuation;
174+
175+
public:
176+
NullaryContinuationJob(AsyncTask *task, JobPriority priority, AsyncTask *continuation)
177+
: Job({JobKind::NullaryContinuation, priority}, &process),
178+
Task(task), Continuation(continuation) {}
179+
180+
SWIFT_CC(swiftasync)
181+
static void process(Job *job);
182+
183+
static bool classof(const Job *job) {
184+
return job->Flags.getKind() == JobKind::NullaryContinuation;
185+
}
186+
};
187+
169188
/// An asynchronous task. Tasks are the analogue of threads for
170189
/// asynchronous functions: that is, they are a persistent identity
171190
/// for the overall async computation.
@@ -588,6 +607,10 @@ class ContinuationAsyncContext : public AsyncContext {
588607
/// The executor that should be resumed to.
589608
ExecutorRef ResumeToExecutor;
590609

610+
void setErrorResult(SwiftError *error) {
611+
ErrorResult = error;
612+
}
613+
591614
static bool classof(const AsyncContext *context) {
592615
return context->Flags.getKind() == AsyncContextKind::Continuation;
593616
}

include/swift/Runtime/Concurrency.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifndef SWIFT_RUNTIME_CONCURRENCY_H
1818
#define SWIFT_RUNTIME_CONCURRENCY_H
1919

20+
#include "swift/ABI/Task.h"
2021
#include "swift/ABI/TaskGroup.h"
2122
#include "swift/ABI/AsyncLet.h"
2223
#include "swift/ABI/TaskStatus.h"
@@ -419,6 +420,13 @@ SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
419420
void swift_task_removeCancellationHandler(
420421
CancellationNotificationStatusRecord *record);
421422

423+
/// Create a NullaryContinuationJob from a continuation.
424+
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
425+
NullaryContinuationJob*
426+
swift_task_createNullaryContinuationJob(
427+
size_t priority,
428+
AsyncTask *continuation);
429+
422430
/// Report error about attempting to bind a task-local value from an illegal context.
423431
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
424432
void swift_task_reportIllegalTaskLocalBindingWithinWithTaskGroup(

stdlib/public/CompatibilityOverride/CompatibilityOverrideConcurrency.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ OVERRIDE_TASK(task_removeCancellationHandler, void,
146146
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
147147
(CancellationNotificationStatusRecord *record), (record))
148148

149+
OVERRIDE_TASK(task_createNullaryContinuationJob, NullaryContinuationJob *,
150+
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
151+
(size_t priority,
152+
AsyncTask *continuation), (priority, continuation))
153+
149154
OVERRIDE_TASK(task_asyncMainDrainQueue, void,
150155
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift), swift::,
151156
, )

stdlib/public/Concurrency/Task.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ FutureFragment::Status AsyncTask::waitFuture(AsyncTask *waitingTask) {
9696
}
9797
}
9898

99+
void NullaryContinuationJob::process(Job *_job) {
100+
auto *job = cast<NullaryContinuationJob>(_job);
101+
102+
auto *task = job->Task;
103+
auto *continuation = job->Continuation;
104+
105+
_swift_task_dealloc_specific(task, job);
106+
107+
auto *context = cast<ContinuationAsyncContext>(continuation->ResumeContext);
108+
109+
context->setErrorResult(nullptr);
110+
swift_continuation_resume(continuation);
111+
}
112+
99113
void AsyncTask::completeFuture(AsyncContext *context) {
100114
using Status = FutureFragment::Status;
101115
using WaitQueueItem = FutureFragment::WaitQueueItem;
@@ -962,6 +976,21 @@ static void swift_task_removeCancellationHandlerImpl(
962976
swift_task_dealloc(record);
963977
}
964978

979+
SWIFT_CC(swift)
980+
static NullaryContinuationJob*
981+
swift_task_createNullaryContinuationJobImpl(
982+
size_t priority,
983+
AsyncTask *continuation) {
984+
void *allocation =
985+
swift_task_alloc(sizeof(NullaryContinuationJob));
986+
auto *job =
987+
new (allocation) NullaryContinuationJob(
988+
swift_task_getCurrent(), static_cast<JobPriority>(priority),
989+
continuation);
990+
991+
return job;
992+
}
993+
965994
SWIFT_CC(swift)
966995
void swift::swift_continuation_logFailedCheck(const char *message) {
967996
swift_reportError(0, message);

stdlib/public/Concurrency/Task.swift

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -597,19 +597,13 @@ extension Task {
597597
///
598598
/// This function does _not_ block the underlying thread.
599599
public static func sleep(_ duration: UInt64) async {
600-
// Set up the job flags for a new task.
601-
var flags = Task.JobFlags()
602-
flags.kind = .task
603-
flags.priority = .default
604-
flags.isFuture = true
600+
let currentTask = Builtin.getCurrentAsyncTask()
601+
let priority = getJobFlags(currentTask).priority ?? Task.currentPriority._downgradeUserInteractive
605602

606-
// Create the asynchronous task future.
607-
let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, {})
608-
609-
// Enqueue the resulting job.
610-
_enqueueJobGlobalWithDelay(duration, Builtin.convertTaskToJob(task))
611-
612-
await Handle<Void, Never>(task).get()
603+
return await Builtin.withUnsafeContinuation { (continuation: Builtin.RawUnsafeContinuation) -> Void in
604+
let job = _taskCreateNullaryContinuationJob(priority: priority.rawValue, continuation: continuation)
605+
_enqueueJobGlobalWithDelay(duration, job)
606+
}
613607
}
614608
}
615609

@@ -625,22 +619,13 @@ extension Task {
625619
/// if the task is the highest-priority task in the system, it might go
626620
/// immediately back to executing.
627621
public static func yield() async {
628-
// Prepare the job flags
629-
var flags = JobFlags()
630-
flags.kind = .task
631-
flags.priority = .default
632-
flags.isFuture = true
633-
634-
// Create the asynchronous task future, it will do nothing, but simply serves
635-
// as a way for us to yield our execution until the executor gets to it and
636-
// resumes us.
637-
// TODO: consider if it would be useful for this task to be a child task
638-
let (task, _) = Builtin.createAsyncTaskFuture(flags.bits, {})
639-
640-
// Enqueue the resulting job.
641-
_enqueueJobGlobal(Builtin.convertTaskToJob(task))
642-
643-
let _ = await Handle<Void, Never>(task).get()
622+
let currentTask = Builtin.getCurrentAsyncTask()
623+
let priority = getJobFlags(currentTask).priority ?? Task.currentPriority._downgradeUserInteractive
624+
625+
return await Builtin.withUnsafeContinuation { (continuation: Builtin.RawUnsafeContinuation) -> Void in
626+
let job = _taskCreateNullaryContinuationJob(priority: priority.rawValue, continuation: continuation)
627+
_enqueueJobGlobal(job)
628+
}
644629
}
645630
}
646631

@@ -818,6 +803,10 @@ func _taskCancel(_ task: Builtin.NativeObject)
818803
@_silgen_name("swift_task_isCancelled")
819804
func _taskIsCancelled(_ task: Builtin.NativeObject) -> Bool
820805

806+
@available(SwiftStdlib 5.5, *)
807+
@_silgen_name("swift_task_createNullaryContinuationJob")
808+
func _taskCreateNullaryContinuationJob(priority: Int, continuation: Builtin.RawUnsafeContinuation) -> Builtin.Job
809+
821810
@available(SwiftStdlib 5.5, *)
822811
@usableFromInline
823812
@_silgen_name("swift_task_isCurrentExecutor")

0 commit comments

Comments
 (0)