From 090c375b7b53c85dbaa7b02d8a04dc5d7d60a823 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 7 Feb 2025 09:05:52 +0000 Subject: [PATCH 01/29] [Concurrency] Swift interface for custom main and global executors. Reorganise the Concurrency code so that it's possible to completely implement executors (both main and global) in Swift. Provide API to choose the desired executors for your application. Also make `Task.Sleep` wait using the current executor, not the global executor, and expose APIs on `Clock` to allow for conversion between time bases. rdar://141348916 --- include/swift/Runtime/Concurrency.h | 23 ++ stdlib/public/Concurrency/Actor.cpp | 35 ++ stdlib/public/Concurrency/CFExecutor.swift | 66 +++ stdlib/public/Concurrency/CMakeLists.txt | 36 +- stdlib/public/Concurrency/Clock.swift | 108 ++++- .../public/Concurrency/ContinuousClock.swift | 39 +- .../public/Concurrency/DispatchExecutor.swift | 226 ++++++++++ .../Concurrency/DispatchGlobalExecutor.cpp | 183 ++------ stdlib/public/Concurrency/Executor.swift | 389 +++++++++++++++++- stdlib/public/Concurrency/ExecutorBridge.cpp | 151 +++++++ stdlib/public/Concurrency/ExecutorBridge.h | 42 ++ .../public/Concurrency/ExecutorBridge.swift | 118 ++++++ stdlib/public/Concurrency/ExecutorImpl.h | 51 ++- stdlib/public/Concurrency/ExecutorImpl.swift | 95 +++++ ...rtialAsyncTask.swift => ExecutorJob.swift} | 158 ++++++- .../GlobalConcurrentExecutor.swift | 11 +- stdlib/public/Concurrency/MainActor.swift | 8 +- .../Concurrency/NonDispatchGlobalExecutor.cpp | 108 ----- .../Concurrency/PlatformExecutorDarwin.swift | 122 ++++++ .../PlatformExecutorEmbedded.swift | 64 +++ .../Concurrency/PlatformExecutorLinux.swift | 21 + .../Concurrency/PlatformExecutorNone.swift | 60 +++ .../Concurrency/PlatformExecutorWindows.swift | 21 + .../public/Concurrency/SuspendingClock.swift | 43 +- .../Concurrency/Task+TaskExecutor.swift | 4 +- stdlib/public/Concurrency/Task.cpp | 12 +- stdlib/public/Concurrency/Task.swift | 20 +- stdlib/public/Concurrency/TaskAlloc.cpp | 14 + stdlib/public/Concurrency/TaskSleep.swift | 30 +- .../Concurrency/TaskSleepDuration.swift | 48 +-- test/Concurrency/Runtime/actor_counters.swift | 8 +- .../Runtime/async_let_fibonacci.swift | 4 +- .../async_task_executor_nsobject.swift | 26 ++ test/Concurrency/Runtime/mainactor.swift | 2 +- 34 files changed, 1934 insertions(+), 412 deletions(-) create mode 100644 stdlib/public/Concurrency/CFExecutor.swift create mode 100644 stdlib/public/Concurrency/DispatchExecutor.swift create mode 100644 stdlib/public/Concurrency/ExecutorBridge.cpp create mode 100644 stdlib/public/Concurrency/ExecutorBridge.h create mode 100644 stdlib/public/Concurrency/ExecutorBridge.swift create mode 100644 stdlib/public/Concurrency/ExecutorImpl.swift rename stdlib/public/Concurrency/{PartialAsyncTask.swift => ExecutorJob.swift} (84%) delete mode 100644 stdlib/public/Concurrency/NonDispatchGlobalExecutor.cpp create mode 100644 stdlib/public/Concurrency/PlatformExecutorDarwin.swift create mode 100644 stdlib/public/Concurrency/PlatformExecutorEmbedded.swift create mode 100644 stdlib/public/Concurrency/PlatformExecutorLinux.swift create mode 100644 stdlib/public/Concurrency/PlatformExecutorNone.swift create mode 100644 stdlib/public/Concurrency/PlatformExecutorWindows.swift diff --git a/include/swift/Runtime/Concurrency.h b/include/swift/Runtime/Concurrency.h index 55995b0926f0b..befda4a72da17 100644 --- a/include/swift/Runtime/Concurrency.h +++ b/include/swift/Runtime/Concurrency.h @@ -149,6 +149,29 @@ SWIFT_EXPORT_FROM(swift_Concurrency) CoroAllocator *const _swift_coro_malloc_allocator; // }} TODO: CoroutineAccessors +/// Deallocate memory in a task. +/// +/// The pointer provided must be the last pointer allocated on +/// this task that has not yet been deallocated; that is, memory +/// must be allocated and deallocated in a strict stack discipline. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_task_dealloc(void *ptr); + +/// Allocate memory in a job. +/// +/// All allocations will be rounded to a multiple of MAX_ALIGNMENT; +/// if the job does not support allocation, this will return NULL. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void *swift_job_allocate(Job *job, size_t size); + +/// Deallocate memory in a job. +/// +/// The pointer provided must be the last pointer allocated on +/// this task that has not yet been deallocated; that is, memory +/// must be allocated and deallocated in a strict stack discipline. +SWIFT_EXPORT_FROM(swift_Concurrency) SWIFT_CC(swift) +void swift_job_deallocate(Job *job, void *ptr); + /// Cancel a task and all of its child tasks. /// /// This can be called from any thread. diff --git a/stdlib/public/Concurrency/Actor.cpp b/stdlib/public/Concurrency/Actor.cpp index d9a493590563e..103af5a70ba0b 100644 --- a/stdlib/public/Concurrency/Actor.cpp +++ b/stdlib/public/Concurrency/Actor.cpp @@ -25,6 +25,7 @@ #include "../CompatibilityOverride/CompatibilityOverride.h" #include "swift/ABI/Actor.h" #include "swift/ABI/Task.h" +#include "ExecutorBridge.h" #include "TaskPrivate.h" #include "swift/Basic/HeaderFooterLayout.h" #include "swift/Basic/PriorityQueue.h" @@ -289,6 +290,40 @@ static SerialExecutorRef swift_task_getCurrentExecutorImpl() { return result; } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" + +extern "C" SWIFT_CC(swift) +SerialExecutorRef _swift_getActiveExecutor() { + auto currentTracking = ExecutorTrackingInfo::current(); + if (currentTracking) { + SerialExecutorRef executor = currentTracking->getActiveExecutor(); + // This might be an actor, in which case return nil ("generic") + if (executor.isDefaultActor()) + return SerialExecutorRef::generic(); + return executor; + } + return swift_getMainExecutor(); +} + +extern "C" SWIFT_CC(swift) +TaskExecutorRef _swift_getCurrentTaskExecutor() { + auto currentTracking = ExecutorTrackingInfo::current(); + if (currentTracking) + return currentTracking->getTaskExecutor(); + return TaskExecutorRef::undefined(); +} + +extern "C" SWIFT_CC(swift) +TaskExecutorRef _swift_getPreferredTaskExecutor() { + AsyncTask *task = swift_task_getCurrent(); + if (!task) + return TaskExecutorRef::undefined(); + return task->getPreferredTaskExecutor(); +} + +#pragma clang diagnostic pop + /// Determine whether we are currently executing on the main thread /// independently of whether we know that we are on the main actor. static bool isExecutingOnMainThread() { diff --git a/stdlib/public/Concurrency/CFExecutor.swift b/stdlib/public/Concurrency/CFExecutor.swift new file mode 100644 index 0000000000000..c24fc73511b46 --- /dev/null +++ b/stdlib/public/Concurrency/CFExecutor.swift @@ -0,0 +1,66 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)) + +import Swift + +internal import Darwin + +// .. Dynamic binding .......................................................... + +enum CoreFoundation { + static let path = + "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" + + static let handle = dlopen(path, RTLD_NOLOAD) + + static var isPresent: Bool { return handle != nil } + + static func symbol(_ name: String) -> T { + guard let result = dlsym(handle, name) else { + fatalError("Unable to look up \(name) in CoreFoundation") + } + return unsafeBitCast(result, to: T.self) + } + + static let CFRunLoopRun: @convention(c) () -> () = + symbol("CFRunLoopRun") + static let CFRunLoopGetMain: @convention(c) () -> OpaquePointer = + symbol("CFRunLoopGetMain") + static let CFRunLoopStop: @convention(c) (OpaquePointer) -> () = + symbol("CFRunLoopStop") +} + +// .. Main Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public final class CFMainExecutor: DispatchMainExecutor, @unchecked Sendable { + + override public func run() throws { + CoreFoundation.CFRunLoopRun() + } + + override public func stop() { + CoreFoundation.CFRunLoopStop(CoreFoundation.CFRunLoopGetMain()) + } + +} + +// .. Task Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public final class CFTaskExecutor: DispatchTaskExecutor, @unchecked Sendable { + +} + +#endif diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 54f7daa100633..67dda68ab1c54 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -79,6 +79,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_C_SOURCES ConcurrencyHooks.cpp EmbeddedSupport.cpp Error.cpp + ExecutorBridge.cpp ExecutorChecks.cpp Setup.cpp Task.cpp @@ -92,25 +93,13 @@ set(SWIFT_RUNTIME_CONCURRENCY_C_SOURCES linker-support/magic-symbols-for-install-name.c ) -if("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "dispatch") - set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES - DispatchGlobalExecutor.cpp - ) -elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "singlethreaded") - set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES - CooperativeGlobalExecutor.cpp - ) -elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "hooked" OR - "${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "none") - set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES - NonDispatchGlobalExecutor.cpp +set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES + DispatchGlobalExecutor.cpp ) -endif() set(LLVM_OPTIONAL_SOURCES CooperativeGlobalExecutor.cpp DispatchGlobalExecutor.cpp - NonDispatchGlobalExecutor.cpp ) set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES @@ -119,6 +108,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES CheckedContinuation.swift Errors.swift Executor.swift + ExecutorBridge.swift ExecutorAssertions.swift AsyncCompactMapSequence.swift AsyncDropFirstSequence.swift @@ -136,10 +126,10 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES AsyncThrowingFlatMapSequence.swift AsyncThrowingMapSequence.swift AsyncThrowingPrefixWhileSequence.swift + ExecutorJob.swift GlobalActor.swift GlobalConcurrentExecutor.swift MainActor.swift - PartialAsyncTask.swift SourceCompatibilityShims.swift Task.swift Task+PriorityEscalation.swift @@ -176,12 +166,26 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES ContinuousClock.swift SuspendingClock.swift TaskSleepDuration.swift + DispatchExecutor.swift + CFExecutor.swift + PlatformExecutorDarwin.swift + PlatformExecutorLinux.swift + PlatformExecutorWindows.swift ) +set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES + ExecutorImpl.swift +) + +set(SWIFT_RUNTIME_CONCURRENCY_EMBEDDED_SWIFT_SOURCES + PlatformExecutorEmbedded.swift + ) + add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB ${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES} + ${SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES} GYB_SOURCES TaskGroup+addTask.swift.gyb @@ -279,6 +283,7 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC ${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES} ${SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES} + ${SWIFT_RUNTIME_CONCURRENCY_EMBEDDED_SWIFT_SOURCES} SWIFT_COMPILE_FLAGS ${extra_swift_compile_flags} -enable-experimental-feature Embedded @@ -358,4 +363,3 @@ if(SWIFT_SHOULD_BUILD_EMBEDDED_STDLIB AND SWIFT_SHOULD_BUILD_EMBEDDED_CONCURRENC add_dependencies(embedded-concurrency "copy_executor_impl_header") endif() - diff --git a/stdlib/public/Concurrency/Clock.swift b/stdlib/public/Concurrency/Clock.swift index 56a85e437fcb3..c5156cfa3615c 100644 --- a/stdlib/public/Concurrency/Clock.swift +++ b/stdlib/public/Concurrency/Clock.swift @@ -11,24 +11,24 @@ //===----------------------------------------------------------------------===// import Swift -/// A mechanism in which to measure time, and delay work until a given point +/// A mechanism in which to measure time, and delay work until a given point /// in time. /// -/// Types that conform to the `Clock` protocol define a concept of "now" which +/// Types that conform to the `Clock` protocol define a concept of "now" which /// is the specific instant in time that property is accessed. Any pair of calls /// to the `now` property may have a minimum duration between them - this /// minimum resolution is exposed by the `minimumResolution` property to inform -/// any user of the type the expected granularity of accuracy. +/// any user of the type the expected granularity of accuracy. /// /// One of the primary uses for clocks is to schedule task sleeping. This method /// resumes the calling task after a given deadline has been met or passed with -/// a given tolerance value. The tolerance is expected as a leeway around the -/// deadline. The clock may reschedule tasks within the tolerance to ensure +/// a given tolerance value. The tolerance is expected as a leeway around the +/// deadline. The clock may reschedule tasks within the tolerance to ensure /// efficient execution of resumptions by reducing potential operating system /// wake-ups. If no tolerance is specified (i.e. nil is passed in) the sleep -/// function is expected to schedule with a default tolerance strategy. +/// function is expected to schedule with a default tolerance strategy. /// -/// For more information about specific clocks see `ContinuousClock` and +/// For more information about specific clocks see `ContinuousClock` and /// `SuspendingClock`. @available(SwiftStdlib 5.7, *) public protocol Clock: Sendable { @@ -41,8 +41,56 @@ public protocol Clock: Sendable { #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws #endif -} +#if !$Embedded + /// Choose which Dispatch clock to use with DispatchExecutor + /// + /// This controls which Dispatch clock is used to enqueue delayed jobs + /// when using this Clock. + @available(SwiftStdlib 6.2, *) + var dispatchClockID: DispatchClockID { get } +#endif + + /// Convert a Clock-specific Duration to a Swift Duration + /// + /// Some clocks may define `C.Duration` to be something other than a + /// `Swift.Duration`, but that makes it tricky to convert timestamps + /// between clocks, which is something we want to be able to support. + /// This method will convert whatever `C.Duration` is to a `Swift.Duration`. + /// + /// Parameters: + /// + /// - from duration: The `Duration` to convert + /// + /// Returns: A `Swift.Duration` representing the equivalent duration, or + /// `nil` if this function is not supported. + @available(SwiftStdlib 6.2, *) + func convert(from duration: Duration) -> Swift.Duration? + + /// Convert a Swift Duration to a Clock-specific Duration + /// + /// Parameters: + /// + /// - from duration: The `Swift.Duration` to convert. + /// + /// Returns: A `Duration` representing the equivalent duration, or + /// `nil` if this function is not supported. + @available(SwiftStdlib 6.2, *) + func convert(from duration: Swift.Duration) -> Duration? + + /// Convert an `Instant` from some other clock's `Instant` + /// + /// Parameters: + /// + /// - instant: The instant to convert. + // - from clock: The clock to convert from. + /// + /// Returns: An `Instant` representing the equivalent instant, or + /// `nil` if this function is not supported. + @available(SwiftStdlib 6.2, *) + func convert(instant: OtherClock.Instant, + from clock: OtherClock) -> Instant? +} @available(SwiftStdlib 5.7, *) extension Clock { @@ -88,6 +136,50 @@ extension Clock { } } +@available(SwiftStdlib 6.2, *) +extension Clock { + #if !$Embedded + public var dispatchClockID: DispatchClockID { + return .suspending + } + #endif + + // For compatibility, return `nil` if this is not implemented + public func convert(from duration: Duration) -> Swift.Duration? { + return nil + } + + public func convert(from duration: Swift.Duration) -> Duration? { + return nil + } + + public func convert(instant: OtherClock.Instant, + from clock: OtherClock) -> Instant? { + let ourNow = now + let otherNow = clock.now + let otherDuration = otherNow.duration(to: instant) + + // Convert to `Swift.Duration` + guard let duration = clock.convert(from: otherDuration) else { + return nil + } + + // Convert from `Swift.Duration` + guard let ourDuration = convert(from: duration) else { + return nil + } + + return ourNow.advanced(by: ourDuration) + } +} + +@available(SwiftStdlib 6.2, *) +extension Clock where Duration == Swift.Duration { + public func convert(from duration: Duration) -> Duration? { + return duration + } +} + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.7, *) extension Clock { diff --git a/stdlib/public/Concurrency/ContinuousClock.swift b/stdlib/public/Concurrency/ContinuousClock.swift index 604f0ff022430..34f4025a4f6d1 100644 --- a/stdlib/public/Concurrency/ContinuousClock.swift +++ b/stdlib/public/Concurrency/ContinuousClock.swift @@ -11,9 +11,8 @@ //===----------------------------------------------------------------------===// import Swift -#if !$Embedded -/// A clock that measures time that always increments and does not stop -/// incrementing while the system is asleep. +/// A clock that measures time that always increments and does not stop +/// incrementing while the system is asleep. /// /// `ContinuousClock` can be considered as a stopwatch style time. The frame of /// reference of the `Instant` may be bound to process launch, machine boot or @@ -22,9 +21,10 @@ import Swift /// /// This clock is suitable for high resolution measurements of execution. @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded public struct ContinuousClock: Sendable { /// A continuous point in time used for `ContinuousClock`. - public struct Instant: Codable, Sendable { + public struct Instant: Sendable { internal var _value: Swift.Duration internal init(_value: Swift.Duration) { @@ -34,6 +34,11 @@ public struct ContinuousClock: Sendable { public init() { } } + +#if !$Embedded +@available(SwiftStdlib 5.7, *) +extension ContinuousClock.Instant: Codable { +} #endif @available(SwiftStdlib 5.7, *) @@ -51,12 +56,11 @@ extension Duration { } } -#if !$Embedded - @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension Clock where Self == ContinuousClock { - /// A clock that measures time that always increments but does not stop - /// incrementing while the system is asleep. + /// A clock that measures time that always increments but does not stop + /// incrementing while the system is asleep. /// /// try await Task.sleep(until: .now + .seconds(3), clock: .continuous) /// @@ -65,6 +69,7 @@ extension Clock where Self == ContinuousClock { } @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension ContinuousClock: Clock { /// The current continuous instant. public var now: ContinuousClock.Instant { @@ -101,18 +106,21 @@ extension ContinuousClock: Clock { /// to coalesce CPU wake-ups to more efficiently process the wake-ups in /// a more power efficient manner. /// - /// If the task is canceled before the time ends, this function throws + /// If the task is canceled before the time ends, this function throws /// `CancellationError`. /// /// This function doesn't block the underlying thread. public func sleep( until deadline: Instant, tolerance: Swift.Duration? = nil ) async throws { - let (seconds, attoseconds) = deadline._value.components - let nanoseconds = attoseconds / 1_000_000_000 - try await Task._sleep(until:seconds, nanoseconds, - tolerance: tolerance, - clock: .continuous) + if #available(SwiftStdlib 6.2, *) { + try await Task._sleep(until: deadline, + tolerance: tolerance, + clock: self) + } else { + // Should never see this + Builtin.unreachable() + } } #else @available(SwiftStdlib 5.7, *) @@ -126,6 +134,7 @@ extension ContinuousClock: Clock { } @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension ContinuousClock.Instant: InstantProtocol { public static var now: ContinuousClock.Instant { ContinuousClock.now } @@ -193,5 +202,3 @@ extension ContinuousClock.Instant: InstantProtocol { rhs.duration(to: lhs) } } - -#endif diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift new file mode 100644 index 0000000000000..d9bd1410755cc --- /dev/null +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -0,0 +1,226 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded + +import Swift + +// We can't import Dispatch from here, sadly, because it apparently has a +// transitive dependency on Combine (which in turn depends on _Concurrency). + +// import Dispatch + +// .. Dispatch Interface ....................................................... + +// .. Main Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public class DispatchMainExecutor: RunLoopExecutor, @unchecked Sendable { + var threaded = false + + public func run() throws { + if threaded { + fatalError("DispatchMainExecutor does not support recursion") + } + + threaded = true + _dispatchMain() + } + + public func stop() { + fatalError("DispatchMainExecutor cannot be stopped") + } +} + +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: SerialExecutor { + public func enqueue(_ job: consuming ExecutorJob) { + _dispatchEnqueueMain(UnownedJob(job)) + } + + public var isMainExecutor: Bool { true } + + public var supportsScheduling: Bool { true } + + public func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration? = nil, + clock: C) { + let tolSec, tolNanosec: CLongLong + if let tolerance = tolerance { + (tolSec, tolNanosec) = delay(from: tolerance, clock: clock) + } else { + tolSec = 0 + tolNanosec = -1 + } + + let (clockID, seconds, nanoseconds) = timestamp(for: instant, clock: clock) + + _dispatchEnqueueWithDeadline(CBool(false), + CLongLong(seconds), CLongLong(nanoseconds), + CLongLong(tolSec), CLongLong(tolNanosec), + clockID.rawValue, + UnownedJob(job)) + } + + public func checkIsolated() { + _dispatchAssertMainQueue() + } +} + +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: EventableExecutor { + + /// Register a new event with a given handler. + public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { + let source = _createDispatchEvent(handler: handler) + + // Stash the pointer in the id of the ExecutorEvent struct + let eventId = unsafeBitCast(source, to: Int.self) + return ExecutorEvent(id: eventId) + } + + /// Deregister the given event. + public func deregister(event: ExecutorEvent) { + // Extract the source and cancel it + let source = unsafeBitCast(event.id, to: OpaquePointer.self) + _destroyDispatchEvent(source) + } + + /// Notify the executor of an event. + public func notify(event: ExecutorEvent) { + // Extract the source, but don't release it + let source = unsafeBitCast(event.id, to: OpaquePointer.self) + _signalDispatchEvent(source) + } + +} + +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: MainExecutor {} + +// .. Task Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public class DispatchTaskExecutor: TaskExecutor, @unchecked Sendable { + public func enqueue(_ job: consuming ExecutorJob) { + _dispatchEnqueueGlobal(UnownedJob(job)) + } + + public var isMainExecutor: Bool { false } + + public var supportsScheduling: Bool { true } + + public func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration? = nil, + clock: C) { + let tolSec, tolNanosec: CLongLong + if let tolerance = tolerance { + (tolSec, tolNanosec) = delay(from: tolerance, clock: clock) + } else { + tolSec = 0 + tolNanosec = -1 + } + + let (clockID, seconds, nanoseconds) = timestamp(for: instant, clock: clock) + + _dispatchEnqueueWithDeadline(CBool(true), + CLongLong(seconds), CLongLong(nanoseconds), + CLongLong(tolSec), CLongLong(tolNanosec), + clockID.rawValue, + UnownedJob(job)) + } +} + +// .. Clock Support ............................................................ + +/// DispatchMainExecutor and DispatchTaskExecutor both implement this +/// protocol. +/// +/// It is used to help convert instants and durations from arbitrary `Clock`s +/// to Dispatch's time base. +@available(SwiftStdlib 6.2, *) +protocol DispatchExecutor: Executor { + + /// Convert an `Instant` from the specified clock to a tuple identifying + /// the Dispatch clock and the seconds and nanoseconds components. + /// + /// Parameters: + /// + /// - for instant: The `Instant` to convert. + /// - clock: The `Clock` instant that the `Instant` came from. + /// + /// Returns: A tuple of `(clockID, seconds, nanoseconds)`. + func timestamp(for instant: C.Instant, clock: C) + -> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64) + + /// Convert a `Duration` from the specified clock to a tuple containing + /// seconds and nanosecond components. + func delay(from duration: C.Duration, clock: C) + -> (seconds: Int64, nanoseconds: Int64) + +} + +/// An enumeration identifying one of the Dispatch-supported clocks +@available(SwiftStdlib 6.2, *) +public enum DispatchClockID: CInt { + case suspending = 1 + case continuous = 2 +} + +@available(SwiftStdlib 6.2, *) +extension DispatchExecutor { + + func timestamp(for instant: C.Instant, clock: C) + -> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64) { + let clockID = clock.dispatchClockID + + switch clockID { + case .suspending: + let dispatchClock: SuspendingClock = .suspending + let instant = dispatchClock.convert(instant: instant, from: clock)! + let (seconds, attoseconds) = instant._value.components + let nanoseconds = attoseconds / 1_000_000_000 + return (clockID: .suspending, + seconds: Int64(seconds), + nanoseconds: Int64(nanoseconds)) + case .continuous: + let dispatchClock: ContinuousClock = .continuous + let instant = dispatchClock.convert(instant: instant, from: clock)! + let (seconds, attoseconds) = instant._value.components + let nanoseconds = attoseconds / 1_000_000_000 + return (clockID: .continuous, + seconds: Int64(seconds), + nanoseconds: Int64(nanoseconds)) + } + } + + func delay(from duration: C.Duration, clock: C) + -> (seconds: Int64, nanoseconds: Int64) { + let swiftDuration = clock.convert(from: duration)! + let (seconds, attoseconds) = swiftDuration.components + let nanoseconds = attoseconds / 1_000_000_000 + return (seconds: seconds, nanoseconds: nanoseconds) + } + +} + +@available(SwiftStdlib 6.2, *) +extension DispatchTaskExecutor: DispatchExecutor { +} + +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: DispatchExecutor { +} + +#endif // !$Embedded diff --git a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp index 58fa5b09ab524..d8b621743e7cb 100644 --- a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp +++ b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp @@ -200,8 +200,8 @@ static dispatch_queue_t getTimerQueue(SwiftJobPriority priority) { return dispatch_get_global_queue((dispatch_qos_class_t)priority, /*flags*/ 0); } -SWIFT_CC(swift) -void swift_task_enqueueGlobalImpl(SwiftJob *job) { +extern "C" SWIFT_CC(swift) +void swift_dispatchEnqueueGlobal(SwiftJob *job) { assert(job && "no job provided"); // We really want four things from the global execution service: // - Enqueuing work should have minimal runtime and memory overhead. @@ -240,33 +240,6 @@ void swift_task_enqueueGlobalImpl(SwiftJob *job) { DISPATCH_QUEUE_GLOBAL_EXECUTOR); } - -SWIFT_CC(swift) -void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay, - SwiftJob *job) { - assert(job && "no job provided"); - - dispatch_function_t dispatchFunction = &__swift_run_job; - void *dispatchContext = job; - - SwiftJobPriority priority = swift_job_getPriority(job); - - auto queue = getTimerQueue(priority); - - job->schedulerPrivate[SwiftJobDispatchQueueIndex] = - DISPATCH_QUEUE_GLOBAL_EXECUTOR; - - // dispatch_time takes a signed int64_t. SwiftJobDelay is unsigned, so - // extremely large values get interpreted as negative numbers, which results - // in zero delay. Clamp the value to INT64_MAX. That's about 292 years, so - // there should be no noticeable difference. - if (delay > (SwiftJobDelay)INT64_MAX) - delay = INT64_MAX; - - dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, delay); - dispatch_after_f(when, queue, dispatchContext, dispatchFunction); -} - #define DISPATCH_UP_OR_MONOTONIC_TIME_MASK (1ULL << 63) #define DISPATCH_WALLTIME_MASK (1ULL << 62) #define DISPATCH_TIME_MAX_VALUE (DISPATCH_WALLTIME_MASK - 1) @@ -335,52 +308,61 @@ clock_and_value_to_time(int clock, long long deadline) { __builtin_unreachable(); } -SWIFT_CC(swift) -void swift_task_enqueueGlobalWithDeadlineImpl(long long sec, - long long nsec, - long long tsec, - long long tnsec, - int clock, SwiftJob *job) { +extern "C" SWIFT_CC(swift) +void swift_dispatchEnqueueWithDeadline(bool global, + long long sec, + long long nsec, + long long tsec, + long long tnsec, + int clock, SwiftJob *job) { assert(job && "no job provided"); SwiftJobPriority priority = swift_job_getPriority(job); - auto queue = getTimerQueue(priority); + dispatch_queue_t queue; + + if (global) { + queue = getTimerQueue(priority); - job->schedulerPrivate[SwiftJobDispatchQueueIndex] = + job->schedulerPrivate[SwiftJobDispatchQueueIndex] = DISPATCH_QUEUE_GLOBAL_EXECUTOR; + } else { + queue = dispatch_get_main_queue(); + + job->schedulerPrivate[SwiftJobDispatchQueueIndex] = queue; + } uint64_t deadline = sec * NSEC_PER_SEC + nsec; dispatch_time_t when = clock_and_value_to_time(clock, deadline); - + if (tnsec != -1) { uint64_t leeway = tsec * NSEC_PER_SEC + tnsec; - dispatch_source_t source = + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); dispatch_source_set_timer(source, when, DISPATCH_TIME_FOREVER, leeway); size_t sz = sizeof(struct __swift_job_source); - struct __swift_job_source *jobSource = + struct __swift_job_source *jobSource = (struct __swift_job_source *)swift_job_alloc(job, sz); jobSource->job = job; jobSource->source = source; dispatch_set_context(source, jobSource); - dispatch_source_set_event_handler_f(source, + dispatch_source_set_event_handler_f(source, (dispatch_function_t)&_swift_run_job_leeway); dispatch_activate(source); } else { - dispatch_after_f(when, queue, (void *)job, + dispatch_after_f(when, queue, (void *)job, (dispatch_function_t)&__swift_run_job); } } -SWIFT_CC(swift) -void swift_task_enqueueMainExecutorImpl(SwiftJob *job) { +extern "C" SWIFT_CC(swift) +void swift_dispatchEnqueueMain(SwiftJob *job) { assert(job && "no job provided"); SwiftJobPriority priority = swift_job_getPriority(job); @@ -397,116 +379,3 @@ void swift::swift_task_enqueueOnDispatchQueue(Job *job, auto queue = reinterpret_cast(_queue); dispatchEnqueue(queue, (SwiftJob *)job, (dispatch_qos_class_t)priority, queue); } - -/// Recognize if the SerialExecutor is specifically a `DispatchSerialQueue` -/// by comparing witness tables and return it if true. -static dispatch_queue_s *getAsDispatchSerialQueue(SwiftExecutorRef executor) { - if (!swift_executor_hasWitnessTable(executor)) { - return nullptr; - } - - auto executorWitnessTable = reinterpret_cast( - swift_executor_getWitnessTable(executor)); - auto serialQueueWitnessTable = reinterpret_cast( - _swift_task_getDispatchQueueSerialExecutorWitnessTable()); - - if (swift_compareWitnessTables(executorWitnessTable, - serialQueueWitnessTable)) { - auto identity = swift_executor_getIdentity(executor); - return reinterpret_cast(identity); - } else { - return nullptr; - } -} - -/// If the executor is a `DispatchSerialQueue` we're able to invoke the -/// dispatch's precondition API directly -- this is more efficient than going -/// through the runtime call to end up calling the same API, and also allows us -/// to perform this assertion on earlier platforms, where the `checkIsolated` -/// requirement/witness was not shipping yet. -SWIFT_CC(swift) -void swift_task_checkIsolatedImpl(SwiftExecutorRef executor) { - // If it is the main executor, compare with the Main queue - if (swift_executor_isMain(executor)) { - dispatch_assert_queue(dispatch_get_main_queue()); - return; - } - - // if able to, use the checkIsolated implementation in Swift - if (swift_executor_invokeSwiftCheckIsolated(executor)) - return; - - if (auto queue = getAsDispatchSerialQueue(executor)) { - // if the executor was not SerialExecutor for some reason but we're able - // to get a queue from it anyway, use the assert directly on it. - dispatch_assert_queue(queue); // TODO(concurrency): could we report a better message here somehow? - return; - } - - // otherwise, we have no way to check, so report an error - // TODO: can we swift_getTypeName(swift_getObjectType(executor.getIdentity()), false).data safely in the message here? - swift_Concurrency_fatalError(0, "Incorrect actor executor assumption"); -} - -SWIFT_CC(swift) -bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) { - return swift_executor_invokeSwiftIsIsolatingCurrentContext(executor); -} - - -SWIFT_CC(swift) -SwiftExecutorRef swift_task_getMainExecutorImpl() { - return swift_executor_ordinary( - reinterpret_cast(&_dispatch_main_q), - reinterpret_cast( - _swift_task_getDispatchQueueSerialExecutorWitnessTable())); -} - -SWIFT_CC(swift) -bool swift_task_isMainExecutorImpl(SwiftExecutorRef executor) { - return swift_executor_getIdentity(executor) - == reinterpret_cast(&_dispatch_main_q); -} - -SWIFT_CC(swift) void -swift_task_donateThreadToGlobalExecutorUntilImpl(bool (*condition)(void *), - void *conditionContext) { - swift_Concurrency_fatalError(0, "Not supported for Dispatch executor"); -} - -SWIFT_RUNTIME_ATTRIBUTE_NORETURN -SWIFT_CC(swift) -void swift_task_asyncMainDrainQueueImpl() { -#if defined(_WIN32) - HMODULE hModule = LoadLibraryW(L"dispatch.dll"); - if (hModule == NULL) { - swift_Concurrency_fatalError(0, - "unable to load dispatch.dll: %lu", GetLastError()); - } - - auto pfndispatch_main = reinterpret_cast( - GetProcAddress(hModule, "dispatch_main")); - if (pfndispatch_main == NULL) { - swift_Concurrency_fatalError(0, - "unable to locate dispatch_main in dispatch.dll: %lu", GetLastError()); - } - - pfndispatch_main(); - swift_unreachable("Returned from dispatch_main()"); -#else - // CFRunLoop is not available on non-Darwin targets. Foundation has an - // implementation, but CoreFoundation is not meant to be exposed. We can only - // assume the existence of `CFRunLoopRun` on Darwin platforms, where the - // system provides an implementation of CoreFoundation. -#if defined(__APPLE__) - auto runLoop = - reinterpret_cast(dlsym(RTLD_DEFAULT, "CFRunLoopRun")); - if (runLoop) { - runLoop(); - exit(0); - } -#endif - - dispatch_main(); -#endif -} diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index c4ff1a319dc58..2c41f6f7e824f 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2021 Apple Inc. and the Swift project authors +// Copyright (c) 2021 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -35,6 +35,130 @@ public protocol Executor: AnyObject, Sendable { @available(SwiftStdlib 5.9, *) func enqueue(_ job: consuming ExecutorJob) #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + + // The functions below could have been added to a separate protocol, + // but doing that would then mean doing an `as?` cast in e.g. + // enqueueOnGlobalExecutor (in ExecutorBridge.swift), which is + // undesirable from a performance perspective. + + /// `true` if this is the main executor. + @available(SwiftStdlib 6.2, *) + var isMainExecutor: Bool { get } + + /// `true` if this Executor supports scheduling. + /// + /// This will default to false. If you attempt to use the delayed + /// enqueuing functions on an executor that does not support scheduling, + /// the default executor will be used to do the scheduling instead, + /// unless the default executor does not support scheduling in which + /// case you will get a fatal error. + @available(SwiftStdlib 6.2, *) + var supportsScheduling: Bool { get } + + #if !$Embedded + + /// Enqueue a job to run after a specified delay. + /// + /// You need only implement one of the two enqueue functions here; + /// the default implementation for the other will then call the one + /// you have implemented. + /// + /// Parameters: + /// + /// - job: The job to schedule. + /// - after: A `Duration` specifying the time after which the job + /// is to run. The job will not be executed before this + /// time has elapsed. + /// - tolerance: The maximum additional delay permissible before the + /// job is executed. `nil` means no limit. + /// - clock: The clock used for the delay. + @available(SwiftStdlib 6.2, *) + func enqueue(_ job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration?, + clock: C) + + /// Enqueue a job to run at a specified time. + /// + /// You need only implement one of the two enqueue functions here; + /// the default implementation for the other will then call the one + /// you have implemented. + /// + /// Parameters: + /// + /// - job: The job to schedule. + /// - at: The `Instant` at which the job should run. The job + /// will not be executed before this time. + /// - tolerance: The maximum additional delay permissible before the + /// job is executed. `nil` means no limit. + /// - clock: The clock used for the delay.. + @available(SwiftStdlib 6.2, *) + func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration?, + clock: C) + + #endif // !$Embedded +} + +@available(SwiftStdlib 6.2, *) +extension Executor { + @usableFromInline + internal var _isComplexEquality: Bool { false } +} + +@available(SwiftStdlib 6.2, *) +extension Executor where Self: Equatable { + @usableFromInline + internal var _isComplexEquality: Bool { true } +} + +// Delay support +extension Executor { + + // This defaults to `false` so that existing third-party Executor + // implementations will work as expected. + @available(SwiftStdlib 6.2, *) + public var isMainExecutor: Bool { false } + + // This defaults to `false` so that existing third-party TaskExecutor + // implementations will work as expected. + @available(SwiftStdlib 6.2, *) + public var supportsScheduling: Bool { false } + + #if !$Embedded + + @available(SwiftStdlib 6.2, *) + public func enqueue(_ job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + if !supportsScheduling { + fatalError("Executor \(self) does not support scheduling") + } + + // If you crash here with a mutual recursion, it's because you didn't + // implement one of these two functions + enqueue(job, at: clock.now.advanced(by: delay), + tolerance: tolerance, clock: clock) + } + + @available(SwiftStdlib 6.2, *) + public func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration? = nil, + clock: C) { + if !supportsScheduling { + fatalError("Executor \(self) does not support scheduling") + } + + // If you crash here with a mutual recursion, it's because you didn't + // implement one of these two functions + enqueue(job, after: clock.now.duration(to: instant), + tolerance: tolerance, clock: clock) + } + + #endif // !$Embedded } /// A service that executes jobs. @@ -203,6 +327,9 @@ public protocol SerialExecutor: Executor { @available(SwiftStdlib 6.0, *) extension SerialExecutor { + @available(SwiftStdlib 6.2, *) + public var isMainExecutor: Bool { return MainActor.executor._isSameExecutor(self) } + @available(SwiftStdlib 6.0, *) public func checkIsolated() { #if !$Embedded @@ -211,6 +338,17 @@ extension SerialExecutor { Builtin.int_trap() #endif } + + @available(SwiftStdlib 6.2, *) + internal func _isSameExecutor(_ rhs: some SerialExecutor) -> Bool { + if rhs === self { + return true + } + if let rhs = rhs as? Self { + return isSameExclusiveExecutionContext(other: rhs) + } + return false + } } @available(SwiftStdlib 6.2, *) @@ -240,7 +378,6 @@ extension SerialExecutor { /// the provided task executor. /// /// Unstructured tasks do not inherit the task executor. -@_unavailableInEmbedded @available(SwiftStdlib 6.0, *) public protocol TaskExecutor: Executor { // This requirement is repeated here as a non-override so that we @@ -272,7 +409,6 @@ public protocol TaskExecutor: Executor { func asUnownedTaskExecutor() -> UnownedTaskExecutor } -@_unavailableInEmbedded @available(SwiftStdlib 6.0, *) extension TaskExecutor { public func asUnownedTaskExecutor() -> UnownedTaskExecutor { @@ -319,6 +455,224 @@ extension SerialExecutor { } +@available(SwiftStdlib 6.2, *) +extension SerialExecutor where Self: Equatable { + + @available(SwiftStdlib 6.2, *) + public func isSameExclusiveExecutionContext(other: Self) -> Bool { + return self == other + } + +} + +/// An executor that is backed by some kind of run loop. +/// +/// The idea here is that some executors may work by running a loop +/// that processes events of some sort; we want a way to enter that loop, +/// and we would also like a way to trigger the loop to exit. +@available(SwiftStdlib 6.2, *) +public protocol RunLoopExecutor: Executor { + /// Run the executor's run loop. + /// + /// This method will synchronously block the calling thread. Nested calls to + /// `run()` may be permitted, however it is not permitted to call `run()` on a + /// single executor instance from more than one thread. + func run() throws + + /// Run the executor's run loop until a condition is satisfied. + /// + /// Not every `RunLoopExecutor` will support this method; you must not call + /// it unless you *know* that it is supported. The default implementation + /// generates a fatal error. + /// + /// Parameters: + /// + /// - until condition: A closure that returns `true` if the run loop should + /// stop. + func run(until condition: () -> Bool) throws + + /// Signal to the run loop to stop running and return. + /// + /// This method may be called from the same thread that is in the `run()` + /// method, or from some other thread. It will not wait for the run loop to + /// stop; calling this method simply signals that the run loop *should*, as + /// soon as is practicable, stop the innermost `run()` invocation and make + /// that `run()` invocation return. + func stop() +} + +@available(SwiftStdlib 6.2, *) +extension RunLoopExecutor { + + public func run(until condition: () -> Bool) throws { + fatalError("run(until condition:) not supported on this executor") + } + +} + + +/// Represents an event; we don't want to allocate, so we can't use +/// a protocol here and use `any Event`. Instead of doing that, wrap +/// an `Int` (which is pointer-sized) in a `struct`. +@available(SwiftStdlib 6.2, *) +public struct ExecutorEvent: Identifiable, Comparable, Sendable { + public typealias ID = Int + + public var id: Self.ID + + public static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.id < rhs.id + } + public static func == (lhs: Self, rhs: Self) -> Bool { + return lhs.id == rhs.id + } +} + + +/// An executor that has support for coalesced events. +@available(SwiftStdlib 6.2, *) +public protocol EventableExecutor { + + /// Register a new event with a given handler. + /// + /// Notifying the executor of the event will cause the executor to + /// execute the handler, however the executor is free to coalesce multiple + /// event notifications, and is also free to execute the handler at a time + /// of its choosing. + /// + /// Parameters + /// + /// - handler: The handler to call when the event fires. + /// + /// Returns a new opaque `Event`. + func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent + + /// Deregister the given event. + /// + /// After this function returns, there will be no further executions of the + /// handler for the given event. + func deregister(event: ExecutorEvent) + + /// Notify the executor of an event. + /// + /// This will trigger, at some future point, the execution of the associated + /// event handler. Prior to that time, multiple calls to `notify` may be + /// coalesced and result in a single invocation of the event handler. + func notify(event: ExecutorEvent) + +} + + +/// The main executor must conform to these three protocols; we have to +/// make this a protocol for compatibility with Embedded Swift. +@available(SwiftStdlib 6.2, *) +public protocol MainExecutor: RunLoopExecutor, SerialExecutor, EventableExecutor { +} + +#if SWIFT_STDLIB_DISABLE_CUSTOM_EXECUTORS +// For this to work, there must be a definition of PlatformMainExecutor and +// PlatformDefaultExecutor available in the Concurrency runtime for the +// platform in question. + +let _platformMainExecutor = PlatformMainExecutor() +let _platformDefaultExecutor = PlatformDefaultExecutor() + +@available(SwiftStdlib 6.2, *) +extension MainActor { + /// The main executor, which is started implicitly by the `async main` + /// entry point and owns the "main" thread. + /// + /// Attempting to set this after the first `enqueue` on the main + /// executor is a fatal error. + public static var executor: PlatformMainExecutor { + return _platformMainExecutor + } +} + +@available(SwiftStdlib 6.2, *) +extension Task where Success == Never, Failure == Never { + /// The default or global executor, which is the default place in which + /// we run tasks. + /// + /// Attempting to set this after the first `enqueue` on the global + /// executor is a fatal error. + public static var defaultExecutor: PlatformDefaultExecutor { + return _platformDefaultExecutor + } +} + +#else // !SWIFT_STDLIB_DISABLE_CUSTOM_EXECUTORS + +@available(SwiftStdlib 6.2, *) +extension MainActor { + private static var _executor: (any MainExecutor)? = nil + + /// The main executor, which is started implicitly by the `async main` + /// entry point and owns the "main" thread. + /// + /// Attempting to set this after the first `enqueue` on the main + /// executor is a fatal error. + public static var executor: any MainExecutor { + get { + if _executor == nil { + _executor = PlatformMainExecutor() + } + return _executor! + } + set { + if _executor != nil { + fatalError("The main executor can be set only once, at program start") + } + _executor = newValue + } + } +} + +@available(SwiftStdlib 6.2, *) +extension Task where Success == Never, Failure == Never { + private static var _defaultExecutor: (any TaskExecutor)? = nil + + /// The default or global executor, which is the default place in which + /// we run tasks. + /// + /// Attempting to set this after the first `enqueue` on the global + /// executor is a fatal error. + public static var defaultExecutor: any TaskExecutor { + get { + if _defaultExecutor == nil { + _defaultExecutor = PlatformDefaultExecutor() + } + return _defaultExecutor! + } + set { + if _defaultExecutor != nil { + fatalError("The default executor can be set only once, at program start") + } + _defaultExecutor = newValue + } + } +} + +#endif + +@available(SwiftStdlib 6.2, *) +extension Task where Success == Never, Failure == Never { + /// Get the current executor; this is the executor that the currently + /// executing task is executing on. + @_unavailableInEmbedded + public static var currentExecutor: (any Executor)? { + if let taskExecutor = _getPreferredTaskExecutor().asTaskExecutor() { + return taskExecutor + } else if let activeExecutor = _getActiveExecutor().asSerialExecutor() { + return activeExecutor + } else if let taskExecutor = _getCurrentTaskExecutor().asTaskExecutor() { + return taskExecutor + } + return nil + } +} + + /// An unowned reference to a serial executor (a `SerialExecutor` /// value). /// @@ -370,16 +724,31 @@ public struct UnownedSerialExecutor: Sendable { unsafe self.executor = Builtin.buildComplexEqualitySerialExecutorRef(executor) } + /// Automatically opt-in to complex equality semantics if the Executor + /// implements `Equatable`. + @available(SwiftStdlib 6.2, *) + @inlinable + public init(_ executor: __shared E) { + if executor._isComplexEquality { + self.executor = Builtin.buildComplexEqualitySerialExecutorRef(executor) + } else { + self.executor = Builtin.buildOrdinarySerialExecutorRef(executor) + } + } + @_spi(ConcurrencyExecutors) @available(SwiftStdlib 5.9, *) public var _isComplexEquality: Bool { unsafe _executor_isComplexEquality(self) } + @available(SwiftStdlib 6.2, *) + public func asSerialExecutor() -> (any SerialExecutor)? { + return unsafeBitCast(executor, to: (any SerialExecutor)?.self) + } } -@_unavailableInEmbedded @available(SwiftStdlib 6.0, *) @frozen public struct UnownedTaskExecutor: Sendable { @@ -402,9 +771,19 @@ public struct UnownedTaskExecutor: Sendable { public init(ordinary executor: __shared E) { self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) } + + @available(SwiftStdlib 6.2, *) + @inlinable + public init(_ executor: __shared E) { + self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) + } + + @available(SwiftStdlib 6.2, *) + public func asTaskExecutor() -> (any TaskExecutor)? { + return unsafeBitCast(executor, to: (any TaskExecutor)?.self) + } } -@_unavailableInEmbedded @available(SwiftStdlib 6.0, *) extension UnownedTaskExecutor: Equatable { @inlinable diff --git a/stdlib/public/Concurrency/ExecutorBridge.cpp b/stdlib/public/Concurrency/ExecutorBridge.cpp new file mode 100644 index 0000000000000..49a1b9e54cbfb --- /dev/null +++ b/stdlib/public/Concurrency/ExecutorBridge.cpp @@ -0,0 +1,151 @@ +//===--- ExecutorBridge.cpp - C++ side of executor bridge -----------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !SWIFT_CONCURRENCY_EMBEDDED +#include +#endif + +#include "Error.h" +#include "ExecutorBridge.h" + +using namespace swift; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" + +extern "C" SWIFT_CC(swift) +void _swift_task_checkIsolatedSwift( + HeapObject *executor, + const Metadata *executorType, + const SerialExecutorWitnessTable *witnessTable +); + +extern "C" SWIFT_CC(swift) +void _swift_task_isIsolatingCurrentContextSwift( + HeapObject *executor, + const Metadata *executorType, + const SerialExecutorWitnessTable *witnessTable +); + +extern "C" SWIFT_CC(swift) +bool _swift_task_isMainExecutorSwift( + HeapObject *executor, + const Metadata *executorType, + const SerialExecutorWitnessTable *witnessTable +); + +extern "C" SWIFT_CC(swift) +void swift_task_checkIsolatedImpl(SerialExecutorRef executor) { + HeapObject *identity = executor.getIdentity(); + + // We might be being called with an actor rather than a "proper" + // SerialExecutor; in that case, we won't have a SerialExecutor witness + // table. + if (executor.hasSerialExecutorWitnessTable()) { + _swift_task_checkIsolatedSwift(identity, + swift_getObjectType(identity), + executor.getSerialExecutorWitnessTable()); + } else { + const Metadata *objectType = swift_getObjectType(executor.getIdentity()); + auto typeName = swift_getTypeName(objectType, true); + + swift_Concurrency_fatalError( + 0, "Incorrect actor executor assumption; expected '%.*s' executor.\n", + (int)typeName.length, typeName.data); + } +} + +extern "C" SWIFT_CC(swift) +void swift_task_isIsolatingCurrentContextImpl(SerialExecutorRef executor) { + HeapObject *identity = executor.getIdentity(); + + // We might be being called with an actor rather than a "proper" + // SerialExecutor; in that case, we won't have a SerialExecutor witness + // table. + if (executor.hasSerialExecutorWitnessTable()) { + return _swift_task_isIsolatingCurrentContextSwift(identity, + swift_getObjectType(identity), + executor.getSerialExecutorWitnessTable()); + } else { + const Metadata *objectType = swift_getObjectType(executor.getIdentity()); + auto typeName = swift_getTypeName(objectType, true); + + swift_Concurrency_fatalError( + 0, "Incorrect actor executor assumption; expected '%.*s' executor.\n", + (int)typeName.length, typeName.data); + } +} + +extern "C" SWIFT_CC(swift) +bool swift_task_isMainExecutorImpl(SerialExecutorRef executor) { + HeapObject *identity = executor.getIdentity(); + return executor.hasSerialExecutorWitnessTable() + && _swift_task_isMainExecutorSwift(identity, + swift_getObjectType(identity), + executor.getSerialExecutorWitnessTable()); +} + +extern "C" SWIFT_CC(swift) +uint8_t swift_job_getPriority(Job *job) { + return (uint8_t)(job->getPriority()); +} + +extern "C" SWIFT_CC(swift) +uint8_t swift_job_getKind(Job *job) { + return (uint8_t)(job->Flags.getKind()); +} + +extern "C" SWIFT_CC(swift) +void *swift_job_getExecutorPrivateData(Job *job) { + return &job->SchedulerPrivate[0]; +} + +#if !SWIFT_CONCURRENCY_EMBEDDED +extern "C" SWIFT_CC(swift) +void *swift_createDispatchEvent(void (^handler)()) { + dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_OR, + 0, 0, + dispatch_get_main_queue()); + dispatch_source_set_event_handler(source, handler); + dispatch_activate(source); + + return source; +} + +extern "C" SWIFT_CC(swift) +void swift_destroyDispatchEvent(void *event) { + dispatch_source_t source = (dispatch_source_t)event; + + dispatch_source_cancel(source); + dispatch_release(source); +} + +extern "C" SWIFT_CC(swift) +void swift_signalDispatchEvent(void *event) { + dispatch_source_t source = (dispatch_source_t)event; + + dispatch_source_merge_data(source, 1); +} + +extern "C" SWIFT_CC(swift) __attribute__((noreturn)) +void swift_dispatchMain() { + dispatch_main(); +} + +extern "C" SWIFT_CC(swift) +void swift_dispatchAssertMainQueue() { + dispatch_assert_queue(dispatch_get_main_queue()); +} + +#endif // !SWIFT_CONCURRENCY_EMBEDDED + +#pragma clang diagnostic pop diff --git a/stdlib/public/Concurrency/ExecutorBridge.h b/stdlib/public/Concurrency/ExecutorBridge.h new file mode 100644 index 0000000000000..a4e1d7ed9c20f --- /dev/null +++ b/stdlib/public/Concurrency/ExecutorBridge.h @@ -0,0 +1,42 @@ +//===--- ExecutorBridge.h - C++ side of executor bridge -------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_EXECUTOR_BRIDGE_H_ +#define SWIFT_EXECUTOR_BRIDGE_H_ + +#include "swift/Runtime/Concurrency.h" + +namespace swift { + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" + +extern "C" SWIFT_CC(swift) +SerialExecutorRef swift_getMainExecutor(); + +extern "C" SWIFT_CC(swift) +void *swift_createDispatchEvent(void (^handler)()); + +extern "C" SWIFT_CC(swift) +void swift_destroyDispatchEvent(void *event); + +extern "C" SWIFT_CC(swift) +void swift_signalDispatchEvent(void *event); + +extern "C" SWIFT_CC(swift) __attribute__((noreturn)) +void swift_dispatchMain(); + +#pragma clang diagnostic pop + +} // namespace swift + +#endif /* SWIFT_EXECUTOR_BRIDGE_H_ */ diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift new file mode 100644 index 0000000000000..8c0633c146440 --- /dev/null +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -0,0 +1,118 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Functions to bridge between C++ and Swift. This does not include the +// *Impl functions because we need them separate for Embedded Swift. +// +//===----------------------------------------------------------------------===// + +import Swift + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_task_isMainExecutorSwift") +internal func _isMainExecutor(_ executor: E) -> Bool where E: SerialExecutor { + return executor.isMainExecutor +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_task_checkIsolatedSwift") +internal func checkIsolated(executor: E) where E: SerialExecutor { + executor.checkIsolated() +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_task_isIsolatingCurrentContextSwift") +internal func isIsolatingCurrentContext(executor: E) -> Bool + where E: SerialExecutor +{ + return executor.isIsolatingCurrentContext() +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_getActiveExecutor") +internal func _getActiveExecutor() -> UnownedSerialExecutor + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_getCurrentTaskExecutor") +internal func _getCurrentTaskExecutor() -> UnownedTaskExecutor + +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_getPreferredTaskExecutor") +internal func _getPreferredTaskExecutor() -> UnownedTaskExecutor + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_allocate") +internal func _jobAllocate(_ job: Builtin.Job, + _ capacity: Int) -> UnsafeMutableRawPointer + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_deallocate") +internal func _jobDeallocate(_ job: Builtin.Job, + _ address: UnsafeMutableRawPointer) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_getPriority") +internal func _jobGetPriority(_ job: Builtin.Job) -> UInt8 + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_getKind") +internal func _jobGetKind(_ job: Builtin.Job) -> UInt8 + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_job_getExecutorPrivateData") +internal func _jobGetExecutorPrivateData( + _ job: Builtin.Job +) -> UnsafeMutableRawPointer + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_getMainExecutor") +internal func _getMainExecutor() -> any MainExecutor { + return MainActor.executor +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchMain") +internal func _dispatchMain() + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchEnqueueMain") +internal func _dispatchEnqueueMain(_ job: UnownedJob) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchEnqueueGlobal") +internal func _dispatchEnqueueGlobal(_ job: UnownedJob) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchEnqueueWithDeadline") +internal func _dispatchEnqueueWithDeadline(_ global: CBool, + _ sec: CLongLong, + _ nsec: CLongLong, + _ tsec: CLongLong, + _ tnsec: CLongLong, + _ clock: CInt, + _ job: UnownedJob) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_dispatchAssertMainQueue") +internal func _dispatchAssertMainQueue() + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_createDispatchEvent") +internal func _createDispatchEvent(handler: @convention(block) @escaping () -> ()) -> OpaquePointer + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_destroyDispatchEvent") +internal func _destroyDispatchEvent(_ event: OpaquePointer) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_signalDispatchEvent") +internal func _signalDispatchEvent(_ event: OpaquePointer) diff --git a/stdlib/public/Concurrency/ExecutorImpl.h b/stdlib/public/Concurrency/ExecutorImpl.h index fe461743cb10d..a068c456b3c05 100644 --- a/stdlib/public/Concurrency/ExecutorImpl.h +++ b/stdlib/public/Concurrency/ExecutorImpl.h @@ -92,9 +92,9 @@ static inline int swift_priority_getBucketIndex(SwiftJobPriority priority) { /// Used by the Concurrency runtime to represent a job. The `schedulerPrivate` /// field may be freely used by the executor implementation. typedef struct { - SwiftHeapMetadata const *__ptrauth_objc_isa_pointer metadata; + SwiftHeapMetadata const *__ptrauth_objc_isa_pointer _Nonnull metadata; uintptr_t refCounts; - void *schedulerPrivate[2]; + void * _Nullable schedulerPrivate[2]; SwiftJobFlags flags; } __attribute__((aligned(2 * sizeof(void *)))) SwiftJob; @@ -109,30 +109,35 @@ enum { }; /// Get the kind of a job, by directly accessing the flags field. -static inline SwiftJobKind swift_job_getKind(SwiftJob *job) { +static inline SwiftJobKind swift_job_getKind(SwiftJob * _Nonnull job) { return (SwiftJobKind)(job->flags & 0xff); } /// Get the priority of a job, by directly accessing the flags field. -static inline SwiftJobPriority swift_job_getPriority(SwiftJob *job) { +static inline SwiftJobPriority swift_job_getPriority(SwiftJob * _Nonnull job) { return (SwiftJobPriority)((job->flags >> 8) & 0xff); } +/// Get a pointer to the scheduler private data. +static inline void * _Nonnull * _Nonnull swift_job_getPrivateData(SwiftJob * _Nonnull job) { + return &job->schedulerPrivate[0]; +} + /// Allocate memory associated with a job. -void *swift_job_alloc(SwiftJob *job, size_t size); +void * _Nullable swift_job_alloc(SwiftJob * _Nonnull job, size_t size); /// Release memory allocated using `swift_job_alloc()`. -void swift_job_dealloc(SwiftJob *job, void *ptr); +void swift_job_dealloc(SwiftJob * _Nonnull job, void * _Nonnull ptr); /// Swift's refcounted objects start with this header typedef struct { - SwiftHeapMetadata const *__ptrauth_objc_isa_pointer metadata; + SwiftHeapMetadata const *__ptrauth_objc_isa_pointer _Nonnull metadata; } SwiftHeapObject; /// A reference to an executor consists of two words; the first is a pointer /// which may or may not be to a Swift heap object. typedef struct { - SwiftHeapObject *identity; + SwiftHeapObject * _Nullable identity; uintptr_t implementation; } SwiftExecutorRef; @@ -153,8 +158,8 @@ static inline SwiftExecutorRef swift_executor_generic(void) { /// Return an ordinary executor with the specified identity and witness table. static inline SwiftExecutorRef -swift_executor_ordinary(SwiftHeapObject *identity, - SwiftExecutorWitnessTable *witnessTable) { +swift_executor_ordinary(SwiftHeapObject * _Nullable identity, + SwiftExecutorWitnessTable * _Nullable witnessTable) { return (SwiftExecutorRef){ identity, (uintptr_t)witnessTable | SwiftExecutorOrdinaryKind }; @@ -163,8 +168,8 @@ swift_executor_ordinary(SwiftHeapObject *identity, /// Return a complex equality executor with the specified identity and /// witness table. static inline SwiftExecutorRef -swift_executor_complexEquality(SwiftHeapObject *identity, - SwiftExecutorWitnessTable *witnessTable) { +swift_executor_complexEquality(SwiftHeapObject * _Nullable identity, + SwiftExecutorWitnessTable * _Nullable witnessTable) { return (SwiftExecutorRef){ identity, (uintptr_t)witnessTable | SwiftExecutorComplexEqualityKind }; @@ -188,7 +193,7 @@ static inline bool swift_executor_isDefaultActor(SwiftExecutorRef executor) { } /// Retrieve the identity of an executor. -static inline SwiftHeapObject * +static inline SwiftHeapObject * _Nullable swift_executor_getIdentity(SwiftExecutorRef executor) { return executor.identity; } @@ -200,7 +205,7 @@ static inline bool swift_executor_hasWitnessTable(SwiftExecutorRef executor) { } /// Retrieve the witness table of an executor. -static inline const SwiftExecutorWitnessTable * +static inline const SwiftExecutorWitnessTable * _Nullable swift_executor_getWitnessTable(SwiftExecutorRef executor) { const uintptr_t mask = ~(uintptr_t)(sizeof(void *) - 1); return (const SwiftExecutorWitnessTable *)(executor.implementation & mask); @@ -230,8 +235,10 @@ swift_executor_invokeSwiftIsIsolatingCurrentContext(SwiftExecutorRef executor) { } /// Execute the specified job while running on the specified executor. -static inline void swift_job_run(SwiftJob *job, SwiftExecutorRef executor) { - extern void _swift_job_run_c(SwiftJob *job, SwiftExecutorRef executor); +static inline void swift_job_run(SwiftJob * _Nonnull job, + SwiftExecutorRef executor) { + extern void _swift_job_run_c(SwiftJob * _Nonnull job, + SwiftExecutorRef executor); _swift_job_run_c(job, executor); } @@ -261,12 +268,12 @@ extern SwiftTime swift_time_getResolution(SwiftClockId clock); // -- Functions that executors must implement ---------------------------------- /// Enqueue a job on the global executor. -SWIFT_CC(swift) void swift_task_enqueueGlobalImpl(SwiftJob *job); +SWIFT_CC(swift) void swift_task_enqueueGlobalImpl(SwiftJob * _Nonnull job); /// Enqueue a job on the global executor, with a specific delay before it /// should execute. SWIFT_CC(swift) void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay, - SwiftJob *job); + SwiftJob * _Nonnull job); /// Enqueue a job on the global executor, with a specific deadline before /// which it must execute. @@ -275,11 +282,11 @@ SWIFT_CC(swift) void swift_task_enqueueGlobalWithDeadlineImpl(long long sec, long long tsec, long long tnsec, int clock, - SwiftJob *job); + SwiftJob * _Nonnull job); /// Enqueue a job on the main executor (which may or may not be the same as /// the global executor). -SWIFT_CC(swift) void swift_task_enqueueMainExecutorImpl(SwiftJob *job); +SWIFT_CC(swift) void swift_task_enqueueMainExecutorImpl(SwiftJob * _Nonnull job); /// Assert that the specified executor is the current executor. SWIFT_CC(swift) void swift_task_checkIsolatedImpl(SwiftExecutorRef executor); @@ -303,8 +310,8 @@ SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_CC(swift) void /// but you should assert or provide a dummy implementation if your executor /// does not support it. SWIFT_CC(swift) void - swift_task_donateThreadToGlobalExecutorUntilImpl(bool (*condition)(void *), - void *conditionContext); + swift_task_donateThreadToGlobalExecutorUntilImpl(bool (* _Nonnull condition)(void * _Nullable), + void * _Nullable conditionContext); #ifdef __cplusplus } // extern "C" diff --git a/stdlib/public/Concurrency/ExecutorImpl.swift b/stdlib/public/Concurrency/ExecutorImpl.swift new file mode 100644 index 0000000000000..3b8879f5a2352 --- /dev/null +++ b/stdlib/public/Concurrency/ExecutorImpl.swift @@ -0,0 +1,95 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2021 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// Implementations of the *Impl functions that bridge to Swift. +// +// These are separate from the functions in ExecutorBridge.swift so that we +// can omit this file from the Embedded Swift version of Concurrency for now. +// (This means that the existing co-operative executor will still work.) +// +//===----------------------------------------------------------------------===// + +import Swift + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_asyncMainDrainQueueImpl") +internal func drainMainQueue() { + try! MainActor.executor.run() +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_donateThreadToGlobalExecutorUntilImpl") +internal func dontateToGlobalExecutor( + condition: @convention(c) (_ ctx: UnsafeMutableRawPointer) -> CBool, + context: UnsafeMutableRawPointer +) { + if let runnableExecutor = Task.defaultExecutor as? RunLoopExecutor { + try! runnableExecutor.run(until: { Bool(condition(context)) }) + } else { + fatalError("Global executor does not support thread donation") + } +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_getMainExecutorImpl") +internal func getMainExecutor() -> UnownedSerialExecutor { + return UnownedSerialExecutor(MainActor.executor) +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_enqueueMainExecutorImpl") +internal func enqueueOnMainExecutor(job unownedJob: UnownedJob) { + MainActor.executor.enqueue(unownedJob) +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_enqueueGlobalImpl") +internal func enqueueOnGlobalExecutor(job unownedJob: UnownedJob) { + Task.defaultExecutor.enqueue(unownedJob) +} + +#if !$Embedded +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_enqueueGlobalWithDelayImpl") +internal func enqueueOnGlobalExecutor(delay: CUnsignedLongLong, + job unownedJob: UnownedJob) { + Task.defaultExecutor.enqueue(ExecutorJob(unownedJob), + after: .nanoseconds(delay), + clock: .continuous) +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_task_enqueueGlobalWithDeadlineImpl") +internal func enqueueOnGlobalExecutor(seconds: CLongLong, + nanoseconds: CLongLong, + leewaySeconds: CLongLong, + leewayNanoseconds: CLongLong, + clock: CInt, + job unownedJob: UnownedJob) { + let delay = Duration.seconds(seconds) + Duration.nanoseconds(nanoseconds) + let leeway = Duration.seconds(leewaySeconds) + Duration.nanoseconds(leewayNanoseconds) + switch clock { + case _ClockID.suspending.rawValue: + Task.defaultExecutor.enqueue(ExecutorJob(unownedJob), + after: delay, + tolerance: leeway, + clock: .suspending) + case _ClockID.continuous.rawValue: + Task.defaultExecutor.enqueue(ExecutorJob(unownedJob), + after: delay, + tolerance: leeway, + clock: .continuous) + default: + fatalError("Unknown clock ID \(clock)") + } +} +#endif diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/ExecutorJob.swift similarity index 84% rename from stdlib/public/Concurrency/PartialAsyncTask.swift rename to stdlib/public/Concurrency/ExecutorJob.swift index 977ebe82f63ef..4dcb92c7be0ca 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/ExecutorJob.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift.org open source project // -// Copyright (c) 2020 - 2021 Apple Inc. and the Swift project authors +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information @@ -12,7 +12,9 @@ import Swift -// TODO(swift): rename the file to ExecutorJob.swift eventually, we don't use PartialTask terminology anymore +// TODO: It would be better if some of the functions here could be inlined into +// the Swift code. In particular, some of the ones that deal with data held on +// ExecutorJob. @available(SwiftStdlib 5.1, *) @_silgen_name("swift_job_run") @@ -75,7 +77,14 @@ public struct UnownedJob: Sendable { /// The priority of this job. @available(SwiftStdlib 5.9, *) public var priority: JobPriority { - let raw = _swift_concurrency_jobPriority(self) + let raw: UInt8 + if #available(SwiftStdlib 6.2, *) { + raw = _jobGetPriority(context) + } else { + // Since we're building the new version of the stdlib, we should + // never get here. + Builtin.unreachable() + } return JobPriority(rawValue: raw) } @@ -209,7 +218,14 @@ public struct Job: Sendable, ~Copyable { } public var priority: JobPriority { - let raw = _swift_concurrency_jobPriority(UnownedJob(context: self.context)) + let raw: UInt8 + if #available(SwiftStdlib 6.2, *) { + raw = _jobGetPriority(self.context) + } else { + // We are building the new version of the code, so we should never + // get here. + Builtin.unreachable() + } return JobPriority(rawValue: raw) } @@ -277,10 +293,59 @@ public struct ExecutorJob: Sendable, ~Copyable { } public var priority: JobPriority { - let raw = _swift_concurrency_jobPriority(UnownedJob(context: self.context)) + let raw: UInt8 + if #available(SwiftStdlib 6.2, *) { + raw = _jobGetPriority(self.context) + } else { + // We are building the new version of the code, so we should never + // get here. + Builtin.unreachable() + } return JobPriority(rawValue: raw) } + /// Execute a closure, passing it the bounds of the executor private data + /// for the job. + /// + /// Parameters: + /// + /// - body: The closure to execute. + /// + /// Returns the result of executing the closure. + @available(SwiftStdlib 6.2, *) + public func withUnsafeExecutorPrivateData(body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R { + let base = _jobGetExecutorPrivateData(self.context) + let size = 2 * MemoryLayout.stride + return try body(UnsafeMutableRawBufferPointer(start: base, count: size)) + } + + /// Kinds of schedulable jobs + @available(SwiftStdlib 6.2, *) + @frozen + public struct Kind: Sendable, RawRepresentable { + public typealias RawValue = UInt8 + + /// The raw job kind value. + public var rawValue: RawValue + + /// Creates a new instance with the specified raw value. + public init?(rawValue: Self.RawValue) { + self.rawValue = rawValue + } + + /// A task. + public static let task = Kind(rawValue: RawValue(0))! + + // Job kinds >= 192 are private to the implementation. + public static let firstReserved = Kind(rawValue: RawValue(192))! + } + + /// What kind of job this is. + @available(SwiftStdlib 6.2, *) + public var kind: Kind { + return Kind(rawValue: _jobGetKind(self.context))! + } + // TODO: move only types cannot conform to protocols, so we can't conform to CustomStringConvertible; // we can still offer a description to be called explicitly though. @_unavailableInEmbedded @@ -370,6 +435,82 @@ extension ExecutorJob { unsafe _swiftJobRunOnTaskExecutor(UnownedJob(self), serialExecutor, taskExecutor) } } + +// Stack-disciplined job-local allocator support +@available(SwiftStdlib 6.2, *) +extension ExecutorJob { + + /// Obtain a stack-disciplined job-local allocator. + /// + /// If the job does not support allocation, this property will be `nil`. + public var allocator: LocalAllocator? { + guard self.kind == .task else { + return nil + } + + return LocalAllocator(context: self.context) + } + + /// A job-local stack-disciplined allocator. + /// + /// This can be used to allocate additional data required by an + /// executor implementation; memory allocated in this manner will + /// be released automatically when the job is disposed of by the + /// runtime. + /// + /// N.B. Because this allocator is stack disciplined, explicitly + /// deallocating memory will also deallocate all memory allocated + /// after the block being deallocated. + public struct LocalAllocator { + internal var context: Builtin.Job + + /// Allocate a specified number of bytes of uninitialized memory. + public func allocate(capacity: Int) -> UnsafeMutableRawBufferPointer { + let base = _jobAllocate(context, capacity) + return UnsafeMutableRawBufferPointer(start: base, count: capacity) + } + + /// Allocate uninitialized memory for a single instance of type `T`. + public func allocate(as type: T.Type) -> UnsafeMutablePointer { + let base = _jobAllocate(context, MemoryLayout.size) + return base.bindMemory(to: type, capacity: 1) + } + + /// Allocate uninitialized memory for the specified number of + /// instances of type `T`. + public func allocate(capacity: Int, as type: T.Type) + -> UnsafeMutableBufferPointer { + let base = _jobAllocate(context, MemoryLayout.stride * capacity) + let typedBase = base.bindMemory(to: T.self, capacity: capacity) + return UnsafeMutableBufferPointer(start: typedBase, count: capacity) + } + + /// Deallocate previously allocated memory. Note that the task + /// allocator is stack disciplined, so if you deallocate a block of + /// memory, all memory allocated after that block is also deallocated. + public func deallocate(_ buffer: UnsafeMutableRawBufferPointer) { + _jobDeallocate(context, buffer.baseAddress!) + } + + /// Deallocate previously allocated memory. Note that the task + /// allocator is stack disciplined, so if you deallocate a block of + /// memory, all memory allocated after that block is also deallocated. + public func deallocate(_ pointer: UnsafeMutablePointer) { + _jobDeallocate(context, UnsafeMutableRawPointer(pointer)) + } + + /// Deallocate previously allocated memory. Note that the task + /// allocator is stack disciplined, so if you deallocate a block of + /// memory, all memory allocated after that block is also deallocated. + public func deallocate(_ buffer: UnsafeMutableBufferPointer) { + _jobDeallocate(context, UnsafeMutableRawPointer(buffer.baseAddress!)) + } + + } + +} + + #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY // ==== ----------------------------------------------------------------------- @@ -764,10 +905,3 @@ public func _unsafeInheritExecutor_withUnsafeThrowingContinuation( public func _abiEnableAwaitContinuation() { fatalError("never use this function") } - -// ==== ----------------------------------------------------------------------- -// MARK: Runtime functions - -@available(SwiftStdlib 5.9, *) -@_silgen_name("swift_concurrency_jobPriority") -internal func _swift_concurrency_jobPriority(_ job: UnownedJob) -> UInt8 diff --git a/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift b/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift index 2588cbde2aa94..12d62dfc27a7a 100644 --- a/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift +++ b/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift @@ -31,13 +31,16 @@ import Swift /// /// Customizing the global concurrent executor is currently not supported. @available(SwiftStdlib 6.0, *) +@available(*, deprecated, renamed: "Task.defaultExecutor") @_unavailableInEmbedded public var globalConcurrentExecutor: any TaskExecutor { get { - _DefaultGlobalConcurrentExecutor.shared + if #available(SwiftStdlib 6.2, *) { + return Task.defaultExecutor + } else { + return _DefaultGlobalConcurrentExecutor.shared + } } - // TODO: introduce a set {} once we are ready to allow customizing the - // default global executor. This should be done the same for main actor } /// A task executor which enqueues all work on the default global concurrent @@ -51,7 +54,7 @@ internal final class _DefaultGlobalConcurrentExecutor: TaskExecutor { private init() {} public func enqueue(_ job: consuming ExecutorJob) { - _enqueueJobGlobal(job.context) + _enqueueJobGlobal(UnownedJob(job)) } public func asUnownedTaskExecutor() -> UnownedTaskExecutor { diff --git a/stdlib/public/Concurrency/MainActor.swift b/stdlib/public/Concurrency/MainActor.swift index 232de7bc17fb7..673ecadee79a2 100644 --- a/stdlib/public/Concurrency/MainActor.swift +++ b/stdlib/public/Concurrency/MainActor.swift @@ -12,8 +12,6 @@ import Swift -#if !$Embedded - #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.1, *) @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") @@ -137,7 +135,11 @@ extension MainActor { let executor: Builtin.Executor = unsafe Self.shared.unownedExecutor.executor guard _taskIsCurrentExecutor(executor) else { // TODO: offer information which executor we actually got + #if !$Embedded fatalError("Incorrect actor executor assumption; Expected same executor as \(self).", file: file, line: line) + #else + Builtin.int_trap() + #endif } // To do the unsafe cast, we have to pretend it's @escaping. @@ -159,5 +161,3 @@ extension MainActor { } } #endif - -#endif diff --git a/stdlib/public/Concurrency/NonDispatchGlobalExecutor.cpp b/stdlib/public/Concurrency/NonDispatchGlobalExecutor.cpp deleted file mode 100644 index d2f4439b67cae..0000000000000 --- a/stdlib/public/Concurrency/NonDispatchGlobalExecutor.cpp +++ /dev/null @@ -1,108 +0,0 @@ -///===--- NonDispatchGlobalExecutor.inc ---------------------*- C++ -*--===/// -/// -/// This source file is part of the Swift.org open source project -/// -/// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors -/// Licensed under Apache License v2.0 with Runtime Library Exception -/// -/// See https:///swift.org/LICENSE.txt for license information -/// See https:///swift.org/CONTRIBUTORS.txt for the list of Swift project authors -/// -///===------------------------------------------------------------------===/// -/// -/// The implementation of the global executor when not using Dispatch but -/// also not using the cooperative global executor. The general assumption -/// is that clients will be installing the appropriate hooks when all of -/// the functions here are called. -/// -/// This file is included into GlobalExecutor.cpp only when both -/// Dispatch integration and the cooperative global executor are disabled. -/// It is expected to define the following functions: -/// swift_task_asyncMainDrainQueueImpl -/// swift_task_checkIsolatedImpl -/// swift_task_donateThreadToGlobalExecutorUntilImpl -/// swift_task_enqueueGlobalImpl -/// swift_task_enqueueGlobalWithDeadlineImpl -/// swift_task_enqueueGlobalWithDelayImpl -/// swift_task_enqueueMainExecutorImpl -/// swift_task_getMainExecutorImpl -/// swift_task_isMainExecutorImpl -/// -///===------------------------------------------------------------------===/// - -#include "swift/Runtime/Debug.h" - -#include "ExecutorImpl.h" - -using namespace swift; - -SWIFT_CC(swift) -void swift_task_enqueueGlobalImpl(SwiftJob *job) { - assert(job && "no job provided"); - - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_enqueueGlobal"); -} - -SWIFT_CC(swift) -void swift_task_enqueueGlobalWithDelayImpl(SwiftJobDelay delay, - SwiftJob *job) { - assert(job && "no job provided"); - - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_enqueueGlobalWithDelay"); -} - -SWIFT_CC(swift) -void swift_task_enqueueGlobalWithDeadlineImpl(long long sec, - long long nsec, - long long tsec, - long long tnsec, - int clock, SwiftJob *job) { - assert(job && "no job provided"); - - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_enqueueGlobalWithDeadline"); -} - -/// Enqueues a task on the main executor. -SWIFT_CC(swift) -void swift_task_enqueueMainExecutorImpl(SwiftJob *job) { - assert(job && "no job provided"); - - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_enqueueMainExecutor"); -} - -SWIFT_CC(swift) -void swift_task_checkIsolatedImpl(SwiftExecutorRef executor) { - swift_executor_invokeSwiftCheckIsolated(executor); -} - -SWIFT_CC(swift) -bool swift_task_isIsolatingCurrentContextImpl(SwiftExecutorRef executor) { - return swift_executor_invokeSwiftIsIsolatingCurrentContext(executor); -} - -SWIFT_CC(swift) -SwiftExecutorRef swift_task_getMainExecutorImpl() { - return swift_executor_generic(); -} - -SWIFT_CC(swift) -bool swift_task_isMainExecutorImpl(SwiftExecutorRef executor) { - return swift_executor_isGeneric(executor); -} - -SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_CC(swift) -void swift_task_asyncMainDrainQueueImpl() { - swift_reportError(0, "operation unsupported without libdispatch: " - "swift_task_asyncMainDrainQueue"); -} - -SWIFT_CC(swift) void -swift_task_donateThreadToGlobalExecutorUntilImpl(bool (*condition)(void *), - void *conditionContext) { - swift_reportError(0, "operation unsupported: " - "swift_task_donateThreadToGlobalExecutorUntilImpl"); -} diff --git a/stdlib/public/Concurrency/PlatformExecutorDarwin.swift b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift new file mode 100644 index 0000000000000..45df1b2981a23 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift @@ -0,0 +1,122 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)) + +import Swift + +// .. Platform Main Executor ................................................... + +/// `PlatformMainExecutor` is used during program start-up and also for any +/// `@MainActor` code. It implements `SerialExecutor`, `RunLoopExecutor` and +/// `EventableExecutor`. +@available(SwiftStdlib 6.2, *) +public class PlatformMainExecutor: MainExecutor, @unchecked Sendable { + var executor: any MainExecutor + + init() { + if CoreFoundation.isPresent { + executor = CFMainExecutor() + } else { + executor = DispatchMainExecutor() + } + } + + public var supportsScheduling: Bool { executor.supportsScheduling } + + public func enqueue(_ job: consuming ExecutorJob) { + executor.enqueue(job) + } + + public func enqueue(_ job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + executor.enqueue(job, after: delay, tolerance: tolerance, clock: clock) + } + + public func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration? = nil, + clock: C) { + executor.enqueue(job, at: instant, tolerance: tolerance, clock: clock) + } + + public func run() throws { + try executor.run() + } + + public func run(until condition: () -> Bool) throws { + try executor.run(until: condition) + } + + public func stop() { + executor.stop() + } + + public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { + return executor.registerEvent(handler: handler) + } + + public func deregister(event: ExecutorEvent) { + executor.deregister(event: event) + } + + public func notify(event: ExecutorEvent) { + executor.notify(event: event) + } + + public func checkIsolated() { + executor.checkIsolated() + } +} + +// .. Platform Task Executor ................................................... + +/// `PlatformDefaultExecutor` is the default executor for non-`@MainActor` +/// tasks. It implements `TaskExecutor` only. +@available(SwiftStdlib 6.2, *) +public class PlatformDefaultExecutor: TaskExecutor, @unchecked Sendable { + var executor: any TaskExecutor + + init() { + if CoreFoundation.isPresent { + executor = CFTaskExecutor() + } else { + executor = DispatchTaskExecutor() + } + } + + public var isMainExecutor: Bool { executor.isMainExecutor } + + public var supportsScheduling: Bool { executor.supportsScheduling } + + public func enqueue(_ job: consuming ExecutorJob) { + executor.enqueue(job) + } + + public func enqueue(_ job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + executor.enqueue(job, after: delay, tolerance: tolerance, clock: clock) + } + + public func enqueue(_ job: consuming ExecutorJob, + at instant: C.Instant, + tolerance: C.Duration? = nil, + clock: C) { + executor.enqueue(job, at: instant, tolerance: tolerance, clock: clock) + } +} + +#endif // os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) diff --git a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift new file mode 100644 index 0000000000000..5aac444118bfd --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift @@ -0,0 +1,64 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +// ###TODO: Flesh this file out + +// .. Platform Main Executor ................................................... + +@available(SwiftStdlib 6.2, *) +public final class PlatformMainExecutor: MainExecutor, @unchecked Sendable { + + public func enqueue(_ job: consuming ExecutorJob) { + } + + // We can't implement enqueue in Embedde Swift because we aren't + // allowed to have generics in an existential there. + + public var supportsScheduling: Bool { false } + + public func run() throws { + } + + public func run(until condition: () -> Bool) throws { + } + + public func stop() { + } + + public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { + return ExecutorEvent(id: 0) + } + + public func deregister(event: ExecutorEvent) { + } + + public func notify(event: ExecutorEvent) { + } + +} + +// .. Platform Task Executor ................................................... + +@available(SwiftStdlib 6.2, *) +public final class PlatformDefaultExecutor: TaskExecutor, @unchecked Sendable { + + public func enqueue(_ job: consuming ExecutorJob) { + } + + // We can't implement enqueue in Embedde Swift because we aren't + // allowed to have generics in an existential there. + + public var supportsScheduling: Bool { false } + +} diff --git a/stdlib/public/Concurrency/PlatformExecutorLinux.swift b/stdlib/public/Concurrency/PlatformExecutorLinux.swift new file mode 100644 index 0000000000000..1e4abdc4c7fd8 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorLinux.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && os(Linux) + +import Swift + +// The default executors for now are Dispatch-based +typealias PlatformMainExecutor = DispatchMainExecutor +typealias PlatformDefaultExecutor = DispatchTaskExecutor + +#endif // os(Linux) diff --git a/stdlib/public/Concurrency/PlatformExecutorNone.swift b/stdlib/public/Concurrency/PlatformExecutorNone.swift new file mode 100644 index 0000000000000..6f78c91f86139 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorNone.swift @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +// .. Platform Main Executor ................................................... + +/// `PlatformMainExecutor` is used during program start-up and also for any +/// `@MainActor` code. It implements `SerialExecutor`, `RunLoopExecutor` and +/// `EventableExecutor`. +@available(SwiftStdlib 6.2, *) +public class PlatformMainExecutor: MainExecutor, @unchecked Sendable { + + public func run() throws { + fatalError("There is no main executor implementation for this platform") + } + + public func stop() { + fatalError("There is no main executor implementation for this platform") + } + + public func enqueue(_ job: consuming ExecutorJob) { + fatalError("There is no main executor implementation for this platform") + } + + public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { + fatalError("There is no main executor implementation for this platform") + } + + public func deregister(event: ExecutorEvent) { + fatalError("There is no main executor implementation for this platform") + } + + public func notify(event: ExecutorEvent) { + fatalError("There is no main executor implementation for this platform") + } + +} + +// .. Platform Default Executor ................................................ + +/// `PlatformDefaultExecutor` is the default executor for non-`@MainActor` +/// tasks. It implements `TaskExecutor` only. +@available(SwiftStdlib 6.2, *) +public class PlatformDefaultExecutor: TaskExecutor, @unchecked Sendable { + + public func enqueue(_ job: consuming ExecutorJob) { + fatalError("There is no default executor implementation for this platform") + } + +} diff --git a/stdlib/public/Concurrency/PlatformExecutorWindows.swift b/stdlib/public/Concurrency/PlatformExecutorWindows.swift new file mode 100644 index 0000000000000..e13fdef366dcb --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorWindows.swift @@ -0,0 +1,21 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if !$Embedded && os(Windows) + +import Swift + +// The default executors for now are Dispatch-based +typealias PlatformMainExecutor = DispatchMainExecutor +typealias PlatformDefaultExecutor = DispatchTaskExecutor + +#endif // os(Windows) diff --git a/stdlib/public/Concurrency/SuspendingClock.swift b/stdlib/public/Concurrency/SuspendingClock.swift index c31e043387469..5bc956f9fa476 100644 --- a/stdlib/public/Concurrency/SuspendingClock.swift +++ b/stdlib/public/Concurrency/SuspendingClock.swift @@ -11,20 +11,19 @@ //===----------------------------------------------------------------------===// import Swift -#if !$Embedded - -/// A clock that measures time that always increments but stops incrementing -/// while the system is asleep. +/// A clock that measures time that always increments but stops incrementing +/// while the system is asleep. /// -/// `SuspendingClock` can be considered as a system awake time clock. The frame -/// of reference of the `Instant` may be bound machine boot or some other +/// `SuspendingClock` can be considered as a system awake time clock. The frame +/// of reference of the `Instant` may be bound machine boot or some other /// locally defined reference point. This means that the instants are /// only comparable on the same machine in the same booted session. /// /// This clock is suitable for high resolution measurements of execution. @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded public struct SuspendingClock: Sendable { - public struct Instant: Codable, Sendable { + public struct Instant: Sendable { internal var _value: Swift.Duration internal init(_value: Swift.Duration) { @@ -35,10 +34,17 @@ public struct SuspendingClock: Sendable { public init() { } } +#if !$Embedded +@available(SwiftStdlib 5.7, *) +extension SuspendingClock.Instant: Codable { +} +#endif + @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension Clock where Self == SuspendingClock { - /// A clock that measures time that always increments but stops incrementing - /// while the system is asleep. + /// A clock that measures time that always increments but stops incrementing + /// while the system is asleep. /// /// try await Task.sleep(until: .now + .seconds(3), clock: .suspending) /// @@ -47,6 +53,7 @@ extension Clock where Self == SuspendingClock { } @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension SuspendingClock: Clock { /// The current instant accounting for machine suspension. @available(SwiftStdlib 5.7, *) @@ -86,7 +93,7 @@ extension SuspendingClock: Clock { /// to coalesce CPU wake-ups to more efficiently process the wake-ups in /// a more power efficient manner. /// - /// If the task is canceled before the time ends, this function throws + /// If the task is canceled before the time ends, this function throws /// `CancellationError`. /// /// This function doesn't block the underlying thread. @@ -94,11 +101,14 @@ extension SuspendingClock: Clock { public func sleep( until deadline: Instant, tolerance: Swift.Duration? = nil ) async throws { - let (seconds, attoseconds) = deadline._value.components - let nanoseconds = attoseconds / 1_000_000_000 - try await Task._sleep(until:seconds, nanoseconds, - tolerance: tolerance, - clock: .suspending) + if #available(SwiftStdlib 6.2, *) { + try await Task._sleep(until: deadline, + tolerance: tolerance, + clock: self) + } else { + // Should never see this + Builtin.unreachable() + } } #else @available(SwiftStdlib 5.7, *) @@ -112,6 +122,7 @@ extension SuspendingClock: Clock { } @available(SwiftStdlib 5.7, *) +@_unavailableInEmbedded extension SuspendingClock.Instant: InstantProtocol { @available(SwiftStdlib 5.7, *) public static var now: SuspendingClock.Instant { SuspendingClock().now } @@ -180,5 +191,3 @@ extension SuspendingClock.Instant: InstantProtocol { rhs.duration(to: lhs) } } - -#endif diff --git a/stdlib/public/Concurrency/Task+TaskExecutor.swift b/stdlib/public/Concurrency/Task+TaskExecutor.swift index be1919da9f540..fd9c409e968db 100644 --- a/stdlib/public/Concurrency/Task+TaskExecutor.swift +++ b/stdlib/public/Concurrency/Task+TaskExecutor.swift @@ -457,7 +457,7 @@ extension UnsafeCurrentTask { /// means to guarantee the executor remains alive while it is in use. @available(SwiftStdlib 6.0, *) public var unownedTaskExecutor: UnownedTaskExecutor? { - let ref = _getPreferredTaskExecutor() + let ref = _getPreferredUnownedTaskExecutor() return UnownedTaskExecutor(ref) } } @@ -466,7 +466,7 @@ extension UnsafeCurrentTask { @available(SwiftStdlib 6.0, *) @_silgen_name("swift_task_getPreferredTaskExecutor") -internal func _getPreferredTaskExecutor() -> Builtin.Executor +internal func _getPreferredUnownedTaskExecutor() -> Builtin.Executor typealias TaskExecutorPreferenceStatusRecord = UnsafeRawPointer diff --git a/stdlib/public/Concurrency/Task.cpp b/stdlib/public/Concurrency/Task.cpp index 43a17e08a2269..3771b01bff4a9 100644 --- a/stdlib/public/Concurrency/Task.cpp +++ b/stdlib/public/Concurrency/Task.cpp @@ -377,11 +377,17 @@ static SerialExecutorRef executorForEnqueuedJob(Job *job) { return SerialExecutorRef::generic(); } - if (auto identity = reinterpret_cast(jobQueue)) { - return SerialExecutorRef::forOrdinary( - identity, _swift_task_getDispatchQueueSerialExecutorWitnessTable()); + if (jobQueue == (void *)dispatch_get_main_queue()) { + return swift_task_getMainExecutor(); } + // It is no longer valid to return a Dispatch Queue as the "identity" here. + // + //if (auto identity = reinterpret_cast(jobQueue)) { + // return SerialExecutorRef::forOrdinary( + // identity, _swift_task_getDispatchQueueSerialExecutorWitnessTable()); + //} + return SerialExecutorRef::generic(); #endif } diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 27a6f156eaafd..9ce4ff6ed8027 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -227,7 +227,7 @@ extension Task { /// recommended that callers release any locks they might be /// holding before they call cancel. /// - /// If the task has already run past the last point where it could have + /// If the task has already run past the last point where it could have /// performed a cancellation check, cancelling it may have no observable effects. /// /// - SeeAlso: `Task.checkCancellation()` @@ -1253,7 +1253,7 @@ extension Task where Success == Never, Failure == Never { let job = _taskCreateNullaryContinuationJob( priority: Int(Task.currentPriority.rawValue), continuation: continuation) - _enqueueJobGlobal(job) + _enqueueJobGlobalDirect(job) } } } @@ -1412,19 +1412,29 @@ func getJobFlags(_ task: Builtin.NativeObject) -> JobFlags @available(SwiftStdlib 5.1, *) @_silgen_name("swift_task_enqueueGlobal") @usableFromInline -func _enqueueJobGlobal(_ task: Builtin.Job) +func _enqueueJobGlobal(_ task: UnownedJob) + +@usableFromInline +func _enqueueJobGlobalDirect(_ task: Builtin.Job) { + if #available(SwiftStdlib 5.9, *) { + _enqueueJobGlobal(UnownedJob(context: task)) + } else { + // Shouldn't ever get here + Builtin.unreachable() + } +} @available(SwiftStdlib 5.1, *) @_silgen_name("swift_task_enqueueGlobalWithDelay") @usableFromInline -func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: Builtin.Job) +func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: UnownedJob) @available(SwiftStdlib 5.7, *) @_silgen_name("swift_task_enqueueGlobalWithDeadline") @usableFromInline func _enqueueJobGlobalWithDeadline(_ seconds: Int64, _ nanoseconds: Int64, _ toleranceSec: Int64, _ toleranceNSec: Int64, - _ clock: Int32, _ task: Builtin.Job) + _ clock: Int32, _ task: UnownedJob) @usableFromInline @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/TaskAlloc.cpp b/stdlib/public/Concurrency/TaskAlloc.cpp index a9c20e442d416..6e92643805a66 100644 --- a/stdlib/public/Concurrency/TaskAlloc.cpp +++ b/stdlib/public/Concurrency/TaskAlloc.cpp @@ -117,3 +117,17 @@ void swift::swift_coro_dealloc(CoroAllocator *allocator, void *ptr) { } allocator->deallocate(ptr); } + +void *swift::swift_job_allocate(Job *job, size_t size) { + if (!job->isAsyncTask()) + return nullptr; + + return allocator(static_cast(job)).alloc(size); +} + +void swift::swift_job_deallocate(Job *job, void *ptr) { + if (!job->isAsyncTask()) + return; + + allocator(static_cast(job)).dealloc(ptr); +} diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift index d1fa4fa59ed59..911d4d3f77f9f 100644 --- a/stdlib/public/Concurrency/TaskSleep.swift +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -27,7 +27,20 @@ extension Task where Success == Never, Failure == Never { let job = _taskCreateNullaryContinuationJob( priority: Int(Task.currentPriority.rawValue), continuation: continuation) - _enqueueJobGlobalWithDelay(duration, job) + + if #available(SwiftStdlib 6.2, *) { + let executor = Task.currentExecutor ?? Task.defaultExecutor + + #if !$Embedded + executor.enqueue(ExecutorJob(context: job), + after: .nanoseconds(duration), + clock: .continuous) + #endif + } else { + // Since we're building the new version of the stdlib, we should + // never get here. + Builtin.unreachable() + } } } @@ -255,8 +268,19 @@ extension Task where Success == Never, Failure == Never { let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) { unsafe onSleepWake(token) } - _enqueueJobGlobalWithDelay( - duration, Builtin.convertTaskToJob(sleepTask)) + + if #available(SwiftStdlib 6.2, *) { + let executor = Task.currentExecutor ?? Task.defaultExecutor + let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) + #if !$Embedded + executor.enqueue(job, + after: .nanoseconds(duration), + clock: .continuous) + #endif + } else { + // Shouldn't be able to get here + Builtin.unreachable() + } return case .activeContinuation, .finished: diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift index 2354c11eb8f43..07073284d5b0c 100644 --- a/stdlib/public/Concurrency/TaskSleepDuration.swift +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -17,10 +17,10 @@ import Swift @_unavailableInEmbedded extension Task where Success == Never, Failure == Never { @available(SwiftStdlib 5.7, *) - internal static func _sleep( - until seconds: Int64, _ nanoseconds: Int64, - tolerance: Duration?, - clock: _ClockID + internal static func _sleep( + until instant: C.Instant, + tolerance: C.Duration?, + clock: C ) async throws { // Create a token which will initially have the value "not started", which // means the continuation has neither been created nor completed. @@ -53,20 +53,22 @@ extension Task where Success == Never, Failure == Never { let (sleepTask, _) = Builtin.createAsyncTask(sleepTaskFlags) { unsafe onSleepWake(token) } - let toleranceSeconds: Int64 - let toleranceNanoseconds: Int64 - if let components = tolerance?.components { - toleranceSeconds = components.seconds - toleranceNanoseconds = components.attoseconds / 1_000_000_000 + + if #available(SwiftStdlib 6.2, *) { + let executor = Task.currentExecutor ?? Task.defaultExecutor + let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) + + #if !$Embedded + executor.enqueue(job, + at: instant, + tolerance: tolerance, + clock: clock) + #endif } else { - toleranceSeconds = 0 - toleranceNanoseconds = -1 + // Since we're building the new version of the stdlib, + // we should never get here + Builtin.unreachable() } - - _enqueueJobGlobalWithDeadline( - seconds, nanoseconds, - toleranceSeconds, toleranceNanoseconds, - clock.rawValue, Builtin.convertTaskToJob(sleepTask)) return case .activeContinuation, .finished: @@ -119,7 +121,7 @@ extension Task where Success == Never, Failure == Never { /// Suspends the current task until the given deadline within a tolerance. /// - /// If the task is canceled before the time ends, this function throws + /// If the task is canceled before the time ends, this function throws /// `CancellationError`. /// /// This function doesn't block the underlying thread. @@ -130,14 +132,14 @@ extension Task where Success == Never, Failure == Never { public static func sleep( until deadline: C.Instant, tolerance: C.Instant.Duration? = nil, - clock: C = ContinuousClock() + clock: C = .continuous ) async throws { try await clock.sleep(until: deadline, tolerance: tolerance) } - + /// Suspends the current task for the given duration. /// - /// If the task is cancelled before the time ends, this function throws + /// If the task is cancelled before the time ends, this function throws /// `CancellationError`. /// /// This function doesn't block the underlying thread. @@ -149,7 +151,7 @@ extension Task where Success == Never, Failure == Never { public static func sleep( for duration: C.Instant.Duration, tolerance: C.Instant.Duration? = nil, - clock: C = ContinuousClock() + clock: C = .continuous ) async throws { try await clock.sleep(for: duration, tolerance: tolerance) } @@ -163,7 +165,7 @@ extension Task where Success == Never, Failure == Never { public static func sleep( until deadline: C.Instant, tolerance: C.Instant.Duration? = nil, - clock: C = ContinuousClock() + clock: C = .continuous ) async throws { fatalError("Unavailable in task-to-thread concurrency model") } @@ -173,7 +175,7 @@ extension Task where Success == Never, Failure == Never { public static func sleep( for duration: C.Instant.Duration, tolerance: C.Instant.Duration? = nil, - clock: C = ContinuousClock() + clock: C = .continuous ) async throws { fatalError("Unavailable in task-to-thread concurrency model") } diff --git a/test/Concurrency/Runtime/actor_counters.swift b/test/Concurrency/Runtime/actor_counters.swift index ef36940e663e9..5749f6012db42 100644 --- a/test/Concurrency/Runtime/actor_counters.swift +++ b/test/Concurrency/Runtime/actor_counters.swift @@ -44,7 +44,7 @@ nonisolated var randomPriority: TaskPriority? { @available(SwiftStdlib 5.1, *) func worker(identity: Int, counters: [Counter], numIterations: Int) async { - for i in 0..] = [] + var workers: [Task] = [] for i in 0.. Int { async let second = await asyncFib(n-1) // Sleep a random amount of time waiting on the result producing a result. - await Task.sleep(UInt64.random(in: 0..<100) * 1_000_000) + try! await Task.sleep(nanoseconds: UInt64.random(in: 0..<100) * 1_000_000) let result = await first + second // Sleep a random amount of time before producing a result. - await Task.sleep(UInt64.random(in: 0..<100) * 1_000_000) + try! await Task.sleep(nanoseconds: UInt64.random(in: 0..<100) * 1_000_000) return result } diff --git a/test/Concurrency/Runtime/async_task_executor_nsobject.swift b/test/Concurrency/Runtime/async_task_executor_nsobject.swift index ae8f31b3bb7dd..6031ef1a2d448 100644 --- a/test/Concurrency/Runtime/async_task_executor_nsobject.swift +++ b/test/Concurrency/Runtime/async_task_executor_nsobject.swift @@ -29,6 +29,32 @@ final class NSQueueTaskExecutor: NSData, TaskExecutor, @unchecked Sendable { job.runSynchronously(on: self.asUnownedTaskExecutor()) } } + + public var supportsScheduling: Bool { true } + + public func enqueue(_ _job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + // Convert to `Swift.Duration` + let duration = clock.convert(from: delay)! + + // Now turn that into nanoseconds + let (seconds, attoseconds) = duration.components + let nanoseconds = attoseconds / 1_000_000_000 + + // Get a Dispatch time + let deadline = DispatchTime.now().advanced( + by: .seconds(Int(seconds)) + ).advanced( + by: .nanoseconds(Int(nanoseconds)) + ) + + let job = UnownedJob(_job) + DispatchQueue.main.asyncAfter(deadline: deadline) { + job.runSynchronously(on: self.asUnownedTaskExecutor()) + } + } } @main struct Main { diff --git a/test/Concurrency/Runtime/mainactor.swift b/test/Concurrency/Runtime/mainactor.swift index 160abae2c0c4b..f2be405fd3152 100644 --- a/test/Concurrency/Runtime/mainactor.swift +++ b/test/Concurrency/Runtime/mainactor.swift @@ -1,4 +1,4 @@ -// RUN: %target-run-simple-swift(-parse-as-library -target %target-swift-5.1-abi-triple %import-libdispatch) | %FileCheck %s +// RUN: %target-run-simple-swift(-parse-as-library -g -target %target-swift-5.1-abi-triple %import-libdispatch) | %FileCheck %s // REQUIRES: executable_test // REQUIRES: concurrency From 55afa47beae1bbb619a36aa99d70af15634124e0 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 7 Mar 2025 20:09:53 +0000 Subject: [PATCH 02/29] [Concurrency] More work on the custom executor implementation. Added an `-executor-factory` argument to the compiler to let you safely specify the executors you wish to use (by naming a type that returns them). Also added some tests of the new functionality. rdar://141348916 --- include/swift/AST/DiagnosticsSIL.def | 8 + include/swift/AST/FeatureAvailability.def | 1 + include/swift/AST/KnownProtocols.def | 1 + include/swift/Basic/LangOptions.h | 4 + include/swift/Option/Options.td | 10 + lib/AST/ASTContext.cpp | 1 + lib/ClangImporter/SortedCFDatabase.def.gyb | 2 +- lib/Driver/ToolChains.cpp | 4 + lib/Frontend/CompilerInvocation.cpp | 6 + lib/IRGen/GenMeta.cpp | 1 + lib/SILGen/SILGen.cpp | 44 +++ lib/SILGen/SILGen.h | 12 + lib/SILGen/SILGenFunction.cpp | 71 ++++- .../public/Concurrency/DispatchExecutor.swift | 6 + .../Concurrency/DispatchGlobalExecutor.cpp | 13 +- stdlib/public/Concurrency/Executor.swift | 79 ++--- stdlib/public/Concurrency/ExecutorBridge.cpp | 9 + .../public/Concurrency/ExecutorBridge.swift | 4 + stdlib/public/Concurrency/ExecutorImpl.swift | 1 + .../Concurrency/PlatformExecutorDarwin.swift | 100 +------ .../PlatformExecutorEmbedded.swift | 14 +- .../Concurrency/PlatformExecutorLinux.swift | 7 +- .../Concurrency/PlatformExecutorWindows.swift | 7 +- test/Concurrency/Runtime/clocks.swift | 272 ++++++++++++++++++ .../Runtime/custom_main_executor.swift | 102 +++++++ test/Concurrency/Runtime/sleep_executor.swift | 91 ++++++ 26 files changed, 713 insertions(+), 157 deletions(-) create mode 100644 test/Concurrency/Runtime/clocks.swift create mode 100644 test/Concurrency/Runtime/custom_main_executor.swift create mode 100644 test/Concurrency/Runtime/sleep_executor.swift diff --git a/include/swift/AST/DiagnosticsSIL.def b/include/swift/AST/DiagnosticsSIL.def index 90643035ec21d..c8906fcb3d426 100644 --- a/include/swift/AST/DiagnosticsSIL.def +++ b/include/swift/AST/DiagnosticsSIL.def @@ -1133,6 +1133,14 @@ NOTE(rbi_add_generic_parameter_sendable_conformance,none, "consider making generic parameter %0 conform to the 'Sendable' protocol", (Type)) +// Concurrency related diagnostics +ERROR(cannot_find_executor_factory_type, none, + "the specified executor factory '%0' could not be found", (StringRef)) +ERROR(executor_factory_must_conform, none, + "the executor factory '%0' does not conform to 'ExecutorFactory'", (Type)) +ERROR(executor_factory_not_supported, none, + "deployment target too low for executor factory specification", ()) + //===----------------------------------------------------------------------===// // MARK: Misc Diagnostics //===----------------------------------------------------------------------===// diff --git a/include/swift/AST/FeatureAvailability.def b/include/swift/AST/FeatureAvailability.def index ae1bbdbc5db67..3270cca5ddfd9 100644 --- a/include/swift/AST/FeatureAvailability.def +++ b/include/swift/AST/FeatureAvailability.def @@ -80,6 +80,7 @@ FEATURE(IsolatedDeinit, (6, 1)) FEATURE(ValueGenericType, (6, 2)) FEATURE(InitRawStructMetadata2, (6, 2)) +FEATURE(CustomExecutors, (6, 2)) FEATURE(TaskExecutor, FUTURE) FEATURE(Differentiation, FUTURE) diff --git a/include/swift/AST/KnownProtocols.def b/include/swift/AST/KnownProtocols.def index 27ecbbcfeaf75..89c14badf0464 100644 --- a/include/swift/AST/KnownProtocols.def +++ b/include/swift/AST/KnownProtocols.def @@ -97,6 +97,7 @@ PROTOCOL(Executor) PROTOCOL(SerialExecutor) PROTOCOL(TaskExecutor) PROTOCOL(GlobalActor) +PROTOCOL(ExecutorFactory) PROTOCOL_(BridgedNSError) PROTOCOL_(BridgedStoredNSError) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 7237ed538b198..4df3e0e9deaaf 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -396,6 +396,10 @@ namespace swift { /// Specifies how strict concurrency checking will be. StrictConcurrency StrictConcurrencyLevel = StrictConcurrency::Minimal; + /// Specifies the name of the executor factory to use to create the + /// default executors for Swift Concurrency. + std::optional ExecutorFactory; + /// Enable experimental concurrency model. bool EnableExperimentalConcurrency = false; diff --git a/include/swift/Option/Options.td b/include/swift/Option/Options.td index d5f1eccc580db..d42fee50fe3ad 100644 --- a/include/swift/Option/Options.td +++ b/include/swift/Option/Options.td @@ -985,6 +985,16 @@ def strict_concurrency : Joined<["-"], "strict-concurrency=">, "concurrency model, or 'complete' ('Sendable' and other checking is " "enabled for all code in the module)">; +def executor_factory : JoinedOrSeparate<["-"], "executor-factory">, + Flags<[FrontendOption]>, + HelpText<"Specify the factory to use to create the default executors for " + "Swift Concurrency. This must be a type conforming to the " + "'ExecutorFactory' protocol.">, + MetaVarName<"">; +def executor_factory_EQ : Joined<["-"], "executor-factory=">, + Flags<[FrontendOption]>, + Alias; + def enable_experimental_feature : Separate<["-"], "enable-experimental-feature">, Flags<[FrontendOption, ModuleInterfaceOption]>, diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 9f7ab7f111bd4..857603e92b545 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -1435,6 +1435,7 @@ ProtocolDecl *ASTContext::getProtocol(KnownProtocolKind kind) const { case KnownProtocolKind::Executor: case KnownProtocolKind::TaskExecutor: case KnownProtocolKind::SerialExecutor: + case KnownProtocolKind::ExecutorFactory: M = getLoadedModule(Id_Concurrency); break; case KnownProtocolKind::DistributedActor: diff --git a/lib/ClangImporter/SortedCFDatabase.def.gyb b/lib/ClangImporter/SortedCFDatabase.def.gyb index 41d83ccb4b0f4..340ed2293a661 100644 --- a/lib/ClangImporter/SortedCFDatabase.def.gyb +++ b/lib/ClangImporter/SortedCFDatabase.def.gyb @@ -39,7 +39,7 @@ with codecs.open(CFDatabaseFile, encoding='utf-8', errors='strict') as f: continue # Otherwise, check for lines like FOO(BAR) - m = re.match('^\w+\((\w+)\)', line) + m = re.match(r'^\w+\((\w+)\)', line) if m: lineForName[m.group(1)] = line }% diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index 6a7ae73498e00..0ca869042faca 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -377,6 +377,10 @@ void ToolChain::addCommonFrontendArgs(const OutputInfo &OI, arguments.push_back(inputArgs.MakeArgString(globalRemapping)); } + if (inputArgs.hasArg(options::OPT_executor_factory)) { + inputArgs.AddLastArg(arguments, options::OPT_executor_factory); + } + // Pass through the values passed to -Xfrontend. inputArgs.AddAllArgValues(arguments, options::OPT_Xfrontend); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 9233295bfd1a0..6a7b9432c6002 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1314,6 +1314,12 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.enableFeature(Feature::RegionBasedIsolation); } + // Get the executor factory name + if (const Arg *A = Args.getLastArg(OPT_executor_factory)) { + printf("Got executor-factory option\n"); + Opts.ExecutorFactory = A->getValue(); + } + Opts.WarnImplicitOverrides = Args.hasArg(OPT_warn_implicit_overrides); diff --git a/lib/IRGen/GenMeta.cpp b/lib/IRGen/GenMeta.cpp index 6fa5ce3a06911..cdf6235ee3364 100644 --- a/lib/IRGen/GenMeta.cpp +++ b/lib/IRGen/GenMeta.cpp @@ -7063,6 +7063,7 @@ SpecialProtocol irgen::getSpecialProtocolID(ProtocolDecl *P) { case KnownProtocolKind::Executor: case KnownProtocolKind::SerialExecutor: case KnownProtocolKind::TaskExecutor: + case KnownProtocolKind::ExecutorFactory: case KnownProtocolKind::Sendable: case KnownProtocolKind::UnsafeSendable: case KnownProtocolKind::RangeReplaceableCollection: diff --git a/lib/SILGen/SILGen.cpp b/lib/SILGen/SILGen.cpp index 0bd0a03e244ef..27c554a71651b 100644 --- a/lib/SILGen/SILGen.cpp +++ b/lib/SILGen/SILGen.cpp @@ -458,6 +458,10 @@ FuncDecl *SILGenModule::getDeinitOnExecutor() { return lookupConcurrencyIntrinsic(getASTContext(), "_deinitOnExecutor"); } +FuncDecl *SILGenModule::getCreateExecutors() { + return lookupConcurrencyIntrinsic(getASTContext(), "_createExecutors"); +} + FuncDecl *SILGenModule::getExit() { ASTContext &C = getASTContext(); @@ -507,6 +511,46 @@ FuncDecl *SILGenModule::getExit() { return exitFunction; } +Type SILGenModule::getExecutorFactory() { + auto &ctx = getASTContext(); + + ModuleDecl *module; + + // Parse the executor factory name + StringRef qualifiedName = *ctx.LangOpts.ExecutorFactory; + StringRef typeName; + + auto parts = qualifiedName.split('.'); + + if (parts.second.empty()) { + // This was an unqualified name; assume it's relative to the main module + module = ctx.MainModule; + typeName = qualifiedName; + } else { + Identifier moduleName = ctx.getIdentifier(parts.first); + module = ctx.getModuleByIdentifier(moduleName); + typeName = parts.second; + } + + return ctx.getNamedSwiftType(module, typeName); +} + +Type SILGenModule::getDefaultExecutorFactory() { + auto &ctx = getASTContext(); + + ModuleDecl *module = ctx.getModuleByIdentifier(ctx.Id_Concurrency); + if (!module) + return Type(); + + return ctx.getNamedSwiftType(module, "DefaultExecutorFactory"); +} + +ProtocolDecl *SILGenModule::getExecutorFactoryProtocol() { + auto &ctx = getASTContext(); + + return ctx.getProtocol(KnownProtocolKind::ExecutorFactory); +} + ProtocolConformance *SILGenModule::getNSErrorConformanceToError() { if (NSErrorConformanceToError) return *NSErrorConformanceToError; diff --git a/lib/SILGen/SILGen.h b/lib/SILGen/SILGen.h index 3599eadf8acd7..daca3223491f6 100644 --- a/lib/SILGen/SILGen.h +++ b/lib/SILGen/SILGen.h @@ -562,6 +562,18 @@ class LLVM_LIBRARY_VISIBILITY SILGenModule : public ASTVisitor { // Retrieve the _SwiftConcurrencyShims.exit intrinsic. FuncDecl *getExit(); + /// Get the ExecutorFactory type. + Type getExecutorFactory(); + + /// Get the DefaultExecutorFactory type. + Type getDefaultExecutorFactory(); + + /// Get the ExecutorFactory protocol. + ProtocolDecl *getExecutorFactoryProtocol(); + + /// Get the swift_createExecutors function. + FuncDecl *getCreateExecutors(); + SILFunction *getKeyPathProjectionCoroutine(bool isReadAccess, KeyPathTypeKind typeKind); diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index ab2aec62042ed..e0aeb7bd44ead 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -486,7 +486,7 @@ SILGenFunction::getOrCreateScope(const ast_scope::ASTScopeImpl *ASTScope, // Collapse BraceStmtScopes whose parent is a .*BodyScope. if (auto Parent = ASTScope->getParent().getPtrOrNull()) if (Parent->getSourceRangeOfThisASTNode() == - ASTScope->getSourceRangeOfThisASTNode()) + ASTScope->getSourceRangeOfThisASTNode()) return cache(getOrCreateScope(Parent, FnScope, InlinedAt)); // The calls to defer closures have cleanup source locations pointing to the @@ -1387,6 +1387,28 @@ void SILGenFunction::emitArtificialTopLevel(Decl *mainDecl) { } } +static bool isCreateExecutorsFunctionAvailable(SILGenModule &SGM) { + FuncDecl *createExecutorsFuncDecl = SGM.getCreateExecutors(); + if (!createExecutorsFuncDecl) + return false; + + auto &ctx = createExecutorsFuncDecl->getASTContext(); + + if (ctx.LangOpts.hasFeature(Feature::Embedded)) + return true; + + if (!ctx.LangOpts.DisableAvailabilityChecking) { + auto deploymentAvailability = AvailabilityRange::forDeploymentTarget(ctx); + auto runtimeAvailability = AvailabilityRange::forRuntimeTarget(ctx); + auto declAvailability = ctx.getCustomExecutorsAvailability(); + auto declRtAvailability = ctx.getCustomExecutorsRuntimeAvailability(); + return deploymentAvailability.isContainedIn(declAvailability) + && runtimeAvailability.isContainedIn(declRtAvailability); + } + + return true; +} + void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) { auto moduleLoc = entryPoint.getAsRegularLocation(); auto *entryBlock = B.getInsertionBB(); @@ -1402,6 +1424,53 @@ void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) { B.setInsertionPoint(entryBlock); + // If we're using a new enough deployment target, call swift_createExecutors() + if (ctx.LangOpts.ExecutorFactory) { + printf("Executor factory is %s\n", ctx.LangOpts.ExecutorFactory->c_str()); + + if (!isCreateExecutorsFunctionAvailable(SGM)) { + ctx.Diags.diagnose(SourceLoc(), diag::executor_factory_not_supported); + } else { + CanType factoryTy = SGM.getExecutorFactory()->getCanonicalType(); + + if (!factoryTy) { + ctx.Diags.diagnose(SourceLoc(), diag::cannot_find_executor_factory_type, + *ctx.LangOpts.ExecutorFactory); + } + + ProtocolDecl *executorFactoryProtocol = SGM.getExecutorFactoryProtocol(); + auto conformance = lookupConformance(factoryTy, executorFactoryProtocol); + + if (conformance.isInvalid()) { + // If this type doesn't conform, ignore it and use the default factory + SourceLoc loc = extractNearestSourceLoc(factoryTy); + + ctx.Diags.diagnose(loc, diag::executor_factory_must_conform, factoryTy); + + factoryTy = SGM.getDefaultExecutorFactory()->getCanonicalType(); + conformance = lookupConformance(factoryTy, executorFactoryProtocol); + + assert(!conformance.isInvalid()); + } + + FuncDecl *createExecutorsFuncDecl = SGM.getCreateExecutors(); + assert(createExecutorsFuncDecl + && "Failed to find swift_createExecutors function decl"); + SILFunction *createExecutorsSILFunc = + SGM.getFunction(SILDeclRef(createExecutorsFuncDecl, SILDeclRef::Kind::Func), + NotForDefinition); + SILValue createExecutorsFunc = + B.createFunctionRefFor(moduleLoc, createExecutorsSILFunc); + MetatypeType *factoryThickMetaTy + = MetatypeType::get(factoryTy, MetatypeRepresentation::Thick); + SILValue factorySILMetaTy + = B.createMetatype(moduleLoc, getLoweredType(factoryThickMetaTy)); + auto ceSubs = SubstitutionMap::getProtocolSubstitutions( + conformance.getRequirement(), factoryTy, conformance); + B.createApply(moduleLoc, createExecutorsFunc, ceSubs, { factorySILMetaTy }); + } + } + auto wrapCallArgs = [this, &moduleLoc](SILValue originalValue, FuncDecl *fd, uint32_t paramIndex) -> SILValue { Type parameterType = fd->getParameters()->get(paramIndex)->getTypeInContext(); diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift index d9bd1410755cc..7e4be9acd2a35 100644 --- a/stdlib/public/Concurrency/DispatchExecutor.swift +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -27,6 +27,8 @@ import Swift public class DispatchMainExecutor: RunLoopExecutor, @unchecked Sendable { var threaded = false + public init() {} + public func run() throws { if threaded { fatalError("DispatchMainExecutor does not support recursion") @@ -43,6 +45,7 @@ public class DispatchMainExecutor: RunLoopExecutor, @unchecked Sendable { @available(SwiftStdlib 6.2, *) extension DispatchMainExecutor: SerialExecutor { + public func enqueue(_ job: consuming ExecutorJob) { _dispatchEnqueueMain(UnownedJob(job)) } @@ -112,6 +115,9 @@ extension DispatchMainExecutor: MainExecutor {} @available(SwiftStdlib 6.2, *) public class DispatchTaskExecutor: TaskExecutor, @unchecked Sendable { + + public init() {} + public func enqueue(_ job: consuming ExecutorJob) { _dispatchEnqueueGlobal(UnownedJob(job)) } diff --git a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp index d8b621743e7cb..b5face3c0fbb3 100644 --- a/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp +++ b/stdlib/public/Concurrency/DispatchGlobalExecutor.cpp @@ -332,11 +332,20 @@ void swift_dispatchEnqueueWithDeadline(bool global, job->schedulerPrivate[SwiftJobDispatchQueueIndex] = queue; } - uint64_t deadline = sec * NSEC_PER_SEC + nsec; + uint64_t deadline; + if (__builtin_mul_overflow(sec, NSEC_PER_SEC, &deadline) + || __builtin_add_overflow(nsec, deadline, &deadline)) { + deadline = UINT64_MAX; + } + dispatch_time_t when = clock_and_value_to_time(clock, deadline); if (tnsec != -1) { - uint64_t leeway = tsec * NSEC_PER_SEC + tnsec; + uint64_t leeway; + if (__builtin_mul_overflow(tsec, NSEC_PER_SEC, &leeway) + || __builtin_add_overflow(tnsec, deadline, &leeway)) { + leeway = UINT64_MAX; + } dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index 2c41f6f7e824f..519115f4ef266 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -520,6 +520,10 @@ public struct ExecutorEvent: Identifiable, Comparable, Sendable { public var id: Self.ID + public init(id: Self.ID) { + self.id = id + } + public static func < (lhs: Self, rhs: Self) -> Bool { return lhs.id < rhs.id } @@ -569,43 +573,30 @@ public protocol EventableExecutor { public protocol MainExecutor: RunLoopExecutor, SerialExecutor, EventableExecutor { } -#if SWIFT_STDLIB_DISABLE_CUSTOM_EXECUTORS -// For this to work, there must be a definition of PlatformMainExecutor and -// PlatformDefaultExecutor available in the Concurrency runtime for the -// platform in question. - -let _platformMainExecutor = PlatformMainExecutor() -let _platformDefaultExecutor = PlatformDefaultExecutor() +/// An ExecutorFactory is used to create the default main and task +/// executors. @available(SwiftStdlib 6.2, *) -extension MainActor { - /// The main executor, which is started implicitly by the `async main` - /// entry point and owns the "main" thread. - /// - /// Attempting to set this after the first `enqueue` on the main - /// executor is a fatal error. - public static var executor: PlatformMainExecutor { - return _platformMainExecutor - } +public protocol ExecutorFactory { + /// Constructs and returns the main executor, which is started implicitly + /// by the `async main` entry point and owns the "main" thread. + static var mainExecutor: any MainExecutor { get } + + /// Constructs and returns the default or global executor, which is the + /// default place in which we run tasks. + static var defaultExecutor: any TaskExecutor { get } } @available(SwiftStdlib 6.2, *) -extension Task where Success == Never, Failure == Never { - /// The default or global executor, which is the default place in which - /// we run tasks. - /// - /// Attempting to set this after the first `enqueue` on the global - /// executor is a fatal error. - public static var defaultExecutor: PlatformDefaultExecutor { - return _platformDefaultExecutor - } +@_silgen_name("swift_createExecutors") +public func _createExecutors(factory: F.Type) { + MainActor._executor = factory.mainExecutor + Task._defaultExecutor = factory.defaultExecutor } -#else // !SWIFT_STDLIB_DISABLE_CUSTOM_EXECUTORS - @available(SwiftStdlib 6.2, *) extension MainActor { - private static var _executor: (any MainExecutor)? = nil + static var _executor: (any MainExecutor)? = nil /// The main executor, which is started implicitly by the `async main` /// entry point and owns the "main" thread. @@ -613,24 +604,16 @@ extension MainActor { /// Attempting to set this after the first `enqueue` on the main /// executor is a fatal error. public static var executor: any MainExecutor { - get { - if _executor == nil { - _executor = PlatformMainExecutor() - } - return _executor! - } - set { - if _executor != nil { - fatalError("The main executor can be set only once, at program start") - } - _executor = newValue + if _executor == nil { + _executor = PlatformExecutorFactory.mainExecutor } + return _executor! } } @available(SwiftStdlib 6.2, *) extension Task where Success == Never, Failure == Never { - private static var _defaultExecutor: (any TaskExecutor)? = nil + static var _defaultExecutor: (any TaskExecutor)? = nil /// The default or global executor, which is the default place in which /// we run tasks. @@ -638,23 +621,13 @@ extension Task where Success == Never, Failure == Never { /// Attempting to set this after the first `enqueue` on the global /// executor is a fatal error. public static var defaultExecutor: any TaskExecutor { - get { - if _defaultExecutor == nil { - _defaultExecutor = PlatformDefaultExecutor() - } - return _defaultExecutor! - } - set { - if _defaultExecutor != nil { - fatalError("The default executor can be set only once, at program start") - } - _defaultExecutor = newValue + if _defaultExecutor == nil { + _defaultExecutor = PlatformExecutorFactory.defaultExecutor } + return _defaultExecutor! } } -#endif - @available(SwiftStdlib 6.2, *) extension Task where Success == Never, Failure == Never { /// Get the current executor; this is the executor that the currently diff --git a/stdlib/public/Concurrency/ExecutorBridge.cpp b/stdlib/public/Concurrency/ExecutorBridge.cpp index 49a1b9e54cbfb..6961e4c988d61 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.cpp +++ b/stdlib/public/Concurrency/ExecutorBridge.cpp @@ -22,6 +22,10 @@ using namespace swift; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +void _swift_exit(int result) { + exit(result); +} + extern "C" SWIFT_CC(swift) void _swift_task_checkIsolatedSwift( HeapObject *executor, @@ -29,6 +33,11 @@ void _swift_task_checkIsolatedSwift( const SerialExecutorWitnessTable *witnessTable ); +extern "C" SWIFT_CC(swift) +void _swift_task_checkIsolatedSwift(HeapObject *executor, + const Metadata *executorType, + const SerialExecutorWitnessTable *witnessTable); + extern "C" SWIFT_CC(swift) void _swift_task_isIsolatingCurrentContextSwift( HeapObject *executor, diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift index 8c0633c146440..343e8ba47a803 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.swift +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -17,6 +17,10 @@ import Swift +@available(SwiftStdlib 6.2, *) +@_silgen_name("_swift_exit") +internal func _exit(result: CInt) + @available(SwiftStdlib 6.2, *) @_silgen_name("_swift_task_isMainExecutorSwift") internal func _isMainExecutor(_ executor: E) -> Bool where E: SerialExecutor { diff --git a/stdlib/public/Concurrency/ExecutorImpl.swift b/stdlib/public/Concurrency/ExecutorImpl.swift index 3b8879f5a2352..e888394d9906c 100644 --- a/stdlib/public/Concurrency/ExecutorImpl.swift +++ b/stdlib/public/Concurrency/ExecutorImpl.swift @@ -24,6 +24,7 @@ import Swift @_silgen_name("swift_task_asyncMainDrainQueueImpl") internal func drainMainQueue() { try! MainActor.executor.run() + _exit(result: 0) } @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/PlatformExecutorDarwin.swift b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift index 45df1b2981a23..28a7f49ec5719 100644 --- a/stdlib/public/Concurrency/PlatformExecutorDarwin.swift +++ b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift @@ -14,109 +14,23 @@ import Swift -// .. Platform Main Executor ................................................... - -/// `PlatformMainExecutor` is used during program start-up and also for any -/// `@MainActor` code. It implements `SerialExecutor`, `RunLoopExecutor` and -/// `EventableExecutor`. @available(SwiftStdlib 6.2, *) -public class PlatformMainExecutor: MainExecutor, @unchecked Sendable { - var executor: any MainExecutor - - init() { +public struct PlatformExecutorFactory: ExecutorFactory { + public static var mainExecutor: any MainExecutor { if CoreFoundation.isPresent { - executor = CFMainExecutor() + return CFMainExecutor() } else { - executor = DispatchMainExecutor() + return DispatchMainExecutor() } } - public var supportsScheduling: Bool { executor.supportsScheduling } - - public func enqueue(_ job: consuming ExecutorJob) { - executor.enqueue(job) - } - - public func enqueue(_ job: consuming ExecutorJob, - after delay: C.Duration, - tolerance: C.Duration? = nil, - clock: C) { - executor.enqueue(job, after: delay, tolerance: tolerance, clock: clock) - } - - public func enqueue(_ job: consuming ExecutorJob, - at instant: C.Instant, - tolerance: C.Duration? = nil, - clock: C) { - executor.enqueue(job, at: instant, tolerance: tolerance, clock: clock) - } - - public func run() throws { - try executor.run() - } - - public func run(until condition: () -> Bool) throws { - try executor.run(until: condition) - } - - public func stop() { - executor.stop() - } - - public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { - return executor.registerEvent(handler: handler) - } - - public func deregister(event: ExecutorEvent) { - executor.deregister(event: event) - } - - public func notify(event: ExecutorEvent) { - executor.notify(event: event) - } - - public func checkIsolated() { - executor.checkIsolated() - } -} - -// .. Platform Task Executor ................................................... - -/// `PlatformDefaultExecutor` is the default executor for non-`@MainActor` -/// tasks. It implements `TaskExecutor` only. -@available(SwiftStdlib 6.2, *) -public class PlatformDefaultExecutor: TaskExecutor, @unchecked Sendable { - var executor: any TaskExecutor - - init() { + public static var defaultExecutor: any TaskExecutor { if CoreFoundation.isPresent { - executor = CFTaskExecutor() + return CFTaskExecutor() } else { - executor = DispatchTaskExecutor() + return DispatchTaskExecutor() } } - - public var isMainExecutor: Bool { executor.isMainExecutor } - - public var supportsScheduling: Bool { executor.supportsScheduling } - - public func enqueue(_ job: consuming ExecutorJob) { - executor.enqueue(job) - } - - public func enqueue(_ job: consuming ExecutorJob, - after delay: C.Duration, - tolerance: C.Duration? = nil, - clock: C) { - executor.enqueue(job, after: delay, tolerance: tolerance, clock: clock) - } - - public func enqueue(_ job: consuming ExecutorJob, - at instant: C.Instant, - tolerance: C.Duration? = nil, - clock: C) { - executor.enqueue(job, at: instant, tolerance: tolerance, clock: clock) - } } #endif // os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS) diff --git a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift index 5aac444118bfd..437cbbbfb9dd0 100644 --- a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift +++ b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift @@ -12,12 +12,18 @@ import Swift -// ###TODO: Flesh this file out +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + public static let mainExecutor: any MainExecutor = EmbeddedMainExecutor() + public static let defaultExecutor: any TaskExecutor = EmbeddedDefaultExecutor() +} // .. Platform Main Executor ................................................... @available(SwiftStdlib 6.2, *) -public final class PlatformMainExecutor: MainExecutor, @unchecked Sendable { +public final class EmbeddedMainExecutor: MainExecutor, @unchecked Sendable { + + public init() {} public func enqueue(_ job: consuming ExecutorJob) { } @@ -51,7 +57,9 @@ public final class PlatformMainExecutor: MainExecutor, @unchecked Sendable { // .. Platform Task Executor ................................................... @available(SwiftStdlib 6.2, *) -public final class PlatformDefaultExecutor: TaskExecutor, @unchecked Sendable { +public final class EmbeddedDefaultExecutor: TaskExecutor, @unchecked Sendable { + + public init() {} public func enqueue(_ job: consuming ExecutorJob) { } diff --git a/stdlib/public/Concurrency/PlatformExecutorLinux.swift b/stdlib/public/Concurrency/PlatformExecutorLinux.swift index 1e4abdc4c7fd8..7083406a1c228 100644 --- a/stdlib/public/Concurrency/PlatformExecutorLinux.swift +++ b/stdlib/public/Concurrency/PlatformExecutorLinux.swift @@ -15,7 +15,10 @@ import Swift // The default executors for now are Dispatch-based -typealias PlatformMainExecutor = DispatchMainExecutor -typealias PlatformDefaultExecutor = DispatchTaskExecutor +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + public static let mainExecutor: any MainExecutor = DispatchMainExecutor() + public static let defaultExecutor: any TaskExecutor = DispatchTaskExecutor() +} #endif // os(Linux) diff --git a/stdlib/public/Concurrency/PlatformExecutorWindows.swift b/stdlib/public/Concurrency/PlatformExecutorWindows.swift index e13fdef366dcb..053d74dd22f16 100644 --- a/stdlib/public/Concurrency/PlatformExecutorWindows.swift +++ b/stdlib/public/Concurrency/PlatformExecutorWindows.swift @@ -15,7 +15,10 @@ import Swift // The default executors for now are Dispatch-based -typealias PlatformMainExecutor = DispatchMainExecutor -typealias PlatformDefaultExecutor = DispatchTaskExecutor +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + public static let mainExecutor: any MainExecutor = DispatchMainExecutor() + public static let defaultExecutor: any TaskExecutor = DispatchTaskExecutor() +} #endif // os(Windows) diff --git a/test/Concurrency/Runtime/clocks.swift b/test/Concurrency/Runtime/clocks.swift new file mode 100644 index 0000000000000..1bf5a377c6dd5 --- /dev/null +++ b/test/Concurrency/Runtime/clocks.swift @@ -0,0 +1,272 @@ +// RUN: %target-run-simple-swift(%import-libdispatch -parse-as-library) + +// REQUIRES: concurrency +// REQUIRES: executable_test + +// rdar://106849189 move-only types should be supported in freestanding mode +// UNSUPPORTED: freestanding + +// UNSUPPORTED: back_deployment_runtime +// REQUIRES: concurrency_runtime + +import StdlibUnittest + +struct TickingClock: Clock { + struct Duration: DurationProtocol { + var ticks: Int + + static func / (_ lhs: Self, _ rhs: Int) -> Self { + return Duration(ticks: lhs.ticks / rhs) + } + static func * (_ lhs: Self, rhs: Int) -> Self { + return Duration(ticks: lhs.ticks * rhs) + } + static func / (_ lhs: Self, _ rhs: Self) -> Double { + return Double(lhs.ticks) / Double(rhs.ticks) + } + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + return lhs.ticks < rhs.ticks + } + static func + (_ lhs: Self, _ rhs: Self) -> Self { + return Duration(ticks: lhs.ticks + rhs.ticks) + } + static func - (_ lhs: Self, _ rhs: Self) -> Self { + return Duration(ticks: lhs.ticks - rhs.ticks) + } + + static var zero: Self { + return Duration(ticks: 0) + } + } + struct Instant: InstantProtocol { + typealias Duration = TickingClock.Duration + var ticksFromStart: Int + + func advanced(by duration: Duration) -> Self { + return Instant(ticksFromStart: self.ticksFromStart + duration.ticks) + } + func duration(to other: Self) -> Duration { + return Duration(ticks: other.ticksFromStart - self.ticksFromStart) + } + + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + return lhs.ticksFromStart < rhs.ticksFromStart + } + } + + private var _now: Instant + var now: Instant { return _now } + var minimumResolution: Duration { return Duration(ticks: 1) } + + init() { + _now = Instant(ticksFromStart: 0) + } + + // These are a bit of a lie, since this clock is weird and doesn't + // actually tell the time; for the purposes of this test, we pretend + // that the ticks are 20ms. + func convert(from duration: Duration) -> Swift.Duration? { + return .seconds(Double(duration.ticks) / 50) + } + + func convert(from duration: Swift.Duration) -> Duration? { + let (seconds, attoseconds) = duration.components + let extraTicks = attoseconds / 20_000_000_000_000_000 + return Duration(ticks: Int(seconds * 50) + Int(extraTicks)) + } + + mutating func tick() { + _now.ticksFromStart += 1 + } + + func sleep( + until instant: Instant, + tolerance: Duration? = nil + ) async throws { + // Do nothing + } +} + +struct TockingClock: Clock { + struct Duration: DurationProtocol { + var tocks: Int + + static func / (_ lhs: Self, _ rhs: Int) -> Self { + return Duration(tocks: lhs.tocks / rhs) + } + static func * (_ lhs: Self, rhs: Int) -> Self { + return Duration(tocks: lhs.tocks * rhs) + } + static func / (_ lhs: Self, _ rhs: Self) -> Double { + return Double(lhs.tocks) / Double(rhs.tocks) + } + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + return lhs.tocks < rhs.tocks + } + static func + (_ lhs: Self, _ rhs: Self) -> Self { + return Duration(tocks: lhs.tocks + rhs.tocks) + } + static func - (_ lhs: Self, _ rhs: Self) -> Self { + return Duration(tocks: lhs.tocks - rhs.tocks) + } + + static var zero: Self { + return Duration(tocks: 0) + } + } + struct Instant: InstantProtocol { + typealias Duration = TockingClock.Duration + var tocksFromStart: Int + + func advanced(by duration: Duration) -> Self { + return Instant(tocksFromStart: self.tocksFromStart + duration.tocks) + } + func duration(to other: Self) -> Duration { + return Duration(tocks: other.tocksFromStart - self.tocksFromStart) + } + + static func < (_ lhs: Self, _ rhs: Self) -> Bool { + return lhs.tocksFromStart < rhs.tocksFromStart + } + } + + private var _now: Instant + var now: Instant { return _now } + var minimumResolution: Duration { return Duration(tocks: 1) } + + init() { + _now = Instant(tocksFromStart: 1000) + } + + // These are a bit of a lie, since this clock is weird and doesn't + // actually tell the time; for the purposes of this test, we pretend + // that the tocks are 20ms. + func convert(from duration: Duration) -> Swift.Duration? { + return .seconds(Double(duration.tocks) / 100) + } + + func convert(from duration: Swift.Duration) -> Duration? { + let (seconds, attoseconds) = duration.components + let extraTocks = attoseconds / 10_000_000_000_000_000 + return Duration(tocks: Int(seconds * 100) + Int(extraTocks)) + } + + mutating func tock() { + _now.tocksFromStart += 1 + } + + func sleep( + until instant: Instant, + tolerance: Duration? = nil + ) async throws { + // Do nothing + } +} + +@available(SwiftStdlib 6.2, *) +@main struct Main { + static func main() { + let tests = TestSuite("clocks") + + var clockA = TickingClock() + let clockB = TickingClock() + + clockA.tick() + clockA.tick() + clockA.tick() + + var clockC = TockingClock() + + clockC.tock() + + tests.test("Convert instants from one clock to another") { + let nowA = clockA.now + let nowB = clockB.now + + expectEqual(nowA.ticksFromStart, 3) + expectEqual(nowB.ticksFromStart, 0) + + let futureA = nowA.advanced(by: TickingClock.Duration(ticks: 23)) + let futureB = nowB.advanced(by: TickingClock.Duration(ticks: 42)) + + expectEqual(futureA.ticksFromStart, 26) + expectEqual(futureB.ticksFromStart, 42) + + let futureAinB = clockB.convert(instant: futureA, from: clockA)! + let futureBinA = clockA.convert(instant: futureB, from: clockB)! + + expectEqual(futureAinB.ticksFromStart, 23) + expectEqual(futureBinA.ticksFromStart, 45) + + let futureAinBinA = clockA.convert(instant: futureAinB, from: clockB)! + let futureBinAinB = clockB.convert(instant: futureBinA, from: clockA)! + + expectEqual(futureAinBinA.ticksFromStart, futureA.ticksFromStart) + expectEqual(futureBinAinB.ticksFromStart, futureB.ticksFromStart) + } + + tests.test("Convert instants between clocks with different representations") { + let nowA = clockA.now + let nowC = clockC.now + + expectEqual(nowA.ticksFromStart, 3) + expectEqual(nowC.tocksFromStart, 1001) + + let futureA = nowA.advanced(by: TickingClock.Duration(ticks: 23)) + let futureC = nowC.advanced(by: TockingClock.Duration(tocks: 42)) + + expectEqual(futureA.ticksFromStart, 26) + expectEqual(futureC.tocksFromStart, 1043) + + let futureAinC = clockC.convert(instant: futureA, from: clockA)! + let futureCinA = clockA.convert(instant: futureC, from: clockC)! + + expectEqual(futureAinC.tocksFromStart, 1047) + expectEqual(futureCinA.ticksFromStart, 24) + + let futureAinCinA = clockA.convert(instant: futureAinC, from: clockC)! + let futureCinAinC = clockC.convert(instant: futureCinA, from: clockA)! + + expectEqual(futureAinCinA.ticksFromStart, futureA.ticksFromStart) + expectEqual(futureCinAinC.tocksFromStart, futureC.tocksFromStart) + } + + tests.test("Convert instants between continuous and suspending clocks") { + let continuous = ContinuousClock() + let suspending = SuspendingClock() + + let nowC = continuous.now + let nowS = suspending.now + + let futureC = nowC.advanced(by: .seconds(5.3)) + let futureS = nowS.advanced(by: .seconds(4.2)) + + let futureCinS = suspending.convert(instant: futureC, + from: .continuous)! + let futureSinC = continuous.convert(instant: futureS, + from: .suspending)! + + let futureCinSinC = continuous.convert(instant: futureCinS, + from: .suspending)! + let futureSinCinS = suspending.convert(instant: futureSinC, + from: .continuous)! + + // These clocks may not be exact, so allow differences of up to 50ms + var delta1 = futureCinSinC - futureC + var delta2 = futureSinCinS - futureS + + // Duration is not SignedNumeric, so we have to do things this way + if delta1 < .zero { + delta1 = .zero - delta1 + } + if delta2 < .zero { + delta2 = .zero - delta2 + } + + expectLT(delta1, .milliseconds(50)) + expectLT(delta2, .milliseconds(50)) + } + + runAllTests() + } +} diff --git a/test/Concurrency/Runtime/custom_main_executor.swift b/test/Concurrency/Runtime/custom_main_executor.swift new file mode 100644 index 0000000000000..645fed765686f --- /dev/null +++ b/test/Concurrency/Runtime/custom_main_executor.swift @@ -0,0 +1,102 @@ +// RUN: %target-run-simple-swift(-Xfrontend -disable-availability-checking -g %import-libdispatch -parse-as-library -executor-factory SimpleExecutorFactory) | %FileCheck %s + +// REQUIRES: concurrency +// REQUIRES: executable_test + +// rdar://106849189 move-only types should be supported in freestanding mode +// UNSUPPORTED: freestanding + +// UNSUPPORTED: back_deployment_runtime +// REQUIRES: concurrency_runtime +// REQUIRES: synchronization + +import StdlibUnittest +import Synchronization + +struct SimpleExecutorFactory: ExecutorFactory { + public static var mainExecutor: any MainExecutor { + print("Creating main executor") + return SimpleMainExecutor() + } + public static var defaultExecutor: any TaskExecutor { + print("Creating task executor") + return DispatchTaskExecutor() + } +} + +@available(SwiftStdlib 6.2, *) +final class SimpleMainExecutor: MainExecutor, @unchecked Sendable { + public var isRunning: Bool = false + + var shouldStop: Bool = false + let queue = Mutex<[UnownedJob]>([]) + + func enqueue(_ job: consuming ExecutorJob) { + print("Enqueued job") + let unownedJob = UnownedJob(job) + queue.withLock { + $0.append(unownedJob) + } + } + + func run() throws { + print("Running") + isRunning = true + while !shouldStop { + let jobs = queue.withLock { + let jobs = $0 + $0.removeAll() + return jobs + } + for job in jobs { + print("Running job") + job.runSynchronously(on: self.asUnownedSerialExecutor()) + } + } + isRunning = false + } + + func stop() { + shouldStop = true + } + + func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { + return ExecutorEvent(id: 0) + } + + func deregister(event: ExecutorEvent) { + } + + func notify(event: ExecutorEvent) { + } +} + +@available(SwiftStdlib 6.2, *) +final class FatalExecutor: TaskExecutor, @unchecked Sendable { + func enqueue(_ job: consuming ExecutorJob) { + fatalError("We should never get here") + } +} + +func myAsyncFunction() async { + print("Hello World") +} + +@available(SwiftStdlib 6.2, *) +@main struct Main { + static func main() async { + print("Hello") + await myAsyncFunction() + print("Goodbye") + } +} + +// CHECK: Creating main executor +// CHECK-NEXT: Creating task executor +// CHECK-NEXT: Hello +// CHECK-NEXT: Running +// CHECK-NEXT: Hello World +// CHECK-NEXT: Enqueued job +// CHECK-NEXT: Running job +// CHECK-NEXT: Goodbye + diff --git a/test/Concurrency/Runtime/sleep_executor.swift b/test/Concurrency/Runtime/sleep_executor.swift new file mode 100644 index 0000000000000..87c5dbd27edb0 --- /dev/null +++ b/test/Concurrency/Runtime/sleep_executor.swift @@ -0,0 +1,91 @@ +// RUN: %target-run-simple-swift(%import-libdispatch -parse-as-library) + +// REQUIRES: concurrency +// REQUIRES: executable_test + +// rdar://106849189 move-only types should be supported in freestanding mode +// UNSUPPORTED: freestanding + +// UNSUPPORTED: back_deployment_runtime +// REQUIRES: concurrency_runtime + +import Dispatch +import StdlibUnittest + +@available(SwiftStdlib 6.2, *) +actor MyActor { + public func doSleep() async { + try! await Task.sleep(for: .seconds(0.1)) + } +} + +@available(SwiftStdlib 6.2, *) +final class TestExecutor: TaskExecutor, @unchecked Sendable { + public func enqueue(_ _job: consuming ExecutorJob) { + let job = UnownedJob(_job) + DispatchQueue.main.async { + job.runSynchronously(on: self.asUnownedTaskExecutor()) + } + } + + public var supportsScheduling: Bool { true} + + public func enqueue(_ _job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + // Convert to `Swift.Duration` + let duration = clock.convert(from: delay)! + + // Now turn that into nanoseconds + let (seconds, attoseconds) = duration.components + let nanoseconds = attoseconds / 1_000_000_000 + + // Get a Dispatch time + let deadline = DispatchTime.now().advanced( + by: .seconds(Int(seconds)) + ).advanced( + by: .nanoseconds(Int(nanoseconds)) + ) + + let job = UnownedJob(_job) + DispatchQueue.main.asyncAfter(deadline: deadline) { + job.runSynchronously(on: self.asUnownedTaskExecutor()) + } + } +} + +@available(SwiftStdlib 6.2, *) +@main struct Main { + static func main() async { + let tests = TestSuite("sleep_executor") + + tests.test("Task.sleep on the main executor") { + try! await Task.sleep(for: .seconds(0.1)) + } + + tests.test("Task.sleep on the global executor") { + let task = Task.detached { + try! await Task.sleep(for: .seconds(0.1)) + } + + await task.value + } + + tests.test("Task.sleep on a custom executor") { + let taskExecutor = TestExecutor() + + let task = Task(executorPreference: taskExecutor) { + try! await Task.sleep(for: .seconds(0.1)) + } + + await task.value + } + + tests.test("Task.sleep on an actor") { + await MyActor().doSleep() + } + + await runAllTestsAsync() + } +} From d89ea190bb77b466d52f85d6c719af98e742aa2c Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 10 Mar 2025 12:13:16 +0000 Subject: [PATCH 03/29] [Concurrency] Add clock traits. Remove the hacky support for mapping clocks to a Dispatch clock ID, in favour of clocks publishing traits and having the Dispatch executor select the clock on the basis of those traits. --- stdlib/public/Concurrency/Clock.swift | 52 ++++++++++++++----- .../public/Concurrency/ContinuousClock.swift | 6 +++ .../public/Concurrency/DispatchExecutor.swift | 18 +++---- .../public/Concurrency/SuspendingClock.swift | 6 +++ test/Concurrency/Runtime/clocks.swift | 4 ++ 5 files changed, 62 insertions(+), 24 deletions(-) diff --git a/stdlib/public/Concurrency/Clock.swift b/stdlib/public/Concurrency/Clock.swift index c5156cfa3615c..2995be4ed4d3a 100644 --- a/stdlib/public/Concurrency/Clock.swift +++ b/stdlib/public/Concurrency/Clock.swift @@ -42,14 +42,9 @@ public protocol Clock: Sendable { func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws #endif -#if !$Embedded - /// Choose which Dispatch clock to use with DispatchExecutor - /// - /// This controls which Dispatch clock is used to enqueue delayed jobs - /// when using this Clock. + /// The traits associated with this clock instance. @available(SwiftStdlib 6.2, *) - var dispatchClockID: DispatchClockID { get } -#endif + var traits: ClockTraits { get } /// Convert a Clock-specific Duration to a Swift Duration /// @@ -138,12 +133,6 @@ extension Clock { @available(SwiftStdlib 6.2, *) extension Clock { - #if !$Embedded - public var dispatchClockID: DispatchClockID { - return .suspending - } - #endif - // For compatibility, return `nil` if this is not implemented public func convert(from duration: Duration) -> Swift.Duration? { return nil @@ -198,6 +187,43 @@ extension Clock { } #endif +/// Represents traits of a particular Clock implementation. +/// +/// Clocks may be of a number of different varieties; executors will likely +/// have specific clocks that they can use to schedule jobs, and will +/// therefore need to be able to convert timestamps to an appropriate clock +/// when asked to enqueue a job with a delay or deadline. +/// +/// Choosing a clock in general requires the ability to tell which of their +/// clocks best matches the clock that the user is trying to specify a +/// time or delay in. Executors are expected to do this on a best effort +/// basis. +@available(SwiftStdlib 6.2, *) +public struct ClockTraits: OptionSet { + public let rawValue: Int32 + + public init(rawValue: Int32) { + self.rawValue = rawValue + } + + /// Clocks with this trait continue running while the machine is asleep. + public static let continuous = ClockTraits(rawValue: 1 << 0) + + /// Indicates that a clock's time will only ever increase. + public static let monotonic = ClockTraits(rawValue: 1 << 1) + + /// Clocks with this trait are tied to "wall time". + public static let wallTime = ClockTraits(rawValue: 1 << 2) +} + +extension Clock { + /// The traits associated with this clock instance. + @available(SwiftStdlib 6.2, *) + var traits: ClockTraits { + return [] + } +} + enum _ClockID: Int32 { case continuous = 1 case suspending = 2 diff --git a/stdlib/public/Concurrency/ContinuousClock.swift b/stdlib/public/Concurrency/ContinuousClock.swift index 34f4025a4f6d1..979a3580d17ea 100644 --- a/stdlib/public/Concurrency/ContinuousClock.swift +++ b/stdlib/public/Concurrency/ContinuousClock.swift @@ -100,6 +100,12 @@ extension ContinuousClock: Clock { ) } + /// The continuous clock is continuous and monotonic + @available(SwiftStdlib 6.2, *) + public var traits: ClockTraits { + return [.continuous, .monotonic] + } + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY /// Suspend task execution until a given deadline within a tolerance. /// If no tolerance is specified then the system may adjust the deadline diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift index 7e4be9acd2a35..b5f3663870126 100644 --- a/stdlib/public/Concurrency/DispatchExecutor.swift +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -178,8 +178,7 @@ protocol DispatchExecutor: Executor { } /// An enumeration identifying one of the Dispatch-supported clocks -@available(SwiftStdlib 6.2, *) -public enum DispatchClockID: CInt { +enum DispatchClockID: CInt { case suspending = 1 case continuous = 2 } @@ -189,23 +188,20 @@ extension DispatchExecutor { func timestamp(for instant: C.Instant, clock: C) -> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64) { - let clockID = clock.dispatchClockID - - switch clockID { - case .suspending: - let dispatchClock: SuspendingClock = .suspending + if clock.traits.contains(.continuous) { + let dispatchClock: ContinuousClock = .continuous let instant = dispatchClock.convert(instant: instant, from: clock)! let (seconds, attoseconds) = instant._value.components let nanoseconds = attoseconds / 1_000_000_000 - return (clockID: .suspending, + return (clockID: .continuous, seconds: Int64(seconds), nanoseconds: Int64(nanoseconds)) - case .continuous: - let dispatchClock: ContinuousClock = .continuous + } else { + let dispatchClock: SuspendingClock = .suspending let instant = dispatchClock.convert(instant: instant, from: clock)! let (seconds, attoseconds) = instant._value.components let nanoseconds = attoseconds / 1_000_000_000 - return (clockID: .continuous, + return (clockID: .suspending, seconds: Int64(seconds), nanoseconds: Int64(nanoseconds)) } diff --git a/stdlib/public/Concurrency/SuspendingClock.swift b/stdlib/public/Concurrency/SuspendingClock.swift index 5bc956f9fa476..cec63ed99f55e 100644 --- a/stdlib/public/Concurrency/SuspendingClock.swift +++ b/stdlib/public/Concurrency/SuspendingClock.swift @@ -87,6 +87,12 @@ extension SuspendingClock: Clock { return Duration(_seconds: seconds, nanoseconds: nanoseconds) } + /// The suspending clock is monotonic + @available(SwiftStdlib 6.2, *) + public var traits: ClockTraits { + return [.monotonic] + } + #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY /// Suspend task execution until a given deadline within a tolerance. /// If no tolerance is specified then the system may adjust the deadline diff --git a/test/Concurrency/Runtime/clocks.swift b/test/Concurrency/Runtime/clocks.swift index 1bf5a377c6dd5..5e8c188cd7b4e 100644 --- a/test/Concurrency/Runtime/clocks.swift +++ b/test/Concurrency/Runtime/clocks.swift @@ -11,6 +11,7 @@ import StdlibUnittest +@available(SwiftStdlib 6.2, *) struct TickingClock: Clock { struct Duration: DurationProtocol { var ticks: Int @@ -57,6 +58,7 @@ struct TickingClock: Clock { private var _now: Instant var now: Instant { return _now } var minimumResolution: Duration { return Duration(ticks: 1) } + var traits: ClockTraits { [.monotonic] } init() { _now = Instant(ticksFromStart: 0) @@ -87,6 +89,7 @@ struct TickingClock: Clock { } } +@available(SwiftStdlib 6.2, *) struct TockingClock: Clock { struct Duration: DurationProtocol { var tocks: Int @@ -133,6 +136,7 @@ struct TockingClock: Clock { private var _now: Instant var now: Instant { return _now } var minimumResolution: Duration { return Duration(tocks: 1) } + var traits: ClockTraits { [.monotonic] } init() { _now = Instant(tocksFromStart: 1000) From 5ae6de288a0a7e399a2090b5f069c3d105fe90ee Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 10 Mar 2025 12:57:01 +0000 Subject: [PATCH 04/29] [Concurrency] Fix potential ABI breakages. Fix a couple of potential ABI breaks. Also add the new functions and types to the baseline lists. rdar://141348916 --- stdlib/public/Concurrency/Executor.swift | 12 +- .../GlobalConcurrentExecutor.swift | 2 +- stdlib/public/Concurrency/Task.swift | 22 +- .../macOS/arm64/concurrency/baseline-asserts | 228 ++++++++++++++++++ .../macOS/x86_64/concurrency/baseline-asserts | 228 ++++++++++++++++++ 5 files changed, 476 insertions(+), 16 deletions(-) diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index 519115f4ef266..21c85f334e319 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -101,14 +101,14 @@ public protocol Executor: AnyObject, Sendable { #endif // !$Embedded } -@available(SwiftStdlib 6.2, *) extension Executor { + @available(SwiftStdlib 6.2, *) @usableFromInline internal var _isComplexEquality: Bool { false } } -@available(SwiftStdlib 6.2, *) extension Executor where Self: Equatable { + @available(SwiftStdlib 6.2, *) @usableFromInline internal var _isComplexEquality: Bool { true } } @@ -594,8 +594,8 @@ public func _createExecutors(factory: F.Type) { Task._defaultExecutor = factory.defaultExecutor } -@available(SwiftStdlib 6.2, *) extension MainActor { + @available(SwiftStdlib 6.2, *) static var _executor: (any MainExecutor)? = nil /// The main executor, which is started implicitly by the `async main` @@ -603,6 +603,7 @@ extension MainActor { /// /// Attempting to set this after the first `enqueue` on the main /// executor is a fatal error. + @available(SwiftStdlib 6.2, *) public static var executor: any MainExecutor { if _executor == nil { _executor = PlatformExecutorFactory.mainExecutor @@ -611,8 +612,8 @@ extension MainActor { } } -@available(SwiftStdlib 6.2, *) extension Task where Success == Never, Failure == Never { + @available(SwiftStdlib 6.2, *) static var _defaultExecutor: (any TaskExecutor)? = nil /// The default or global executor, which is the default place in which @@ -620,6 +621,7 @@ extension Task where Success == Never, Failure == Never { /// /// Attempting to set this after the first `enqueue` on the global /// executor is a fatal error. + @available(SwiftStdlib 6.2, *) public static var defaultExecutor: any TaskExecutor { if _defaultExecutor == nil { _defaultExecutor = PlatformExecutorFactory.defaultExecutor @@ -628,10 +630,10 @@ extension Task where Success == Never, Failure == Never { } } -@available(SwiftStdlib 6.2, *) extension Task where Success == Never, Failure == Never { /// Get the current executor; this is the executor that the currently /// executing task is executing on. + @available(SwiftStdlib 6.2, *) @_unavailableInEmbedded public static var currentExecutor: (any Executor)? { if let taskExecutor = _getPreferredTaskExecutor().asTaskExecutor() { diff --git a/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift b/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift index 12d62dfc27a7a..1ca789ce117d1 100644 --- a/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift +++ b/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift @@ -54,7 +54,7 @@ internal final class _DefaultGlobalConcurrentExecutor: TaskExecutor { private init() {} public func enqueue(_ job: consuming ExecutorJob) { - _enqueueJobGlobal(UnownedJob(job)) + _enqueueJobGlobal(UnownedJob(job)._context) } public func asUnownedTaskExecutor() -> UnownedTaskExecutor { diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 9ce4ff6ed8027..576c3a4d125c8 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -1253,7 +1253,7 @@ extension Task where Success == Never, Failure == Never { let job = _taskCreateNullaryContinuationJob( priority: Int(Task.currentPriority.rawValue), continuation: continuation) - _enqueueJobGlobalDirect(job) + _enqueueJobGlobal(job) } } } @@ -1412,22 +1412,24 @@ func getJobFlags(_ task: Builtin.NativeObject) -> JobFlags @available(SwiftStdlib 5.1, *) @_silgen_name("swift_task_enqueueGlobal") @usableFromInline -func _enqueueJobGlobal(_ task: UnownedJob) +func _enqueueJobGlobal(_ task: Builtin.Job) +@available(SwiftStdlib 6.2, *) @usableFromInline -func _enqueueJobGlobalDirect(_ task: Builtin.Job) { - if #available(SwiftStdlib 5.9, *) { - _enqueueJobGlobal(UnownedJob(context: task)) - } else { - // Shouldn't ever get here - Builtin.unreachable() - } +func _enqueueJobGlobal(_ task: UnownedJob) { + _enqueueJobGlobal(task._context) } @available(SwiftStdlib 5.1, *) @_silgen_name("swift_task_enqueueGlobalWithDelay") @usableFromInline -func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: UnownedJob) +func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: Builtin.Job) + +@available(SwiftStdlib 6.2, *) +@usableFromInline +func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: UnownedJob) { + return _enqueueJobGlobalWithDelay(delay, task._context) +} @available(SwiftStdlib 5.7, *) @_silgen_name("swift_task_enqueueGlobalWithDeadline") diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index 38037f92cc94b..de2c7c75f7df7 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -23,6 +23,14 @@ _$sScEMa _$sScEMn _$sScEN _$sScEs5ErrorsMc +_$sScF14isMainExecutorSbvgTj +_$sScF14isMainExecutorSbvgTq +_$sScF18supportsSchedulingSbvgTj +_$sScF18supportsSchedulingSbvgTq +_$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj +_$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq +_$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTj +_$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTq _$sScF7enqueueyyScJFTj _$sScF7enqueueyyScJFTq _$sScF7enqueueyys11ExecutorJobVnFTj @@ -31,9 +39,19 @@ _$sScF7enqueueyys3JobVnFTj _$sScF7enqueueyys3JobVnFTq _$sScFMp _$sScFTL +_$sScFsE14isMainExecutorSbvg +_$sScFsE14isMainExecutorSbvpMV +_$sScFsE18_isComplexEqualitySbvg +_$sScFsE18_isComplexEqualitySbvpMV +_$sScFsE18supportsSchedulingSbvg +_$sScFsE18supportsSchedulingSbvpMV +_$sScFsE7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF +_$sScFsE7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lF _$sScFsE7enqueueyyScJF _$sScFsE7enqueueyys11ExecutorJobVnF _$sScFsE7enqueueyys3JobVnF +_$sScFsSQRzrlE18_isComplexEqualitySbvg +_$sScFsSQRzrlE18_isComplexEqualitySbvpMV _$sScG11isCancelledSbvg _$sScG11isCancelledSbvpMV _$sScG17makeAsyncIteratorScG0C0Vyx_GyF @@ -88,6 +106,8 @@ _$sScM3run10resultType4bodyxxm_xyYbKScMYcXEtYaKlFZ _$sScM3run10resultType4bodyxxm_xyYbKScMYcXEtYaKlFZTu _$sScM6sharedScMvgZ _$sScM7enqueueyyScJF +_$sScM8executors12MainExecutor_pvgZ +_$sScM8executors12MainExecutor_pvpZMV _$sScMMa _$sScMMm _$sScMMn @@ -195,7 +215,11 @@ _$sScTss5NeverORs_rlE5valuexvgTu _$sScTss5NeverORs_rlE5valuexvpMV _$sScTss5NeverORszABRs_rlE11isCancelledSbvgZ _$sScTss5NeverORszABRs_rlE12basePriorityScPSgvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pSgvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pSgvpZMV _$sScTss5NeverORszABRs_rlE15currentPriorityScPvgZ +_$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvgZ +_$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvpZMV _$sScTss5NeverORszABRs_rlE17checkCancellationyyKFZ _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZ _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZTu @@ -214,6 +238,7 @@ _$sScc7contextBcvs _$sSccMa _$sSccMn _$sSce15complexEqualityScexh_tcScfRzlufC +_$sSce16asSerialExecutorScf_pSgyF _$sSce18_isComplexEqualitySbvg _$sSce18_isComplexEqualitySbvpMV _$sSce8executorBevM @@ -227,6 +252,7 @@ _$sSceMa _$sSceMn _$sSceN _$sSceySceBecfC +_$sSceyScexhcScfRzlufC _$sScf23asUnownedSerialExecutorSceyFTj _$sScf23asUnownedSerialExecutorSceyFTq _$sScf31isSameExclusiveExecutionContext5otherSbx_tFTj @@ -241,9 +267,12 @@ _$sScfMp _$sScfScFTb _$sScfTL _$sScfsE14assertIsolated_4file4lineySSyXK_s12StaticStringVSutF +_$sScfsE14isMainExecutorSbvg +_$sScfsE14isMainExecutorSbvpMV _$sScfsE20preconditionIsolated_4file4lineySSyXK_s12StaticStringVSutF _$sScfsE23asUnownedSerialExecutorSceyF _$sScfsE31isSameExclusiveExecutionContext5otherSbx_tF +_$sScfsSQRzrlE31isSameExclusiveExecutionContext5otherSbx_tF _$sScg10nextResults0B0Oyxq_GSgyYaKF _$sScg10nextResults0B0Oyxq_GSgyYaKFTu _$sScg11_waitForAllyyYaKF @@ -370,9 +399,51 @@ _$sSctN _$sSctSHsMc _$sSctSQsMc _$ss039_checkIllegalTaskLocalBindingWithinWithC5Group4file4lineySS_SutF +_$ss11ClockTraitsV10continuousABvgZ +_$ss11ClockTraitsV10continuousABvpZMV +_$ss11ClockTraitsV8rawValueABs5Int32V_tcfC +_$ss11ClockTraitsV8rawValues5Int32Vvg +_$ss11ClockTraitsV8rawValues5Int32VvpMV +_$ss11ClockTraitsV8wallTimeABvgZ +_$ss11ClockTraitsV8wallTimeABvpZMV +_$ss11ClockTraitsV9monotonicABvgZ +_$ss11ClockTraitsV9monotonicABvpZMV +_$ss11ClockTraitsVMa +_$ss11ClockTraitsVMn +_$ss11ClockTraitsVN +_$ss11ClockTraitsVSQsMc +_$ss11ClockTraitsVSYsMc +_$ss11ClockTraitsVs10SetAlgebrasMc +_$ss11ClockTraitsVs25ExpressibleByArrayLiteralsMc +_$ss11ClockTraitsVs9OptionSetsMc +_$ss11ExecutorJobV010withUnsafeA11PrivateData4bodyxxSwKXE_tKlF _$ss11ExecutorJobV11descriptionSSvg +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySpyxGlF +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySryxGlF +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySwF +_$ss11ExecutorJobV14LocalAllocatorV8allocate2asSpyxGxm_tlF +_$ss11ExecutorJobV14LocalAllocatorV8allocate8capacity2asSryxGSi_xmtlF +_$ss11ExecutorJobV14LocalAllocatorV8allocate8capacitySwSi_tF +_$ss11ExecutorJobV14LocalAllocatorVMa +_$ss11ExecutorJobV14LocalAllocatorVMn +_$ss11ExecutorJobV14LocalAllocatorVN +_$ss11ExecutorJobV4KindV13firstReservedADvgZ +_$ss11ExecutorJobV4KindV13firstReservedADvpZMV +_$ss11ExecutorJobV4KindV4taskADvgZ +_$ss11ExecutorJobV4KindV4taskADvpZMV +_$ss11ExecutorJobV4KindV8rawValueADSgs5UInt8V_tcfC +_$ss11ExecutorJobV4KindV8rawValues5UInt8VvM +_$ss11ExecutorJobV4KindV8rawValues5UInt8Vvg +_$ss11ExecutorJobV4KindV8rawValues5UInt8VvpMV +_$ss11ExecutorJobV4KindV8rawValues5UInt8Vvs +_$ss11ExecutorJobV4KindVMa +_$ss11ExecutorJobV4KindVMn +_$ss11ExecutorJobV4KindVN +_$ss11ExecutorJobV4KindVSYsMc +_$ss11ExecutorJobV4kindAB4KindVvg _$ss11ExecutorJobV7contextABBjn_tcfC _$ss11ExecutorJobV8prioritys0B8PriorityVvg +_$ss11ExecutorJobV9allocatorAB14LocalAllocatorVSgvg _$ss11ExecutorJobVMa _$ss11ExecutorJobVMn _$ss11ExecutorJobVN @@ -403,9 +474,48 @@ _$ss11JobPriorityVMn _$ss11JobPriorityVN _$ss11JobPriorityVSLsMc _$ss11JobPriorityVSQsMc +_$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ +_$ss12MainExecutorMp +_$ss12MainExecutorPScfTb +_$ss12MainExecutorPs07RunLoopB0Tb +_$ss12MainExecutorPs09EventableB0Tb +_$ss12MainExecutorTL +_$ss13ExecutorEventV1loiySbAB_ABtFZ +_$ss13ExecutorEventV2eeoiySbAB_ABtFZ +_$ss13ExecutorEventV2idABSi_tcfC +_$ss13ExecutorEventV2idSivM +_$ss13ExecutorEventV2idSivg +_$ss13ExecutorEventV2idSivpMV +_$ss13ExecutorEventV2idSivs +_$ss13ExecutorEventVMa +_$ss13ExecutorEventVMn +_$ss13ExecutorEventVN +_$ss13ExecutorEventVSLsMc +_$ss13ExecutorEventVSQsMc +_$ss13ExecutorEventVs12IdentifiablesMc _$ss13_runAsyncMainyyyyYaKcF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lFTu +_$ss14CFMainExecutorC3runyyKF +_$ss14CFMainExecutorC4stopyyF +_$ss14CFMainExecutorCABycfC +_$ss14CFMainExecutorCABycfc +_$ss14CFMainExecutorCMa +_$ss14CFMainExecutorCMm +_$ss14CFMainExecutorCMn +_$ss14CFMainExecutorCMo +_$ss14CFMainExecutorCN +_$ss14CFMainExecutorCfD +_$ss14CFMainExecutorCfd +_$ss14CFTaskExecutorCABycfC +_$ss14CFTaskExecutorCABycfc +_$ss14CFTaskExecutorCMa +_$ss14CFTaskExecutorCMm +_$ss14CFTaskExecutorCMn +_$ss14CFTaskExecutorCMo +_$ss14CFTaskExecutorCN +_$ss14CFTaskExecutorCfD +_$ss14CFTaskExecutorCfd _$ss15ContinuousClockV17minimumResolutions8DurationVvg _$ss15ContinuousClockV17minimumResolutions8DurationVvpMV _$ss15ContinuousClockV3nowAB7InstantVvg @@ -413,6 +523,8 @@ _$ss15ContinuousClockV3nowAB7InstantVvgZ _$ss15ContinuousClockV3nowAB7InstantVvpMV _$ss15ContinuousClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKF _$ss15ContinuousClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKFTu +_$ss15ContinuousClockV6traitss0B6TraitsVvg +_$ss15ContinuousClockV6traitss0B6TraitsVvpMV _$ss15ContinuousClockV7InstantV1loiySbAD_ADtFZ _$ss15ContinuousClockV7InstantV2eeoiySbAD_ADtFZ _$ss15ContinuousClockV7InstantV3nowADvgZ @@ -438,6 +550,22 @@ _$ss15ContinuousClockVMn _$ss15ContinuousClockVN _$ss15ContinuousClockVs0B0sMc _$ss15ContinuousClockVs0B0sWP +_$ss15ExecutorFactoryMp +_$ss15ExecutorFactoryP04mainA0s04MainA0_pvgZTj +_$ss15ExecutorFactoryP04mainA0s04MainA0_pvgZTq +_$ss15ExecutorFactoryP07defaultA0Sch_pvgZTj +_$ss15ExecutorFactoryP07defaultA0Sch_pvgZTq +_$ss15ExecutorFactoryTL +_$ss15RunLoopExecutorMp +_$ss15RunLoopExecutorP3run5untilySbyXE_tKFTj +_$ss15RunLoopExecutorP3run5untilySbyXE_tKFTq +_$ss15RunLoopExecutorP3runyyKFTj +_$ss15RunLoopExecutorP3runyyKFTq +_$ss15RunLoopExecutorP4stopyyFTj +_$ss15RunLoopExecutorP4stopyyFTq +_$ss15RunLoopExecutorPScFTb +_$ss15RunLoopExecutorPsE3run5untilySbyXE_tKF +_$ss15RunLoopExecutorTL _$ss15SuspendingClockV17minimumResolutions8DurationVvg _$ss15SuspendingClockV17minimumResolutions8DurationVvpMV _$ss15SuspendingClockV3nowAB7InstantVvg @@ -445,6 +573,8 @@ _$ss15SuspendingClockV3nowAB7InstantVvgZ _$ss15SuspendingClockV3nowAB7InstantVvpMV _$ss15SuspendingClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKF _$ss15SuspendingClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKFTu +_$ss15SuspendingClockV6traitss0B6TraitsVvg +_$ss15SuspendingClockV6traitss0B6TraitsVvpMV _$ss15SuspendingClockV7InstantV1loiySbAD_ADtFZ _$ss15SuspendingClockV7InstantV1poiyA2D_s8DurationVtFZ _$ss15SuspendingClockV7InstantV1soiyA2D_s8DurationVtFZ @@ -496,6 +626,15 @@ _$ss16AsyncMapSequenceVMa _$ss16AsyncMapSequenceVMn _$ss16AsyncMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss16AsyncMapSequenceVyxq_GScisMc +_$ss17EventableExecutorMp +_$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTj +_$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTq +_$ss17EventableExecutorP13registerEvent7handlers0bD0Vyyc_tFTj +_$ss17EventableExecutorP13registerEvent7handlers0bD0Vyyc_tFTq +_$ss17EventableExecutorP6notify5eventys0B5EventV_tFTj +_$ss17EventableExecutorP6notify5eventys0B5EventV_tFTq +_$ss17EventableExecutorTL +_$ss17_enqueueJobGlobalyyScJF _$ss19AsyncFilterSequenceV04makeA8IteratorAB0E0Vyx_GyF _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvg _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvpMV @@ -553,6 +692,8 @@ _$ss19DiscardingTaskGroupV9cancelAllyyF _$ss19DiscardingTaskGroupVMa _$ss19DiscardingTaskGroupVMn _$ss19DiscardingTaskGroupVN +_$ss19UnownedTaskExecutorV02asbC0Sch_pSgyF +_$ss19UnownedTaskExecutorVyABxhcSchRzlufC _$ss20AsyncFlatMapSequenceV04makeA8IteratorAB0F0Vyxq__GyF _$ss20AsyncFlatMapSequenceV4basexvg _$ss20AsyncFlatMapSequenceV4basexvpMV @@ -582,6 +723,68 @@ _$ss20AsyncFlatMapSequenceVMa _$ss20AsyncFlatMapSequenceVMn _$ss20AsyncFlatMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss20AsyncFlatMapSequenceVyxq_GScisMc +_$ss20DispatchMainExecutorC02isbC0Sbvg +_$ss20DispatchMainExecutorC02isbC0SbvpMV +_$ss20DispatchMainExecutorC10deregister5eventys0C5EventV_tF +_$ss20DispatchMainExecutorC13checkIsolatedyyF +_$ss20DispatchMainExecutorC13registerEvent7handlers0cE0Vyyc_tF +_$ss20DispatchMainExecutorC18supportsSchedulingSbvg +_$ss20DispatchMainExecutorC18supportsSchedulingSbvpMV +_$ss20DispatchMainExecutorC3runyyKFTj +_$ss20DispatchMainExecutorC3runyyKFTq +_$ss20DispatchMainExecutorC4stopyyFTj +_$ss20DispatchMainExecutorC4stopyyFTq +_$ss20DispatchMainExecutorC6notify5eventys0C5EventV_tF +_$ss20DispatchMainExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlF +_$ss20DispatchMainExecutorC7enqueueyys0C3JobVnF +_$ss20DispatchMainExecutorCABycfC +_$ss20DispatchMainExecutorCABycfCTj +_$ss20DispatchMainExecutorCABycfCTq +_$ss20DispatchMainExecutorCABycfc +_$ss20DispatchMainExecutorCMa +_$ss20DispatchMainExecutorCMm +_$ss20DispatchMainExecutorCMn +_$ss20DispatchMainExecutorCMo +_$ss20DispatchMainExecutorCMu +_$ss20DispatchMainExecutorCN +_$ss20DispatchMainExecutorCScFsMc +_$ss20DispatchMainExecutorCScFsWP +_$ss20DispatchMainExecutorCScfsMc +_$ss20DispatchMainExecutorCScfsWP +_$ss20DispatchMainExecutorCfD +_$ss20DispatchMainExecutorCfd +_$ss20DispatchMainExecutorCs07RunLoopC0sMc +_$ss20DispatchMainExecutorCs07RunLoopC0sWP +_$ss20DispatchMainExecutorCs09EventableC0sMc +_$ss20DispatchMainExecutorCs09EventableC0sWP +_$ss20DispatchMainExecutorCs0bC0sMc +_$ss20DispatchMainExecutorCs0bC0sWP +_$ss20DispatchTaskExecutorC06isMainC0SbvgTj +_$ss20DispatchTaskExecutorC06isMainC0SbvgTq +_$ss20DispatchTaskExecutorC06isMainC0SbvpMV +_$ss20DispatchTaskExecutorC18supportsSchedulingSbvgTj +_$ss20DispatchTaskExecutorC18supportsSchedulingSbvgTq +_$ss20DispatchTaskExecutorC18supportsSchedulingSbvpMV +_$ss20DispatchTaskExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj +_$ss20DispatchTaskExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq +_$ss20DispatchTaskExecutorC7enqueueyys0C3JobVnFTj +_$ss20DispatchTaskExecutorC7enqueueyys0C3JobVnFTq +_$ss20DispatchTaskExecutorCABycfC +_$ss20DispatchTaskExecutorCABycfCTj +_$ss20DispatchTaskExecutorCABycfCTq +_$ss20DispatchTaskExecutorCABycfc +_$ss20DispatchTaskExecutorCMa +_$ss20DispatchTaskExecutorCMm +_$ss20DispatchTaskExecutorCMn +_$ss20DispatchTaskExecutorCMo +_$ss20DispatchTaskExecutorCMu +_$ss20DispatchTaskExecutorCN +_$ss20DispatchTaskExecutorCScFsMc +_$ss20DispatchTaskExecutorCScFsWP +_$ss20DispatchTaskExecutorCSchsMc +_$ss20DispatchTaskExecutorCSchsWP +_$ss20DispatchTaskExecutorCfD +_$ss20DispatchTaskExecutorCfd _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lF _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lFTu _$ss21withUnsafeCurrentTask4bodyxxSctSgKXE_tKlF @@ -654,6 +857,15 @@ _$ss23AsyncCompactMapSequenceVMa _$ss23AsyncCompactMapSequenceVMn _$ss23AsyncCompactMapSequenceV_9transformAByxq_Gx_q_Sg7ElementQzYactcfC _$ss23AsyncCompactMapSequenceVyxq_GScisMc +_$ss23PlatformExecutorFactoryV04mainB0s04MainB0_pvgZ +_$ss23PlatformExecutorFactoryV04mainB0s04MainB0_pvpZMV +_$ss23PlatformExecutorFactoryV07defaultB0Sch_pvgZ +_$ss23PlatformExecutorFactoryV07defaultB0Sch_pvpZMV +_$ss23PlatformExecutorFactoryVMa +_$ss23PlatformExecutorFactoryVMn +_$ss23PlatformExecutorFactoryVN +_$ss23PlatformExecutorFactoryVs0bC0sMc +_$ss23PlatformExecutorFactoryVs0bC0sWP _$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalF _$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalFTu _$ss23withDiscardingTaskGroup9returning4bodyxxm_xs0bcD0VzYaXEtYalF @@ -708,6 +920,7 @@ _$ss24AsyncThrowingMapSequenceVMa _$ss24AsyncThrowingMapSequenceVMn _$ss24AsyncThrowingMapSequenceV_9transformAByxq_Gx_q_7ElementQzYaKctcfC _$ss24AsyncThrowingMapSequenceVyxq_GScisMc +_$ss26_enqueueJobGlobalWithDelayyys6UInt64V_ScJtF _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvpMV @@ -875,12 +1088,24 @@ _$ss5ClockP3now7InstantQzvgTq _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTj _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTjTu _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTq +_$ss5ClockP6traitss0A6TraitsVvgTj +_$ss5ClockP6traitss0A6TraitsVvgTq _$ss5ClockP7InstantAB_s0B8ProtocolTn +_$ss5ClockP7convert4from8DurationQzSgsAEV_tFTj +_$ss5ClockP7convert4from8DurationQzSgsAEV_tFTq +_$ss5ClockP7convert4froms8DurationVSgAEQz_tFTj +_$ss5ClockP7convert4froms8DurationVSgAEQz_tFTq +_$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTj +_$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTq +_$ss5ClockPsE7convert4from8DurationQzSgsAEV_tF +_$ss5ClockPsE7convert4froms8DurationVSgAEQz_tF +_$ss5ClockPsE7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lF _$ss5ClockPsE7measurey8DurationQzyyKXEKF _$ss5ClockPsE7measurey8DurationQzyyYaKXEYaKF _$ss5ClockPsE7measurey8DurationQzyyYaKXEYaKFTu _$ss5ClockPss010ContinuousA0VRszrlE10continuousADvgZ _$ss5ClockPss010SuspendingA0VRszrlE10suspendingADvgZ +_$ss5ClockPss8DurationVACRtzrlE7convert4fromADSgAD_tF _$ss5ClockTL _$ss6_first_5where7ElementQzSgx_SbADYaKXEtYaKSciRzlF _$ss6_first_5where7ElementQzSgx_SbADYaKXEtYaKSciRzlFTu @@ -938,6 +1163,7 @@ _swift_continuation_init _swift_continuation_resume _swift_continuation_throwingResume _swift_continuation_throwingResumeWithError +_swift_createExecutors _swift_defaultActor_deallocate _swift_defaultActor_deallocateResilient _swift_defaultActor_destroy @@ -950,6 +1176,8 @@ _swift_distributed_actor_is_remote _swift_executor_isComplexEquality _swift_get_clock_res _swift_get_time +_swift_job_allocate +_swift_job_deallocate _swift_job_run _swift_nonDefaultDistributedActor_initialize _swift_taskGroup_addPending diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index 38037f92cc94b..de2c7c75f7df7 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -23,6 +23,14 @@ _$sScEMa _$sScEMn _$sScEN _$sScEs5ErrorsMc +_$sScF14isMainExecutorSbvgTj +_$sScF14isMainExecutorSbvgTq +_$sScF18supportsSchedulingSbvgTj +_$sScF18supportsSchedulingSbvgTq +_$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj +_$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq +_$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTj +_$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTq _$sScF7enqueueyyScJFTj _$sScF7enqueueyyScJFTq _$sScF7enqueueyys11ExecutorJobVnFTj @@ -31,9 +39,19 @@ _$sScF7enqueueyys3JobVnFTj _$sScF7enqueueyys3JobVnFTq _$sScFMp _$sScFTL +_$sScFsE14isMainExecutorSbvg +_$sScFsE14isMainExecutorSbvpMV +_$sScFsE18_isComplexEqualitySbvg +_$sScFsE18_isComplexEqualitySbvpMV +_$sScFsE18supportsSchedulingSbvg +_$sScFsE18supportsSchedulingSbvpMV +_$sScFsE7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF +_$sScFsE7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lF _$sScFsE7enqueueyyScJF _$sScFsE7enqueueyys11ExecutorJobVnF _$sScFsE7enqueueyys3JobVnF +_$sScFsSQRzrlE18_isComplexEqualitySbvg +_$sScFsSQRzrlE18_isComplexEqualitySbvpMV _$sScG11isCancelledSbvg _$sScG11isCancelledSbvpMV _$sScG17makeAsyncIteratorScG0C0Vyx_GyF @@ -88,6 +106,8 @@ _$sScM3run10resultType4bodyxxm_xyYbKScMYcXEtYaKlFZ _$sScM3run10resultType4bodyxxm_xyYbKScMYcXEtYaKlFZTu _$sScM6sharedScMvgZ _$sScM7enqueueyyScJF +_$sScM8executors12MainExecutor_pvgZ +_$sScM8executors12MainExecutor_pvpZMV _$sScMMa _$sScMMm _$sScMMn @@ -195,7 +215,11 @@ _$sScTss5NeverORs_rlE5valuexvgTu _$sScTss5NeverORs_rlE5valuexvpMV _$sScTss5NeverORszABRs_rlE11isCancelledSbvgZ _$sScTss5NeverORszABRs_rlE12basePriorityScPSgvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pSgvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pSgvpZMV _$sScTss5NeverORszABRs_rlE15currentPriorityScPvgZ +_$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvgZ +_$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvpZMV _$sScTss5NeverORszABRs_rlE17checkCancellationyyKFZ _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZ _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZTu @@ -214,6 +238,7 @@ _$sScc7contextBcvs _$sSccMa _$sSccMn _$sSce15complexEqualityScexh_tcScfRzlufC +_$sSce16asSerialExecutorScf_pSgyF _$sSce18_isComplexEqualitySbvg _$sSce18_isComplexEqualitySbvpMV _$sSce8executorBevM @@ -227,6 +252,7 @@ _$sSceMa _$sSceMn _$sSceN _$sSceySceBecfC +_$sSceyScexhcScfRzlufC _$sScf23asUnownedSerialExecutorSceyFTj _$sScf23asUnownedSerialExecutorSceyFTq _$sScf31isSameExclusiveExecutionContext5otherSbx_tFTj @@ -241,9 +267,12 @@ _$sScfMp _$sScfScFTb _$sScfTL _$sScfsE14assertIsolated_4file4lineySSyXK_s12StaticStringVSutF +_$sScfsE14isMainExecutorSbvg +_$sScfsE14isMainExecutorSbvpMV _$sScfsE20preconditionIsolated_4file4lineySSyXK_s12StaticStringVSutF _$sScfsE23asUnownedSerialExecutorSceyF _$sScfsE31isSameExclusiveExecutionContext5otherSbx_tF +_$sScfsSQRzrlE31isSameExclusiveExecutionContext5otherSbx_tF _$sScg10nextResults0B0Oyxq_GSgyYaKF _$sScg10nextResults0B0Oyxq_GSgyYaKFTu _$sScg11_waitForAllyyYaKF @@ -370,9 +399,51 @@ _$sSctN _$sSctSHsMc _$sSctSQsMc _$ss039_checkIllegalTaskLocalBindingWithinWithC5Group4file4lineySS_SutF +_$ss11ClockTraitsV10continuousABvgZ +_$ss11ClockTraitsV10continuousABvpZMV +_$ss11ClockTraitsV8rawValueABs5Int32V_tcfC +_$ss11ClockTraitsV8rawValues5Int32Vvg +_$ss11ClockTraitsV8rawValues5Int32VvpMV +_$ss11ClockTraitsV8wallTimeABvgZ +_$ss11ClockTraitsV8wallTimeABvpZMV +_$ss11ClockTraitsV9monotonicABvgZ +_$ss11ClockTraitsV9monotonicABvpZMV +_$ss11ClockTraitsVMa +_$ss11ClockTraitsVMn +_$ss11ClockTraitsVN +_$ss11ClockTraitsVSQsMc +_$ss11ClockTraitsVSYsMc +_$ss11ClockTraitsVs10SetAlgebrasMc +_$ss11ClockTraitsVs25ExpressibleByArrayLiteralsMc +_$ss11ClockTraitsVs9OptionSetsMc +_$ss11ExecutorJobV010withUnsafeA11PrivateData4bodyxxSwKXE_tKlF _$ss11ExecutorJobV11descriptionSSvg +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySpyxGlF +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySryxGlF +_$ss11ExecutorJobV14LocalAllocatorV10deallocateyySwF +_$ss11ExecutorJobV14LocalAllocatorV8allocate2asSpyxGxm_tlF +_$ss11ExecutorJobV14LocalAllocatorV8allocate8capacity2asSryxGSi_xmtlF +_$ss11ExecutorJobV14LocalAllocatorV8allocate8capacitySwSi_tF +_$ss11ExecutorJobV14LocalAllocatorVMa +_$ss11ExecutorJobV14LocalAllocatorVMn +_$ss11ExecutorJobV14LocalAllocatorVN +_$ss11ExecutorJobV4KindV13firstReservedADvgZ +_$ss11ExecutorJobV4KindV13firstReservedADvpZMV +_$ss11ExecutorJobV4KindV4taskADvgZ +_$ss11ExecutorJobV4KindV4taskADvpZMV +_$ss11ExecutorJobV4KindV8rawValueADSgs5UInt8V_tcfC +_$ss11ExecutorJobV4KindV8rawValues5UInt8VvM +_$ss11ExecutorJobV4KindV8rawValues5UInt8Vvg +_$ss11ExecutorJobV4KindV8rawValues5UInt8VvpMV +_$ss11ExecutorJobV4KindV8rawValues5UInt8Vvs +_$ss11ExecutorJobV4KindVMa +_$ss11ExecutorJobV4KindVMn +_$ss11ExecutorJobV4KindVN +_$ss11ExecutorJobV4KindVSYsMc +_$ss11ExecutorJobV4kindAB4KindVvg _$ss11ExecutorJobV7contextABBjn_tcfC _$ss11ExecutorJobV8prioritys0B8PriorityVvg +_$ss11ExecutorJobV9allocatorAB14LocalAllocatorVSgvg _$ss11ExecutorJobVMa _$ss11ExecutorJobVMn _$ss11ExecutorJobVN @@ -403,9 +474,48 @@ _$ss11JobPriorityVMn _$ss11JobPriorityVN _$ss11JobPriorityVSLsMc _$ss11JobPriorityVSQsMc +_$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ +_$ss12MainExecutorMp +_$ss12MainExecutorPScfTb +_$ss12MainExecutorPs07RunLoopB0Tb +_$ss12MainExecutorPs09EventableB0Tb +_$ss12MainExecutorTL +_$ss13ExecutorEventV1loiySbAB_ABtFZ +_$ss13ExecutorEventV2eeoiySbAB_ABtFZ +_$ss13ExecutorEventV2idABSi_tcfC +_$ss13ExecutorEventV2idSivM +_$ss13ExecutorEventV2idSivg +_$ss13ExecutorEventV2idSivpMV +_$ss13ExecutorEventV2idSivs +_$ss13ExecutorEventVMa +_$ss13ExecutorEventVMn +_$ss13ExecutorEventVN +_$ss13ExecutorEventVSLsMc +_$ss13ExecutorEventVSQsMc +_$ss13ExecutorEventVs12IdentifiablesMc _$ss13_runAsyncMainyyyyYaKcF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lFTu +_$ss14CFMainExecutorC3runyyKF +_$ss14CFMainExecutorC4stopyyF +_$ss14CFMainExecutorCABycfC +_$ss14CFMainExecutorCABycfc +_$ss14CFMainExecutorCMa +_$ss14CFMainExecutorCMm +_$ss14CFMainExecutorCMn +_$ss14CFMainExecutorCMo +_$ss14CFMainExecutorCN +_$ss14CFMainExecutorCfD +_$ss14CFMainExecutorCfd +_$ss14CFTaskExecutorCABycfC +_$ss14CFTaskExecutorCABycfc +_$ss14CFTaskExecutorCMa +_$ss14CFTaskExecutorCMm +_$ss14CFTaskExecutorCMn +_$ss14CFTaskExecutorCMo +_$ss14CFTaskExecutorCN +_$ss14CFTaskExecutorCfD +_$ss14CFTaskExecutorCfd _$ss15ContinuousClockV17minimumResolutions8DurationVvg _$ss15ContinuousClockV17minimumResolutions8DurationVvpMV _$ss15ContinuousClockV3nowAB7InstantVvg @@ -413,6 +523,8 @@ _$ss15ContinuousClockV3nowAB7InstantVvgZ _$ss15ContinuousClockV3nowAB7InstantVvpMV _$ss15ContinuousClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKF _$ss15ContinuousClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKFTu +_$ss15ContinuousClockV6traitss0B6TraitsVvg +_$ss15ContinuousClockV6traitss0B6TraitsVvpMV _$ss15ContinuousClockV7InstantV1loiySbAD_ADtFZ _$ss15ContinuousClockV7InstantV2eeoiySbAD_ADtFZ _$ss15ContinuousClockV7InstantV3nowADvgZ @@ -438,6 +550,22 @@ _$ss15ContinuousClockVMn _$ss15ContinuousClockVN _$ss15ContinuousClockVs0B0sMc _$ss15ContinuousClockVs0B0sWP +_$ss15ExecutorFactoryMp +_$ss15ExecutorFactoryP04mainA0s04MainA0_pvgZTj +_$ss15ExecutorFactoryP04mainA0s04MainA0_pvgZTq +_$ss15ExecutorFactoryP07defaultA0Sch_pvgZTj +_$ss15ExecutorFactoryP07defaultA0Sch_pvgZTq +_$ss15ExecutorFactoryTL +_$ss15RunLoopExecutorMp +_$ss15RunLoopExecutorP3run5untilySbyXE_tKFTj +_$ss15RunLoopExecutorP3run5untilySbyXE_tKFTq +_$ss15RunLoopExecutorP3runyyKFTj +_$ss15RunLoopExecutorP3runyyKFTq +_$ss15RunLoopExecutorP4stopyyFTj +_$ss15RunLoopExecutorP4stopyyFTq +_$ss15RunLoopExecutorPScFTb +_$ss15RunLoopExecutorPsE3run5untilySbyXE_tKF +_$ss15RunLoopExecutorTL _$ss15SuspendingClockV17minimumResolutions8DurationVvg _$ss15SuspendingClockV17minimumResolutions8DurationVvpMV _$ss15SuspendingClockV3nowAB7InstantVvg @@ -445,6 +573,8 @@ _$ss15SuspendingClockV3nowAB7InstantVvgZ _$ss15SuspendingClockV3nowAB7InstantVvpMV _$ss15SuspendingClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKF _$ss15SuspendingClockV5sleep5until9toleranceyAB7InstantV_s8DurationVSgtYaKFTu +_$ss15SuspendingClockV6traitss0B6TraitsVvg +_$ss15SuspendingClockV6traitss0B6TraitsVvpMV _$ss15SuspendingClockV7InstantV1loiySbAD_ADtFZ _$ss15SuspendingClockV7InstantV1poiyA2D_s8DurationVtFZ _$ss15SuspendingClockV7InstantV1soiyA2D_s8DurationVtFZ @@ -496,6 +626,15 @@ _$ss16AsyncMapSequenceVMa _$ss16AsyncMapSequenceVMn _$ss16AsyncMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss16AsyncMapSequenceVyxq_GScisMc +_$ss17EventableExecutorMp +_$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTj +_$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTq +_$ss17EventableExecutorP13registerEvent7handlers0bD0Vyyc_tFTj +_$ss17EventableExecutorP13registerEvent7handlers0bD0Vyyc_tFTq +_$ss17EventableExecutorP6notify5eventys0B5EventV_tFTj +_$ss17EventableExecutorP6notify5eventys0B5EventV_tFTq +_$ss17EventableExecutorTL +_$ss17_enqueueJobGlobalyyScJF _$ss19AsyncFilterSequenceV04makeA8IteratorAB0E0Vyx_GyF _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvg _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvpMV @@ -553,6 +692,8 @@ _$ss19DiscardingTaskGroupV9cancelAllyyF _$ss19DiscardingTaskGroupVMa _$ss19DiscardingTaskGroupVMn _$ss19DiscardingTaskGroupVN +_$ss19UnownedTaskExecutorV02asbC0Sch_pSgyF +_$ss19UnownedTaskExecutorVyABxhcSchRzlufC _$ss20AsyncFlatMapSequenceV04makeA8IteratorAB0F0Vyxq__GyF _$ss20AsyncFlatMapSequenceV4basexvg _$ss20AsyncFlatMapSequenceV4basexvpMV @@ -582,6 +723,68 @@ _$ss20AsyncFlatMapSequenceVMa _$ss20AsyncFlatMapSequenceVMn _$ss20AsyncFlatMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss20AsyncFlatMapSequenceVyxq_GScisMc +_$ss20DispatchMainExecutorC02isbC0Sbvg +_$ss20DispatchMainExecutorC02isbC0SbvpMV +_$ss20DispatchMainExecutorC10deregister5eventys0C5EventV_tF +_$ss20DispatchMainExecutorC13checkIsolatedyyF +_$ss20DispatchMainExecutorC13registerEvent7handlers0cE0Vyyc_tF +_$ss20DispatchMainExecutorC18supportsSchedulingSbvg +_$ss20DispatchMainExecutorC18supportsSchedulingSbvpMV +_$ss20DispatchMainExecutorC3runyyKFTj +_$ss20DispatchMainExecutorC3runyyKFTq +_$ss20DispatchMainExecutorC4stopyyFTj +_$ss20DispatchMainExecutorC4stopyyFTq +_$ss20DispatchMainExecutorC6notify5eventys0C5EventV_tF +_$ss20DispatchMainExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlF +_$ss20DispatchMainExecutorC7enqueueyys0C3JobVnF +_$ss20DispatchMainExecutorCABycfC +_$ss20DispatchMainExecutorCABycfCTj +_$ss20DispatchMainExecutorCABycfCTq +_$ss20DispatchMainExecutorCABycfc +_$ss20DispatchMainExecutorCMa +_$ss20DispatchMainExecutorCMm +_$ss20DispatchMainExecutorCMn +_$ss20DispatchMainExecutorCMo +_$ss20DispatchMainExecutorCMu +_$ss20DispatchMainExecutorCN +_$ss20DispatchMainExecutorCScFsMc +_$ss20DispatchMainExecutorCScFsWP +_$ss20DispatchMainExecutorCScfsMc +_$ss20DispatchMainExecutorCScfsWP +_$ss20DispatchMainExecutorCfD +_$ss20DispatchMainExecutorCfd +_$ss20DispatchMainExecutorCs07RunLoopC0sMc +_$ss20DispatchMainExecutorCs07RunLoopC0sWP +_$ss20DispatchMainExecutorCs09EventableC0sMc +_$ss20DispatchMainExecutorCs09EventableC0sWP +_$ss20DispatchMainExecutorCs0bC0sMc +_$ss20DispatchMainExecutorCs0bC0sWP +_$ss20DispatchTaskExecutorC06isMainC0SbvgTj +_$ss20DispatchTaskExecutorC06isMainC0SbvgTq +_$ss20DispatchTaskExecutorC06isMainC0SbvpMV +_$ss20DispatchTaskExecutorC18supportsSchedulingSbvgTj +_$ss20DispatchTaskExecutorC18supportsSchedulingSbvgTq +_$ss20DispatchTaskExecutorC18supportsSchedulingSbvpMV +_$ss20DispatchTaskExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj +_$ss20DispatchTaskExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq +_$ss20DispatchTaskExecutorC7enqueueyys0C3JobVnFTj +_$ss20DispatchTaskExecutorC7enqueueyys0C3JobVnFTq +_$ss20DispatchTaskExecutorCABycfC +_$ss20DispatchTaskExecutorCABycfCTj +_$ss20DispatchTaskExecutorCABycfCTq +_$ss20DispatchTaskExecutorCABycfc +_$ss20DispatchTaskExecutorCMa +_$ss20DispatchTaskExecutorCMm +_$ss20DispatchTaskExecutorCMn +_$ss20DispatchTaskExecutorCMo +_$ss20DispatchTaskExecutorCMu +_$ss20DispatchTaskExecutorCN +_$ss20DispatchTaskExecutorCScFsMc +_$ss20DispatchTaskExecutorCScFsWP +_$ss20DispatchTaskExecutorCSchsMc +_$ss20DispatchTaskExecutorCSchsWP +_$ss20DispatchTaskExecutorCfD +_$ss20DispatchTaskExecutorCfd _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lF _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lFTu _$ss21withUnsafeCurrentTask4bodyxxSctSgKXE_tKlF @@ -654,6 +857,15 @@ _$ss23AsyncCompactMapSequenceVMa _$ss23AsyncCompactMapSequenceVMn _$ss23AsyncCompactMapSequenceV_9transformAByxq_Gx_q_Sg7ElementQzYactcfC _$ss23AsyncCompactMapSequenceVyxq_GScisMc +_$ss23PlatformExecutorFactoryV04mainB0s04MainB0_pvgZ +_$ss23PlatformExecutorFactoryV04mainB0s04MainB0_pvpZMV +_$ss23PlatformExecutorFactoryV07defaultB0Sch_pvgZ +_$ss23PlatformExecutorFactoryV07defaultB0Sch_pvpZMV +_$ss23PlatformExecutorFactoryVMa +_$ss23PlatformExecutorFactoryVMn +_$ss23PlatformExecutorFactoryVN +_$ss23PlatformExecutorFactoryVs0bC0sMc +_$ss23PlatformExecutorFactoryVs0bC0sWP _$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalF _$ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalFTu _$ss23withDiscardingTaskGroup9returning4bodyxxm_xs0bcD0VzYaXEtYalF @@ -708,6 +920,7 @@ _$ss24AsyncThrowingMapSequenceVMa _$ss24AsyncThrowingMapSequenceVMn _$ss24AsyncThrowingMapSequenceV_9transformAByxq_Gx_q_7ElementQzYaKctcfC _$ss24AsyncThrowingMapSequenceVyxq_GScisMc +_$ss26_enqueueJobGlobalWithDelayyys6UInt64V_ScJtF _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvpMV @@ -875,12 +1088,24 @@ _$ss5ClockP3now7InstantQzvgTq _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTj _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTjTu _$ss5ClockP5sleep5until9tolerancey7InstantQz_8DurationQzSgtYaKFTq +_$ss5ClockP6traitss0A6TraitsVvgTj +_$ss5ClockP6traitss0A6TraitsVvgTq _$ss5ClockP7InstantAB_s0B8ProtocolTn +_$ss5ClockP7convert4from8DurationQzSgsAEV_tFTj +_$ss5ClockP7convert4from8DurationQzSgsAEV_tFTq +_$ss5ClockP7convert4froms8DurationVSgAEQz_tFTj +_$ss5ClockP7convert4froms8DurationVSgAEQz_tFTq +_$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTj +_$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTq +_$ss5ClockPsE7convert4from8DurationQzSgsAEV_tF +_$ss5ClockPsE7convert4froms8DurationVSgAEQz_tF +_$ss5ClockPsE7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lF _$ss5ClockPsE7measurey8DurationQzyyKXEKF _$ss5ClockPsE7measurey8DurationQzyyYaKXEYaKF _$ss5ClockPsE7measurey8DurationQzyyYaKXEYaKFTu _$ss5ClockPss010ContinuousA0VRszrlE10continuousADvgZ _$ss5ClockPss010SuspendingA0VRszrlE10suspendingADvgZ +_$ss5ClockPss8DurationVACRtzrlE7convert4fromADSgAD_tF _$ss5ClockTL _$ss6_first_5where7ElementQzSgx_SbADYaKXEtYaKSciRzlF _$ss6_first_5where7ElementQzSgx_SbADYaKXEtYaKSciRzlFTu @@ -938,6 +1163,7 @@ _swift_continuation_init _swift_continuation_resume _swift_continuation_throwingResume _swift_continuation_throwingResumeWithError +_swift_createExecutors _swift_defaultActor_deallocate _swift_defaultActor_deallocateResilient _swift_defaultActor_destroy @@ -950,6 +1176,8 @@ _swift_distributed_actor_is_remote _swift_executor_isComplexEquality _swift_get_clock_res _swift_get_time +_swift_job_allocate +_swift_job_deallocate _swift_job_run _swift_nonDefaultDistributedActor_initialize _swift_taskGroup_addPending From 229865192f69679ae68caa8b2095c2a7168f84a3 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 10 Mar 2025 15:47:07 +0000 Subject: [PATCH 05/29] [Concurrency] Rename ExecutorJob back to PartialAsyncTask. The rename appears to have had some kind of potentially ABI breaking consequence. rdar://141348916 --- stdlib/public/Concurrency/CMakeLists.txt | 2 +- .../{ExecutorJob.swift => PartialAsyncTask.swift} | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) rename stdlib/public/Concurrency/{ExecutorJob.swift => PartialAsyncTask.swift} (98%) diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 67dda68ab1c54..adc2ef8ba0db1 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -126,7 +126,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES AsyncThrowingFlatMapSequence.swift AsyncThrowingMapSequence.swift AsyncThrowingPrefixWhileSequence.swift - ExecutorJob.swift + PartialAsyncTask.swift GlobalActor.swift GlobalConcurrentExecutor.swift MainActor.swift diff --git a/stdlib/public/Concurrency/ExecutorJob.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift similarity index 98% rename from stdlib/public/Concurrency/ExecutorJob.swift rename to stdlib/public/Concurrency/PartialAsyncTask.swift index 4dcb92c7be0ca..627bc2f915f4c 100644 --- a/stdlib/public/Concurrency/ExecutorJob.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -12,6 +12,16 @@ import Swift +// N.B. It would be nice to rename this file to ExecutorJob.swift, but when +// trying that we hit an error from the API digester claiming that +// +// +Var UnownedJob.context has mangled name changing from +// 'Swift.UnownedJob.(context in #UNSTABLE ID#) : Builtin.Job' to +// 'Swift.UnownedJob.(context in #UNSTABLE ID#) : Builtin.Job' +// +// This is odd because `context` is private, so it isn't really part of +// the module API. + // TODO: It would be better if some of the functions here could be inlined into // the Swift code. In particular, some of the ones that deal with data held on // ExecutorJob. From 3a7fc7c206669080a35aa5370ce7c959187f3582 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 10 Mar 2025 18:42:44 +0000 Subject: [PATCH 06/29] [Concurrency] Remove spurious printf(). Remove a printf() I'd added for debugging. rdar://141348916 --- lib/SILGen/SILGenFunction.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/SILGen/SILGenFunction.cpp b/lib/SILGen/SILGenFunction.cpp index e0aeb7bd44ead..14c5eb98d82f5 100644 --- a/lib/SILGen/SILGenFunction.cpp +++ b/lib/SILGen/SILGenFunction.cpp @@ -1426,8 +1426,6 @@ void SILGenFunction::emitAsyncMainThreadStart(SILDeclRef entryPoint) { // If we're using a new enough deployment target, call swift_createExecutors() if (ctx.LangOpts.ExecutorFactory) { - printf("Executor factory is %s\n", ctx.LangOpts.ExecutorFactory->c_str()); - if (!isCreateExecutorsFunctionAvailable(SGM)) { ctx.Diags.diagnose(SourceLoc(), diag::executor_factory_not_supported); } else { From ef0e09d0b7f4677db4d22d98ebb3d8b2adafe325 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 11 Mar 2025 10:30:39 +0000 Subject: [PATCH 07/29] [Concurrency][Embedded] Remove MainActor/MainExecutor everywhere. Embedded Swift doesn't have MainActor, so remove it. rdar://141348916 --- stdlib/public/Concurrency/Executor.swift | 12 ++++++++++++ stdlib/public/Concurrency/ExecutorBridge.swift | 4 ++++ stdlib/public/Concurrency/MainActor.swift | 6 +++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index 21c85f334e319..e719893df0b75 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -36,6 +36,7 @@ public protocol Executor: AnyObject, Sendable { func enqueue(_ job: consuming ExecutorJob) #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + #if !$Embedded // The functions below could have been added to a separate protocol, // but doing that would then mean doing an `as?` cast in e.g. // enqueueOnGlobalExecutor (in ExecutorBridge.swift), which is @@ -44,6 +45,7 @@ public protocol Executor: AnyObject, Sendable { /// `true` if this is the main executor. @available(SwiftStdlib 6.2, *) var isMainExecutor: Bool { get } + #endif /// `true` if this Executor supports scheduling. /// @@ -116,10 +118,12 @@ extension Executor where Self: Equatable { // Delay support extension Executor { + #if !$Embedded // This defaults to `false` so that existing third-party Executor // implementations will work as expected. @available(SwiftStdlib 6.2, *) public var isMainExecutor: Bool { false } + #endif // This defaults to `false` so that existing third-party TaskExecutor // implementations will work as expected. @@ -327,8 +331,10 @@ public protocol SerialExecutor: Executor { @available(SwiftStdlib 6.0, *) extension SerialExecutor { + #if !$Embedded @available(SwiftStdlib 6.2, *) public var isMainExecutor: Bool { return MainActor.executor._isSameExecutor(self) } + #endif @available(SwiftStdlib 6.0, *) public func checkIsolated() { @@ -578,9 +584,11 @@ public protocol MainExecutor: RunLoopExecutor, SerialExecutor, EventableExecutor /// executors. @available(SwiftStdlib 6.2, *) public protocol ExecutorFactory { + #if !$Embedded /// Constructs and returns the main executor, which is started implicitly /// by the `async main` entry point and owns the "main" thread. static var mainExecutor: any MainExecutor { get } + #endif /// Constructs and returns the default or global executor, which is the /// default place in which we run tasks. @@ -590,10 +598,13 @@ public protocol ExecutorFactory { @available(SwiftStdlib 6.2, *) @_silgen_name("swift_createExecutors") public func _createExecutors(factory: F.Type) { + #if !$Embedded MainActor._executor = factory.mainExecutor + #endif Task._defaultExecutor = factory.defaultExecutor } +#if !$Embedded extension MainActor { @available(SwiftStdlib 6.2, *) static var _executor: (any MainExecutor)? = nil @@ -611,6 +622,7 @@ extension MainActor { return _executor! } } +#endif // !$Embedded extension Task where Success == Never, Failure == Never { @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift index 343e8ba47a803..1abb77d3050e2 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.swift +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -21,11 +21,13 @@ import Swift @_silgen_name("_swift_exit") internal func _exit(result: CInt) +#if !$Embedded @available(SwiftStdlib 6.2, *) @_silgen_name("_swift_task_isMainExecutorSwift") internal func _isMainExecutor(_ executor: E) -> Bool where E: SerialExecutor { return executor.isMainExecutor } +#endif @available(SwiftStdlib 6.2, *) @_silgen_name("_swift_task_checkIsolatedSwift") @@ -77,11 +79,13 @@ internal func _jobGetExecutorPrivateData( _ job: Builtin.Job ) -> UnsafeMutableRawPointer +#if !$Embedded @available(SwiftStdlib 6.2, *) @_silgen_name("swift_getMainExecutor") internal func _getMainExecutor() -> any MainExecutor { return MainActor.executor } +#endif @available(SwiftStdlib 6.2, *) @_silgen_name("swift_dispatchMain") diff --git a/stdlib/public/Concurrency/MainActor.swift b/stdlib/public/Concurrency/MainActor.swift index 673ecadee79a2..840a1519ae735 100644 --- a/stdlib/public/Concurrency/MainActor.swift +++ b/stdlib/public/Concurrency/MainActor.swift @@ -12,6 +12,8 @@ import Swift +#if !$Embedded + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 5.1, *) @available(*, unavailable, message: "Unavailable in task-to-thread concurrency model") @@ -160,4 +162,6 @@ extension MainActor { try assumeIsolated(operation, file: file, line: line) } } -#endif +#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + +#endif // !$Embedded From d14b9370fbfea160d1c319b4a4df9b2c1aa8e273 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 11 Mar 2025 11:05:34 +0000 Subject: [PATCH 08/29] [Concurrency] Don't use blocks. We can't use blocks, because Swift doesn't support them on Linux or Windows. Instead, use a C function pointer, and box up the handler. rdar://141348916 --- stdlib/public/Concurrency/ExecutorBridge.cpp | 11 +++-- .../public/Concurrency/ExecutorBridge.swift | 42 +++++++++++++++++-- 2 files changed, 46 insertions(+), 7 deletions(-) diff --git a/stdlib/public/Concurrency/ExecutorBridge.cpp b/stdlib/public/Concurrency/ExecutorBridge.cpp index 6961e4c988d61..a98346d884e2d 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.cpp +++ b/stdlib/public/Concurrency/ExecutorBridge.cpp @@ -120,24 +120,29 @@ void *swift_job_getExecutorPrivateData(Job *job) { #if !SWIFT_CONCURRENCY_EMBEDDED extern "C" SWIFT_CC(swift) -void *swift_createDispatchEvent(void (^handler)()) { +void *swift_createDispatchEventC(void (*handler)(void *), void *context) { dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_OR, 0, 0, dispatch_get_main_queue()); - dispatch_source_set_event_handler(source, handler); + dispatch_source_set_event_handler_f(source, handler); dispatch_activate(source); return source; } extern "C" SWIFT_CC(swift) -void swift_destroyDispatchEvent(void *event) { +void swift_destroyDispatchEventC(void *event) { dispatch_source_t source = (dispatch_source_t)event; dispatch_source_cancel(source); dispatch_release(source); } +extern "C" SWIFT_CC(swift) +void *swift_getDispatchEventContext(void *event) { + return dispatch_get_context((dispatch_source_t)event); +} + extern "C" SWIFT_CC(swift) void swift_signalDispatchEvent(void *event) { dispatch_source_t source = (dispatch_source_t)event; diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift index 1abb77d3050e2..31dfca81c1985 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.swift +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -114,12 +114,46 @@ internal func _dispatchEnqueueWithDeadline(_ global: CBool, internal func _dispatchAssertMainQueue() @available(SwiftStdlib 6.2, *) -@_silgen_name("swift_createDispatchEvent") -internal func _createDispatchEvent(handler: @convention(block) @escaping () -> ()) -> OpaquePointer +@_silgen_name("swift_createDispatchEventC") +internal func _createDispatchEventC( + handler: @convention(c) @escaping (UnsafeMutableRawPointer) -> (), + context: UnsafeMutableRawPointer +) -> OpaquePointer + +fileprivate class DispatchEventHandlerBox { + var handler: () -> () + init(handler: @escaping () -> ()) { + self.handler = handler + } +} @available(SwiftStdlib 6.2, *) -@_silgen_name("swift_destroyDispatchEvent") -internal func _destroyDispatchEvent(_ event: OpaquePointer) +internal func _createDispatchEvent(handler: @escaping () -> ()) -> OpaquePointer { + let boxed = DispatchEventHandlerBox(handler: handler) + let opaqueHandlerBox = Unmanaged.passRetained(boxed).toOpaque() + return _createDispatchEventC( + handler: { context in + let unmanaged = Unmanaged.fromOpaque(context) + unmanaged.takeUnretainedValue().handler() + }, + context: opaqueHandlerBox + ) +} + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_destroyDispatchEventC") +internal func _destroyDispatchEventC(_ event: OpaquePointer) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_getDispatchEventContext") +internal func _getDispatchEventContext(_ event: OpaquePointer) -> UnsafeMutableRawPointer + +@available(SwiftStdlib 6.2, *) +internal func _destroyDispatchEvent(_ event: OpaquePointer) { + let context = _getDispatchEventContext(event) + Unmanaged.fromOpaque(context).release() + _destroyDispatchEventC(event) +} @available(SwiftStdlib 6.2, *) @_silgen_name("swift_signalDispatchEvent") From b33666cf08a91cc56b1497cc52ceb9f848ae8542 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 11 Mar 2025 14:52:30 +0000 Subject: [PATCH 09/29] [Concurrency] Fixes from initial review. Rename `DispatchTaskExecutor` to `DispatchGlobalTaskExecutor` as we may want to use the former for an executor that runs things on an arbitrary Dispatch queue. Rename `DispatchExecutor` to `DispatchExecutorProtocol`; again, we might want the name for something else. Add `@Sendable` attribute to `registerEvent`. Fix missing `extern "C" SWIFT_CC(swift)` on `_swift_exit` (merge error). Remove stray whitespace from `CMakeLists.txt` rdar://141348916 --- stdlib/public/Concurrency/CFExecutor.swift | 3 +- stdlib/public/Concurrency/CMakeLists.txt | 2 +- .../public/Concurrency/DispatchExecutor.swift | 14 +++-- stdlib/public/Concurrency/Executor.swift | 2 +- stdlib/public/Concurrency/ExecutorBridge.cpp | 1 + .../public/Concurrency/ExecutorBridge.swift | 6 +- .../Concurrency/PlatformExecutorDarwin.swift | 2 +- .../Concurrency/PlatformExecutorLinux.swift | 3 +- .../Concurrency/PlatformExecutorWindows.swift | 3 +- .../Runtime/custom_main_executor.swift | 4 +- .../macOS/arm64/concurrency/baseline-asserts | 60 +++++++++---------- .../macOS/x86_64/concurrency/baseline-asserts | 60 +++++++++---------- 12 files changed, 83 insertions(+), 77 deletions(-) diff --git a/stdlib/public/Concurrency/CFExecutor.swift b/stdlib/public/Concurrency/CFExecutor.swift index c24fc73511b46..c12701a6872a9 100644 --- a/stdlib/public/Concurrency/CFExecutor.swift +++ b/stdlib/public/Concurrency/CFExecutor.swift @@ -59,7 +59,8 @@ public final class CFMainExecutor: DispatchMainExecutor, @unchecked Sendable { // .. Task Executor ............................................................ @available(SwiftStdlib 6.2, *) -public final class CFTaskExecutor: DispatchTaskExecutor, @unchecked Sendable { +public final class CFTaskExecutor: DispatchGlobalTaskExecutor, + @unchecked Sendable { } diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index adc2ef8ba0db1..ce0d6488fbd53 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -179,7 +179,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES set(SWIFT_RUNTIME_CONCURRENCY_EMBEDDED_SWIFT_SOURCES PlatformExecutorEmbedded.swift - ) +) add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB ${SWIFT_RUNTIME_CONCURRENCY_C_SOURCES} diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift index b5f3663870126..1c88c43caef32 100644 --- a/stdlib/public/Concurrency/DispatchExecutor.swift +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -84,7 +84,9 @@ extension DispatchMainExecutor: SerialExecutor { extension DispatchMainExecutor: EventableExecutor { /// Register a new event with a given handler. - public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { + public func registerEvent( + handler: @escaping @Sendable () -> () + ) -> ExecutorEvent { let source = _createDispatchEvent(handler: handler) // Stash the pointer in the id of the ExecutorEvent struct @@ -114,7 +116,7 @@ extension DispatchMainExecutor: MainExecutor {} // .. Task Executor ............................................................ @available(SwiftStdlib 6.2, *) -public class DispatchTaskExecutor: TaskExecutor, @unchecked Sendable { +public class DispatchGlobalTaskExecutor: TaskExecutor, @unchecked Sendable { public init() {} @@ -156,7 +158,7 @@ public class DispatchTaskExecutor: TaskExecutor, @unchecked Sendable { /// It is used to help convert instants and durations from arbitrary `Clock`s /// to Dispatch's time base. @available(SwiftStdlib 6.2, *) -protocol DispatchExecutor: Executor { +protocol DispatchExecutorProtocol: Executor { /// Convert an `Instant` from the specified clock to a tuple identifying /// the Dispatch clock and the seconds and nanoseconds components. @@ -184,7 +186,7 @@ enum DispatchClockID: CInt { } @available(SwiftStdlib 6.2, *) -extension DispatchExecutor { +extension DispatchExecutorProtocol { func timestamp(for instant: C.Instant, clock: C) -> (clockID: DispatchClockID, seconds: Int64, nanoseconds: Int64) { @@ -218,11 +220,11 @@ extension DispatchExecutor { } @available(SwiftStdlib 6.2, *) -extension DispatchTaskExecutor: DispatchExecutor { +extension DispatchGlobalTaskExecutor: DispatchExecutorProtocol { } @available(SwiftStdlib 6.2, *) -extension DispatchMainExecutor: DispatchExecutor { +extension DispatchMainExecutor: DispatchExecutorProtocol { } #endif // !$Embedded diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index e719893df0b75..b73b1b8849acd 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -555,7 +555,7 @@ public protocol EventableExecutor { /// - handler: The handler to call when the event fires. /// /// Returns a new opaque `Event`. - func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent + func registerEvent(handler: @escaping @Sendable () -> ()) -> ExecutorEvent /// Deregister the given event. /// diff --git a/stdlib/public/Concurrency/ExecutorBridge.cpp b/stdlib/public/Concurrency/ExecutorBridge.cpp index a98346d884e2d..289ef1265917e 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.cpp +++ b/stdlib/public/Concurrency/ExecutorBridge.cpp @@ -22,6 +22,7 @@ using namespace swift; #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +extern "C" SWIFT_CC(swift) void _swift_exit(int result) { exit(result); } diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift index 31dfca81c1985..bbd8f06ad8501 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.swift +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -121,14 +121,14 @@ internal func _createDispatchEventC( ) -> OpaquePointer fileprivate class DispatchEventHandlerBox { - var handler: () -> () - init(handler: @escaping () -> ()) { + var handler: @Sendable () -> () + init(handler: @escaping @Sendable () -> ()) { self.handler = handler } } @available(SwiftStdlib 6.2, *) -internal func _createDispatchEvent(handler: @escaping () -> ()) -> OpaquePointer { +internal func _createDispatchEvent(handler: @escaping @Sendable () -> ()) -> OpaquePointer { let boxed = DispatchEventHandlerBox(handler: handler) let opaqueHandlerBox = Unmanaged.passRetained(boxed).toOpaque() return _createDispatchEventC( diff --git a/stdlib/public/Concurrency/PlatformExecutorDarwin.swift b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift index 28a7f49ec5719..b8ac350668dcc 100644 --- a/stdlib/public/Concurrency/PlatformExecutorDarwin.swift +++ b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift @@ -28,7 +28,7 @@ public struct PlatformExecutorFactory: ExecutorFactory { if CoreFoundation.isPresent { return CFTaskExecutor() } else { - return DispatchTaskExecutor() + return DispatchGlobalTaskExecutor() } } } diff --git a/stdlib/public/Concurrency/PlatformExecutorLinux.swift b/stdlib/public/Concurrency/PlatformExecutorLinux.swift index 7083406a1c228..3ceb1d1c413e1 100644 --- a/stdlib/public/Concurrency/PlatformExecutorLinux.swift +++ b/stdlib/public/Concurrency/PlatformExecutorLinux.swift @@ -18,7 +18,8 @@ import Swift @available(SwiftStdlib 6.2, *) public struct PlatformExecutorFactory: ExecutorFactory { public static let mainExecutor: any MainExecutor = DispatchMainExecutor() - public static let defaultExecutor: any TaskExecutor = DispatchTaskExecutor() + public static let defaultExecutor: any TaskExecutor + = DispatchGlobalTaskExecutor() } #endif // os(Linux) diff --git a/stdlib/public/Concurrency/PlatformExecutorWindows.swift b/stdlib/public/Concurrency/PlatformExecutorWindows.swift index 053d74dd22f16..e64665a94da37 100644 --- a/stdlib/public/Concurrency/PlatformExecutorWindows.swift +++ b/stdlib/public/Concurrency/PlatformExecutorWindows.swift @@ -18,7 +18,8 @@ import Swift @available(SwiftStdlib 6.2, *) public struct PlatformExecutorFactory: ExecutorFactory { public static let mainExecutor: any MainExecutor = DispatchMainExecutor() - public static let defaultExecutor: any TaskExecutor = DispatchTaskExecutor() + public static let defaultExecutor: any TaskExecutor = + DispatchGlobalTaskExecutor() } #endif // os(Windows) diff --git a/test/Concurrency/Runtime/custom_main_executor.swift b/test/Concurrency/Runtime/custom_main_executor.swift index 645fed765686f..ddf55fb640ecb 100644 --- a/test/Concurrency/Runtime/custom_main_executor.swift +++ b/test/Concurrency/Runtime/custom_main_executor.swift @@ -20,7 +20,7 @@ struct SimpleExecutorFactory: ExecutorFactory { } public static var defaultExecutor: any TaskExecutor { print("Creating task executor") - return DispatchTaskExecutor() + return DispatchGlobalTaskExecutor() } } @@ -60,7 +60,7 @@ final class SimpleMainExecutor: MainExecutor, @unchecked Sendable { shouldStop = true } - func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { + func registerEvent(handler: @escaping @Sendable () -> ()) -> ExecutorEvent { return ExecutorEvent(id: 0) } diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index de2c7c75f7df7..c36c3dc1dec7a 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -474,12 +474,12 @@ _$ss11JobPriorityVMn _$ss11JobPriorityVN _$ss11JobPriorityVSLsMc _$ss11JobPriorityVSQsMc -_$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ _$ss12MainExecutorMp _$ss12MainExecutorPScfTb _$ss12MainExecutorPs07RunLoopB0Tb _$ss12MainExecutorPs09EventableB0Tb _$ss12MainExecutorTL +_$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ _$ss13ExecutorEventV1loiySbAB_ABtFZ _$ss13ExecutorEventV2eeoiySbAB_ABtFZ _$ss13ExecutorEventV2idABSi_tcfC @@ -629,8 +629,8 @@ _$ss16AsyncMapSequenceVyxq_GScisMc _$ss17EventableExecutorMp _$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTj _$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTq -_$ss17EventableExecutorP13registerEvent7handlers0bD0Vyyc_tFTj -_$ss17EventableExecutorP13registerEvent7handlers0bD0Vyyc_tFTq +_$ss17EventableExecutorP13registerEvent7handlers0bD0VyyYbc_tFTj +_$ss17EventableExecutorP13registerEvent7handlers0bD0VyyYbc_tFTq _$ss17EventableExecutorP6notify5eventys0B5EventV_tFTj _$ss17EventableExecutorP6notify5eventys0B5EventV_tFTq _$ss17EventableExecutorTL @@ -727,7 +727,7 @@ _$ss20DispatchMainExecutorC02isbC0Sbvg _$ss20DispatchMainExecutorC02isbC0SbvpMV _$ss20DispatchMainExecutorC10deregister5eventys0C5EventV_tF _$ss20DispatchMainExecutorC13checkIsolatedyyF -_$ss20DispatchMainExecutorC13registerEvent7handlers0cE0Vyyc_tF +_$ss20DispatchMainExecutorC13registerEvent7handlers0cE0VyyYbc_tF _$ss20DispatchMainExecutorC18supportsSchedulingSbvg _$ss20DispatchMainExecutorC18supportsSchedulingSbvpMV _$ss20DispatchMainExecutorC3runyyKFTj @@ -759,32 +759,6 @@ _$ss20DispatchMainExecutorCs09EventableC0sMc _$ss20DispatchMainExecutorCs09EventableC0sWP _$ss20DispatchMainExecutorCs0bC0sMc _$ss20DispatchMainExecutorCs0bC0sWP -_$ss20DispatchTaskExecutorC06isMainC0SbvgTj -_$ss20DispatchTaskExecutorC06isMainC0SbvgTq -_$ss20DispatchTaskExecutorC06isMainC0SbvpMV -_$ss20DispatchTaskExecutorC18supportsSchedulingSbvgTj -_$ss20DispatchTaskExecutorC18supportsSchedulingSbvgTq -_$ss20DispatchTaskExecutorC18supportsSchedulingSbvpMV -_$ss20DispatchTaskExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj -_$ss20DispatchTaskExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq -_$ss20DispatchTaskExecutorC7enqueueyys0C3JobVnFTj -_$ss20DispatchTaskExecutorC7enqueueyys0C3JobVnFTq -_$ss20DispatchTaskExecutorCABycfC -_$ss20DispatchTaskExecutorCABycfCTj -_$ss20DispatchTaskExecutorCABycfCTq -_$ss20DispatchTaskExecutorCABycfc -_$ss20DispatchTaskExecutorCMa -_$ss20DispatchTaskExecutorCMm -_$ss20DispatchTaskExecutorCMn -_$ss20DispatchTaskExecutorCMo -_$ss20DispatchTaskExecutorCMu -_$ss20DispatchTaskExecutorCN -_$ss20DispatchTaskExecutorCScFsMc -_$ss20DispatchTaskExecutorCScFsWP -_$ss20DispatchTaskExecutorCSchsMc -_$ss20DispatchTaskExecutorCSchsWP -_$ss20DispatchTaskExecutorCfD -_$ss20DispatchTaskExecutorCfd _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lF _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lFTu _$ss21withUnsafeCurrentTask4bodyxxSctSgKXE_tKlF @@ -920,6 +894,32 @@ _$ss24AsyncThrowingMapSequenceVMa _$ss24AsyncThrowingMapSequenceVMn _$ss24AsyncThrowingMapSequenceV_9transformAByxq_Gx_q_7ElementQzYaKctcfC _$ss24AsyncThrowingMapSequenceVyxq_GScisMc +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTj +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTq +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvpMV +_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvgTj +_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvgTq +_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvpMV +_$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj +_$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq +_$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTj +_$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTq +_$ss26DispatchGlobalTaskExecutorCABycfC +_$ss26DispatchGlobalTaskExecutorCABycfCTj +_$ss26DispatchGlobalTaskExecutorCABycfCTq +_$ss26DispatchGlobalTaskExecutorCABycfc +_$ss26DispatchGlobalTaskExecutorCMa +_$ss26DispatchGlobalTaskExecutorCMm +_$ss26DispatchGlobalTaskExecutorCMn +_$ss26DispatchGlobalTaskExecutorCMo +_$ss26DispatchGlobalTaskExecutorCMu +_$ss26DispatchGlobalTaskExecutorCN +_$ss26DispatchGlobalTaskExecutorCScFsMc +_$ss26DispatchGlobalTaskExecutorCScFsWP +_$ss26DispatchGlobalTaskExecutorCSchsMc +_$ss26DispatchGlobalTaskExecutorCSchsWP +_$ss26DispatchGlobalTaskExecutorCfD +_$ss26DispatchGlobalTaskExecutorCfd _$ss26_enqueueJobGlobalWithDelayyys6UInt64V_ScJtF _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index de2c7c75f7df7..c36c3dc1dec7a 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -474,12 +474,12 @@ _$ss11JobPriorityVMn _$ss11JobPriorityVN _$ss11JobPriorityVSLsMc _$ss11JobPriorityVSQsMc -_$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ _$ss12MainExecutorMp _$ss12MainExecutorPScfTb _$ss12MainExecutorPs07RunLoopB0Tb _$ss12MainExecutorPs09EventableB0Tb _$ss12MainExecutorTL +_$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ _$ss13ExecutorEventV1loiySbAB_ABtFZ _$ss13ExecutorEventV2eeoiySbAB_ABtFZ _$ss13ExecutorEventV2idABSi_tcfC @@ -629,8 +629,8 @@ _$ss16AsyncMapSequenceVyxq_GScisMc _$ss17EventableExecutorMp _$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTj _$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTq -_$ss17EventableExecutorP13registerEvent7handlers0bD0Vyyc_tFTj -_$ss17EventableExecutorP13registerEvent7handlers0bD0Vyyc_tFTq +_$ss17EventableExecutorP13registerEvent7handlers0bD0VyyYbc_tFTj +_$ss17EventableExecutorP13registerEvent7handlers0bD0VyyYbc_tFTq _$ss17EventableExecutorP6notify5eventys0B5EventV_tFTj _$ss17EventableExecutorP6notify5eventys0B5EventV_tFTq _$ss17EventableExecutorTL @@ -727,7 +727,7 @@ _$ss20DispatchMainExecutorC02isbC0Sbvg _$ss20DispatchMainExecutorC02isbC0SbvpMV _$ss20DispatchMainExecutorC10deregister5eventys0C5EventV_tF _$ss20DispatchMainExecutorC13checkIsolatedyyF -_$ss20DispatchMainExecutorC13registerEvent7handlers0cE0Vyyc_tF +_$ss20DispatchMainExecutorC13registerEvent7handlers0cE0VyyYbc_tF _$ss20DispatchMainExecutorC18supportsSchedulingSbvg _$ss20DispatchMainExecutorC18supportsSchedulingSbvpMV _$ss20DispatchMainExecutorC3runyyKFTj @@ -759,32 +759,6 @@ _$ss20DispatchMainExecutorCs09EventableC0sMc _$ss20DispatchMainExecutorCs09EventableC0sWP _$ss20DispatchMainExecutorCs0bC0sMc _$ss20DispatchMainExecutorCs0bC0sWP -_$ss20DispatchTaskExecutorC06isMainC0SbvgTj -_$ss20DispatchTaskExecutorC06isMainC0SbvgTq -_$ss20DispatchTaskExecutorC06isMainC0SbvpMV -_$ss20DispatchTaskExecutorC18supportsSchedulingSbvgTj -_$ss20DispatchTaskExecutorC18supportsSchedulingSbvgTq -_$ss20DispatchTaskExecutorC18supportsSchedulingSbvpMV -_$ss20DispatchTaskExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj -_$ss20DispatchTaskExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq -_$ss20DispatchTaskExecutorC7enqueueyys0C3JobVnFTj -_$ss20DispatchTaskExecutorC7enqueueyys0C3JobVnFTq -_$ss20DispatchTaskExecutorCABycfC -_$ss20DispatchTaskExecutorCABycfCTj -_$ss20DispatchTaskExecutorCABycfCTq -_$ss20DispatchTaskExecutorCABycfc -_$ss20DispatchTaskExecutorCMa -_$ss20DispatchTaskExecutorCMm -_$ss20DispatchTaskExecutorCMn -_$ss20DispatchTaskExecutorCMo -_$ss20DispatchTaskExecutorCMu -_$ss20DispatchTaskExecutorCN -_$ss20DispatchTaskExecutorCScFsMc -_$ss20DispatchTaskExecutorCScFsWP -_$ss20DispatchTaskExecutorCSchsMc -_$ss20DispatchTaskExecutorCSchsWP -_$ss20DispatchTaskExecutorCfD -_$ss20DispatchTaskExecutorCfd _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lF _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lFTu _$ss21withUnsafeCurrentTask4bodyxxSctSgKXE_tKlF @@ -920,6 +894,32 @@ _$ss24AsyncThrowingMapSequenceVMa _$ss24AsyncThrowingMapSequenceVMn _$ss24AsyncThrowingMapSequenceV_9transformAByxq_Gx_q_7ElementQzYaKctcfC _$ss24AsyncThrowingMapSequenceVyxq_GScisMc +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTj +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTq +_$ss26DispatchGlobalTaskExecutorC06isMainD0SbvpMV +_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvgTj +_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvgTq +_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvpMV +_$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj +_$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq +_$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTj +_$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTq +_$ss26DispatchGlobalTaskExecutorCABycfC +_$ss26DispatchGlobalTaskExecutorCABycfCTj +_$ss26DispatchGlobalTaskExecutorCABycfCTq +_$ss26DispatchGlobalTaskExecutorCABycfc +_$ss26DispatchGlobalTaskExecutorCMa +_$ss26DispatchGlobalTaskExecutorCMm +_$ss26DispatchGlobalTaskExecutorCMn +_$ss26DispatchGlobalTaskExecutorCMo +_$ss26DispatchGlobalTaskExecutorCMu +_$ss26DispatchGlobalTaskExecutorCN +_$ss26DispatchGlobalTaskExecutorCScFsMc +_$ss26DispatchGlobalTaskExecutorCScFsWP +_$ss26DispatchGlobalTaskExecutorCSchsMc +_$ss26DispatchGlobalTaskExecutorCSchsWP +_$ss26DispatchGlobalTaskExecutorCfD +_$ss26DispatchGlobalTaskExecutorCfd _$ss26_enqueueJobGlobalWithDelayyys6UInt64V_ScJtF _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg From fc67cc3b60759a747658735d25abb0871da6e495 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 11 Mar 2025 17:02:37 +0000 Subject: [PATCH 10/29] [Concurrency] Mark expressions as `unsafe`. Also fix a missing availability annotation. rdar://141348916 --- stdlib/public/Concurrency/CFExecutor.swift | 14 ++++----- stdlib/public/Concurrency/Clock.swift | 1 + .../public/Concurrency/DispatchExecutor.swift | 12 +++---- stdlib/public/Concurrency/Executor.swift | 27 ++++++++-------- .../public/Concurrency/ExecutorBridge.swift | 14 ++++----- stdlib/public/Concurrency/ExecutorImpl.swift | 4 +-- .../GlobalConcurrentExecutor.swift | 2 +- .../public/Concurrency/PartialAsyncTask.swift | 31 ++++++++++--------- .../Concurrency/Task+TaskExecutor.swift | 6 ++-- 9 files changed, 57 insertions(+), 54 deletions(-) diff --git a/stdlib/public/Concurrency/CFExecutor.swift b/stdlib/public/Concurrency/CFExecutor.swift index c12701a6872a9..451e0860e7975 100644 --- a/stdlib/public/Concurrency/CFExecutor.swift +++ b/stdlib/public/Concurrency/CFExecutor.swift @@ -22,23 +22,23 @@ enum CoreFoundation { static let path = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation" - static let handle = dlopen(path, RTLD_NOLOAD) + static let handle = unsafe dlopen(path, RTLD_NOLOAD) - static var isPresent: Bool { return handle != nil } + static var isPresent: Bool { return unsafe handle != nil } static func symbol(_ name: String) -> T { - guard let result = dlsym(handle, name) else { + guard let result = unsafe dlsym(handle, name) else { fatalError("Unable to look up \(name) in CoreFoundation") } - return unsafeBitCast(result, to: T.self) + return unsafe unsafeBitCast(result, to: T.self) } static let CFRunLoopRun: @convention(c) () -> () = symbol("CFRunLoopRun") static let CFRunLoopGetMain: @convention(c) () -> OpaquePointer = - symbol("CFRunLoopGetMain") + unsafe symbol("CFRunLoopGetMain") static let CFRunLoopStop: @convention(c) (OpaquePointer) -> () = - symbol("CFRunLoopStop") + unsafe symbol("CFRunLoopStop") } // .. Main Executor ............................................................ @@ -51,7 +51,7 @@ public final class CFMainExecutor: DispatchMainExecutor, @unchecked Sendable { } override public func stop() { - CoreFoundation.CFRunLoopStop(CoreFoundation.CFRunLoopGetMain()) + unsafe CoreFoundation.CFRunLoopStop(CoreFoundation.CFRunLoopGetMain()) } } diff --git a/stdlib/public/Concurrency/Clock.swift b/stdlib/public/Concurrency/Clock.swift index 2995be4ed4d3a..cf373fd2d1883 100644 --- a/stdlib/public/Concurrency/Clock.swift +++ b/stdlib/public/Concurrency/Clock.swift @@ -216,6 +216,7 @@ public struct ClockTraits: OptionSet { public static let wallTime = ClockTraits(rawValue: 1 << 2) } +@available(SwiftStdlib 6.2, *) extension Clock { /// The traits associated with this clock instance. @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift index 1c88c43caef32..cebdedc650a7a 100644 --- a/stdlib/public/Concurrency/DispatchExecutor.swift +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -87,25 +87,25 @@ extension DispatchMainExecutor: EventableExecutor { public func registerEvent( handler: @escaping @Sendable () -> () ) -> ExecutorEvent { - let source = _createDispatchEvent(handler: handler) + let source = unsafe _createDispatchEvent(handler: handler) // Stash the pointer in the id of the ExecutorEvent struct - let eventId = unsafeBitCast(source, to: Int.self) + let eventId = unsafe unsafeBitCast(source, to: Int.self) return ExecutorEvent(id: eventId) } /// Deregister the given event. public func deregister(event: ExecutorEvent) { // Extract the source and cancel it - let source = unsafeBitCast(event.id, to: OpaquePointer.self) - _destroyDispatchEvent(source) + let source = unsafe unsafeBitCast(event.id, to: OpaquePointer.self) + unsafe _destroyDispatchEvent(source) } /// Notify the executor of an event. public func notify(event: ExecutorEvent) { // Extract the source, but don't release it - let source = unsafeBitCast(event.id, to: OpaquePointer.self) - _signalDispatchEvent(source) + let source = unsafe unsafeBitCast(event.id, to: OpaquePointer.self) + unsafe _signalDispatchEvent(source) } } diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index b73b1b8849acd..6e4e46f97ee2d 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -418,7 +418,7 @@ public protocol TaskExecutor: Executor { @available(SwiftStdlib 6.0, *) extension TaskExecutor { public func asUnownedTaskExecutor() -> UnownedTaskExecutor { - UnownedTaskExecutor(ordinary: self) + unsafe UnownedTaskExecutor(ordinary: self) } } @@ -648,11 +648,11 @@ extension Task where Success == Never, Failure == Never { @available(SwiftStdlib 6.2, *) @_unavailableInEmbedded public static var currentExecutor: (any Executor)? { - if let taskExecutor = _getPreferredTaskExecutor().asTaskExecutor() { + if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() { return taskExecutor - } else if let activeExecutor = _getActiveExecutor().asSerialExecutor() { + } else if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor() { return activeExecutor - } else if let taskExecutor = _getCurrentTaskExecutor().asTaskExecutor() { + } else if let taskExecutor = unsafe _getCurrentTaskExecutor().asTaskExecutor() { return taskExecutor } return nil @@ -717,9 +717,9 @@ public struct UnownedSerialExecutor: Sendable { @inlinable public init(_ executor: __shared E) { if executor._isComplexEquality { - self.executor = Builtin.buildComplexEqualitySerialExecutorRef(executor) + unsafe self.executor = Builtin.buildComplexEqualitySerialExecutorRef(executor) } else { - self.executor = Builtin.buildOrdinarySerialExecutorRef(executor) + unsafe self.executor = Builtin.buildOrdinarySerialExecutorRef(executor) } } @@ -731,12 +731,13 @@ public struct UnownedSerialExecutor: Sendable { @available(SwiftStdlib 6.2, *) public func asSerialExecutor() -> (any SerialExecutor)? { - return unsafeBitCast(executor, to: (any SerialExecutor)?.self) + return unsafe unsafeBitCast(executor, to: (any SerialExecutor)?.self) } } @available(SwiftStdlib 6.0, *) +@unsafe @frozen public struct UnownedTaskExecutor: Sendable { @usableFromInline @@ -746,28 +747,28 @@ public struct UnownedTaskExecutor: Sendable { /// which needs to reach for this from an @_transparent function which prevents @_spi use. @available(SwiftStdlib 6.0, *) public var _executor: Builtin.Executor { - self.executor + unsafe self.executor } @inlinable public init(_ executor: Builtin.Executor) { - self.executor = executor + unsafe self.executor = executor } @inlinable public init(ordinary executor: __shared E) { - self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) + unsafe self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) } @available(SwiftStdlib 6.2, *) @inlinable public init(_ executor: __shared E) { - self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) + unsafe self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor) } @available(SwiftStdlib 6.2, *) public func asTaskExecutor() -> (any TaskExecutor)? { - return unsafeBitCast(executor, to: (any TaskExecutor)?.self) + return unsafe unsafeBitCast(executor, to: (any TaskExecutor)?.self) } } @@ -869,7 +870,7 @@ internal func _task_serialExecutor_getExecutorRef(_ executor: E) -> Builtin.E @_silgen_name("_task_taskExecutor_getTaskExecutorRef") internal func _task_taskExecutor_getTaskExecutorRef(_ taskExecutor: E) -> Builtin.Executor where E: TaskExecutor { - return taskExecutor.asUnownedTaskExecutor().executor + return unsafe taskExecutor.asUnownedTaskExecutor().executor } // Used by the concurrency runtime diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift index bbd8f06ad8501..95a0cdbcecb49 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.swift +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -130,11 +130,11 @@ fileprivate class DispatchEventHandlerBox { @available(SwiftStdlib 6.2, *) internal func _createDispatchEvent(handler: @escaping @Sendable () -> ()) -> OpaquePointer { let boxed = DispatchEventHandlerBox(handler: handler) - let opaqueHandlerBox = Unmanaged.passRetained(boxed).toOpaque() - return _createDispatchEventC( + let opaqueHandlerBox = unsafe Unmanaged.passRetained(boxed).toOpaque() + return unsafe _createDispatchEventC( handler: { context in - let unmanaged = Unmanaged.fromOpaque(context) - unmanaged.takeUnretainedValue().handler() + let unmanaged = unsafe Unmanaged.fromOpaque(context) + unsafe unmanaged.takeUnretainedValue().handler() }, context: opaqueHandlerBox ) @@ -150,9 +150,9 @@ internal func _getDispatchEventContext(_ event: OpaquePointer) -> UnsafeMutableR @available(SwiftStdlib 6.2, *) internal func _destroyDispatchEvent(_ event: OpaquePointer) { - let context = _getDispatchEventContext(event) - Unmanaged.fromOpaque(context).release() - _destroyDispatchEventC(event) + let context = unsafe _getDispatchEventContext(event) + unsafe Unmanaged.fromOpaque(context).release() + unsafe _destroyDispatchEventC(event) } @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/ExecutorImpl.swift b/stdlib/public/Concurrency/ExecutorImpl.swift index e888394d9906c..3c44758578c19 100644 --- a/stdlib/public/Concurrency/ExecutorImpl.swift +++ b/stdlib/public/Concurrency/ExecutorImpl.swift @@ -34,7 +34,7 @@ internal func dontateToGlobalExecutor( context: UnsafeMutableRawPointer ) { if let runnableExecutor = Task.defaultExecutor as? RunLoopExecutor { - try! runnableExecutor.run(until: { Bool(condition(context)) }) + try! runnableExecutor.run(until: { unsafe Bool(condition(context)) }) } else { fatalError("Global executor does not support thread donation") } @@ -43,7 +43,7 @@ internal func dontateToGlobalExecutor( @available(SwiftStdlib 6.2, *) @_silgen_name("swift_task_getMainExecutorImpl") internal func getMainExecutor() -> UnownedSerialExecutor { - return UnownedSerialExecutor(MainActor.executor) + return unsafe UnownedSerialExecutor(MainActor.executor) } @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift b/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift index 1ca789ce117d1..7c876a99472d1 100644 --- a/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift +++ b/stdlib/public/Concurrency/GlobalConcurrentExecutor.swift @@ -62,7 +62,7 @@ internal final class _DefaultGlobalConcurrentExecutor: TaskExecutor { // We represent it as the `(0, 0)` ExecutorRef and it is handled properly // by the runtime, without having to call through to the // `_DefaultGlobalConcurrentExecutor` declared in Swift. - UnownedTaskExecutor(_getUndefinedTaskExecutor()) + unsafe UnownedTaskExecutor(_getUndefinedTaskExecutor()) } } diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift index 627bc2f915f4c..f6c4dfed6e288 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -149,7 +149,7 @@ public struct UnownedJob: Sendable { @_alwaysEmitIntoClient @inlinable public func runSynchronously(on executor: UnownedTaskExecutor) { - _swiftJobRunOnTaskExecutor(self, executor) + unsafe _swiftJobRunOnTaskExecutor(self, executor) } /// Run this job isolated to the passed in serial executor, while executing it on the specified task executor. @@ -324,9 +324,10 @@ public struct ExecutorJob: Sendable, ~Copyable { /// Returns the result of executing the closure. @available(SwiftStdlib 6.2, *) public func withUnsafeExecutorPrivateData(body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R { - let base = _jobGetExecutorPrivateData(self.context) - let size = 2 * MemoryLayout.stride - return try body(UnsafeMutableRawBufferPointer(start: base, count: size)) + let base = unsafe _jobGetExecutorPrivateData(self.context) + let size = unsafe 2 * MemoryLayout.stride + return unsafe try body(UnsafeMutableRawBufferPointer(start: base, + count: size)) } /// Kinds of schedulable jobs @@ -418,7 +419,7 @@ extension ExecutorJob { @_alwaysEmitIntoClient @inlinable __consuming public func runSynchronously(on executor: UnownedTaskExecutor) { - _swiftJobRunOnTaskExecutor(UnownedJob(self), executor) + unsafe _swiftJobRunOnTaskExecutor(UnownedJob(self), executor) } /// Run this job isolated to the passed in serial executor, while executing it on the specified task executor. @@ -476,44 +477,44 @@ extension ExecutorJob { /// Allocate a specified number of bytes of uninitialized memory. public func allocate(capacity: Int) -> UnsafeMutableRawBufferPointer { - let base = _jobAllocate(context, capacity) - return UnsafeMutableRawBufferPointer(start: base, count: capacity) + let base = unsafe _jobAllocate(context, capacity) + return unsafe UnsafeMutableRawBufferPointer(start: base, count: capacity) } /// Allocate uninitialized memory for a single instance of type `T`. public func allocate(as type: T.Type) -> UnsafeMutablePointer { - let base = _jobAllocate(context, MemoryLayout.size) - return base.bindMemory(to: type, capacity: 1) + let base = unsafe _jobAllocate(context, MemoryLayout.size) + return unsafe base.bindMemory(to: type, capacity: 1) } /// Allocate uninitialized memory for the specified number of /// instances of type `T`. public func allocate(capacity: Int, as type: T.Type) -> UnsafeMutableBufferPointer { - let base = _jobAllocate(context, MemoryLayout.stride * capacity) - let typedBase = base.bindMemory(to: T.self, capacity: capacity) - return UnsafeMutableBufferPointer(start: typedBase, count: capacity) + let base = unsafe _jobAllocate(context, MemoryLayout.stride * capacity) + let typedBase = unsafe base.bindMemory(to: T.self, capacity: capacity) + return unsafe UnsafeMutableBufferPointer(start: typedBase, count: capacity) } /// Deallocate previously allocated memory. Note that the task /// allocator is stack disciplined, so if you deallocate a block of /// memory, all memory allocated after that block is also deallocated. public func deallocate(_ buffer: UnsafeMutableRawBufferPointer) { - _jobDeallocate(context, buffer.baseAddress!) + unsafe _jobDeallocate(context, buffer.baseAddress!) } /// Deallocate previously allocated memory. Note that the task /// allocator is stack disciplined, so if you deallocate a block of /// memory, all memory allocated after that block is also deallocated. public func deallocate(_ pointer: UnsafeMutablePointer) { - _jobDeallocate(context, UnsafeMutableRawPointer(pointer)) + unsafe _jobDeallocate(context, UnsafeMutableRawPointer(pointer)) } /// Deallocate previously allocated memory. Note that the task /// allocator is stack disciplined, so if you deallocate a block of /// memory, all memory allocated after that block is also deallocated. public func deallocate(_ buffer: UnsafeMutableBufferPointer) { - _jobDeallocate(context, UnsafeMutableRawPointer(buffer.baseAddress!)) + unsafe _jobDeallocate(context, UnsafeMutableRawPointer(buffer.baseAddress!)) } } diff --git a/stdlib/public/Concurrency/Task+TaskExecutor.swift b/stdlib/public/Concurrency/Task+TaskExecutor.swift index fd9c409e968db..611fe9265630f 100644 --- a/stdlib/public/Concurrency/Task+TaskExecutor.swift +++ b/stdlib/public/Concurrency/Task+TaskExecutor.swift @@ -146,7 +146,7 @@ public func withTaskExecutorPreference( } let taskExecutorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor + unsafe taskExecutor.asUnownedTaskExecutor().executor let record = unsafe _pushTaskExecutorPreference(taskExecutorBuiltin) defer { @@ -177,7 +177,7 @@ public func _unsafeInheritExecutor_withTaskExecutorPreference( } let taskExecutorBuiltin: Builtin.Executor = - taskExecutor.asUnownedTaskExecutor().executor + unsafe taskExecutor.asUnownedTaskExecutor().executor let record = unsafe _pushTaskExecutorPreference(taskExecutorBuiltin) defer { @@ -458,7 +458,7 @@ extension UnsafeCurrentTask { @available(SwiftStdlib 6.0, *) public var unownedTaskExecutor: UnownedTaskExecutor? { let ref = _getPreferredUnownedTaskExecutor() - return UnownedTaskExecutor(ref) + return unsafe UnownedTaskExecutor(ref) } } From 23d0ca7a6a16d954dd6596cc9fd4b0ea3224a0c0 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 11 Mar 2025 17:05:19 +0000 Subject: [PATCH 11/29] [Concurrency] Update header file. This needed an update to make things build on Linux and Windows. rdar://141348916 --- stdlib/public/Concurrency/ExecutorBridge.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stdlib/public/Concurrency/ExecutorBridge.h b/stdlib/public/Concurrency/ExecutorBridge.h index a4e1d7ed9c20f..aa2f720eb264f 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.h +++ b/stdlib/public/Concurrency/ExecutorBridge.h @@ -23,15 +23,17 @@ namespace swift { extern "C" SWIFT_CC(swift) SerialExecutorRef swift_getMainExecutor(); +#if !SWIFT_CONCURRENCY_EMBEDDED extern "C" SWIFT_CC(swift) -void *swift_createDispatchEvent(void (^handler)()); +void *swift_createDispatchEventC(void (*handler)(void *), void *context); extern "C" SWIFT_CC(swift) -void swift_destroyDispatchEvent(void *event); +void swift_destroyDispatchEventC(void *event); extern "C" SWIFT_CC(swift) void swift_signalDispatchEvent(void *event); - +#endif // !SWIFT_CONCURRENCY_EMBEDDED + extern "C" SWIFT_CC(swift) __attribute__((noreturn)) void swift_dispatchMain(); From 444bbd5b00a2bcb363655875fd2010a4288ae41a Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 12 Mar 2025 13:28:24 +0000 Subject: [PATCH 12/29] [Concurrency] Update following pitch comments. Remove `supportsScheduling` in favour of a type-based approach. Update the storage for `ClockTraits` to `UInt32`. Adjust ordering of executors for `currentExecutor`. rdar://141348916 --- stdlib/public/Concurrency/Clock.swift | 4 +- .../public/Concurrency/DispatchExecutor.swift | 20 +-- stdlib/public/Concurrency/Executor.swift | 126 +++++++++++++----- stdlib/public/Concurrency/ExecutorImpl.swift | 24 ++-- .../PlatformExecutorEmbedded.swift | 7 - stdlib/public/Concurrency/Task.swift | 11 ++ stdlib/public/Concurrency/TaskSleep.swift | 18 +-- .../Concurrency/TaskSleepDuration.swift | 10 +- .../async_task_executor_nsobject.swift | 4 +- test/Concurrency/Runtime/sleep_executor.swift | 4 +- .../macOS/arm64/concurrency/baseline-asserts | 9 -- .../macOS/x86_64/concurrency/baseline-asserts | 9 -- 12 files changed, 149 insertions(+), 97 deletions(-) diff --git a/stdlib/public/Concurrency/Clock.swift b/stdlib/public/Concurrency/Clock.swift index cf373fd2d1883..c2cdc30771363 100644 --- a/stdlib/public/Concurrency/Clock.swift +++ b/stdlib/public/Concurrency/Clock.swift @@ -200,9 +200,9 @@ extension Clock { /// basis. @available(SwiftStdlib 6.2, *) public struct ClockTraits: OptionSet { - public let rawValue: Int32 + public let rawValue: UInt32 - public init(rawValue: Int32) { + public init(rawValue: UInt32) { self.rawValue = rawValue } diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift index cebdedc650a7a..4f267632e9163 100644 --- a/stdlib/public/Concurrency/DispatchExecutor.swift +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -25,6 +25,8 @@ import Swift @available(SwiftStdlib 6.2, *) public class DispatchMainExecutor: RunLoopExecutor, @unchecked Sendable { + public typealias AsSchedulable = DispatchMainExecutor + var threaded = false public init() {} @@ -52,8 +54,13 @@ extension DispatchMainExecutor: SerialExecutor { public var isMainExecutor: Bool { true } - public var supportsScheduling: Bool { true } + public func checkIsolated() { + _dispatchAssertMainQueue() + } +} +@available(SwiftStdlib 6.2, *) +extension DispatchMainExecutor: SchedulableExecutor { public func enqueue(_ job: consuming ExecutorJob, at instant: C.Instant, tolerance: C.Duration? = nil, @@ -74,10 +81,6 @@ extension DispatchMainExecutor: SerialExecutor { clockID.rawValue, UnownedJob(job)) } - - public func checkIsolated() { - _dispatchAssertMainQueue() - } } @available(SwiftStdlib 6.2, *) @@ -116,7 +119,10 @@ extension DispatchMainExecutor: MainExecutor {} // .. Task Executor ............................................................ @available(SwiftStdlib 6.2, *) -public class DispatchGlobalTaskExecutor: TaskExecutor, @unchecked Sendable { +public class DispatchGlobalTaskExecutor: TaskExecutor, SchedulableExecutor, + @unchecked Sendable { + + public typealias AsSchedulable = DispatchGlobalTaskExecutor public init() {} @@ -126,8 +132,6 @@ public class DispatchGlobalTaskExecutor: TaskExecutor, @unchecked Sendable { public var isMainExecutor: Bool { false } - public var supportsScheduling: Bool { true } - public func enqueue(_ job: consuming ExecutorJob, at instant: C.Instant, tolerance: C.Duration? = nil, diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index 6e4e46f97ee2d..7ece65d1ac271 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -16,6 +16,13 @@ import Swift @available(SwiftStdlib 5.1, *) public protocol Executor: AnyObject, Sendable { + /// This Executor type as a schedulable executor. + /// + /// If the conforming type also conforms to `SchedulableExecutor`, then this is + /// bound to `Self`. Otherwise, it is an uninhabited type (such as Never). + @available(SwiftStdlib 6.2, *) + associatedtype AsSchedulable: SchedulableExecutor = SchedulableExecutorNever + // Since lack move-only type support in the SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY configuration // Do not deprecate the UnownedJob enqueue in that configuration just yet - as we cannot introduce the replacements. #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @@ -46,16 +53,25 @@ public protocol Executor: AnyObject, Sendable { @available(SwiftStdlib 6.2, *) var isMainExecutor: Bool { get } #endif +} - /// `true` if this Executor supports scheduling. - /// - /// This will default to false. If you attempt to use the delayed - /// enqueuing functions on an executor that does not support scheduling, - /// the default executor will be used to do the scheduling instead, - /// unless the default executor does not support scheduling in which - /// case you will get a fatal error. - @available(SwiftStdlib 6.2, *) - var supportsScheduling: Bool { get } +/// Uninhabited class type to indicate we don't support scheduling. +@available(SwiftStdlib 6.2, *) +public class SchedulableExecutorNever { + private init(_ n: Never) {} +} + +@available(SwiftStdlib 6.2, *) +extension SchedulableExecutorNever: SchedulableExecutor, @unchecked Sendable { + + public func enqueue(_ job: consuming ExecutorJob) { + fatalError("This should never be reached") + } + +} + +@available(SwiftStdlib 6.2, *) +public protocol SchedulableExecutor: Executor where Self.AsSchedulable == Self { #if !$Embedded @@ -101,6 +117,24 @@ public protocol Executor: AnyObject, Sendable { clock: C) #endif // !$Embedded + +} + +extension Executor { + /// Return this executable as a SchedulableExecutor, or nil if that is + /// unsupported. + @available(SwiftStdlib 6.2, *) + var asSchedulable: AsSchedulable? { + #if !$Embedded + if Self.self == AsSchedulable.self { + return _identityCast(self, to: AsSchedulable.self) + } else { + return nil + } + #else + return nil + #endif + } } extension Executor { @@ -115,7 +149,6 @@ extension Executor where Self: Equatable { internal var _isComplexEquality: Bool { true } } -// Delay support extension Executor { #if !$Embedded @@ -125,10 +158,11 @@ extension Executor { public var isMainExecutor: Bool { false } #endif - // This defaults to `false` so that existing third-party TaskExecutor - // implementations will work as expected. - @available(SwiftStdlib 6.2, *) - public var supportsScheduling: Bool { false } +} + +// Delay support +@available(SwiftStdlib 6.2, *) +extension SchedulableExecutor { #if !$Embedded @@ -137,10 +171,6 @@ extension Executor { after delay: C.Duration, tolerance: C.Duration? = nil, clock: C) { - if !supportsScheduling { - fatalError("Executor \(self) does not support scheduling") - } - // If you crash here with a mutual recursion, it's because you didn't // implement one of these two functions enqueue(job, at: clock.now.advanced(by: delay), @@ -152,10 +182,6 @@ extension Executor { at instant: C.Instant, tolerance: C.Duration? = nil, clock: C) { - if !supportsScheduling { - fatalError("Executor \(self) does not support scheduling") - } - // If you crash here with a mutual recursion, it's because you didn't // implement one of these two functions enqueue(job, after: clock.now.duration(to: instant), @@ -493,9 +519,9 @@ public protocol RunLoopExecutor: Executor { /// /// Parameters: /// - /// - until condition: A closure that returns `true` if the run loop should - /// stop. - func run(until condition: () -> Bool) throws + /// - condition: A closure that returns `true` if the run loop should + /// stop. + func runUntil(_ condition: () -> Bool) throws /// Signal to the run loop to stop running and return. /// @@ -517,9 +543,7 @@ extension RunLoopExecutor { } -/// Represents an event; we don't want to allocate, so we can't use -/// a protocol here and use `any Event`. Instead of doing that, wrap -/// an `Int` (which is pointer-sized) in a `struct`. +/// Represents an event registered with an `EventableExecutor`. @available(SwiftStdlib 6.2, *) public struct ExecutorEvent: Identifiable, Comparable, Sendable { public typealias ID = Int @@ -645,18 +669,58 @@ extension Task where Success == Never, Failure == Never { extension Task where Success == Never, Failure == Never { /// Get the current executor; this is the executor that the currently /// executing task is executing on. + /// + /// This will return, in order of preference: + /// + /// 1. The custom executor associated with an `Actor` on which we are + /// currently running, or + /// 2. The preferred executor for the currently executing `Task`, or + /// 3. The task executor for the current thread + /// + /// If none of these exist, this property will be `nil`. @available(SwiftStdlib 6.2, *) @_unavailableInEmbedded public static var currentExecutor: (any Executor)? { - if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() { - return taskExecutor - } else if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor() { + if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor() { return activeExecutor + } else if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() { + return taskExecutor } else if let taskExecutor = unsafe _getCurrentTaskExecutor().asTaskExecutor() { return taskExecutor } return nil } + + /// Get the preferred executor for the current `Task`, if any. + @available(SwiftStdlib 6.2, *) + public static var preferredExecutor: (any TaskExecutor)? { + if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() { + return taskExecutor + } + return nil + } + + /// Get the current *schedulable* executor, if any. + /// + /// This follows the same logic as `currentExecutor`, except that it ignores + /// any executor that isn't a `SchedulableExecutor`. + @available(SwiftStdlib 6.2, *) + @_unavailableInEmbedded + public static var currentSchedulableExecutor: (any SchedulableExecutor)? { + if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor(), + let schedulable = activeExecutor.asSchedulable { + return schedulable + } + if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor(), + let schedulable = taskExecutor.asSchedulable { + return schedulable + } + if let taskExecutor = unsafe _getCurrentTaskExecutor().asTaskExecutor(), + let schedulable = taskExecutor.asSchedulable { + return schedulable + } + return nil + } } diff --git a/stdlib/public/Concurrency/ExecutorImpl.swift b/stdlib/public/Concurrency/ExecutorImpl.swift index 3c44758578c19..c396d6823903b 100644 --- a/stdlib/public/Concurrency/ExecutorImpl.swift +++ b/stdlib/public/Concurrency/ExecutorImpl.swift @@ -34,7 +34,7 @@ internal func dontateToGlobalExecutor( context: UnsafeMutableRawPointer ) { if let runnableExecutor = Task.defaultExecutor as? RunLoopExecutor { - try! runnableExecutor.run(until: { unsafe Bool(condition(context)) }) + try! runnableExecutor.runUntil { unsafe Bool(condition(context)) } } else { fatalError("Global executor does not support thread donation") } @@ -63,9 +63,9 @@ internal func enqueueOnGlobalExecutor(job unownedJob: UnownedJob) { @_silgen_name("swift_task_enqueueGlobalWithDelayImpl") internal func enqueueOnGlobalExecutor(delay: CUnsignedLongLong, job unownedJob: UnownedJob) { - Task.defaultExecutor.enqueue(ExecutorJob(unownedJob), - after: .nanoseconds(delay), - clock: .continuous) + Task.defaultExecutor.asSchedulable!.enqueue(ExecutorJob(unownedJob), + after: .nanoseconds(delay), + clock: .continuous) } @available(SwiftStdlib 6.2, *) @@ -80,15 +80,15 @@ internal func enqueueOnGlobalExecutor(seconds: CLongLong, let leeway = Duration.seconds(leewaySeconds) + Duration.nanoseconds(leewayNanoseconds) switch clock { case _ClockID.suspending.rawValue: - Task.defaultExecutor.enqueue(ExecutorJob(unownedJob), - after: delay, - tolerance: leeway, - clock: .suspending) + Task.defaultExecutor.asSchedulable!.enqueue(ExecutorJob(unownedJob), + after: delay, + tolerance: leeway, + clock: .suspending) case _ClockID.continuous.rawValue: - Task.defaultExecutor.enqueue(ExecutorJob(unownedJob), - after: delay, - tolerance: leeway, - clock: .continuous) + Task.defaultExecutor.asSchedulable!.enqueue(ExecutorJob(unownedJob), + after: delay, + tolerance: leeway, + clock: .continuous) default: fatalError("Unknown clock ID \(clock)") } diff --git a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift index 437cbbbfb9dd0..7652abc88d1ac 100644 --- a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift +++ b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift @@ -31,8 +31,6 @@ public final class EmbeddedMainExecutor: MainExecutor, @unchecked Sendable { // We can't implement enqueue in Embedde Swift because we aren't // allowed to have generics in an existential there. - public var supportsScheduling: Bool { false } - public func run() throws { } @@ -64,9 +62,4 @@ public final class EmbeddedDefaultExecutor: TaskExecutor, @unchecked Sendable { public func enqueue(_ job: consuming ExecutorJob) { } - // We can't implement enqueue in Embedde Swift because we aren't - // allowed to have generics in an existential there. - - public var supportsScheduling: Bool { false } - } diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 576c3a4d125c8..e8481dbb22f12 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -1253,7 +1253,18 @@ extension Task where Success == Never, Failure == Never { let job = _taskCreateNullaryContinuationJob( priority: Int(Task.currentPriority.rawValue), continuation: continuation) + + #if !$Embedded + if #available(SwiftStdlib 6.2, *) { + let executor = Task.currentExecutor ?? Task.defaultExecutor + + executor.enqueue(ExecutorJob(context: job)) + } else { + _enqueueJobGlobal(job) + } + #else _enqueueJobGlobal(job) + #endif } } } diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift index 911d4d3f77f9f..55e9a1b23cb13 100644 --- a/stdlib/public/Concurrency/TaskSleep.swift +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -29,12 +29,13 @@ extension Task where Success == Never, Failure == Never { continuation: continuation) if #available(SwiftStdlib 6.2, *) { - let executor = Task.currentExecutor ?? Task.defaultExecutor + let executor = Task.currentSchedulableExecutor + ?? Task.defaultExecutor.asSchdulable #if !$Embedded - executor.enqueue(ExecutorJob(context: job), - after: .nanoseconds(duration), - clock: .continuous) + executor!.enqueue(ExecutorJob(context: job), + after: .nanoseconds(duration), + clock: .continuous) #endif } else { // Since we're building the new version of the stdlib, we should @@ -270,12 +271,13 @@ extension Task where Success == Never, Failure == Never { } if #available(SwiftStdlib 6.2, *) { - let executor = Task.currentExecutor ?? Task.defaultExecutor + let executor = Task.currentSchedulableExecutor + ?? Task.defaultExecutor.asSchedulable let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) #if !$Embedded - executor.enqueue(job, - after: .nanoseconds(duration), - clock: .continuous) + executor!.enqueue(job, + after: .nanoseconds(duration), + clock: .continuous) #endif } else { // Shouldn't be able to get here diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift index 07073284d5b0c..396288eb9c1da 100644 --- a/stdlib/public/Concurrency/TaskSleepDuration.swift +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -55,14 +55,14 @@ extension Task where Success == Never, Failure == Never { } if #available(SwiftStdlib 6.2, *) { - let executor = Task.currentExecutor ?? Task.defaultExecutor + let executor = Task.currentSchdulableExecutor ?? Task.defaultExecutor.asSchedulable let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) #if !$Embedded - executor.enqueue(job, - at: instant, - tolerance: tolerance, - clock: clock) + executor!.enqueue(job, + at: instant, + tolerance: tolerance, + clock: clock) #endif } else { // Since we're building the new version of the stdlib, diff --git a/test/Concurrency/Runtime/async_task_executor_nsobject.swift b/test/Concurrency/Runtime/async_task_executor_nsobject.swift index 6031ef1a2d448..d652f7d246f01 100644 --- a/test/Concurrency/Runtime/async_task_executor_nsobject.swift +++ b/test/Concurrency/Runtime/async_task_executor_nsobject.swift @@ -22,7 +22,7 @@ import Darwin // This test specifically checks that our reference counting accounts for existence of // objective-c types as TaskExecutors -- which was a bug where we'd swift_release // obj-c excecutors by accident (rdar://131151645). -final class NSQueueTaskExecutor: NSData, TaskExecutor, @unchecked Sendable { +final class NSQueueTaskExecutor: NSData, TaskExecutor, SchedulableExecutor, @unchecked Sendable { public func enqueue(_ _job: consuming ExecutorJob) { let job = UnownedJob(_job) DispatchQueue.main.async { @@ -30,8 +30,6 @@ final class NSQueueTaskExecutor: NSData, TaskExecutor, @unchecked Sendable { } } - public var supportsScheduling: Bool { true } - public func enqueue(_ _job: consuming ExecutorJob, after delay: C.Duration, tolerance: C.Duration? = nil, diff --git a/test/Concurrency/Runtime/sleep_executor.swift b/test/Concurrency/Runtime/sleep_executor.swift index 87c5dbd27edb0..41847a9ecc108 100644 --- a/test/Concurrency/Runtime/sleep_executor.swift +++ b/test/Concurrency/Runtime/sleep_executor.swift @@ -20,7 +20,7 @@ actor MyActor { } @available(SwiftStdlib 6.2, *) -final class TestExecutor: TaskExecutor, @unchecked Sendable { +final class TestExecutor: TaskExecutor, SchedulableExecutor @unchecked Sendable { public func enqueue(_ _job: consuming ExecutorJob) { let job = UnownedJob(_job) DispatchQueue.main.async { @@ -28,8 +28,6 @@ final class TestExecutor: TaskExecutor, @unchecked Sendable { } } - public var supportsScheduling: Bool { true} - public func enqueue(_ _job: consuming ExecutorJob, after delay: C.Duration, tolerance: C.Duration? = nil, diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index c36c3dc1dec7a..d103c32092f78 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -25,8 +25,6 @@ _$sScEN _$sScEs5ErrorsMc _$sScF14isMainExecutorSbvgTj _$sScF14isMainExecutorSbvgTq -_$sScF18supportsSchedulingSbvgTj -_$sScF18supportsSchedulingSbvgTq _$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj _$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq _$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTj @@ -43,8 +41,6 @@ _$sScFsE14isMainExecutorSbvg _$sScFsE14isMainExecutorSbvpMV _$sScFsE18_isComplexEqualitySbvg _$sScFsE18_isComplexEqualitySbvpMV -_$sScFsE18supportsSchedulingSbvg -_$sScFsE18supportsSchedulingSbvpMV _$sScFsE7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF _$sScFsE7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lF _$sScFsE7enqueueyyScJF @@ -728,8 +724,6 @@ _$ss20DispatchMainExecutorC02isbC0SbvpMV _$ss20DispatchMainExecutorC10deregister5eventys0C5EventV_tF _$ss20DispatchMainExecutorC13checkIsolatedyyF _$ss20DispatchMainExecutorC13registerEvent7handlers0cE0VyyYbc_tF -_$ss20DispatchMainExecutorC18supportsSchedulingSbvg -_$ss20DispatchMainExecutorC18supportsSchedulingSbvpMV _$ss20DispatchMainExecutorC3runyyKFTj _$ss20DispatchMainExecutorC3runyyKFTq _$ss20DispatchMainExecutorC4stopyyFTj @@ -897,9 +891,6 @@ _$ss24AsyncThrowingMapSequenceVyxq_GScisMc _$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTj _$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTq _$ss26DispatchGlobalTaskExecutorC06isMainD0SbvpMV -_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvgTj -_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvgTq -_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvpMV _$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj _$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq _$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTj diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index c36c3dc1dec7a..d103c32092f78 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -25,8 +25,6 @@ _$sScEN _$sScEs5ErrorsMc _$sScF14isMainExecutorSbvgTj _$sScF14isMainExecutorSbvgTq -_$sScF18supportsSchedulingSbvgTj -_$sScF18supportsSchedulingSbvgTq _$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj _$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq _$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTj @@ -43,8 +41,6 @@ _$sScFsE14isMainExecutorSbvg _$sScFsE14isMainExecutorSbvpMV _$sScFsE18_isComplexEqualitySbvg _$sScFsE18_isComplexEqualitySbvpMV -_$sScFsE18supportsSchedulingSbvg -_$sScFsE18supportsSchedulingSbvpMV _$sScFsE7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF _$sScFsE7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lF _$sScFsE7enqueueyyScJF @@ -728,8 +724,6 @@ _$ss20DispatchMainExecutorC02isbC0SbvpMV _$ss20DispatchMainExecutorC10deregister5eventys0C5EventV_tF _$ss20DispatchMainExecutorC13checkIsolatedyyF _$ss20DispatchMainExecutorC13registerEvent7handlers0cE0VyyYbc_tF -_$ss20DispatchMainExecutorC18supportsSchedulingSbvg -_$ss20DispatchMainExecutorC18supportsSchedulingSbvpMV _$ss20DispatchMainExecutorC3runyyKFTj _$ss20DispatchMainExecutorC3runyyKFTq _$ss20DispatchMainExecutorC4stopyyFTj @@ -897,9 +891,6 @@ _$ss24AsyncThrowingMapSequenceVyxq_GScisMc _$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTj _$ss26DispatchGlobalTaskExecutorC06isMainD0SbvgTq _$ss26DispatchGlobalTaskExecutorC06isMainD0SbvpMV -_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvgTj -_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvgTq -_$ss26DispatchGlobalTaskExecutorC18supportsSchedulingSbvpMV _$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTj _$ss26DispatchGlobalTaskExecutorC7enqueue_2at9tolerance5clockys0D3JobVn_7InstantQz8DurationQzSgxts5ClockRzlFTq _$ss26DispatchGlobalTaskExecutorC7enqueueyys0D3JobVnFTj From 00e7ef2d218e11834caa2a1d0bfd47043fc8fa2d Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2025 12:04:50 +0000 Subject: [PATCH 13/29] [Concurrency] Remove EventableExecutor, alter asSchedulable. EventableExecutor is being removed, for now, but hopefully will return in some form in the future. The `asSchedulable` implementation needs to change for reasons of ABI stability. rdar://141348916 --- .../public/Concurrency/DispatchExecutor.swift | 39 +------ stdlib/public/Concurrency/Executor.swift | 101 +++--------------- .../PlatformExecutorEmbedded.swift | 12 +-- .../Concurrency/PlatformExecutorNone.swift | 12 --- stdlib/public/Concurrency/TaskSleep.swift | 3 - .../Concurrency/TaskSleepDuration.swift | 2 +- .../Runtime/custom_main_executor.swift | 10 -- test/Concurrency/Runtime/sleep_executor.swift | 6 +- .../macOS/arm64/concurrency/baseline-asserts | 64 +++++------ .../macOS/x86_64/concurrency/baseline-asserts | 64 +++++------ 10 files changed, 73 insertions(+), 240 deletions(-) diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift index 4f267632e9163..405c0afbcd3b3 100644 --- a/stdlib/public/Concurrency/DispatchExecutor.swift +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -25,8 +25,6 @@ import Swift @available(SwiftStdlib 6.2, *) public class DispatchMainExecutor: RunLoopExecutor, @unchecked Sendable { - public typealias AsSchedulable = DispatchMainExecutor - var threaded = false public init() {} @@ -61,6 +59,10 @@ extension DispatchMainExecutor: SerialExecutor { @available(SwiftStdlib 6.2, *) extension DispatchMainExecutor: SchedulableExecutor { + public var asSchedulable: SchedulableExecutor? { + return self + } + public func enqueue(_ job: consuming ExecutorJob, at instant: C.Instant, tolerance: C.Duration? = nil, @@ -83,36 +85,6 @@ extension DispatchMainExecutor: SchedulableExecutor { } } -@available(SwiftStdlib 6.2, *) -extension DispatchMainExecutor: EventableExecutor { - - /// Register a new event with a given handler. - public func registerEvent( - handler: @escaping @Sendable () -> () - ) -> ExecutorEvent { - let source = unsafe _createDispatchEvent(handler: handler) - - // Stash the pointer in the id of the ExecutorEvent struct - let eventId = unsafe unsafeBitCast(source, to: Int.self) - return ExecutorEvent(id: eventId) - } - - /// Deregister the given event. - public func deregister(event: ExecutorEvent) { - // Extract the source and cancel it - let source = unsafe unsafeBitCast(event.id, to: OpaquePointer.self) - unsafe _destroyDispatchEvent(source) - } - - /// Notify the executor of an event. - public func notify(event: ExecutorEvent) { - // Extract the source, but don't release it - let source = unsafe unsafeBitCast(event.id, to: OpaquePointer.self) - unsafe _signalDispatchEvent(source) - } - -} - @available(SwiftStdlib 6.2, *) extension DispatchMainExecutor: MainExecutor {} @@ -121,9 +93,6 @@ extension DispatchMainExecutor: MainExecutor {} @available(SwiftStdlib 6.2, *) public class DispatchGlobalTaskExecutor: TaskExecutor, SchedulableExecutor, @unchecked Sendable { - - public typealias AsSchedulable = DispatchGlobalTaskExecutor - public init() {} public func enqueue(_ job: consuming ExecutorJob) { diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index 7ece65d1ac271..73ef230810417 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -16,13 +16,6 @@ import Swift @available(SwiftStdlib 5.1, *) public protocol Executor: AnyObject, Sendable { - /// This Executor type as a schedulable executor. - /// - /// If the conforming type also conforms to `SchedulableExecutor`, then this is - /// bound to `Self`. Otherwise, it is an uninhabited type (such as Never). - @available(SwiftStdlib 6.2, *) - associatedtype AsSchedulable: SchedulableExecutor = SchedulableExecutorNever - // Since lack move-only type support in the SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY configuration // Do not deprecate the UnownedJob enqueue in that configuration just yet - as we cannot introduce the replacements. #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @@ -55,23 +48,8 @@ public protocol Executor: AnyObject, Sendable { #endif } -/// Uninhabited class type to indicate we don't support scheduling. -@available(SwiftStdlib 6.2, *) -public class SchedulableExecutorNever { - private init(_ n: Never) {} -} - @available(SwiftStdlib 6.2, *) -extension SchedulableExecutorNever: SchedulableExecutor, @unchecked Sendable { - - public func enqueue(_ job: consuming ExecutorJob) { - fatalError("This should never be reached") - } - -} - -@available(SwiftStdlib 6.2, *) -public protocol SchedulableExecutor: Executor where Self.AsSchedulable == Self { +public protocol SchedulableExecutor: Executor { #if !$Embedded @@ -123,17 +101,13 @@ public protocol SchedulableExecutor: Executor where Self.AsSchedulable == Self { extension Executor { /// Return this executable as a SchedulableExecutor, or nil if that is /// unsupported. + /// + /// Executors that implement SchedulableExecutor should provide their + /// own copy of this method, which will allow the compiler to avoid a + /// potentially expensive runtime cast. @available(SwiftStdlib 6.2, *) - var asSchedulable: AsSchedulable? { - #if !$Embedded - if Self.self == AsSchedulable.self { - return _identityCast(self, to: AsSchedulable.self) - } else { - return nil - } - #else - return nil - #endif + var asSchedulable: SchedulableExecutor? { + return self as? SchedulableExecutor } } @@ -536,71 +510,17 @@ public protocol RunLoopExecutor: Executor { @available(SwiftStdlib 6.2, *) extension RunLoopExecutor { - public func run(until condition: () -> Bool) throws { + public func runUntil(_ condition: () -> Bool) throws { fatalError("run(until condition:) not supported on this executor") } } -/// Represents an event registered with an `EventableExecutor`. -@available(SwiftStdlib 6.2, *) -public struct ExecutorEvent: Identifiable, Comparable, Sendable { - public typealias ID = Int - - public var id: Self.ID - - public init(id: Self.ID) { - self.id = id - } - - public static func < (lhs: Self, rhs: Self) -> Bool { - return lhs.id < rhs.id - } - public static func == (lhs: Self, rhs: Self) -> Bool { - return lhs.id == rhs.id - } -} - - -/// An executor that has support for coalesced events. -@available(SwiftStdlib 6.2, *) -public protocol EventableExecutor { - - /// Register a new event with a given handler. - /// - /// Notifying the executor of the event will cause the executor to - /// execute the handler, however the executor is free to coalesce multiple - /// event notifications, and is also free to execute the handler at a time - /// of its choosing. - /// - /// Parameters - /// - /// - handler: The handler to call when the event fires. - /// - /// Returns a new opaque `Event`. - func registerEvent(handler: @escaping @Sendable () -> ()) -> ExecutorEvent - - /// Deregister the given event. - /// - /// After this function returns, there will be no further executions of the - /// handler for the given event. - func deregister(event: ExecutorEvent) - - /// Notify the executor of an event. - /// - /// This will trigger, at some future point, the execution of the associated - /// event handler. Prior to that time, multiple calls to `notify` may be - /// coalesced and result in a single invocation of the event handler. - func notify(event: ExecutorEvent) - -} - - /// The main executor must conform to these three protocols; we have to /// make this a protocol for compatibility with Embedded Swift. @available(SwiftStdlib 6.2, *) -public protocol MainExecutor: RunLoopExecutor, SerialExecutor, EventableExecutor { +public protocol MainExecutor: RunLoopExecutor, SerialExecutor { } @@ -719,6 +639,9 @@ extension Task where Success == Never, Failure == Never { let schedulable = taskExecutor.asSchedulable { return schedulable } + if let schedulable = defaultExecutor.asSchedulable { + return schedulable + } return nil } } diff --git a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift index 7652abc88d1ac..b90224a0974ef 100644 --- a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift +++ b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift @@ -34,22 +34,12 @@ public final class EmbeddedMainExecutor: MainExecutor, @unchecked Sendable { public func run() throws { } - public func run(until condition: () -> Bool) throws { + public func runUntil(_ condition: () -> Bool) throws { } public func stop() { } - public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { - return ExecutorEvent(id: 0) - } - - public func deregister(event: ExecutorEvent) { - } - - public func notify(event: ExecutorEvent) { - } - } // .. Platform Task Executor ................................................... diff --git a/stdlib/public/Concurrency/PlatformExecutorNone.swift b/stdlib/public/Concurrency/PlatformExecutorNone.swift index 6f78c91f86139..0d311492356bb 100644 --- a/stdlib/public/Concurrency/PlatformExecutorNone.swift +++ b/stdlib/public/Concurrency/PlatformExecutorNone.swift @@ -32,18 +32,6 @@ public class PlatformMainExecutor: MainExecutor, @unchecked Sendable { fatalError("There is no main executor implementation for this platform") } - public func registerEvent(handler: @escaping () -> ()) -> ExecutorEvent { - fatalError("There is no main executor implementation for this platform") - } - - public func deregister(event: ExecutorEvent) { - fatalError("There is no main executor implementation for this platform") - } - - public func notify(event: ExecutorEvent) { - fatalError("There is no main executor implementation for this platform") - } - } // .. Platform Default Executor ................................................ diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift index 55e9a1b23cb13..bf42e6387a08a 100644 --- a/stdlib/public/Concurrency/TaskSleep.swift +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -30,8 +30,6 @@ extension Task where Success == Never, Failure == Never { if #available(SwiftStdlib 6.2, *) { let executor = Task.currentSchedulableExecutor - ?? Task.defaultExecutor.asSchdulable - #if !$Embedded executor!.enqueue(ExecutorJob(context: job), after: .nanoseconds(duration), @@ -272,7 +270,6 @@ extension Task where Success == Never, Failure == Never { if #available(SwiftStdlib 6.2, *) { let executor = Task.currentSchedulableExecutor - ?? Task.defaultExecutor.asSchedulable let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) #if !$Embedded executor!.enqueue(job, diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift index 396288eb9c1da..4b8d279f9207d 100644 --- a/stdlib/public/Concurrency/TaskSleepDuration.swift +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -55,7 +55,7 @@ extension Task where Success == Never, Failure == Never { } if #available(SwiftStdlib 6.2, *) { - let executor = Task.currentSchdulableExecutor ?? Task.defaultExecutor.asSchedulable + let executor = Task.currentSchedulableExecutor let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) #if !$Embedded diff --git a/test/Concurrency/Runtime/custom_main_executor.swift b/test/Concurrency/Runtime/custom_main_executor.swift index ddf55fb640ecb..ad89e63da46ca 100644 --- a/test/Concurrency/Runtime/custom_main_executor.swift +++ b/test/Concurrency/Runtime/custom_main_executor.swift @@ -59,16 +59,6 @@ final class SimpleMainExecutor: MainExecutor, @unchecked Sendable { func stop() { shouldStop = true } - - func registerEvent(handler: @escaping @Sendable () -> ()) -> ExecutorEvent { - return ExecutorEvent(id: 0) - } - - func deregister(event: ExecutorEvent) { - } - - func notify(event: ExecutorEvent) { - } } @available(SwiftStdlib 6.2, *) diff --git a/test/Concurrency/Runtime/sleep_executor.swift b/test/Concurrency/Runtime/sleep_executor.swift index 41847a9ecc108..a0b4e8d0770cd 100644 --- a/test/Concurrency/Runtime/sleep_executor.swift +++ b/test/Concurrency/Runtime/sleep_executor.swift @@ -20,7 +20,11 @@ actor MyActor { } @available(SwiftStdlib 6.2, *) -final class TestExecutor: TaskExecutor, SchedulableExecutor @unchecked Sendable { +final class TestExecutor: TaskExecutor, SchedulableExecutor, @unchecked Sendable { + var asSchedulable: SchedulableExecutor? { + return self + } + public func enqueue(_ _job: consuming ExecutorJob) { let job = UnownedJob(_job) DispatchQueue.main.async { diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index d103c32092f78..003ddc4f62a8d 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -25,10 +25,6 @@ _$sScEN _$sScEs5ErrorsMc _$sScF14isMainExecutorSbvgTj _$sScF14isMainExecutorSbvgTq -_$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj -_$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq -_$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTj -_$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTq _$sScF7enqueueyyScJFTj _$sScF7enqueueyyScJFTq _$sScF7enqueueyys11ExecutorJobVnFTj @@ -41,8 +37,6 @@ _$sScFsE14isMainExecutorSbvg _$sScFsE14isMainExecutorSbvpMV _$sScFsE18_isComplexEqualitySbvg _$sScFsE18_isComplexEqualitySbvpMV -_$sScFsE7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF -_$sScFsE7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lF _$sScFsE7enqueueyyScJF _$sScFsE7enqueueyys11ExecutorJobVnF _$sScFsE7enqueueyys3JobVnF @@ -217,6 +211,10 @@ _$sScTss5NeverORszABRs_rlE15currentPriorityScPvgZ _$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvgZ _$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvpZMV _$sScTss5NeverORszABRs_rlE17checkCancellationyyKFZ +_$sScTss5NeverORszABRs_rlE17preferredExecutorSch_pSgvgZ +_$sScTss5NeverORszABRs_rlE17preferredExecutorSch_pSgvpZMV +_$sScTss5NeverORszABRs_rlE26currentSchedulableExecutors0cD0_pSgvgZ +_$sScTss5NeverORszABRs_rlE26currentSchedulableExecutors0cD0_pSgvpZMV _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZ _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZTu _$sScTss5NeverORszABRs_rlE5sleep5until9tolerance5clocky7InstantQyd___8DurationQyd__Sgqd__tYaKs5ClockRd__lFZ @@ -397,9 +395,9 @@ _$sSctSQsMc _$ss039_checkIllegalTaskLocalBindingWithinWithC5Group4file4lineySS_SutF _$ss11ClockTraitsV10continuousABvgZ _$ss11ClockTraitsV10continuousABvpZMV -_$ss11ClockTraitsV8rawValueABs5Int32V_tcfC -_$ss11ClockTraitsV8rawValues5Int32Vvg -_$ss11ClockTraitsV8rawValues5Int32VvpMV +_$ss11ClockTraitsV8rawValueABs6UInt32V_tcfC +_$ss11ClockTraitsV8rawValues6UInt32Vvg +_$ss11ClockTraitsV8rawValues6UInt32VvpMV _$ss11ClockTraitsV8wallTimeABvgZ _$ss11ClockTraitsV8wallTimeABvpZMV _$ss11ClockTraitsV9monotonicABvgZ @@ -473,22 +471,8 @@ _$ss11JobPriorityVSQsMc _$ss12MainExecutorMp _$ss12MainExecutorPScfTb _$ss12MainExecutorPs07RunLoopB0Tb -_$ss12MainExecutorPs09EventableB0Tb _$ss12MainExecutorTL _$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ -_$ss13ExecutorEventV1loiySbAB_ABtFZ -_$ss13ExecutorEventV2eeoiySbAB_ABtFZ -_$ss13ExecutorEventV2idABSi_tcfC -_$ss13ExecutorEventV2idSivM -_$ss13ExecutorEventV2idSivg -_$ss13ExecutorEventV2idSivpMV -_$ss13ExecutorEventV2idSivs -_$ss13ExecutorEventVMa -_$ss13ExecutorEventVMn -_$ss13ExecutorEventVN -_$ss13ExecutorEventVSLsMc -_$ss13ExecutorEventVSQsMc -_$ss13ExecutorEventVs12IdentifiablesMc _$ss13_runAsyncMainyyyyYaKcF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lFTu @@ -553,14 +537,14 @@ _$ss15ExecutorFactoryP07defaultA0Sch_pvgZTj _$ss15ExecutorFactoryP07defaultA0Sch_pvgZTq _$ss15ExecutorFactoryTL _$ss15RunLoopExecutorMp -_$ss15RunLoopExecutorP3run5untilySbyXE_tKFTj -_$ss15RunLoopExecutorP3run5untilySbyXE_tKFTq _$ss15RunLoopExecutorP3runyyKFTj _$ss15RunLoopExecutorP3runyyKFTq _$ss15RunLoopExecutorP4stopyyFTj _$ss15RunLoopExecutorP4stopyyFTq +_$ss15RunLoopExecutorP8runUntilyySbyXEKFTj +_$ss15RunLoopExecutorP8runUntilyySbyXEKFTq _$ss15RunLoopExecutorPScFTb -_$ss15RunLoopExecutorPsE3run5untilySbyXE_tKF +_$ss15RunLoopExecutorPsE8runUntilyySbyXEKF _$ss15RunLoopExecutorTL _$ss15SuspendingClockV17minimumResolutions8DurationVvg _$ss15SuspendingClockV17minimumResolutions8DurationVvpMV @@ -622,14 +606,6 @@ _$ss16AsyncMapSequenceVMa _$ss16AsyncMapSequenceVMn _$ss16AsyncMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss16AsyncMapSequenceVyxq_GScisMc -_$ss17EventableExecutorMp -_$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTj -_$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTq -_$ss17EventableExecutorP13registerEvent7handlers0bD0VyyYbc_tFTj -_$ss17EventableExecutorP13registerEvent7handlers0bD0VyyYbc_tFTq -_$ss17EventableExecutorP6notify5eventys0B5EventV_tFTj -_$ss17EventableExecutorP6notify5eventys0B5EventV_tFTq -_$ss17EventableExecutorTL _$ss17_enqueueJobGlobalyyScJF _$ss19AsyncFilterSequenceV04makeA8IteratorAB0E0Vyx_GyF _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvg @@ -688,6 +664,15 @@ _$ss19DiscardingTaskGroupV9cancelAllyyF _$ss19DiscardingTaskGroupVMa _$ss19DiscardingTaskGroupVMn _$ss19DiscardingTaskGroupVN +_$ss19SchedulableExecutorMp +_$ss19SchedulableExecutorP7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj +_$ss19SchedulableExecutorP7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq +_$ss19SchedulableExecutorP7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lFTj +_$ss19SchedulableExecutorP7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lFTq +_$ss19SchedulableExecutorPScFTb +_$ss19SchedulableExecutorPsE7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF +_$ss19SchedulableExecutorPsE7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lF +_$ss19SchedulableExecutorTL _$ss19UnownedTaskExecutorV02asbC0Sch_pSgyF _$ss19UnownedTaskExecutorVyABxhcSchRzlufC _$ss20AsyncFlatMapSequenceV04makeA8IteratorAB0F0Vyxq__GyF @@ -721,14 +706,13 @@ _$ss20AsyncFlatMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss20AsyncFlatMapSequenceVyxq_GScisMc _$ss20DispatchMainExecutorC02isbC0Sbvg _$ss20DispatchMainExecutorC02isbC0SbvpMV -_$ss20DispatchMainExecutorC10deregister5eventys0C5EventV_tF +_$ss20DispatchMainExecutorC13asSchedulables0eC0_pSgvg +_$ss20DispatchMainExecutorC13asSchedulables0eC0_pSgvpMV _$ss20DispatchMainExecutorC13checkIsolatedyyF -_$ss20DispatchMainExecutorC13registerEvent7handlers0cE0VyyYbc_tF _$ss20DispatchMainExecutorC3runyyKFTj _$ss20DispatchMainExecutorC3runyyKFTq _$ss20DispatchMainExecutorC4stopyyFTj _$ss20DispatchMainExecutorC4stopyyFTq -_$ss20DispatchMainExecutorC6notify5eventys0C5EventV_tF _$ss20DispatchMainExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlF _$ss20DispatchMainExecutorC7enqueueyys0C3JobVnF _$ss20DispatchMainExecutorCABycfC @@ -747,10 +731,10 @@ _$ss20DispatchMainExecutorCScfsMc _$ss20DispatchMainExecutorCScfsWP _$ss20DispatchMainExecutorCfD _$ss20DispatchMainExecutorCfd +_$ss20DispatchMainExecutorCs011SchedulableC0sMc +_$ss20DispatchMainExecutorCs011SchedulableC0sWP _$ss20DispatchMainExecutorCs07RunLoopC0sMc _$ss20DispatchMainExecutorCs07RunLoopC0sWP -_$ss20DispatchMainExecutorCs09EventableC0sMc -_$ss20DispatchMainExecutorCs09EventableC0sWP _$ss20DispatchMainExecutorCs0bC0sMc _$ss20DispatchMainExecutorCs0bC0sWP _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lF @@ -911,6 +895,8 @@ _$ss26DispatchGlobalTaskExecutorCSchsMc _$ss26DispatchGlobalTaskExecutorCSchsWP _$ss26DispatchGlobalTaskExecutorCfD _$ss26DispatchGlobalTaskExecutorCfd +_$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sMc +_$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sWP _$ss26_enqueueJobGlobalWithDelayyys6UInt64V_ScJtF _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index d103c32092f78..003ddc4f62a8d 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -25,10 +25,6 @@ _$sScEN _$sScEs5ErrorsMc _$sScF14isMainExecutorSbvgTj _$sScF14isMainExecutorSbvgTq -_$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj -_$sScF7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq -_$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTj -_$sScF7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lFTq _$sScF7enqueueyyScJFTj _$sScF7enqueueyyScJFTq _$sScF7enqueueyys11ExecutorJobVnFTj @@ -41,8 +37,6 @@ _$sScFsE14isMainExecutorSbvg _$sScFsE14isMainExecutorSbvpMV _$sScFsE18_isComplexEqualitySbvg _$sScFsE18_isComplexEqualitySbvpMV -_$sScFsE7enqueue_2at9tolerance5clockys11ExecutorJobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF -_$sScFsE7enqueue_5after9tolerance5clockys11ExecutorJobVn_8DurationQyd__AHSgqd__ts5ClockRd__lF _$sScFsE7enqueueyyScJF _$sScFsE7enqueueyys11ExecutorJobVnF _$sScFsE7enqueueyys3JobVnF @@ -217,6 +211,10 @@ _$sScTss5NeverORszABRs_rlE15currentPriorityScPvgZ _$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvgZ _$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvpZMV _$sScTss5NeverORszABRs_rlE17checkCancellationyyKFZ +_$sScTss5NeverORszABRs_rlE17preferredExecutorSch_pSgvgZ +_$sScTss5NeverORszABRs_rlE17preferredExecutorSch_pSgvpZMV +_$sScTss5NeverORszABRs_rlE26currentSchedulableExecutors0cD0_pSgvgZ +_$sScTss5NeverORszABRs_rlE26currentSchedulableExecutors0cD0_pSgvpZMV _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZ _$sScTss5NeverORszABRs_rlE5sleep11nanosecondsys6UInt64V_tYaKFZTu _$sScTss5NeverORszABRs_rlE5sleep5until9tolerance5clocky7InstantQyd___8DurationQyd__Sgqd__tYaKs5ClockRd__lFZ @@ -397,9 +395,9 @@ _$sSctSQsMc _$ss039_checkIllegalTaskLocalBindingWithinWithC5Group4file4lineySS_SutF _$ss11ClockTraitsV10continuousABvgZ _$ss11ClockTraitsV10continuousABvpZMV -_$ss11ClockTraitsV8rawValueABs5Int32V_tcfC -_$ss11ClockTraitsV8rawValues5Int32Vvg -_$ss11ClockTraitsV8rawValues5Int32VvpMV +_$ss11ClockTraitsV8rawValueABs6UInt32V_tcfC +_$ss11ClockTraitsV8rawValues6UInt32Vvg +_$ss11ClockTraitsV8rawValues6UInt32VvpMV _$ss11ClockTraitsV8wallTimeABvgZ _$ss11ClockTraitsV8wallTimeABvpZMV _$ss11ClockTraitsV9monotonicABvgZ @@ -473,22 +471,8 @@ _$ss11JobPriorityVSQsMc _$ss12MainExecutorMp _$ss12MainExecutorPScfTb _$ss12MainExecutorPs07RunLoopB0Tb -_$ss12MainExecutorPs09EventableB0Tb _$ss12MainExecutorTL _$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ -_$ss13ExecutorEventV1loiySbAB_ABtFZ -_$ss13ExecutorEventV2eeoiySbAB_ABtFZ -_$ss13ExecutorEventV2idABSi_tcfC -_$ss13ExecutorEventV2idSivM -_$ss13ExecutorEventV2idSivg -_$ss13ExecutorEventV2idSivpMV -_$ss13ExecutorEventV2idSivs -_$ss13ExecutorEventVMa -_$ss13ExecutorEventVMn -_$ss13ExecutorEventVN -_$ss13ExecutorEventVSLsMc -_$ss13ExecutorEventVSQsMc -_$ss13ExecutorEventVs12IdentifiablesMc _$ss13_runAsyncMainyyyyYaKcF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lFTu @@ -553,14 +537,14 @@ _$ss15ExecutorFactoryP07defaultA0Sch_pvgZTj _$ss15ExecutorFactoryP07defaultA0Sch_pvgZTq _$ss15ExecutorFactoryTL _$ss15RunLoopExecutorMp -_$ss15RunLoopExecutorP3run5untilySbyXE_tKFTj -_$ss15RunLoopExecutorP3run5untilySbyXE_tKFTq _$ss15RunLoopExecutorP3runyyKFTj _$ss15RunLoopExecutorP3runyyKFTq _$ss15RunLoopExecutorP4stopyyFTj _$ss15RunLoopExecutorP4stopyyFTq +_$ss15RunLoopExecutorP8runUntilyySbyXEKFTj +_$ss15RunLoopExecutorP8runUntilyySbyXEKFTq _$ss15RunLoopExecutorPScFTb -_$ss15RunLoopExecutorPsE3run5untilySbyXE_tKF +_$ss15RunLoopExecutorPsE8runUntilyySbyXEKF _$ss15RunLoopExecutorTL _$ss15SuspendingClockV17minimumResolutions8DurationVvg _$ss15SuspendingClockV17minimumResolutions8DurationVvpMV @@ -622,14 +606,6 @@ _$ss16AsyncMapSequenceVMa _$ss16AsyncMapSequenceVMn _$ss16AsyncMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss16AsyncMapSequenceVyxq_GScisMc -_$ss17EventableExecutorMp -_$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTj -_$ss17EventableExecutorP10deregister5eventys0B5EventV_tFTq -_$ss17EventableExecutorP13registerEvent7handlers0bD0VyyYbc_tFTj -_$ss17EventableExecutorP13registerEvent7handlers0bD0VyyYbc_tFTq -_$ss17EventableExecutorP6notify5eventys0B5EventV_tFTj -_$ss17EventableExecutorP6notify5eventys0B5EventV_tFTq -_$ss17EventableExecutorTL _$ss17_enqueueJobGlobalyyScJF _$ss19AsyncFilterSequenceV04makeA8IteratorAB0E0Vyx_GyF _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvg @@ -688,6 +664,15 @@ _$ss19DiscardingTaskGroupV9cancelAllyyF _$ss19DiscardingTaskGroupVMa _$ss19DiscardingTaskGroupVMn _$ss19DiscardingTaskGroupVN +_$ss19SchedulableExecutorMp +_$ss19SchedulableExecutorP7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTj +_$ss19SchedulableExecutorP7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lFTq +_$ss19SchedulableExecutorP7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lFTj +_$ss19SchedulableExecutorP7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lFTq +_$ss19SchedulableExecutorPScFTb +_$ss19SchedulableExecutorPsE7enqueue_2at9tolerance5clockys0B3JobVn_7InstantQyd__8DurationQyd__Sgqd__ts5ClockRd__lF +_$ss19SchedulableExecutorPsE7enqueue_5after9tolerance5clockys0B3JobVn_8DurationQyd__AJSgqd__ts5ClockRd__lF +_$ss19SchedulableExecutorTL _$ss19UnownedTaskExecutorV02asbC0Sch_pSgyF _$ss19UnownedTaskExecutorVyABxhcSchRzlufC _$ss20AsyncFlatMapSequenceV04makeA8IteratorAB0F0Vyxq__GyF @@ -721,14 +706,13 @@ _$ss20AsyncFlatMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss20AsyncFlatMapSequenceVyxq_GScisMc _$ss20DispatchMainExecutorC02isbC0Sbvg _$ss20DispatchMainExecutorC02isbC0SbvpMV -_$ss20DispatchMainExecutorC10deregister5eventys0C5EventV_tF +_$ss20DispatchMainExecutorC13asSchedulables0eC0_pSgvg +_$ss20DispatchMainExecutorC13asSchedulables0eC0_pSgvpMV _$ss20DispatchMainExecutorC13checkIsolatedyyF -_$ss20DispatchMainExecutorC13registerEvent7handlers0cE0VyyYbc_tF _$ss20DispatchMainExecutorC3runyyKFTj _$ss20DispatchMainExecutorC3runyyKFTq _$ss20DispatchMainExecutorC4stopyyFTj _$ss20DispatchMainExecutorC4stopyyFTq -_$ss20DispatchMainExecutorC6notify5eventys0C5EventV_tF _$ss20DispatchMainExecutorC7enqueue_2at9tolerance5clockys0C3JobVn_7InstantQz8DurationQzSgxts5ClockRzlF _$ss20DispatchMainExecutorC7enqueueyys0C3JobVnF _$ss20DispatchMainExecutorCABycfC @@ -747,10 +731,10 @@ _$ss20DispatchMainExecutorCScfsMc _$ss20DispatchMainExecutorCScfsWP _$ss20DispatchMainExecutorCfD _$ss20DispatchMainExecutorCfd +_$ss20DispatchMainExecutorCs011SchedulableC0sMc +_$ss20DispatchMainExecutorCs011SchedulableC0sWP _$ss20DispatchMainExecutorCs07RunLoopC0sMc _$ss20DispatchMainExecutorCs07RunLoopC0sWP -_$ss20DispatchMainExecutorCs09EventableC0sMc -_$ss20DispatchMainExecutorCs09EventableC0sWP _$ss20DispatchMainExecutorCs0bC0sMc _$ss20DispatchMainExecutorCs0bC0sWP _$ss21withThrowingTaskGroup2of9returning4bodyq_xm_q_mq_Scgyxs5Error_pGzYaKXEtYaKr0_lF @@ -911,6 +895,8 @@ _$ss26DispatchGlobalTaskExecutorCSchsMc _$ss26DispatchGlobalTaskExecutorCSchsWP _$ss26DispatchGlobalTaskExecutorCfD _$ss26DispatchGlobalTaskExecutorCfd +_$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sMc +_$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sWP _$ss26_enqueueJobGlobalWithDelayyys6UInt64V_ScJtF _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg From d197f38226aa8abd845096ed37d435fa9b50dab3 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2025 13:25:19 +0000 Subject: [PATCH 14/29] [Concurrency] Make `currentExecutor` return a non-optional. If we don't have any other executor to return, return the default executor. rdar://141348916 --- stdlib/public/Concurrency/Executor.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index 73ef230810417..a35ea8fd6ca4e 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -600,7 +600,7 @@ extension Task where Success == Never, Failure == Never { /// If none of these exist, this property will be `nil`. @available(SwiftStdlib 6.2, *) @_unavailableInEmbedded - public static var currentExecutor: (any Executor)? { + public static var currentExecutor: any Executor { if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor() { return activeExecutor } else if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() { @@ -608,7 +608,7 @@ extension Task where Success == Never, Failure == Never { } else if let taskExecutor = unsafe _getCurrentTaskExecutor().asTaskExecutor() { return taskExecutor } - return nil + return defaultExecutor } /// Get the preferred executor for the current `Task`, if any. From f782499aaf1b0a5e6932d6de781bc1fb19ab046b Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2025 14:43:01 +0000 Subject: [PATCH 15/29] [Concurrency][Tests] Fix baseline-asserts. Apparently I hadn't included the change for `currentExecutor` being a non-`Optional`. rdar://141348916 --- test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts | 4 ++-- test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index 003ddc4f62a8d..17babf8a3ac10 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -205,8 +205,8 @@ _$sScTss5NeverORs_rlE5valuexvgTu _$sScTss5NeverORs_rlE5valuexvpMV _$sScTss5NeverORszABRs_rlE11isCancelledSbvgZ _$sScTss5NeverORszABRs_rlE12basePriorityScPSgvgZ -_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pSgvgZ -_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pSgvpZMV +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pvpZMV _$sScTss5NeverORszABRs_rlE15currentPriorityScPvgZ _$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvgZ _$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvpZMV diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index 003ddc4f62a8d..17babf8a3ac10 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -205,8 +205,8 @@ _$sScTss5NeverORs_rlE5valuexvgTu _$sScTss5NeverORs_rlE5valuexvpMV _$sScTss5NeverORszABRs_rlE11isCancelledSbvgZ _$sScTss5NeverORszABRs_rlE12basePriorityScPSgvgZ -_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pSgvgZ -_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pSgvpZMV +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pvgZ +_$sScTss5NeverORszABRs_rlE15currentExecutorScF_pvpZMV _$sScTss5NeverORszABRs_rlE15currentPriorityScPvgZ _$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvgZ _$sScTss5NeverORszABRs_rlE15defaultExecutorSch_pvpZMV From ff0ce6255e1cc064a3811190ecb0d972b068b87b Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 13 Mar 2025 14:50:15 +0000 Subject: [PATCH 16/29] [Concurrency][Tests] Linux doesn't have DispatchTime.advanced(by:). We need instead to use the `+` operator. rdar://141348916 --- test/Concurrency/Runtime/sleep_executor.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/Concurrency/Runtime/sleep_executor.swift b/test/Concurrency/Runtime/sleep_executor.swift index a0b4e8d0770cd..a13a8fa66cc95 100644 --- a/test/Concurrency/Runtime/sleep_executor.swift +++ b/test/Concurrency/Runtime/sleep_executor.swift @@ -44,11 +44,9 @@ final class TestExecutor: TaskExecutor, SchedulableExecutor, @unchecked Sendable let nanoseconds = attoseconds / 1_000_000_000 // Get a Dispatch time - let deadline = DispatchTime.now().advanced( - by: .seconds(Int(seconds)) - ).advanced( - by: .nanoseconds(Int(nanoseconds)) - ) + let deadline = DispatchTime.now() + + .seconds(Int(seconds)) + + .nanoseconds(Int(nanoseconds)) let job = UnownedJob(_job) DispatchQueue.main.asyncAfter(deadline: deadline) { From 869622fc12b184f0871fa396f0229310cb812120 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2025 09:58:36 +0000 Subject: [PATCH 17/29] [Concurrency][Tests] Remove spurious line in baseline-asserts. I think this might have been a merge error at some point while rebasing. rdar://141348916 --- test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts | 1 - test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts | 1 - 2 files changed, 2 deletions(-) diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index 17babf8a3ac10..15e91e8c2b78e 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -472,7 +472,6 @@ _$ss12MainExecutorMp _$ss12MainExecutorPScfTb _$ss12MainExecutorPs07RunLoopB0Tb _$ss12MainExecutorTL -_$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ _$ss13_runAsyncMainyyyyYaKcF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lFTu diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index 17babf8a3ac10..15e91e8c2b78e 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -472,7 +472,6 @@ _$ss12MainExecutorMp _$ss12MainExecutorPScfTb _$ss12MainExecutorPs07RunLoopB0Tb _$ss12MainExecutorTL -_$ss12SwiftSettingVsE16defaultIsolationyABScA_pXpSgFZ _$ss13_runAsyncMainyyyyYaKcF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lF _$ss13withTaskGroup2of9returning4bodyq_xm_q_mq_ScGyxGzYaXEtYar0_lFTu From 8caa5c5c0d04a8ba7d0adaa4abe7b864468850bc Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2025 10:01:37 +0000 Subject: [PATCH 18/29] [Concurrency] Add a missing `public`. The default implementation of `traits` on `Clock` needs to be `public`. rdar://141348916 --- stdlib/public/Concurrency/Clock.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Concurrency/Clock.swift b/stdlib/public/Concurrency/Clock.swift index c2cdc30771363..aa6805b946d3f 100644 --- a/stdlib/public/Concurrency/Clock.swift +++ b/stdlib/public/Concurrency/Clock.swift @@ -220,7 +220,7 @@ public struct ClockTraits: OptionSet { extension Clock { /// The traits associated with this clock instance. @available(SwiftStdlib 6.2, *) - var traits: ClockTraits { + public var traits: ClockTraits { return [] } } From a4f79f367a0d04bb48cf9bcc38ab33c7e126542c Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2025 14:57:15 +0000 Subject: [PATCH 19/29] [Concurrency][Linux] Fix WASI build. This doesn't actually add proper WASI support - it just fixes the build so that we're no worse off than we were. rdar://141348916 --- stdlib/public/Concurrency/CMakeLists.txt | 9 +++- .../public/Concurrency/DispatchExecutor.swift | 4 +- stdlib/public/Concurrency/DummyExecutor.swift | 51 +++++++++++++++++++ stdlib/public/Concurrency/ExecutorBridge.cpp | 38 ++------------ .../public/Concurrency/ExecutorBridge.swift | 46 ----------------- .../Concurrency/PlatformExecutorWASI.swift | 25 +++++++++ 6 files changed, 88 insertions(+), 85 deletions(-) create mode 100644 stdlib/public/Concurrency/DummyExecutor.swift create mode 100644 stdlib/public/Concurrency/PlatformExecutorWASI.swift diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index ce0d6488fbd53..d9699ce50bad4 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -93,9 +93,12 @@ set(SWIFT_RUNTIME_CONCURRENCY_C_SOURCES linker-support/magic-symbols-for-install-name.c ) -set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES - DispatchGlobalExecutor.cpp +set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES) +if("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "dispatch") + set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES + DispatchGlobalExecutor.cpp ) +endif() set(LLVM_OPTIONAL_SOURCES CooperativeGlobalExecutor.cpp @@ -168,9 +171,11 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES TaskSleepDuration.swift DispatchExecutor.swift CFExecutor.swift + DummyExecutor.swift PlatformExecutorDarwin.swift PlatformExecutorLinux.swift PlatformExecutorWindows.swift + PlatformExecutorWASI.swift ) set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES diff --git a/stdlib/public/Concurrency/DispatchExecutor.swift b/stdlib/public/Concurrency/DispatchExecutor.swift index 405c0afbcd3b3..3a5abbdf73cc6 100644 --- a/stdlib/public/Concurrency/DispatchExecutor.swift +++ b/stdlib/public/Concurrency/DispatchExecutor.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if !$Embedded +#if !$Embedded && !os(WASI) import Swift @@ -200,4 +200,4 @@ extension DispatchGlobalTaskExecutor: DispatchExecutorProtocol { extension DispatchMainExecutor: DispatchExecutorProtocol { } -#endif // !$Embedded +#endif // !$Embedded && !os(WASI) diff --git a/stdlib/public/Concurrency/DummyExecutor.swift b/stdlib/public/Concurrency/DummyExecutor.swift new file mode 100644 index 0000000000000..628b035ab2255 --- /dev/null +++ b/stdlib/public/Concurrency/DummyExecutor.swift @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +// .. Main Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public class DummyMainExecutor: MainExecutor, @unchecked Sendable { + public init() {} + + public func run() throws { + fatalError("There is no executor implementation active") + } + + public func stop() { + fatalError("There is no executor implementation active") + } + + public func enqueue(_ job: consuming ExecutorJob) { + fatalError("There is no executor implementation active") + } + + public var isMainExecutor: Bool { true } + + public func checkIsolated() { + // Do nothing + } +} + +// .. Task Executor ............................................................ + +@available(SwiftStdlib 6.2, *) +public class DummyTaskExecutor: TaskExecutor, @unchecked Sendable { + public init() {} + + public func enqueue(_ job: consuming ExecutorJob) { + fatalError("There is no executor implementation active") + } + + public var isMainExecutor: Bool { false } +} diff --git a/stdlib/public/Concurrency/ExecutorBridge.cpp b/stdlib/public/Concurrency/ExecutorBridge.cpp index 289ef1265917e..9faf70b136a75 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.cpp +++ b/stdlib/public/Concurrency/ExecutorBridge.cpp @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if !SWIFT_CONCURRENCY_EMBEDDED +#if SWIFT_CONCURRENCY_USES_DISPATCH #include #endif @@ -119,38 +119,7 @@ void *swift_job_getExecutorPrivateData(Job *job) { return &job->SchedulerPrivate[0]; } -#if !SWIFT_CONCURRENCY_EMBEDDED -extern "C" SWIFT_CC(swift) -void *swift_createDispatchEventC(void (*handler)(void *), void *context) { - dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_OR, - 0, 0, - dispatch_get_main_queue()); - dispatch_source_set_event_handler_f(source, handler); - dispatch_activate(source); - - return source; -} - -extern "C" SWIFT_CC(swift) -void swift_destroyDispatchEventC(void *event) { - dispatch_source_t source = (dispatch_source_t)event; - - dispatch_source_cancel(source); - dispatch_release(source); -} - -extern "C" SWIFT_CC(swift) -void *swift_getDispatchEventContext(void *event) { - return dispatch_get_context((dispatch_source_t)event); -} - -extern "C" SWIFT_CC(swift) -void swift_signalDispatchEvent(void *event) { - dispatch_source_t source = (dispatch_source_t)event; - - dispatch_source_merge_data(source, 1); -} - +#if SWIFT_CONCURRENCY_USES_DISPATCH extern "C" SWIFT_CC(swift) __attribute__((noreturn)) void swift_dispatchMain() { dispatch_main(); @@ -160,7 +129,6 @@ extern "C" SWIFT_CC(swift) void swift_dispatchAssertMainQueue() { dispatch_assert_queue(dispatch_get_main_queue()); } - -#endif // !SWIFT_CONCURRENCY_EMBEDDED +#endif // SWIFT_CONCURRENCY_ENABLE_DISPATCH #pragma clang diagnostic pop diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift index 95a0cdbcecb49..b806ff307d34e 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.swift +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -112,49 +112,3 @@ internal func _dispatchEnqueueWithDeadline(_ global: CBool, @available(SwiftStdlib 6.2, *) @_silgen_name("swift_dispatchAssertMainQueue") internal func _dispatchAssertMainQueue() - -@available(SwiftStdlib 6.2, *) -@_silgen_name("swift_createDispatchEventC") -internal func _createDispatchEventC( - handler: @convention(c) @escaping (UnsafeMutableRawPointer) -> (), - context: UnsafeMutableRawPointer -) -> OpaquePointer - -fileprivate class DispatchEventHandlerBox { - var handler: @Sendable () -> () - init(handler: @escaping @Sendable () -> ()) { - self.handler = handler - } -} - -@available(SwiftStdlib 6.2, *) -internal func _createDispatchEvent(handler: @escaping @Sendable () -> ()) -> OpaquePointer { - let boxed = DispatchEventHandlerBox(handler: handler) - let opaqueHandlerBox = unsafe Unmanaged.passRetained(boxed).toOpaque() - return unsafe _createDispatchEventC( - handler: { context in - let unmanaged = unsafe Unmanaged.fromOpaque(context) - unsafe unmanaged.takeUnretainedValue().handler() - }, - context: opaqueHandlerBox - ) -} - -@available(SwiftStdlib 6.2, *) -@_silgen_name("swift_destroyDispatchEventC") -internal func _destroyDispatchEventC(_ event: OpaquePointer) - -@available(SwiftStdlib 6.2, *) -@_silgen_name("swift_getDispatchEventContext") -internal func _getDispatchEventContext(_ event: OpaquePointer) -> UnsafeMutableRawPointer - -@available(SwiftStdlib 6.2, *) -internal func _destroyDispatchEvent(_ event: OpaquePointer) { - let context = unsafe _getDispatchEventContext(event) - unsafe Unmanaged.fromOpaque(context).release() - unsafe _destroyDispatchEventC(event) -} - -@available(SwiftStdlib 6.2, *) -@_silgen_name("swift_signalDispatchEvent") -internal func _signalDispatchEvent(_ event: OpaquePointer) diff --git a/stdlib/public/Concurrency/PlatformExecutorWASI.swift b/stdlib/public/Concurrency/PlatformExecutorWASI.swift new file mode 100644 index 0000000000000..93f40262a4c83 --- /dev/null +++ b/stdlib/public/Concurrency/PlatformExecutorWASI.swift @@ -0,0 +1,25 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +#if os(WASI) + +import Swift + +// The default executors for now are Dispatch-based +@available(SwiftStdlib 6.2, *) +public struct PlatformExecutorFactory: ExecutorFactory { + public static let mainExecutor: any MainExecutor = DummyMainExecutor() + public static let defaultExecutor: any TaskExecutor + = DummyTaskExecutor() +} + +#endif // os(WASI) From 14b0e73701ee803beeba75b13ea34c7b5730a53a Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 14 Mar 2025 15:04:38 +0000 Subject: [PATCH 20/29] [Concurrency][Tests] Add missing `traits` symbol to baseline-asserts. Having made it public, we need to add it to baseline-asserts. rdar://141348916 --- test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts | 2 ++ test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index 15e91e8c2b78e..626eacbaeb642 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -1073,6 +1073,8 @@ _$ss5ClockP7convert4froms8DurationVSgAEQz_tFTj _$ss5ClockP7convert4froms8DurationVSgAEQz_tFTq _$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTj _$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTq +_$ss5ClockPsE6traitss0A6TraitsVvg +_$ss5ClockPsE6traitss0A6TraitsVvpMV _$ss5ClockPsE7convert4from8DurationQzSgsAEV_tF _$ss5ClockPsE7convert4froms8DurationVSgAEQz_tF _$ss5ClockPsE7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lF diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index 15e91e8c2b78e..626eacbaeb642 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -1073,6 +1073,8 @@ _$ss5ClockP7convert4froms8DurationVSgAEQz_tFTj _$ss5ClockP7convert4froms8DurationVSgAEQz_tFTq _$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTj _$ss5ClockP7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lFTq +_$ss5ClockPsE6traitss0A6TraitsVvg +_$ss5ClockPsE6traitss0A6TraitsVvpMV _$ss5ClockPsE7convert4from8DurationQzSgsAEV_tF _$ss5ClockPsE7convert4froms8DurationVSgAEQz_tF _$ss5ClockPsE7convert7instant4from7InstantQzSgAFQyd___qd__tsAARd__lF From f0defd83bd142a917ee07c3f3420957f3073ce5e Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 17 Mar 2025 13:08:16 +0000 Subject: [PATCH 21/29] [Concurrency] Add CooperativeExecutor, use it. Also tweak the sleep implementations to let the hooks run if there isn't a `SchedulableExecutor` (for hooked mode). rdar://141348916 --- stdlib/public/Concurrency/CMakeLists.txt | 50 +++-- stdlib/public/Concurrency/Clock.cpp | 76 +++++++ stdlib/public/Concurrency/Clock.swift | 6 + .../Concurrency/CooperativeExecutor.swift | 184 ++++++++++++++++ stdlib/public/Concurrency/DummyExecutor.swift | 4 +- ...wift => PlatformExecutorCooperative.swift} | 12 +- .../PlatformExecutorEmbedded.swift | 55 ----- .../Concurrency/PlatformExecutorNone.swift | 35 +--- stdlib/public/Concurrency/PriorityQueue.swift | 198 ++++++++++++++++++ stdlib/public/Concurrency/Task.swift | 2 +- stdlib/public/Concurrency/TaskSleep.swift | 38 ++-- .../Concurrency/TaskSleepDuration.swift | 68 +++++- 12 files changed, 585 insertions(+), 143 deletions(-) create mode 100644 stdlib/public/Concurrency/CooperativeExecutor.swift rename stdlib/public/Concurrency/{PlatformExecutorWASI.swift => PlatformExecutorCooperative.swift} (72%) delete mode 100644 stdlib/public/Concurrency/PlatformExecutorEmbedded.swift create mode 100644 stdlib/public/Concurrency/PriorityQueue.swift diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index d9699ce50bad4..72996020bde7c 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -93,18 +93,6 @@ set(SWIFT_RUNTIME_CONCURRENCY_C_SOURCES linker-support/magic-symbols-for-install-name.c ) -set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES) -if("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "dispatch") - set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES - DispatchGlobalExecutor.cpp - ) -endif() - -set(LLVM_OPTIONAL_SOURCES - CooperativeGlobalExecutor.cpp - DispatchGlobalExecutor.cpp -) - set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES Actor.swift AsyncLet.swift @@ -133,6 +121,7 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES GlobalActor.swift GlobalConcurrentExecutor.swift MainActor.swift + PriorityQueue.swift SourceCompatibilityShims.swift Task.swift Task+PriorityEscalation.swift @@ -169,21 +158,44 @@ set(SWIFT_RUNTIME_CONCURRENCY_SWIFT_SOURCES ContinuousClock.swift SuspendingClock.swift TaskSleepDuration.swift - DispatchExecutor.swift - CFExecutor.swift DummyExecutor.swift + CooperativeExecutor.swift PlatformExecutorDarwin.swift PlatformExecutorLinux.swift PlatformExecutorWindows.swift - PlatformExecutorWASI.swift ) -set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES - ExecutorImpl.swift -) +set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES) +set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES) +if("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "dispatch") + set(SWIFT_RUNTIME_CONCURRENCY_EXECUTOR_SOURCES + DispatchGlobalExecutor.cpp + ) + set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES + DispatchExecutor.swift + CFExecutor.swift + ExecutorImpl.swift + ) +elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "singlethreaded") + set(SWIFT_RUNTIME_CONCURRENCYU_NONEMBEDDED_SWIFT_SOURCES + ExecutorImpl.swift + PlatformExecutorCooperative.swift + ) +else() + set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES + ExecutorImpl.swift + PlatformExecutorNone.swift + ) +endif() set(SWIFT_RUNTIME_CONCURRENCY_EMBEDDED_SWIFT_SOURCES - PlatformExecutorEmbedded.swift + PlatformExecutorNone.swift + ) + +set(LLVM_OPTIONAL_SOURCES + DispatchGlobalExecutor.cpp + CooperativeGlobalExecutor.cpp + DispatchGlobalExecutor.cpp ) add_swift_target_library(swift_Concurrency ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB diff --git a/stdlib/public/Concurrency/Clock.cpp b/stdlib/public/Concurrency/Clock.cpp index 2c9e43c8f4cdd..908e05fbc01a0 100644 --- a/stdlib/public/Concurrency/Clock.cpp +++ b/stdlib/public/Concurrency/Clock.cpp @@ -21,6 +21,16 @@ #include #endif +#if __has_include() +#define WE_HAVE_STD_CHRONO 1 +#include + +#if __has_include() +#define WE_HAVE_STD_THIS_THREAD 1 +#include +#endif +#endif // __has_include() + #include "Error.h" using namespace swift; @@ -50,6 +60,14 @@ void swift_get_time( (void)QueryInterruptTimePrecise(&interruptTime); continuous.tv_sec = interruptTime / 10'000'000; continuous.tv_nsec = (interruptTime % 10'000'000) * 100; +#elif WE_HAVE_STD_CHRONO + auto now = std::chrono::steady_clock::now(); + auto epoch = std::chrono::steady_clock::min(); + auto timeSinceEpoch = now - epoch; + auto sec = std::chrono::duration_cast(timeSinceEpoch); + auto ns = std::chrono::duration_cast(timeSinceEpoch - sec); + continuous.tv_sec = sec; + continuous.tv_nsec = ns; #else #error Missing platform continuous time definition #endif @@ -77,6 +95,14 @@ void swift_get_time( (void)QueryUnbiasedInterruptTimePrecise(&unbiasedTime); suspending.tv_sec = unbiasedTime / 10'000'000; suspending.tv_nsec = (unbiasedTime % 10'000'000) * 100; +#elif WE_HAVE_STD_CHRONO + auto now = std::chrono::steady_clock::now(); + auto epoch = std::chrono::steady_clock::min(); + auto timeSinceEpoch = now - epoch; + auto sec = std::chrono::duration_cast(timeSinceEpoch); + auto ns = std::chrono::duration_cast(timeSinceEpoch - sec); + suspending.tv_sec = sec; + suspending.tv_nsec = ns; #else #error Missing platform suspending time definition #endif @@ -107,6 +133,11 @@ switch (clock_id) { #elif defined(_WIN32) continuous.tv_sec = 0; continuous.tv_nsec = 100; +#elif WE_HAVE_STD_CHRONO + auto num = std::chrono::steady_clock::period::num; + auto den = std::chrono::steady_clock::period::den; + continuous.tv_sec = num / den; + continuous.tv_nsec = (num * 1000000000ll) % den #else #error Missing platform continuous time definition #endif @@ -127,6 +158,11 @@ switch (clock_id) { #elif defined(_WIN32) suspending.tv_sec = 0; suspending.tv_nsec = 100; +#elif WE_HAVE_STD_CHRONO + auto num = std::chrono::steady_clock::period::num; + auto den = std::chrono::steady_clock::period::den; + continuous.tv_sec = num / den; + continuous.tv_nsec = (num * 1'000'000'000ll) % den #else #error Missing platform suspending time definition #endif @@ -138,3 +174,43 @@ switch (clock_id) { swift_Concurrency_fatalError(0, "Fatal error: invalid clock ID %d\n", clock_id); } + +SWIFT_EXPORT_FROM(swift_Concurrency) +SWIFT_CC(swift) +void swift_sleep( + long long seconds, + long long nanoseconds) { +#if defined(_WIN32) + ULONGLONG now; + (void)QueryInterruptTimePrecise(&now); + ULONGLONG delay = seconds * 10'000'000 + nanoseconds / 100; + ULONGLONG deadline = now + delay; + while (deadline > now) { + DWORD dwMsec = delay / 10'000; + + // For sleeps over 15ms, Windows may return up to 15ms early(!); + // for sleeps less than 15ms, Windows does a delay koop internally, + // which is acceptable here. + if (dwMsec > 15) + deMsec += 15; + + (void)SleepEx(dwMsec, TRUE); + (void)QueryInterruptTimePrecise(&now); + delay = deadline - now; + } +#elif defined(__linux__) || defined(__APPLE__) || defined(__wasi__) \ + || defined(__OpenBSD) || defined(__FreeBSD__) + struct timespec ts; + ts.tv_sec = seconds; + ts.tv_nsec = nanoseconds; + while (nanosleep(&ts, &ts) == -1 && errno == EINTR); +#elif WE_HAVE_STD_THIS_THREAD && !defined(SWIFT_THREADING_NONE) + auto duration + = std::chrono::duration_cast( + std::chrono::seconds(seconds) + std::chrono::nanoseconds(nanoseconds) + ); + std::this_thread::sleep_for(duration); +#else + #error Missing platform sleep definition +#endif +} diff --git a/stdlib/public/Concurrency/Clock.swift b/stdlib/public/Concurrency/Clock.swift index aa6805b946d3f..b1c790c55716d 100644 --- a/stdlib/public/Concurrency/Clock.swift +++ b/stdlib/public/Concurrency/Clock.swift @@ -243,3 +243,9 @@ internal func _getClockRes( seconds: UnsafeMutablePointer, nanoseconds: UnsafeMutablePointer, clock: CInt) + +@available(SwiftStdlib 6.2, *) +@_silgen_name("swift_sleep") +internal func _sleep( + seconds: Int64, + nanoseconds: Int64) diff --git a/stdlib/public/Concurrency/CooperativeExecutor.swift b/stdlib/public/Concurrency/CooperativeExecutor.swift new file mode 100644 index 0000000000000..de43939cf20c8 --- /dev/null +++ b/stdlib/public/Concurrency/CooperativeExecutor.swift @@ -0,0 +1,184 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +extension ExecutorJob { + fileprivate var cooperativeExecutorTimestamp: CooperativeExecutor.Timestamp { + get { + return unsafe withUnsafeExecutorPrivateData { + return unsafe $0.assumingMemoryBound( + to: CooperativeExecutor.Timestamp.self + )[0] + } + } + set { + unsafe withUnsafeExecutorPrivateData { + unsafe $0.withMemoryRebound(to: CooperativeExecutor.Timestamp.self) { + unsafe $0[0] = newValue + } + } + } + } +} + +/// A co-operative executor that can be used as the main executor or as a +/// task executor. +class CooperativeExecutor: Executor, @unchecked Sendable { + var runQueue: PriorityQueue + var waitQueue: PriorityQueue + var shouldStop: Bool = false + + /// Internal representation of a duration for CooperativeExecutor + struct Duration { + var seconds: Int64 + var nanoseconds: Int64 + + init(seconds: Int64, nanoseconds: Int64) { + self.seconds = seconds + self.nanoseconds = nanoseconds + } + + init(from duration: Swift.Duration) { + let (seconds, attoseconds) = duration.components + self.seconds = seconds + self.nanoseconds = attoseconds / 1_000_000_000 + } + } + + /// Internal representation of a timestamp for CooperativeExecutor + struct Timestamp: Comparable { + var seconds: Int64 + var nanoseconds: Int64 + + static var zero: Timestamp { + return Timestamp(seconds: 0, nanoseconds: 0) + } + + static func == (lhs: Timestamp, rhs: Timestamp) -> Bool { + return lhs.seconds == rhs.seconds && lhs.nanoseconds == rhs.nanoseconds + } + static func < (lhs: Timestamp, rhs: Timestamp) -> Bool { + return lhs.seconds < rhs.seconds || ( + lhs.seconds == rhs.seconds + && lhs.nanoseconds < rhs.nanoseconds + ) + } + static func - (lhs: Timestamp, rhs: Timestamp) -> Duration { + if lhs.nanoseconds < rhs.nanoseconds { + return Duration(seconds: lhs.seconds - rhs.seconds - 1, + nanoseconds: 1_000_000_000 + lhs.nanoseconds + - rhs.nanoseconds) + } + return Duration(seconds: lhs.seconds - rhs.seconds, + nanoseconds: lhs.nanoseconds - rhs.nanoseconds) + } + static func + (lhs: Timestamp, rhs: Duration) -> Timestamp { + var seconds = lhs.seconds + rhs.seconds + var nanoseconds = lhs.nanoseconds + rhs.nanoseconds + // Normally will run only once + while nanoseconds > 1_000_000_000 { + seconds += 1 + nanoseconds -= 1_000_000_000 + } + return Timestamp(seconds: seconds, nanoseconds: nanoseconds) + } + } + + public init() { + runQueue = PriorityQueue(compare: { $0.priority > $1.priority }) + waitQueue = + PriorityQueue(compare: { + ExecutorJob($0).cooperativeExecutorTimestamp + < ExecutorJob($1).cooperativeExecutorTimestamp + }) + } + + public func enqueue(_ job: consuming ExecutorJob) { + runQueue.push(UnownedJob(job)) + } + + public var isMainExecutor: Bool { true } + + public var asSchedulable: any SchedulableExecutor { self } +} + +extension CooperativeExecutor: SchedulableExecutor { + var currentTime: Timestamp { + var now: Timestamp = .zero + unsafe _getTime(seconds: &now.seconds, + nanoseconds: &now.nanoseconds, + clock: _ClockID.suspending.rawValue) + return now + } + + public func enqueue(_ job: consuming ExecutorJob, + after delay: C.Duration, + tolerance: C.Duration? = nil, + clock: C) { + let duration = Duration(from: clock.convert(from: delay)!) + let deadline = self.currentTime + duration + + job.cooperativeExecutorTimestamp = deadline + waitQueue.push(UnownedJob(job)) + } +} + +extension CooperativeExecutor: RunLoopExecutor { + public func run() throws { + try runUntil { false } + } + + public func runUntil(_ condition: () -> Bool) throws { + shouldStop = false + while !shouldStop && !condition() { + // Process the timer queue + let now = currentTime + while let job = waitQueue.pop(when: { + ExecutorJob($0).cooperativeExecutorTimestamp <= now + }) { + runQueue.push(job) + } + + // Now run any queued jobs + while let job = runQueue.pop() { + unsafe ExecutorJob(job).runSynchronously( + on: self.asUnownedSerialExecutor() + ) + } + + // Finally, wait until the next deadline + if let job = waitQueue.top { + let deadline = ExecutorJob(job).cooperativeExecutorTimestamp + let now = self.currentTime + if deadline > now { + let toWait = deadline - now + _sleep(seconds: toWait.seconds, + nanoseconds: toWait.nanoseconds) + } + } else { + // Stop if no more jobs are available + break + } + } + } + + public func stop() { + shouldStop = true + } +} + +extension CooperativeExecutor: SerialExecutor {} + +extension CooperativeExecutor: TaskExecutor {} + +extension CooperativeExecutor: MainExecutor {} diff --git a/stdlib/public/Concurrency/DummyExecutor.swift b/stdlib/public/Concurrency/DummyExecutor.swift index 628b035ab2255..ca664292c0e9e 100644 --- a/stdlib/public/Concurrency/DummyExecutor.swift +++ b/stdlib/public/Concurrency/DummyExecutor.swift @@ -15,7 +15,7 @@ import Swift // .. Main Executor ............................................................ @available(SwiftStdlib 6.2, *) -public class DummyMainExecutor: MainExecutor, @unchecked Sendable { +public final class DummyMainExecutor: MainExecutor, @unchecked Sendable { public init() {} public func run() throws { @@ -40,7 +40,7 @@ public class DummyMainExecutor: MainExecutor, @unchecked Sendable { // .. Task Executor ............................................................ @available(SwiftStdlib 6.2, *) -public class DummyTaskExecutor: TaskExecutor, @unchecked Sendable { +public final class DummyTaskExecutor: TaskExecutor, @unchecked Sendable { public init() {} public func enqueue(_ job: consuming ExecutorJob) { diff --git a/stdlib/public/Concurrency/PlatformExecutorWASI.swift b/stdlib/public/Concurrency/PlatformExecutorCooperative.swift similarity index 72% rename from stdlib/public/Concurrency/PlatformExecutorWASI.swift rename to stdlib/public/Concurrency/PlatformExecutorCooperative.swift index 93f40262a4c83..395154bf26310 100644 --- a/stdlib/public/Concurrency/PlatformExecutorWASI.swift +++ b/stdlib/public/Concurrency/PlatformExecutorCooperative.swift @@ -10,16 +10,12 @@ // //===----------------------------------------------------------------------===// -#if os(WASI) - import Swift -// The default executors for now are Dispatch-based +// This platform uses a single, global, CooperativeExecutor @available(SwiftStdlib 6.2, *) public struct PlatformExecutorFactory: ExecutorFactory { - public static let mainExecutor: any MainExecutor = DummyMainExecutor() - public static let defaultExecutor: any TaskExecutor - = DummyTaskExecutor() + static let executor = CooperativeExecutor() + public static let mainExecutor: any MainExecutor { executor } + public static let defaultExecutor: any TaskExecutor { executor } } - -#endif // os(WASI) diff --git a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift b/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift deleted file mode 100644 index b90224a0974ef..0000000000000 --- a/stdlib/public/Concurrency/PlatformExecutorEmbedded.swift +++ /dev/null @@ -1,55 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// This source file is part of the Swift.org open source project -// -// Copyright (c) 2020 - 2025 Apple Inc. and the Swift project authors -// Licensed under Apache License v2.0 with Runtime Library Exception -// -// See https://swift.org/LICENSE.txt for license information -// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -// -//===----------------------------------------------------------------------===// - -import Swift - -@available(SwiftStdlib 6.2, *) -public struct PlatformExecutorFactory: ExecutorFactory { - public static let mainExecutor: any MainExecutor = EmbeddedMainExecutor() - public static let defaultExecutor: any TaskExecutor = EmbeddedDefaultExecutor() -} - -// .. Platform Main Executor ................................................... - -@available(SwiftStdlib 6.2, *) -public final class EmbeddedMainExecutor: MainExecutor, @unchecked Sendable { - - public init() {} - - public func enqueue(_ job: consuming ExecutorJob) { - } - - // We can't implement enqueue in Embedde Swift because we aren't - // allowed to have generics in an existential there. - - public func run() throws { - } - - public func runUntil(_ condition: () -> Bool) throws { - } - - public func stop() { - } - -} - -// .. Platform Task Executor ................................................... - -@available(SwiftStdlib 6.2, *) -public final class EmbeddedDefaultExecutor: TaskExecutor, @unchecked Sendable { - - public init() {} - - public func enqueue(_ job: consuming ExecutorJob) { - } - -} diff --git a/stdlib/public/Concurrency/PlatformExecutorNone.swift b/stdlib/public/Concurrency/PlatformExecutorNone.swift index 0d311492356bb..a524f07005d85 100644 --- a/stdlib/public/Concurrency/PlatformExecutorNone.swift +++ b/stdlib/public/Concurrency/PlatformExecutorNone.swift @@ -12,37 +12,8 @@ import Swift -// .. Platform Main Executor ................................................... - -/// `PlatformMainExecutor` is used during program start-up and also for any -/// `@MainActor` code. It implements `SerialExecutor`, `RunLoopExecutor` and -/// `EventableExecutor`. @available(SwiftStdlib 6.2, *) -public class PlatformMainExecutor: MainExecutor, @unchecked Sendable { - - public func run() throws { - fatalError("There is no main executor implementation for this platform") - } - - public func stop() { - fatalError("There is no main executor implementation for this platform") - } - - public func enqueue(_ job: consuming ExecutorJob) { - fatalError("There is no main executor implementation for this platform") - } - -} - -// .. Platform Default Executor ................................................ - -/// `PlatformDefaultExecutor` is the default executor for non-`@MainActor` -/// tasks. It implements `TaskExecutor` only. -@available(SwiftStdlib 6.2, *) -public class PlatformDefaultExecutor: TaskExecutor, @unchecked Sendable { - - public func enqueue(_ job: consuming ExecutorJob) { - fatalError("There is no default executor implementation for this platform") - } - +public struct PlatformExecutorFactory: ExecutorFactory { + public static let mainExecutor: any MainExecutor = DummyMainExecutor() + public static let defaultExecutor: any TaskExecutor = DummyTaskExecutor() } diff --git a/stdlib/public/Concurrency/PriorityQueue.swift b/stdlib/public/Concurrency/PriorityQueue.swift new file mode 100644 index 0000000000000..744c9c4c257a2 --- /dev/null +++ b/stdlib/public/Concurrency/PriorityQueue.swift @@ -0,0 +1,198 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2025 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import Swift + +/// A generic priority queue, with a user-defined comparison function. +struct PriorityQueue { + /// The backing store. + var storage: [T] = [] + + /// The comparison function; this should compare the priorities of + /// two items in the queue, returning `true` if they satisfy the + /// desired condition. + /// + /// A `>` test will produce a max-queue, while a `<` test will + /// result in a min-queue. + var compare: (borrowing T, borrowing T) -> Bool + + /// Initialise the queue. + /// + /// The queue is set up such that if the comparison function in use + /// is `>`, it will be a max-queue, while if the comparison function + /// is `<`, it will be a min-queue. + /// + /// Parameters: + /// + /// - compare: A closure that takes two arguments of type T, and + /// returns true if some condition holds. + /// + init(compare: @escaping (borrowing T, borrowing T) -> Bool) { + self.compare = compare + } + + /// Push an item onto the queue. + /// + /// Parameters: + /// + /// - _ value: The item to push onto the queue. + /// + mutating func push(_ value: T) { + storage.append(value) + upHeap(ndx: storage.count - 1) + } + + /// The highest priority item from the queue, or `nil` if none. + var top: T? { + if storage.isEmpty { + return nil + } + return storage[0] + } + + /// Conditionally pop the highest priority item from the queue. + /// + /// If the comparison function is `>`, this will return the largest + /// item in the queue. If the comparison function is `<`, it will + /// return the smallest. + /// + /// Parameters: + /// + /// - when: A closure that allows code to test the top item before + /// popping. + /// + /// Returns: The next item in the queue, following the comparison + /// rule. + /// + mutating func pop(when condition: (borrowing T) -> Bool) -> T? { + if storage.isEmpty { + return nil + } + if !condition(storage[0]) { + return nil + } + storage.swapAt(0, storage.count - 1) + let result = storage.removeLast() + if !storage.isEmpty { + downHeap(ndx: 0) + } + return result + } + + /// Pop the highest priority item from the queue. + /// + /// If the comparison function is `>`, this will return the largest + /// item in the queue. If the comparison function is `<`, it will + /// return the smallest. + /// + /// Returns: The next item in the queue, following the comparison + /// rule. + /// + mutating func pop() -> T? { + if storage.isEmpty { + return nil + } + storage.swapAt(0, storage.count - 1) + let result = storage.removeLast() + if !storage.isEmpty { + downHeap(ndx: 0) + } + return result + } + + /// Fix the heap condition by iterating upwards. + /// + /// Iterate upwards from the specified entry, exchanging it with + /// its parent if the parent and child do not satisfy the comparison + /// function. + /// + /// This is used when pushing items onto the queue. + /// + /// Parameters: + /// + /// - ndx: The index at which to start. + /// + private mutating func upHeap(ndx: Int) { + var theNdx = ndx + while theNdx > 0 { + let parentNdx = theNdx / 2 + + if !compare(storage[theNdx], storage[parentNdx]) { + break + } + + storage.swapAt(theNdx, parentNdx) + theNdx = parentNdx + } + } + + /// Fix the heap condition by iterating downwards. + /// + /// Iterate downwards from the specified entry, checking that its + /// children satisfy the comparison function. If they do, stop, + /// otherwise exchange the parent entry with the highest priority + /// child. + /// + /// This is used when popping items from the queue. + /// + /// Parameters: + /// + /// - ndx: The index at which to start. + /// + private mutating func downHeap(ndx: Int) { + var theNdx = ndx + while true { + let leftNdx = 2 * theNdx + + if leftNdx >= storage.count { + break + } + + let rightNdx = 2 * theNdx + 1 + var largestNdx = theNdx + + if compare(storage[leftNdx], storage[largestNdx]) { + largestNdx = leftNdx + } + + if rightNdx < storage.count + && compare(storage[rightNdx], storage[largestNdx]) { + largestNdx = rightNdx + } + + if largestNdx == theNdx { + break + } + + storage.swapAt(theNdx, largestNdx) + theNdx = largestNdx + } + } +} + +extension PriorityQueue where T: Comparable { + /// Initialise the priority queue. + /// + /// `Comparable` types have a default implementation, which passes the + /// `>` operator as the comparison function, thus providing a max-queue. + /// + /// If you are using a `Comparable` type and require a min-queue, you + /// can make one with + /// + /// ```swift + /// let minQueue = PriorityQueue(compare: <) + /// ``` + /// + init() { + self.init(compare: >) + } +} diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index e8481dbb22f12..7eb1b0648a082 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -1256,7 +1256,7 @@ extension Task where Success == Never, Failure == Never { #if !$Embedded if #available(SwiftStdlib 6.2, *) { - let executor = Task.currentExecutor ?? Task.defaultExecutor + let executor = Task.currentExecutor executor.enqueue(ExecutorJob(context: job)) } else { diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift index bf42e6387a08a..e5d23b80c2036 100644 --- a/stdlib/public/Concurrency/TaskSleep.swift +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -29,17 +29,19 @@ extension Task where Success == Never, Failure == Never { continuation: continuation) if #available(SwiftStdlib 6.2, *) { - let executor = Task.currentSchedulableExecutor #if !$Embedded - executor!.enqueue(ExecutorJob(context: job), - after: .nanoseconds(duration), - clock: .continuous) + if let executor = Task.currentSchedulableExecutor { + executor.enqueue(ExecutorJob(context: job), + after: .nanoseconds(duration), + clock: .continuous) + return + } #endif - } else { - // Since we're building the new version of the stdlib, we should - // never get here. - Builtin.unreachable() } + + // If there is no current schedulable executor, fall back to + // _enqueueJobGlobalWithDelay() + _enqueueJobGlobalWithDelay(duration, job) } } @@ -268,18 +270,22 @@ extension Task where Success == Never, Failure == Never { unsafe onSleepWake(token) } + let job = ExecutorJob(context: + Builtin.convertTaskToJob(sleepTask)) if #available(SwiftStdlib 6.2, *) { - let executor = Task.currentSchedulableExecutor - let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) #if !$Embedded - executor!.enqueue(job, - after: .nanoseconds(duration), - clock: .continuous) + if let executor = Task.currentSchedulableExecutor { + executor.enqueue(job, + after: .nanoseconds(duration), + clock: .continuous) + return + } #endif - } else { - // Shouldn't be able to get here - Builtin.unreachable() } + + // If there is no current schedulable executor, fall back to + // _enqueueJobGlobalWithDelay() + _enqueueJobGlobalWithDelay(duration, UnownedJob(job)) return case .activeContinuation, .finished: diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift index 4b8d279f9207d..f95eddf746e20 100644 --- a/stdlib/public/Concurrency/TaskSleepDuration.swift +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -13,6 +13,36 @@ import Swift #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +fileprivate func timestamp(for instant: C.Instant, clock: C) + -> (clockID: _ClockID, seconds: Int64, nanoseconds: Int64) { + var clockID: _ClockID + if clock.traits.contains(.continuous) { + clockID = .continuous + } else { + clockID = .suspending + } + + var seconds: Int64 = 0 + var nanoseconds: Int64 = 0 + unsafe _getTime(seconds: &seconds, + nanoseconds: &nanoseconds, + clock: clockID.rawValue) + + let delta = clock.convert(from: clock.now.duration(to: instant))! + let (deltaSeconds, deltaAttoseconds) = delta.components + let deltaNanoseconds = deltaAttoseconds / 1_000_000_000 + seconds += deltaSeconds + nanoseconds += deltaNanoseconds + if nanoseconds > 1_000_000_000 { + seconds += 1 + nanoseconds -= 1_000_000_000 + } + + return (clockID: clockID, + seconds: Int64(seconds), + nanoseconds: Int64(nanoseconds)) +} + @available(SwiftStdlib 5.7, *) @_unavailableInEmbedded extension Task where Success == Never, Failure == Never { @@ -54,21 +84,39 @@ extension Task where Success == Never, Failure == Never { unsafe onSleepWake(token) } - if #available(SwiftStdlib 6.2, *) { - let executor = Task.currentSchedulableExecutor - let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) + let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) + if #available(SwiftStdlib 6.2, *) { #if !$Embedded - executor!.enqueue(job, - at: instant, - tolerance: tolerance, - clock: clock) + if let executor = Task.currentSchedulableExecutor { + executor.enqueue(job, + at: instant, + tolerance: tolerance, + clock: clock) + return + } #endif + } + + // If there is no current schedulable executor, fall back to + // calling _enqueueJobGlobalWithDeadline(). + let (clockID, seconds, nanoseconds) = timestamp(for: instant, + clock: clock) + let toleranceSeconds: Int64 + let toleranceNanoseconds: Int64 + if let tolerance = tolerance, + let components = clock.convert(from: tolerance)?.components { + toleranceSeconds = components.seconds + toleranceNanoseconds = components.attoseconds / 1_000_000_000 } else { - // Since we're building the new version of the stdlib, - // we should never get here - Builtin.unreachable() + toleranceSeconds = 0 + toleranceNanoseconds = -1 } + + _enqueueJobGlobalWithDeadline( + seconds, nanoseconds, + toleranceSeconds, toleranceNanoseconds, + clockID.rawValue, UnownedJob(job)) return case .activeContinuation, .finished: From ed08858998bfabcc6cd2b29e0b006f571c80b0b9 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 17 Mar 2025 16:24:06 +0000 Subject: [PATCH 22/29] [Concurrency] Fix availability. Fix up availability after the non-Darwin changes. Also update the ABI baseline. Fix a Win32 typo. rdar://141348916 --- stdlib/public/Concurrency/Clock.cpp | 2 +- .../Concurrency/CooperativeExecutor.swift | 7 +++ stdlib/public/Concurrency/ExecutorBridge.cpp | 4 +- stdlib/public/Concurrency/Task.swift | 6 +-- stdlib/public/Concurrency/TaskSleep.swift | 8 ++-- .../Concurrency/TaskSleepDuration.swift | 48 +++++++++++++------ .../macOS/arm64/concurrency/baseline-asserts | 48 ++++++++++++++++++- .../macOS/x86_64/concurrency/baseline-asserts | 48 ++++++++++++++++++- 8 files changed, 142 insertions(+), 29 deletions(-) diff --git a/stdlib/public/Concurrency/Clock.cpp b/stdlib/public/Concurrency/Clock.cpp index 908e05fbc01a0..0ae357a5ddad5 100644 --- a/stdlib/public/Concurrency/Clock.cpp +++ b/stdlib/public/Concurrency/Clock.cpp @@ -192,7 +192,7 @@ void swift_sleep( // for sleeps less than 15ms, Windows does a delay koop internally, // which is acceptable here. if (dwMsec > 15) - deMsec += 15; + dwMsec += 15; (void)SleepEx(dwMsec, TRUE); (void)QueryInterruptTimePrecise(&now); diff --git a/stdlib/public/Concurrency/CooperativeExecutor.swift b/stdlib/public/Concurrency/CooperativeExecutor.swift index de43939cf20c8..50f5819f0747a 100644 --- a/stdlib/public/Concurrency/CooperativeExecutor.swift +++ b/stdlib/public/Concurrency/CooperativeExecutor.swift @@ -12,6 +12,7 @@ import Swift +@available(SwiftStdlib 6.2, *) extension ExecutorJob { fileprivate var cooperativeExecutorTimestamp: CooperativeExecutor.Timestamp { get { @@ -33,6 +34,7 @@ extension ExecutorJob { /// A co-operative executor that can be used as the main executor or as a /// task executor. +@available(SwiftStdlib 6.2, *) class CooperativeExecutor: Executor, @unchecked Sendable { var runQueue: PriorityQueue var waitQueue: PriorityQueue @@ -112,6 +114,7 @@ class CooperativeExecutor: Executor, @unchecked Sendable { public var asSchedulable: any SchedulableExecutor { self } } +@available(SwiftStdlib 6.2, *) extension CooperativeExecutor: SchedulableExecutor { var currentTime: Timestamp { var now: Timestamp = .zero @@ -133,6 +136,7 @@ extension CooperativeExecutor: SchedulableExecutor { } } +@available(SwiftStdlib 6.2, *) extension CooperativeExecutor: RunLoopExecutor { public func run() throws { try runUntil { false } @@ -177,8 +181,11 @@ extension CooperativeExecutor: RunLoopExecutor { } } +@available(SwiftStdlib 6.2, *) extension CooperativeExecutor: SerialExecutor {} +@available(SwiftStdlib 6.2, *) extension CooperativeExecutor: TaskExecutor {} +@available(SwiftStdlib 6.2, *) extension CooperativeExecutor: MainExecutor {} diff --git a/stdlib/public/Concurrency/ExecutorBridge.cpp b/stdlib/public/Concurrency/ExecutorBridge.cpp index 9faf70b136a75..50595d7180b82 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.cpp +++ b/stdlib/public/Concurrency/ExecutorBridge.cpp @@ -40,7 +40,7 @@ void _swift_task_checkIsolatedSwift(HeapObject *executor, const SerialExecutorWitnessTable *witnessTable); extern "C" SWIFT_CC(swift) -void _swift_task_isIsolatingCurrentContextSwift( +bool _swift_task_isIsolatingCurrentContextSwift( HeapObject *executor, const Metadata *executorType, const SerialExecutorWitnessTable *witnessTable @@ -75,7 +75,7 @@ void swift_task_checkIsolatedImpl(SerialExecutorRef executor) { } extern "C" SWIFT_CC(swift) -void swift_task_isIsolatingCurrentContextImpl(SerialExecutorRef executor) { +bool swift_task_isIsolatingCurrentContextImpl(SerialExecutorRef executor) { HeapObject *identity = executor.getIdentity(); // We might be being called with an actor rather than a "proper" diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 7eb1b0648a082..023193428285e 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -1425,8 +1425,7 @@ func getJobFlags(_ task: Builtin.NativeObject) -> JobFlags @usableFromInline func _enqueueJobGlobal(_ task: Builtin.Job) -@available(SwiftStdlib 6.2, *) -@usableFromInline +@available(SwiftStdlib 5.9, *) func _enqueueJobGlobal(_ task: UnownedJob) { _enqueueJobGlobal(task._context) } @@ -1436,8 +1435,7 @@ func _enqueueJobGlobal(_ task: UnownedJob) { @usableFromInline func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: Builtin.Job) -@available(SwiftStdlib 6.2, *) -@usableFromInline +@available(SwiftStdlib 5.9, *) func _enqueueJobGlobalWithDelay(_ delay: UInt64, _ task: UnownedJob) { return _enqueueJobGlobalWithDelay(delay, task._context) } diff --git a/stdlib/public/Concurrency/TaskSleep.swift b/stdlib/public/Concurrency/TaskSleep.swift index e5d23b80c2036..653a343c546d8 100644 --- a/stdlib/public/Concurrency/TaskSleep.swift +++ b/stdlib/public/Concurrency/TaskSleep.swift @@ -270,12 +270,12 @@ extension Task where Success == Never, Failure == Never { unsafe onSleepWake(token) } - let job = ExecutorJob(context: - Builtin.convertTaskToJob(sleepTask)) + let job = Builtin.convertTaskToJob(sleepTask) + if #available(SwiftStdlib 6.2, *) { #if !$Embedded if let executor = Task.currentSchedulableExecutor { - executor.enqueue(job, + executor.enqueue(ExecutorJob(context: job), after: .nanoseconds(duration), clock: .continuous) return @@ -285,7 +285,7 @@ extension Task where Success == Never, Failure == Never { // If there is no current schedulable executor, fall back to // _enqueueJobGlobalWithDelay() - _enqueueJobGlobalWithDelay(duration, UnownedJob(job)) + _enqueueJobGlobalWithDelay(duration, job) return case .activeContinuation, .finished: diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift index f95eddf746e20..e5d1acc4175b2 100644 --- a/stdlib/public/Concurrency/TaskSleepDuration.swift +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -16,10 +16,14 @@ import Swift fileprivate func timestamp(for instant: C.Instant, clock: C) -> (clockID: _ClockID, seconds: Int64, nanoseconds: Int64) { var clockID: _ClockID - if clock.traits.contains(.continuous) { - clockID = .continuous + if #available(SwiftStdlib 6.2, *) { + if clock.traits.contains(.continuous) { + clockID = .continuous + } else { + clockID = .suspending + } } else { - clockID = .suspending + Builtin.unreachable() } var seconds: Int64 = 0 @@ -28,7 +32,13 @@ fileprivate func timestamp(for instant: C.Instant, clock: C) nanoseconds: &nanoseconds, clock: clockID.rawValue) - let delta = clock.convert(from: clock.now.duration(to: instant))! + let delta: Swift.Duration + if #available(SwiftStdlib 6.2, *) { + delta = clock.convert(from: clock.now.duration(to: instant))! + } else { + Builtin.unreachable() + } + let (deltaSeconds, deltaAttoseconds) = delta.components let deltaNanoseconds = deltaAttoseconds / 1_000_000_000 seconds += deltaSeconds @@ -84,18 +94,20 @@ extension Task where Success == Never, Failure == Never { unsafe onSleepWake(token) } - let job = ExecutorJob(context: Builtin.convertTaskToJob(sleepTask)) + let job = Builtin.convertTaskToJob(sleepTask) if #available(SwiftStdlib 6.2, *) { #if !$Embedded if let executor = Task.currentSchedulableExecutor { - executor.enqueue(job, + executor.enqueue(ExecutorJob(context: job), at: instant, tolerance: tolerance, clock: clock) return } #endif + } else { + Builtin.unreachable() } // If there is no current schedulable executor, fall back to @@ -104,19 +116,27 @@ extension Task where Success == Never, Failure == Never { clock: clock) let toleranceSeconds: Int64 let toleranceNanoseconds: Int64 - if let tolerance = tolerance, - let components = clock.convert(from: tolerance)?.components { - toleranceSeconds = components.seconds - toleranceNanoseconds = components.attoseconds / 1_000_000_000 + if #available(SwiftStdlib 6.2, *) { + if let tolerance = tolerance, + let components = clock.convert(from: tolerance)?.components { + toleranceSeconds = components.seconds + toleranceNanoseconds = components.attoseconds / 1_000_000_000 + } else { + toleranceSeconds = 0 + toleranceNanoseconds = -1 + } } else { - toleranceSeconds = 0 - toleranceNanoseconds = -1 + Builtin.unreachable() } - _enqueueJobGlobalWithDeadline( + if #available(SwiftStdlib 5.9, *) { + _enqueueJobGlobalWithDeadline( seconds, nanoseconds, toleranceSeconds, toleranceNanoseconds, - clockID.rawValue, UnownedJob(job)) + clockID.rawValue, UnownedJob(context: job)) + } else { + Builtin.unreachable() + } return case .activeContinuation, .finished: diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index 626eacbaeb642..669cb9d55f8b0 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -605,7 +605,51 @@ _$ss16AsyncMapSequenceVMa _$ss16AsyncMapSequenceVMn _$ss16AsyncMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss16AsyncMapSequenceVyxq_GScisMc -_$ss17_enqueueJobGlobalyyScJF +_$ss17DummyMainExecutorC02isbC0Sbvg +_$ss17DummyMainExecutorC02isbC0SbvpMV +_$ss17DummyMainExecutorC13checkIsolatedyyF +_$ss17DummyMainExecutorC3runyyKF +_$ss17DummyMainExecutorC4stopyyF +_$ss17DummyMainExecutorC7enqueueyys0C3JobVnF +_$ss17DummyMainExecutorCABycfC +_$ss17DummyMainExecutorCABycfCTj +_$ss17DummyMainExecutorCABycfCTq +_$ss17DummyMainExecutorCABycfc +_$ss17DummyMainExecutorCMa +_$ss17DummyMainExecutorCMm +_$ss17DummyMainExecutorCMn +_$ss17DummyMainExecutorCMo +_$ss17DummyMainExecutorCMu +_$ss17DummyMainExecutorCN +_$ss17DummyMainExecutorCScFsMc +_$ss17DummyMainExecutorCScFsWP +_$ss17DummyMainExecutorCScfsMc +_$ss17DummyMainExecutorCScfsWP +_$ss17DummyMainExecutorCfD +_$ss17DummyMainExecutorCfd +_$ss17DummyMainExecutorCs07RunLoopC0sMc +_$ss17DummyMainExecutorCs07RunLoopC0sWP +_$ss17DummyMainExecutorCs0bC0sMc +_$ss17DummyMainExecutorCs0bC0sWP +_$ss17DummyTaskExecutorC06isMainC0Sbvg +_$ss17DummyTaskExecutorC06isMainC0SbvpMV +_$ss17DummyTaskExecutorC7enqueueyys0C3JobVnF +_$ss17DummyTaskExecutorCABycfC +_$ss17DummyTaskExecutorCABycfCTj +_$ss17DummyTaskExecutorCABycfCTq +_$ss17DummyTaskExecutorCABycfc +_$ss17DummyTaskExecutorCMa +_$ss17DummyTaskExecutorCMm +_$ss17DummyTaskExecutorCMn +_$ss17DummyTaskExecutorCMo +_$ss17DummyTaskExecutorCMu +_$ss17DummyTaskExecutorCN +_$ss17DummyTaskExecutorCScFsMc +_$ss17DummyTaskExecutorCScFsWP +_$ss17DummyTaskExecutorCSchsMc +_$ss17DummyTaskExecutorCSchsWP +_$ss17DummyTaskExecutorCfD +_$ss17DummyTaskExecutorCfd _$ss19AsyncFilterSequenceV04makeA8IteratorAB0E0Vyx_GyF _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvg _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvpMV @@ -896,7 +940,6 @@ _$ss26DispatchGlobalTaskExecutorCfD _$ss26DispatchGlobalTaskExecutorCfd _$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sMc _$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sWP -_$ss26_enqueueJobGlobalWithDelayyys6UInt64V_ScJtF _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvpMV @@ -1158,6 +1201,7 @@ _swift_job_allocate _swift_job_deallocate _swift_job_run _swift_nonDefaultDistributedActor_initialize +_swift_sleep _swift_taskGroup_addPending _swift_taskGroup_attachChild _swift_taskGroup_cancelAll diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index 626eacbaeb642..669cb9d55f8b0 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -605,7 +605,51 @@ _$ss16AsyncMapSequenceVMa _$ss16AsyncMapSequenceVMn _$ss16AsyncMapSequenceV_9transformAByxq_Gx_q_7ElementQzYactcfC _$ss16AsyncMapSequenceVyxq_GScisMc -_$ss17_enqueueJobGlobalyyScJF +_$ss17DummyMainExecutorC02isbC0Sbvg +_$ss17DummyMainExecutorC02isbC0SbvpMV +_$ss17DummyMainExecutorC13checkIsolatedyyF +_$ss17DummyMainExecutorC3runyyKF +_$ss17DummyMainExecutorC4stopyyF +_$ss17DummyMainExecutorC7enqueueyys0C3JobVnF +_$ss17DummyMainExecutorCABycfC +_$ss17DummyMainExecutorCABycfCTj +_$ss17DummyMainExecutorCABycfCTq +_$ss17DummyMainExecutorCABycfc +_$ss17DummyMainExecutorCMa +_$ss17DummyMainExecutorCMm +_$ss17DummyMainExecutorCMn +_$ss17DummyMainExecutorCMo +_$ss17DummyMainExecutorCMu +_$ss17DummyMainExecutorCN +_$ss17DummyMainExecutorCScFsMc +_$ss17DummyMainExecutorCScFsWP +_$ss17DummyMainExecutorCScfsMc +_$ss17DummyMainExecutorCScfsWP +_$ss17DummyMainExecutorCfD +_$ss17DummyMainExecutorCfd +_$ss17DummyMainExecutorCs07RunLoopC0sMc +_$ss17DummyMainExecutorCs07RunLoopC0sWP +_$ss17DummyMainExecutorCs0bC0sMc +_$ss17DummyMainExecutorCs0bC0sWP +_$ss17DummyTaskExecutorC06isMainC0Sbvg +_$ss17DummyTaskExecutorC06isMainC0SbvpMV +_$ss17DummyTaskExecutorC7enqueueyys0C3JobVnF +_$ss17DummyTaskExecutorCABycfC +_$ss17DummyTaskExecutorCABycfCTj +_$ss17DummyTaskExecutorCABycfCTq +_$ss17DummyTaskExecutorCABycfc +_$ss17DummyTaskExecutorCMa +_$ss17DummyTaskExecutorCMm +_$ss17DummyTaskExecutorCMn +_$ss17DummyTaskExecutorCMo +_$ss17DummyTaskExecutorCMu +_$ss17DummyTaskExecutorCN +_$ss17DummyTaskExecutorCScFsMc +_$ss17DummyTaskExecutorCScFsWP +_$ss17DummyTaskExecutorCSchsMc +_$ss17DummyTaskExecutorCSchsWP +_$ss17DummyTaskExecutorCfD +_$ss17DummyTaskExecutorCfd _$ss19AsyncFilterSequenceV04makeA8IteratorAB0E0Vyx_GyF _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvg _$ss19AsyncFilterSequenceV10isIncludedySb7ElementQzYacvpMV @@ -896,7 +940,6 @@ _$ss26DispatchGlobalTaskExecutorCfD _$ss26DispatchGlobalTaskExecutorCfd _$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sMc _$ss26DispatchGlobalTaskExecutorCs011SchedulableD0sWP -_$ss26_enqueueJobGlobalWithDelayyys6UInt64V_ScJtF _$ss27AsyncThrowingFilterSequenceV04makeA8IteratorAB0F0Vyx_GyF _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvg _$ss27AsyncThrowingFilterSequenceV10isIncludedySb7ElementQzYaKcvpMV @@ -1158,6 +1201,7 @@ _swift_job_allocate _swift_job_deallocate _swift_job_run _swift_nonDefaultDistributedActor_initialize +_swift_sleep _swift_taskGroup_addPending _swift_taskGroup_attachChild _swift_taskGroup_cancelAll From ad5b76a6638f888184782a5ff0d2fc50cf249fe1 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 17 Mar 2025 18:48:17 +0000 Subject: [PATCH 23/29] [Concurrency] Another missing availability annotation. We need to annotate the `timestamp()` function for watchOS (apparently not for macOS for some reason). rdar://141348916 --- stdlib/public/Concurrency/TaskSleepDuration.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/stdlib/public/Concurrency/TaskSleepDuration.swift b/stdlib/public/Concurrency/TaskSleepDuration.swift index e5d1acc4175b2..6f60c4eeb6631 100644 --- a/stdlib/public/Concurrency/TaskSleepDuration.swift +++ b/stdlib/public/Concurrency/TaskSleepDuration.swift @@ -13,6 +13,7 @@ import Swift #if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY +@available(SwiftStdlib 5.7, *) fileprivate func timestamp(for instant: C.Instant, clock: C) -> (clockID: _ClockID, seconds: Int64, nanoseconds: Int64) { var clockID: _ClockID From 167449f3db450b6b50b5135ad996e4a0d0945c1b Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 18 Mar 2025 10:39:41 +0000 Subject: [PATCH 24/29] [Concurrency] Fix a typo and use `var` not `let`. Fix a typo that caused us to not include the correct code for platforms that use "singlethreaded" concurrency. Also use `var` not `let` for the computed property in `PlatformExecutorFactory`. rdar://141348916 --- stdlib/public/Concurrency/CMakeLists.txt | 2 +- stdlib/public/Concurrency/PlatformExecutorCooperative.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/stdlib/public/Concurrency/CMakeLists.txt b/stdlib/public/Concurrency/CMakeLists.txt index 72996020bde7c..05f19620530ef 100644 --- a/stdlib/public/Concurrency/CMakeLists.txt +++ b/stdlib/public/Concurrency/CMakeLists.txt @@ -177,7 +177,7 @@ if("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "dispatch") ExecutorImpl.swift ) elseif("${SWIFT_CONCURRENCY_GLOBAL_EXECUTOR}" STREQUAL "singlethreaded") - set(SWIFT_RUNTIME_CONCURRENCYU_NONEMBEDDED_SWIFT_SOURCES + set(SWIFT_RUNTIME_CONCURRENCY_NONEMBEDDED_SWIFT_SOURCES ExecutorImpl.swift PlatformExecutorCooperative.swift ) diff --git a/stdlib/public/Concurrency/PlatformExecutorCooperative.swift b/stdlib/public/Concurrency/PlatformExecutorCooperative.swift index 395154bf26310..5c17e1d2ba084 100644 --- a/stdlib/public/Concurrency/PlatformExecutorCooperative.swift +++ b/stdlib/public/Concurrency/PlatformExecutorCooperative.swift @@ -16,6 +16,6 @@ import Swift @available(SwiftStdlib 6.2, *) public struct PlatformExecutorFactory: ExecutorFactory { static let executor = CooperativeExecutor() - public static let mainExecutor: any MainExecutor { executor } - public static let defaultExecutor: any TaskExecutor { executor } + public static var mainExecutor: any MainExecutor { executor } + public static var defaultExecutor: any TaskExecutor { executor } } From 4576d535e5a5fe478bcab08b590db35be7f581f1 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 18 Mar 2025 10:43:23 +0000 Subject: [PATCH 25/29] [Concurrency][Tests] Require `libdispatch` in the sleep test. We need to explicitly require `libdispatch` here, otherwise the Windows build will fail. rdar://141348916 --- test/Concurrency/Runtime/sleep_executor.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/test/Concurrency/Runtime/sleep_executor.swift b/test/Concurrency/Runtime/sleep_executor.swift index a13a8fa66cc95..f4767e5a068b3 100644 --- a/test/Concurrency/Runtime/sleep_executor.swift +++ b/test/Concurrency/Runtime/sleep_executor.swift @@ -2,6 +2,7 @@ // REQUIRES: concurrency // REQUIRES: executable_test +// REQUIRES: libdispatch // rdar://106849189 move-only types should be supported in freestanding mode // UNSUPPORTED: freestanding From 60fb31cf01aa2572b01d8d0934ac26a4268184c5 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 18 Mar 2025 15:46:51 +0000 Subject: [PATCH 26/29] [Concurrency][32-bit] Store timestamp out-of-line on 32-bit. CooperativeExecutor needs to store its timestamp out-of-line for 32-bit architectures (like WASI). rdar://141348916 --- .../Concurrency/CooperativeExecutor.swift | 66 +++++++++++++++++-- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/stdlib/public/Concurrency/CooperativeExecutor.swift b/stdlib/public/Concurrency/CooperativeExecutor.swift index 50f5819f0747a..996039adc7b9d 100644 --- a/stdlib/public/Concurrency/CooperativeExecutor.swift +++ b/stdlib/public/Concurrency/CooperativeExecutor.swift @@ -12,24 +12,77 @@ import Swift +// Store the Timestamp in the executor private data, if it will fit; otherwise, +// use the allocator to allocate space for it and stash a pointer in the private +// data area. @available(SwiftStdlib 6.2, *) extension ExecutorJob { - fileprivate var cooperativeExecutorTimestamp: CooperativeExecutor.Timestamp { + fileprivate var cooperativeExecutorTimestampIsIndirect: Bool { + return MemoryLayout<(Int, Int)>.size + < MemoryLayout.size + } + + fileprivate var cooperativeExecutorTimestampPointer: UnsafeMutablePointer { get { + assert(cooperativeExecutorTimestampIsIndirect) return unsafe withUnsafeExecutorPrivateData { - return unsafe $0.assumingMemoryBound( - to: CooperativeExecutor.Timestamp.self - )[0] + unsafe $0.withMemoryRebound(to: UnsafeMutablePointer.self) { + return unsafe $0[0] + } } } set { + assert(cooperativeExecutorTimestampIsIndirect) unsafe withUnsafeExecutorPrivateData { - unsafe $0.withMemoryRebound(to: CooperativeExecutor.Timestamp.self) { + unsafe $0.withMemoryRebound(to: UnsafeMutablePointer.self) { unsafe $0[0] = newValue } } } } + + fileprivate var cooperativeExecutorTimestamp: CooperativeExecutor.Timestamp { + get { + if cooperativeExecutorTimestampIsIndirect { + let ptr = unsafe cooperativeExecutorTimestampPointer + return unsafe ptr.pointee + } else { + return unsafe withUnsafeExecutorPrivateData { + return unsafe $0.assumingMemoryBound( + to: CooperativeExecutor.Timestamp.self + )[0] + } + } + } + set { + if cooperativeExecutorTimestampIsIndirect { + let ptr = unsafe cooperativeExecutorTimestampPointer + unsafe ptr.pointee = newValue + } else { + unsafe withUnsafeExecutorPrivateData { + unsafe $0.withMemoryRebound(to: CooperativeExecutor.Timestamp.self) { + unsafe $0[0] = newValue + } + } + } + } + } + + fileprivate mutating func setupCooperativeExecutorTimestamp() { + // If a Timestamp won't fit, allocate + if cooperativeExecutorTimestampIsIndirect { + let ptr = unsafe allocator!.allocate(as: CooperativeExecutor.Timestamp.self) + unsafe self.cooperativeExecutorTimestampPointer = ptr + } + } + + fileprivate mutating func clearCooperativeExecutorTimestamp() { + // If a Timestamp won't fit, deallocate + if cooperativeExecutorTimestampIsIndirect { + let ptr = unsafe self.cooperativeExecutorTimestampPointer + unsafe allocator!.deallocate(ptr) + } + } } /// A co-operative executor that can be used as the main executor or as a @@ -131,6 +184,7 @@ extension CooperativeExecutor: SchedulableExecutor { let duration = Duration(from: clock.convert(from: delay)!) let deadline = self.currentTime + duration + job.setupCooperativeExecutorTimestamp() job.cooperativeExecutorTimestamp = deadline waitQueue.push(UnownedJob(job)) } @@ -150,6 +204,8 @@ extension CooperativeExecutor: RunLoopExecutor { while let job = waitQueue.pop(when: { ExecutorJob($0).cooperativeExecutorTimestamp <= now }) { + var theJob = ExecutorJob(job) + theJob.clearCooperativeExecutorTimestamp() runQueue.push(job) } From fb0396c509f8189e16a0a316c2075868a0e99d5e Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 18 Mar 2025 17:59:57 +0000 Subject: [PATCH 27/29] [Concurrency] Fall back to allocating from the heap, fix a test. `CooperativeExecutor` should fall back to heap allocations if the task allocator isn't available when it's enqueuing delayed jobs. Also, remove the reference to `DispatchGlobalExecutor` from the custom executor test. rdar://141348916 --- .../public/Concurrency/CooperativeExecutor.swift | 14 ++++++++++++-- .../Concurrency/Runtime/custom_main_executor.swift | 10 +++++----- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/stdlib/public/Concurrency/CooperativeExecutor.swift b/stdlib/public/Concurrency/CooperativeExecutor.swift index 996039adc7b9d..d2bcc7b9086eb 100644 --- a/stdlib/public/Concurrency/CooperativeExecutor.swift +++ b/stdlib/public/Concurrency/CooperativeExecutor.swift @@ -71,7 +71,13 @@ extension ExecutorJob { fileprivate mutating func setupCooperativeExecutorTimestamp() { // If a Timestamp won't fit, allocate if cooperativeExecutorTimestampIsIndirect { - let ptr = unsafe allocator!.allocate(as: CooperativeExecutor.Timestamp.self) + let ptr: UnsafeMutablePointer + // Try to use the task allocator if it has one + if let allocator { + ptr = unsafe allocator.allocate(as: CooperativeExecutor.Timestamp.self) + } else { + ptr = .allocate(capacity: 1) + } unsafe self.cooperativeExecutorTimestampPointer = ptr } } @@ -80,7 +86,11 @@ extension ExecutorJob { // If a Timestamp won't fit, deallocate if cooperativeExecutorTimestampIsIndirect { let ptr = unsafe self.cooperativeExecutorTimestampPointer - unsafe allocator!.deallocate(ptr) + if let allocator { + unsafe allocator.deallocate(ptr) + } else { + unsafe ptr.deallocate() + } } } } diff --git a/test/Concurrency/Runtime/custom_main_executor.swift b/test/Concurrency/Runtime/custom_main_executor.swift index ad89e63da46ca..bb5c0f5ef1b21 100644 --- a/test/Concurrency/Runtime/custom_main_executor.swift +++ b/test/Concurrency/Runtime/custom_main_executor.swift @@ -20,7 +20,7 @@ struct SimpleExecutorFactory: ExecutorFactory { } public static var defaultExecutor: any TaskExecutor { print("Creating task executor") - return DispatchGlobalTaskExecutor() + return SimpleTaskExecutor() } } @@ -62,9 +62,9 @@ final class SimpleMainExecutor: MainExecutor, @unchecked Sendable { } @available(SwiftStdlib 6.2, *) -final class FatalExecutor: TaskExecutor, @unchecked Sendable { +final class SimpleTaskExecutor: TaskExecutor, @unchecked Sendable { func enqueue(_ job: consuming ExecutorJob) { - fatalError("We should never get here") + MainActor.executor.enqueue(job) } } @@ -84,9 +84,9 @@ func myAsyncFunction() async { // CHECK: Creating main executor // CHECK-NEXT: Creating task executor // CHECK-NEXT: Hello -// CHECK-NEXT: Running -// CHECK-NEXT: Hello World // CHECK-NEXT: Enqueued job +// CHECK-NEXT: Running // CHECK-NEXT: Running job +// CHECK-NEXT: Hello World // CHECK-NEXT: Goodbye From 0da95eb20d8d026be08fe79876ac565093552b49 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 18 Mar 2025 18:37:31 +0000 Subject: [PATCH 28/29] [Concurrency] Fix some warnings, use typed throws. Fix a couple of `unsafe` warnings. Also update `withUnsafeExecutorPrivateData` to use typed throws instead of `rethrow`. rdar://141348916 --- stdlib/public/Concurrency/CooperativeExecutor.swift | 4 ++-- stdlib/public/Concurrency/PartialAsyncTask.swift | 2 +- test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts | 2 +- test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/stdlib/public/Concurrency/CooperativeExecutor.swift b/stdlib/public/Concurrency/CooperativeExecutor.swift index d2bcc7b9086eb..61b8c0f45b3e5 100644 --- a/stdlib/public/Concurrency/CooperativeExecutor.swift +++ b/stdlib/public/Concurrency/CooperativeExecutor.swift @@ -74,9 +74,9 @@ extension ExecutorJob { let ptr: UnsafeMutablePointer // Try to use the task allocator if it has one if let allocator { - ptr = unsafe allocator.allocate(as: CooperativeExecutor.Timestamp.self) + unsafe ptr = allocator.allocate(as: CooperativeExecutor.Timestamp.self) } else { - ptr = .allocate(capacity: 1) + unsafe ptr = .allocate(capacity: 1) } unsafe self.cooperativeExecutorTimestampPointer = ptr } diff --git a/stdlib/public/Concurrency/PartialAsyncTask.swift b/stdlib/public/Concurrency/PartialAsyncTask.swift index f6c4dfed6e288..026657d59187f 100644 --- a/stdlib/public/Concurrency/PartialAsyncTask.swift +++ b/stdlib/public/Concurrency/PartialAsyncTask.swift @@ -323,7 +323,7 @@ public struct ExecutorJob: Sendable, ~Copyable { /// /// Returns the result of executing the closure. @available(SwiftStdlib 6.2, *) - public func withUnsafeExecutorPrivateData(body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R { + public func withUnsafeExecutorPrivateData(body: (UnsafeMutableRawBufferPointer) throws(E) -> R) throws(E) -> R { let base = unsafe _jobGetExecutorPrivateData(self.context) let size = unsafe 2 * MemoryLayout.stride return unsafe try body(UnsafeMutableRawBufferPointer(start: base, diff --git a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts index 669cb9d55f8b0..557c626a2cada 100644 --- a/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/arm64/concurrency/baseline-asserts @@ -410,7 +410,7 @@ _$ss11ClockTraitsVSYsMc _$ss11ClockTraitsVs10SetAlgebrasMc _$ss11ClockTraitsVs25ExpressibleByArrayLiteralsMc _$ss11ClockTraitsVs9OptionSetsMc -_$ss11ExecutorJobV010withUnsafeA11PrivateData4bodyxxSwKXE_tKlF +_$ss11ExecutorJobV010withUnsafeA11PrivateData4bodyxxSwq_YKXE_tq_YKs5ErrorR_r0_lF _$ss11ExecutorJobV11descriptionSSvg _$ss11ExecutorJobV14LocalAllocatorV10deallocateyySpyxGlF _$ss11ExecutorJobV14LocalAllocatorV10deallocateyySryxGlF diff --git a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts index 669cb9d55f8b0..557c626a2cada 100644 --- a/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts +++ b/test/abi/Inputs/macOS/x86_64/concurrency/baseline-asserts @@ -410,7 +410,7 @@ _$ss11ClockTraitsVSYsMc _$ss11ClockTraitsVs10SetAlgebrasMc _$ss11ClockTraitsVs25ExpressibleByArrayLiteralsMc _$ss11ClockTraitsVs9OptionSetsMc -_$ss11ExecutorJobV010withUnsafeA11PrivateData4bodyxxSwKXE_tKlF +_$ss11ExecutorJobV010withUnsafeA11PrivateData4bodyxxSwq_YKXE_tq_YKs5ErrorR_r0_lF _$ss11ExecutorJobV11descriptionSSvg _$ss11ExecutorJobV14LocalAllocatorV10deallocateyySpyxGlF _$ss11ExecutorJobV14LocalAllocatorV10deallocateyySryxGlF From c20aa667f210bc0d80760671b8fce38af9e6bbb4 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 19 Mar 2025 11:15:00 +0000 Subject: [PATCH 29/29] [Concurrency] Disable various things for task-to-thread model. When in task-to-thread model concurrency mode, there is no `MainActor` and we cannot use `ExecutorJob`, so disable various things. rdar://141348916 --- .../Concurrency/CooperativeExecutor.swift | 4 ++++ stdlib/public/Concurrency/DummyExecutor.swift | 12 +++++++++++ stdlib/public/Concurrency/Executor.swift | 20 +++++++++---------- .../public/Concurrency/ExecutorBridge.swift | 4 ++-- stdlib/public/Concurrency/ExecutorImpl.swift | 6 +++++- stdlib/public/Concurrency/MainActor.swift | 4 ++-- .../Concurrency/PlatformExecutorDarwin.swift | 2 +- stdlib/public/Concurrency/Task.swift | 2 +- 8 files changed, 37 insertions(+), 17 deletions(-) diff --git a/stdlib/public/Concurrency/CooperativeExecutor.swift b/stdlib/public/Concurrency/CooperativeExecutor.swift index 61b8c0f45b3e5..794f9fc542a2a 100644 --- a/stdlib/public/Concurrency/CooperativeExecutor.swift +++ b/stdlib/public/Concurrency/CooperativeExecutor.swift @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + import Swift // Store the Timestamp in the executor private data, if it will fit; otherwise, @@ -255,3 +257,5 @@ extension CooperativeExecutor: TaskExecutor {} @available(SwiftStdlib 6.2, *) extension CooperativeExecutor: MainExecutor {} + +#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY diff --git a/stdlib/public/Concurrency/DummyExecutor.swift b/stdlib/public/Concurrency/DummyExecutor.swift index ca664292c0e9e..ec7bc7897198a 100644 --- a/stdlib/public/Concurrency/DummyExecutor.swift +++ b/stdlib/public/Concurrency/DummyExecutor.swift @@ -26,9 +26,15 @@ public final class DummyMainExecutor: MainExecutor, @unchecked Sendable { fatalError("There is no executor implementation active") } + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + public func enqueue(_ job: UnownedJob) { + fatalError("There is no executor implementation active") + } + #else public func enqueue(_ job: consuming ExecutorJob) { fatalError("There is no executor implementation active") } + #endif public var isMainExecutor: Bool { true } @@ -43,9 +49,15 @@ public final class DummyMainExecutor: MainExecutor, @unchecked Sendable { public final class DummyTaskExecutor: TaskExecutor, @unchecked Sendable { public init() {} + #if SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY + public func enqueue(_ job: UnownedJob) { + fatalError("There is no executor implementation active") + } + #else public func enqueue(_ job: consuming ExecutorJob) { fatalError("There is no executor implementation active") } + #endif public var isMainExecutor: Bool { false } } diff --git a/stdlib/public/Concurrency/Executor.swift b/stdlib/public/Concurrency/Executor.swift index a35ea8fd6ca4e..1c90ccccd83c7 100644 --- a/stdlib/public/Concurrency/Executor.swift +++ b/stdlib/public/Concurrency/Executor.swift @@ -36,7 +36,7 @@ public protocol Executor: AnyObject, Sendable { func enqueue(_ job: consuming ExecutorJob) #endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY - #if !$Embedded + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY // The functions below could have been added to a separate protocol, // but doing that would then mean doing an `as?` cast in e.g. // enqueueOnGlobalExecutor (in ExecutorBridge.swift), which is @@ -51,7 +51,7 @@ public protocol Executor: AnyObject, Sendable { @available(SwiftStdlib 6.2, *) public protocol SchedulableExecutor: Executor { - #if !$Embedded + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY /// Enqueue a job to run after a specified delay. /// @@ -94,7 +94,7 @@ public protocol SchedulableExecutor: Executor { tolerance: C.Duration?, clock: C) - #endif // !$Embedded + #endif // !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY } @@ -125,7 +125,7 @@ extension Executor where Self: Equatable { extension Executor { - #if !$Embedded + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY // This defaults to `false` so that existing third-party Executor // implementations will work as expected. @available(SwiftStdlib 6.2, *) @@ -138,7 +138,7 @@ extension Executor { @available(SwiftStdlib 6.2, *) extension SchedulableExecutor { - #if !$Embedded + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 6.2, *) public func enqueue(_ job: consuming ExecutorJob, @@ -162,7 +162,7 @@ extension SchedulableExecutor { tolerance: tolerance, clock: clock) } - #endif // !$Embedded + #endif // !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY } /// A service that executes jobs. @@ -331,7 +331,7 @@ public protocol SerialExecutor: Executor { @available(SwiftStdlib 6.0, *) extension SerialExecutor { - #if !$Embedded + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 6.2, *) public var isMainExecutor: Bool { return MainActor.executor._isSameExecutor(self) } #endif @@ -542,13 +542,13 @@ public protocol ExecutorFactory { @available(SwiftStdlib 6.2, *) @_silgen_name("swift_createExecutors") public func _createExecutors(factory: F.Type) { - #if !$Embedded + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY MainActor._executor = factory.mainExecutor #endif Task._defaultExecutor = factory.defaultExecutor } -#if !$Embedded +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY extension MainActor { @available(SwiftStdlib 6.2, *) static var _executor: (any MainExecutor)? = nil @@ -566,7 +566,7 @@ extension MainActor { return _executor! } } -#endif // !$Embedded +#endif // !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY extension Task where Success == Never, Failure == Never { @available(SwiftStdlib 6.2, *) diff --git a/stdlib/public/Concurrency/ExecutorBridge.swift b/stdlib/public/Concurrency/ExecutorBridge.swift index b806ff307d34e..1caa4e31aee57 100644 --- a/stdlib/public/Concurrency/ExecutorBridge.swift +++ b/stdlib/public/Concurrency/ExecutorBridge.swift @@ -21,7 +21,7 @@ import Swift @_silgen_name("_swift_exit") internal func _exit(result: CInt) -#if !$Embedded +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 6.2, *) @_silgen_name("_swift_task_isMainExecutorSwift") internal func _isMainExecutor(_ executor: E) -> Bool where E: SerialExecutor { @@ -79,7 +79,7 @@ internal func _jobGetExecutorPrivateData( _ job: Builtin.Job ) -> UnsafeMutableRawPointer -#if !$Embedded +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 6.2, *) @_silgen_name("swift_getMainExecutor") internal func _getMainExecutor() -> any MainExecutor { diff --git a/stdlib/public/Concurrency/ExecutorImpl.swift b/stdlib/public/Concurrency/ExecutorImpl.swift index c396d6823903b..d50aaa60e1d93 100644 --- a/stdlib/public/Concurrency/ExecutorImpl.swift +++ b/stdlib/public/Concurrency/ExecutorImpl.swift @@ -20,12 +20,14 @@ import Swift +#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 6.2, *) @_silgen_name("swift_task_asyncMainDrainQueueImpl") internal func drainMainQueue() { try! MainActor.executor.run() _exit(result: 0) } +#endif @available(SwiftStdlib 6.2, *) @_silgen_name("swift_task_donateThreadToGlobalExecutorUntilImpl") @@ -40,6 +42,7 @@ internal func dontateToGlobalExecutor( } } +#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 6.2, *) @_silgen_name("swift_task_getMainExecutorImpl") internal func getMainExecutor() -> UnownedSerialExecutor { @@ -51,6 +54,7 @@ internal func getMainExecutor() -> UnownedSerialExecutor { internal func enqueueOnMainExecutor(job unownedJob: UnownedJob) { MainActor.executor.enqueue(unownedJob) } +#endif @available(SwiftStdlib 6.2, *) @_silgen_name("swift_task_enqueueGlobalImpl") @@ -58,7 +62,7 @@ internal func enqueueOnGlobalExecutor(job unownedJob: UnownedJob) { Task.defaultExecutor.enqueue(unownedJob) } -#if !$Embedded +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY @available(SwiftStdlib 6.2, *) @_silgen_name("swift_task_enqueueGlobalWithDelayImpl") internal func enqueueOnGlobalExecutor(delay: CUnsignedLongLong, diff --git a/stdlib/public/Concurrency/MainActor.swift b/stdlib/public/Concurrency/MainActor.swift index 840a1519ae735..cddb4f83162b6 100644 --- a/stdlib/public/Concurrency/MainActor.swift +++ b/stdlib/public/Concurrency/MainActor.swift @@ -22,12 +22,12 @@ import Swift @inlinable public nonisolated var unownedExecutor: UnownedSerialExecutor { - return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) + return unsafe UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) } @inlinable public static var sharedUnownedExecutor: UnownedSerialExecutor { - return UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) + return unsafe UnownedSerialExecutor(Builtin.buildMainActorExecutorRef()) } @inlinable diff --git a/stdlib/public/Concurrency/PlatformExecutorDarwin.swift b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift index b8ac350668dcc..90017beb45106 100644 --- a/stdlib/public/Concurrency/PlatformExecutorDarwin.swift +++ b/stdlib/public/Concurrency/PlatformExecutorDarwin.swift @@ -10,7 +10,7 @@ // //===----------------------------------------------------------------------===// -#if !$Embedded && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)) +#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY && (os(macOS) || os(iOS) || os(tvOS) || os(watchOS) || os(visionOS)) import Swift diff --git a/stdlib/public/Concurrency/Task.swift b/stdlib/public/Concurrency/Task.swift index 023193428285e..f1f7c7e742cb0 100644 --- a/stdlib/public/Concurrency/Task.swift +++ b/stdlib/public/Concurrency/Task.swift @@ -1254,7 +1254,7 @@ extension Task where Success == Never, Failure == Never { priority: Int(Task.currentPriority.rawValue), continuation: continuation) - #if !$Embedded + #if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY if #available(SwiftStdlib 6.2, *) { let executor = Task.currentExecutor