Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Runtimes/Core/Concurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
add_subdirectory(InternalShims)

gyb_expand(Task+startSynchronously.swift.gyb Task+startSynchronously.swift)

add_library(swift_Concurrency
Actor.cpp
AsyncLet.cpp
Expand Down Expand Up @@ -80,7 +82,9 @@ add_library(swift_Concurrency
TaskGroup+TaskExecutor.swift
TaskLocal.swift
TaskSleep.swift
TaskSleepDuration.swift)
TaskSleepDuration.swift
"${CMAKE_CURRENT_BINARY_DIR}/Task+startSynchronously.swift")

include(${SwiftCore_CONCURRENCY_GLOBAL_EXECUTOR}.cmake)
target_compile_definitions(swift_Concurrency PRIVATE
$<$<COMPILE_LANGUAGE:C,CXX>:-DSWIFT_TARGET_LIBRARY_NAME=swift_Concurrency>
Expand Down
16 changes: 15 additions & 1 deletion include/swift/ABI/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ class SerialExecutorRef {
/// Executor that may need to participate in complex "same context" checks,
/// by invoking `isSameExclusiveExecutionContext` when comparing execution contexts.
ComplexEquality = 0b01,
/// Mark this executor as the one used by `Task.startSynchronously`,
/// It cannot participate in switching.
// TODO: Perhaps make this a generic "cannot switch" rather than start synchronously specific.
StartSynchronously = 0b10,
};

static_assert(static_cast<uintptr_t>(ExecutorKind::Ordinary) == 0);
Expand All @@ -101,6 +105,16 @@ class SerialExecutorRef {
return SerialExecutorRef(actor, 0);
}

static SerialExecutorRef forSynchronousStart() {
auto wtable = reinterpret_cast<uintptr_t>(nullptr) |
static_cast<uintptr_t>(ExecutorKind::StartSynchronously);
return SerialExecutorRef(nullptr, wtable);
}
bool isForSynchronousStart() const {
return getIdentity() == nullptr &&
getExecutorKind() == ExecutorKind::StartSynchronously;
}

/// Given a pointer to a serial executor and its SerialExecutor
/// conformance, return an executor reference for it.
static SerialExecutorRef forOrdinary(HeapObject *identity,
Expand All @@ -127,7 +141,7 @@ class SerialExecutorRef {

const char* getIdentityDebugName() const {
return isMainExecutor() ? " (MainActorExecutor)"
: isGeneric() ? " (GenericExecutor)"
: isGeneric() ? (isForSynchronousStart() ? " (GenericExecutor/SynchronousStart)" : " (GenericExecutor)")
: "";
}

Expand Down
7 changes: 5 additions & 2 deletions include/swift/ABI/MetadataValues.h
Original file line number Diff line number Diff line change
Expand Up @@ -2668,13 +2668,13 @@ class TaskCreateFlags : public FlagSet<size_t> {
Task_EnqueueJob = 12,
Task_AddPendingGroupTaskUnconditionally = 13,
Task_IsDiscardingTask = 14,

/// The task function is consumed by calling it (@callee_owned).
/// The context pointer should be treated as opaque and non-copyable;
/// in particular, it should not be retained or released.
///
/// Supported starting in Swift 6.1.
Task_IsTaskFunctionConsumed = 15,
Task_IsTaskFunctionConsumed = 15,
Task_IsStartSynchronouslyTask = 16,
};

explicit constexpr TaskCreateFlags(size_t bits) : FlagSet(bits) {}
Expand Down Expand Up @@ -2707,6 +2707,9 @@ class TaskCreateFlags : public FlagSet<size_t> {
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsTaskFunctionConsumed,
isTaskFunctionConsumed,
setIsTaskFunctionConsumed)
FLAGSET_DEFINE_FLAG_ACCESSORS(Task_IsStartSynchronouslyTask,
isSynchronousStartTask,
setIsSYnchronousStartTask)
};

/// Flags for schedulable jobs.
Expand Down
3 changes: 3 additions & 0 deletions include/swift/Runtime/Concurrency.h
Original file line number Diff line number Diff line change
Expand Up @@ -1025,6 +1025,9 @@ JobPriority swift_task_getCurrentThreadPriority(void);
SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_startOnMainActor(AsyncTask* job);

SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift)
void swift_task_startSynchronously(AsyncTask* job);

/// Donate this thread to the global executor until either the
/// given condition returns true or we've run out of cooperative
/// tasks to run.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,11 @@ OVERRIDE_TASK(task_startOnMainActor, void,
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
swift::, (AsyncTask *task), (task))

// In ACTOR since we need ExecutorTracking info
OVERRIDE_ACTOR(task_startSynchronously, void,
SWIFT_EXPORT_FROM(swift_Concurrency), SWIFT_CC(swift),
swift::, (AsyncTask *task), (task))

#undef OVERRIDE
#undef OVERRIDE_ACTOR
#undef OVERRIDE_TASK
Expand Down
47 changes: 40 additions & 7 deletions stdlib/public/Concurrency/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1683,8 +1683,8 @@ static void defaultActorDrain(DefaultActorImpl *actor) {
trackingInfo.setTaskExecutor(taskExecutor);
}

// This thread is now going to follow the task on this actor. It may hop off
// the actor
// This thread is now going to follow the task on this actor.
// It may hop off the actor
runJobInEstablishedExecutorContext(job);

// We could have come back from the job on a generic executor and not as
Expand Down Expand Up @@ -2027,7 +2027,9 @@ static void swift_job_runImpl(Job *job, SerialExecutorRef executor) {
// run jobs. Actor executors won't expect us to switch off them
// during this operation. But do allow switching if the executor
// is generic.
if (!executor.isGeneric()) trackingInfo.disallowSwitching();
if (!executor.isGeneric()) {
trackingInfo.disallowSwitching();
}

auto taskExecutor = executor.isGeneric()
? TaskExecutorRef::fromTaskExecutorPreference(job)
Expand Down Expand Up @@ -2149,13 +2151,19 @@ static bool canGiveUpThreadForSwitch(ExecutorTrackingInfo *trackingInfo,
assert(trackingInfo || currentExecutor.isGeneric());

// Some contexts don't allow switching at all.
if (trackingInfo && !trackingInfo->allowsSwitching())
if (trackingInfo && !trackingInfo->allowsSwitching()) {
return false;
}

// We can certainly "give up" a generic executor to try to run
// a task for an actor.
if (currentExecutor.isGeneric())
if (currentExecutor.isGeneric()) {
if (currentExecutor.isForSynchronousStart()) {
return false;
}

return true;
}

// If the current executor is a default actor, we know how to make
// it give up its thread.
Expand Down Expand Up @@ -2276,7 +2284,8 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
: TaskExecutorRef::undefined());
auto newTaskExecutor = task->getPreferredTaskExecutor();
SWIFT_TASK_DEBUG_LOG("Task %p trying to switch executors: executor %p%s to "
"new serial executor: %p%s; task executor: from %p%s to %p%s",
"new serial executor: %p%s; task executor: from %p%s to %p%s"
"%s",
task,
currentExecutor.getIdentity(),
currentExecutor.getIdentityDebugName(),
Expand All @@ -2285,13 +2294,15 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
currentTaskExecutor.getIdentity(),
currentTaskExecutor.isDefined() ? "" : " (undefined)",
newTaskExecutor.getIdentity(),
newTaskExecutor.isDefined() ? "" : " (undefined)");
newTaskExecutor.isDefined() ? "" : " (undefined)",
trackingInfo->isSynchronousStart() ? "[synchronous start]" : "");

// If the current executor is compatible with running the new executor,
// we can just immediately continue running with the resume function
// we were passed in.
if (!mustSwitchToRun(currentExecutor, newExecutor, currentTaskExecutor,
newTaskExecutor)) {
SWIFT_TASK_DEBUG_LOG("Task %p run inline", task);
return resumeFunction(resumeContext); // 'return' forces tail call
}

Expand All @@ -2303,6 +2314,7 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
// If the current executor can give up its thread, and the new executor
// can take over a thread, try to do so; but don't do this if we've
// been asked to yield the thread.
SWIFT_TASK_DEBUG_LOG("Task %p can give up thread?", task);
if (currentTaskExecutor.isUndefined() &&
canGiveUpThreadForSwitch(trackingInfo, currentExecutor) &&
!shouldYieldThread() &&
Expand All @@ -2325,6 +2337,27 @@ static void swift_task_switchImpl(SWIFT_ASYNC_CONTEXT AsyncContext *resumeContex
task->flagAsAndEnqueueOnExecutor(newExecutor);
}

SWIFT_CC(swift)
static void swift_task_startSynchronouslyImpl(AsyncTask* task) {
swift_retain(task);

auto currentTracking = ExecutorTrackingInfo::current();
if (currentTracking) {
auto currentExecutor = currentTracking->getActiveExecutor();
AsyncTask * originalTask = _swift_task_clearCurrent();

swift_job_run(task, currentExecutor);
_swift_task_setCurrent(originalTask);
} else {
auto originalTask = ActiveTask::swap(task);
assert(!originalTask);

SerialExecutorRef executor = SerialExecutorRef::forSynchronousStart();
swift_job_run(task, executor);
_swift_task_setCurrent(originalTask);
}
}

#if !SWIFT_CONCURRENCY_ACTORS_AS_LOCKS
namespace {
/// Job that allows to use executor API to schedule a block of task-less
Expand Down
3 changes: 3 additions & 0 deletions stdlib/public/Concurrency/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} I
${SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES}
${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES}

GYB_SOURCES
Task+startSynchronously.swift.gyb

SWIFT_MODULE_DEPENDS_ANDROID Android
SWIFT_MODULE_DEPENDS_LINUX Glibc
SWIFT_MODULE_DEPENDS_LINUX_STATIC Musl
Expand Down
24 changes: 16 additions & 8 deletions stdlib/public/Concurrency/DiscardingTaskGroup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,15 @@ public struct DiscardingTaskGroup {
let flags = taskCreateFlags(
priority: priority, isChildTask: true, copyTaskLocals: false,
inheritContext: false, enqueueJob: false,
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true,
isSynchronousStart: false
)
#else
let flags = taskCreateFlags(
priority: priority, isChildTask: true, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true,
isSynchronousStart: false
)
#endif

Expand Down Expand Up @@ -252,13 +254,15 @@ public struct DiscardingTaskGroup {
let flags = taskCreateFlags(
priority: priority, isChildTask: true, copyTaskLocals: false,
inheritContext: false, enqueueJob: false,
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true,
isSynchronousStart: false
)
#else
let flags = taskCreateFlags(
priority: priority, isChildTask: true, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true,
isSynchronousStart: false
)
#endif

Expand All @@ -281,7 +285,8 @@ public struct DiscardingTaskGroup {
let flags = taskCreateFlags(
priority: nil, isChildTask: true, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true,
isSynchronousStart: false
)

// Create the task in this group.
Expand Down Expand Up @@ -317,7 +322,8 @@ public struct DiscardingTaskGroup {
let flags = taskCreateFlags(
priority: nil, isChildTask: true, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true,
isSynchronousStart: false
)

// Create the task in this group.
Expand Down Expand Up @@ -635,7 +641,8 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
let flags = taskCreateFlags(
priority: priority, isChildTask: true, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true
addPendingGroupTaskUnconditionally: true, isDiscardingTask: true,
isSynchronousStart: false
)

// Create the task in this group.
Expand Down Expand Up @@ -666,7 +673,8 @@ public struct ThrowingDiscardingTaskGroup<Failure: Error> {
let flags = taskCreateFlags(
priority: priority, isChildTask: true, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true
addPendingGroupTaskUnconditionally: false, isDiscardingTask: true,
isSynchronousStart: false
)

// Create the task in this group.
Expand Down
1 change: 0 additions & 1 deletion stdlib/public/Concurrency/Executor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,6 @@ internal final class DispatchQueueShim: @unchecked Sendable, SerialExecutor {
}
#endif // SWIFT_CONCURRENCY_USES_DISPATCH


@available(SwiftStdlib 6.1, *)
@_silgen_name("swift_task_deinitOnExecutor")
@usableFromInline
Expand Down
8 changes: 4 additions & 4 deletions stdlib/public/Concurrency/Task+TaskExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ extension Task where Failure == Never {
priority: priority, isChildTask: false, copyTaskLocals: true,
inheritContext: true, enqueueJob: true,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false)
isDiscardingTask: false, isSynchronousStart: false)

#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
let (task, _) = Builtin.createTask(
Expand Down Expand Up @@ -303,7 +303,7 @@ extension Task where Failure == Error {
priority: priority, isChildTask: false, copyTaskLocals: true,
inheritContext: true, enqueueJob: true,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false)
isDiscardingTask: false, isSynchronousStart: false)

#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
let (task, _) = Builtin.createTask(
Expand Down Expand Up @@ -364,7 +364,7 @@ extension Task where Failure == Never {
priority: priority, isChildTask: false, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false)
isDiscardingTask: false, isSynchronousStart: false)

#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
let (task, _) = Builtin.createTask(
Expand Down Expand Up @@ -425,7 +425,7 @@ extension Task where Failure == Error {
priority: priority, isChildTask: false, copyTaskLocals: false,
inheritContext: false, enqueueJob: true,
addPendingGroupTaskUnconditionally: false,
isDiscardingTask: false)
isDiscardingTask: false, isSynchronousStart: false)

#if $BuiltinCreateAsyncTaskOwnedTaskExecutor
let (task, _) = Builtin.createTask(
Expand Down
Loading