Skip to content

Commit 687e09d

Browse files
authored
Merge pull request swiftlang#82456 from al45tair/concurrency-updates
[Concurrency] Updates after second SE pitch.
2 parents 53028ab + d6224d1 commit 687e09d

21 files changed

+682
-1139
lines changed

lib/SILGen/SILGen.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,36 @@ FuncDecl *SILGenModule::getExit() {
527527
Type SILGenModule::getConfiguredExecutorFactory() {
528528
auto &ctx = getASTContext();
529529

530-
// Look in the main module for a typealias
530+
// First look in the @main struct, if any
531+
NominalTypeDecl *mainType = ctx.MainModule->getMainTypeDecl();
532+
if (mainType) {
533+
SmallVector<ValueDecl *, 1> decls;
534+
auto identifier = ctx.getIdentifier("DefaultExecutorFactory");
535+
mainType->lookupQualified(mainType,
536+
DeclNameRef(identifier),
537+
SourceLoc(),
538+
NL_RemoveNonVisible | NL_RemoveOverridden
539+
| NL_OnlyTypes | NL_ProtocolMembers,
540+
decls);
541+
for (auto decl : decls) {
542+
TypeDecl *typeDecl = dyn_cast<TypeDecl>(decl);
543+
if (typeDecl) {
544+
if (auto *nominalDecl = dyn_cast<NominalTypeDecl>(typeDecl)) {
545+
return nominalDecl->getDeclaredType();
546+
}
547+
548+
if (isa<AssociatedTypeDecl>(typeDecl)) {
549+
// We ignore associatedtype declarations; those with a default will
550+
// turn into a `typealias` instead.
551+
continue;
552+
}
553+
554+
return typeDecl->getDeclaredInterfaceType();
555+
}
556+
}
557+
}
558+
559+
// Failing that, look at the top level
531560
Type factory = ctx.getNamedSwiftType(ctx.MainModule, "DefaultExecutorFactory");
532561

533562
// If we don't find it, fall back to _Concurrency.PlatformExecutorFactory

stdlib/public/Concurrency/CFExecutor.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ enum CoreFoundation {
4747

4848
// .. Main Executor ............................................................
4949

50+
/// A CFRunLoop-based main executor (Apple platforms only)
5051
@available(StdlibDeploymentTarget 6.2, *)
51-
public final class CFMainExecutor: DispatchMainExecutor, @unchecked Sendable {
52+
final class CFMainExecutor: DispatchMainExecutor, @unchecked Sendable {
5253

5354
override public func run() throws {
5455
CoreFoundation.CFRunLoopRun()
@@ -62,9 +63,10 @@ public final class CFMainExecutor: DispatchMainExecutor, @unchecked Sendable {
6263

6364
// .. Task Executor ............................................................
6465

66+
/// A `TaskExecutor` to match `CFMainExecutor` (Apple platforms only)
6567
@available(StdlibDeploymentTarget 6.2, *)
66-
public final class CFTaskExecutor: DispatchGlobalTaskExecutor,
67-
@unchecked Sendable {
68+
final class CFTaskExecutor: DispatchGlobalTaskExecutor,
69+
@unchecked Sendable {
6870

6971
}
7072

stdlib/public/Concurrency/Clock.swift

Lines changed: 44 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -40,52 +40,64 @@ public protocol Clock<Duration>: Sendable {
4040

4141
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
4242
func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws
43-
#endif
44-
45-
/// The traits associated with this clock instance.
46-
@available(StdlibDeploymentTarget 6.2, *)
47-
var traits: ClockTraits { get }
4843

49-
/// Convert a Clock-specific Duration to a Swift Duration
50-
///
51-
/// Some clocks may define `C.Duration` to be something other than a
52-
/// `Swift.Duration`, but that makes it tricky to convert timestamps
53-
/// between clocks, which is something we want to be able to support.
54-
/// This method will convert whatever `C.Duration` is to a `Swift.Duration`.
44+
/// Run the given job on an unspecified executor at some point
45+
/// after the given instant.
5546
///
5647
/// Parameters:
5748
///
58-
/// - from duration: The `Duration` to convert
49+
/// - job: The job we wish to run
50+
/// - at instant: The time at which we would like it to run.
51+
/// - tolerance: The ideal maximum delay we are willing to tolerate.
5952
///
60-
/// Returns: A `Swift.Duration` representing the equivalent duration, or
61-
/// `nil` if this function is not supported.
6253
@available(StdlibDeploymentTarget 6.2, *)
63-
func convert(from duration: Duration) -> Swift.Duration?
54+
func run(_ job: consuming ExecutorJob,
55+
at instant: Instant, tolerance: Duration?)
6456

65-
/// Convert a Swift Duration to a Clock-specific Duration
57+
/// Enqueue the given job on the specified executor at some point after the
58+
/// given instant.
59+
///
60+
/// The default implementation uses the `run` method to trigger a job that
61+
/// does `executor.enqueue(job)`. If a particular `Clock` knows that the
62+
/// executor it has been asked to use is the same one that it will run jobs
63+
/// on, it can short-circuit this behaviour and directly use `run` with
64+
/// the original job.
6665
///
6766
/// Parameters:
6867
///
69-
/// - from duration: The `Swift.Duration` to convert.
68+
/// - job: The job we wish to run
69+
/// - on executor: The executor on which we would like it to run.
70+
/// - at instant: The time at which we would like it to run.
71+
/// - tolerance: The ideal maximum delay we are willing to tolerate.
7072
///
71-
/// Returns: A `Duration` representing the equivalent duration, or
72-
/// `nil` if this function is not supported.
7373
@available(StdlibDeploymentTarget 6.2, *)
74-
func convert(from duration: Swift.Duration) -> Duration?
74+
func enqueue(_ job: consuming ExecutorJob,
75+
on executor: some Executor,
76+
at instant: Instant, tolerance: Duration?)
77+
#endif
78+
}
7579

76-
/// Convert an `Instant` from some other clock's `Instant`
77-
///
78-
/// Parameters:
79-
///
80-
/// - instant: The instant to convert.
81-
// - from clock: The clock to convert from.
82-
///
83-
/// Returns: An `Instant` representing the equivalent instant, or
84-
/// `nil` if this function is not supported.
80+
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
81+
extension Clock {
82+
// The default implementation works by creating a trampoline and calling
83+
// the run() method.
8584
@available(StdlibDeploymentTarget 6.2, *)
86-
func convert<OtherClock: Clock>(instant: OtherClock.Instant,
87-
from clock: OtherClock) -> Instant?
85+
public func enqueue(_ job: consuming ExecutorJob,
86+
on executor: some Executor,
87+
at instant: Instant, tolerance: Duration?) {
88+
let trampoline = job.createTrampoline(to: executor)
89+
run(trampoline, at: instant, tolerance: tolerance)
90+
}
91+
92+
// Clocks that do not implement run will fatalError() if you try to use
93+
// them with an executor that does not understand them.
94+
@available(StdlibDeploymentTarget 6.2, *)
95+
public func run(_ job: consuming ExecutorJob,
96+
at instant: Instant, tolerance: Duration?) {
97+
fatalError("\(Self.self) does not implement run(_:at:tolerance:).")
98+
}
8899
}
100+
#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
89101

90102
@available(StdlibDeploymentTarget 5.7, *)
91103
extension Clock {
@@ -140,44 +152,6 @@ extension Clock {
140152
}
141153
}
142154

143-
@available(StdlibDeploymentTarget 6.2, *)
144-
extension Clock {
145-
// For compatibility, return `nil` if this is not implemented
146-
public func convert(from duration: Duration) -> Swift.Duration? {
147-
return nil
148-
}
149-
150-
public func convert(from duration: Swift.Duration) -> Duration? {
151-
return nil
152-
}
153-
154-
public func convert<OtherClock: Clock>(instant: OtherClock.Instant,
155-
from clock: OtherClock) -> Instant? {
156-
let ourNow = now
157-
let otherNow = clock.now
158-
let otherDuration = otherNow.duration(to: instant)
159-
160-
// Convert to `Swift.Duration`
161-
guard let duration = clock.convert(from: otherDuration) else {
162-
return nil
163-
}
164-
165-
// Convert from `Swift.Duration`
166-
guard let ourDuration = convert(from: duration) else {
167-
return nil
168-
}
169-
170-
return ourNow.advanced(by: ourDuration)
171-
}
172-
}
173-
174-
@available(StdlibDeploymentTarget 6.2, *)
175-
extension Clock where Duration == Swift.Duration {
176-
public func convert(from duration: Duration) -> Duration? {
177-
return duration
178-
}
179-
}
180-
181155
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
182156
@available(StdlibDeploymentTarget 5.7, *)
183157
extension Clock {
@@ -196,47 +170,10 @@ extension Clock {
196170
}
197171
#endif
198172

199-
/// Represents traits of a particular Clock implementation.
200-
///
201-
/// Clocks may be of a number of different varieties; executors will likely
202-
/// have specific clocks that they can use to schedule jobs, and will
203-
/// therefore need to be able to convert timestamps to an appropriate clock
204-
/// when asked to enqueue a job with a delay or deadline.
205-
///
206-
/// Choosing a clock in general requires the ability to tell which of their
207-
/// clocks best matches the clock that the user is trying to specify a
208-
/// time or delay in. Executors are expected to do this on a best effort
209-
/// basis.
210-
@available(StdlibDeploymentTarget 6.2, *)
211-
public struct ClockTraits: OptionSet {
212-
public let rawValue: UInt32
213-
214-
public init(rawValue: UInt32) {
215-
self.rawValue = rawValue
216-
}
217-
218-
/// Clocks with this trait continue running while the machine is asleep.
219-
public static let continuous = ClockTraits(rawValue: 1 << 0)
220-
221-
/// Indicates that a clock's time will only ever increase.
222-
public static let monotonic = ClockTraits(rawValue: 1 << 1)
223-
224-
/// Clocks with this trait are tied to "wall time".
225-
public static let wallTime = ClockTraits(rawValue: 1 << 2)
226-
}
227-
228-
@available(StdlibDeploymentTarget 6.2, *)
229-
extension Clock {
230-
/// The traits associated with this clock instance.
231-
@available(StdlibDeploymentTarget 6.2, *)
232-
public var traits: ClockTraits {
233-
return []
234-
}
235-
}
236-
237173
enum _ClockID: Int32 {
238174
case continuous = 1
239175
case suspending = 2
176+
case walltime = 3
240177
}
241178

242179
@available(StdlibDeploymentTarget 5.7, *)

stdlib/public/Concurrency/ContinuousClock.swift

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,6 @@ extension ContinuousClock: Clock {
100100
)
101101
}
102102

103-
/// The continuous clock is continuous and monotonic
104-
@available(StdlibDeploymentTarget 6.2, *)
105-
public var traits: ClockTraits {
106-
return [.continuous, .monotonic]
107-
}
108-
109103
#if !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
110104
/// Suspend task execution until a given deadline within a tolerance.
111105
/// If no tolerance is specified then the system may adjust the deadline
@@ -217,3 +211,36 @@ extension ContinuousClock.Instant: InstantProtocol {
217211
rhs.duration(to: lhs)
218212
}
219213
}
214+
215+
#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
216+
@available(StdlibDeploymentTarget 6.2, *)
217+
extension ContinuousClock {
218+
219+
public func run(_ job: consuming ExecutorJob,
220+
at instant: Instant,
221+
tolerance: Duration?) {
222+
guard let executor = Task<Never,Never>.currentSchedulingExecutor else {
223+
fatalError("no scheduling executor is available")
224+
}
225+
226+
executor.enqueue(job, at: instant,
227+
tolerance: tolerance,
228+
clock: self)
229+
}
230+
231+
public func enqueue(_ job: consuming ExecutorJob,
232+
on executor: some Executor,
233+
at instant: Instant,
234+
tolerance: Duration?) {
235+
if let schedulingExecutor = executor.asSchedulingExecutor {
236+
schedulingExecutor.enqueue(job, at: instant,
237+
tolerance: tolerance,
238+
clock: self)
239+
} else {
240+
let trampoline = job.createTrampoline(to: executor)
241+
run(trampoline, at: instant, tolerance: tolerance)
242+
}
243+
}
244+
245+
}
246+
#endif

0 commit comments

Comments
 (0)