Skip to content

Commit ee536ce

Browse files
committed
[Concurrency] Make all the new functionality public.
This makes the custom global executor support and associated functionality public.
1 parent 8526926 commit ee536ce

20 files changed

+636
-51
lines changed

include/swift/ABI/Task.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ class alignas(2 * alignof(void*)) Job :
154154
return Flags.getPriority();
155155
}
156156

157+
void setPriority(JobPriority priority) {
158+
Flags.setPriority(priority);
159+
}
160+
157161
uint32_t getJobId() const {
158162
return Id;
159163
}

stdlib/public/Concurrency/Clock.swift

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,40 @@ 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+
44+
/// Run the given job on an unspecified executor at some point
45+
/// after the given instant.
46+
///
47+
/// Parameters:
48+
///
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.
52+
///
53+
@available(StdlibDeploymentTarget 6.2, *)
54+
func run(_ job: consuming ExecutorJob,
55+
at instant: Instant, tolerance: Duration?)
56+
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.
65+
///
66+
/// Parameters:
67+
///
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.
72+
///
73+
@available(StdlibDeploymentTarget 6.2, *)
74+
func enqueue(_ job: consuming ExecutorJob,
75+
on executor: some Executor,
76+
at instant: Instant, tolerance: Duration?)
4377
#endif
4478
}
4579

@@ -48,18 +82,18 @@ extension Clock {
4882
// The default implementation works by creating a trampoline and calling
4983
// the run() method.
5084
@available(StdlibDeploymentTarget 6.2, *)
51-
func enqueue(_ job: consuming ExecutorJob,
52-
on executor: some Executor,
53-
at instant: Instant, tolerance: Duration?) {
85+
public func enqueue(_ job: consuming ExecutorJob,
86+
on executor: some Executor,
87+
at instant: Instant, tolerance: Duration?) {
5488
let trampoline = job.createTrampoline(to: executor)
5589
run(trampoline, at: instant, tolerance: tolerance)
5690
}
5791

5892
// Clocks that do not implement run will fatalError() if you try to use
5993
// them with an executor that does not understand them.
6094
@available(StdlibDeploymentTarget 6.2, *)
61-
func run(_ job: consuming ExecutorJob,
62-
at instant: Instant, tolerance: Duration?) {
95+
public func run(_ job: consuming ExecutorJob,
96+
at instant: Instant, tolerance: Duration?) {
6397
fatalError("\(Self.self) does not implement run(_:at:tolerance:).")
6498
}
6599
}

stdlib/public/Concurrency/DispatchGlobalExecutor.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ void swift_dispatchEnqueueWithDeadline(bool global,
333333
}
334334

335335
uint64_t deadline;
336-
if (sec < 0 || sec == 0 && nsec < 0)
336+
if (sec < 0 || (sec == 0 && nsec < 0))
337337
deadline = 0;
338338
else if (__builtin_mul_overflow(sec, NSEC_PER_SEC, &deadline)
339339
|| __builtin_add_overflow(nsec, deadline, &deadline)) {
@@ -344,7 +344,7 @@ void swift_dispatchEnqueueWithDeadline(bool global,
344344

345345
if (tnsec != -1) {
346346
uint64_t leeway;
347-
if (tsec < 0 || tsec == 0 && tnsec < 0)
347+
if (tsec < 0 || (tsec == 0 && tnsec < 0))
348348
leeway = 0;
349349
else if (__builtin_mul_overflow(tsec, NSEC_PER_SEC, &leeway)
350350
|| __builtin_add_overflow(tnsec, leeway, &leeway)) {

stdlib/public/Concurrency/Executor.swift

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,24 @@ public protocol Executor: AnyObject, Sendable {
3636
func enqueue(_ job: consuming ExecutorJob)
3737
#endif // !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
3838

39+
#if !$Embedded
40+
/// `true` if this is the main executor.
41+
@available(StdlibDeploymentTarget 6.2, *)
42+
var isMainExecutor: Bool { get }
43+
44+
/// Return this executable as a SchedulingExecutor, or nil if that is
45+
/// unsupported.
46+
///
47+
/// Executors that implement SchedulingExecutor should provide their
48+
/// own copy of this method, which will allow the compiler to avoid a
49+
/// potentially expensive runtime cast.
50+
@available(StdlibDeploymentTarget 6.2, *)
51+
var asSchedulingExecutor: (any SchedulingExecutor)? { get }
52+
#endif
3953
}
4054

4155
@available(StdlibDeploymentTarget 6.2, *)
42-
protocol SchedulingExecutor: Executor {
56+
public protocol SchedulingExecutor: Executor {
4357

4458
#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
4559

@@ -58,6 +72,7 @@ protocol SchedulingExecutor: Executor {
5872
/// - tolerance: The maximum additional delay permissible before the
5973
/// job is executed. `nil` means no limit.
6074
/// - clock: The clock used for the delay.
75+
@available(StdlibDeploymentTarget 6.2, *)
6176
func enqueue<C: Clock>(_ job: consuming ExecutorJob,
6277
after delay: C.Duration,
6378
tolerance: C.Duration?,
@@ -77,6 +92,7 @@ protocol SchedulingExecutor: Executor {
7792
/// - tolerance: The maximum additional delay permissible before the
7893
/// job is executed. `nil` means no limit.
7994
/// - clock: The clock used for the delay..
95+
@available(StdlibDeploymentTarget 6.2, *)
8096
func enqueue<C: Clock>(_ job: consuming ExecutorJob,
8197
at instant: C.Instant,
8298
tolerance: C.Duration?,
@@ -113,7 +129,7 @@ extension Executor {
113129
/// own copy of this method, which will allow the compiler to avoid a
114130
/// potentially expensive runtime cast.
115131
@available(StdlibDeploymentTarget 6.2, *)
116-
var asSchedulingExecutor: (any SchedulingExecutor)? {
132+
public var asSchedulingExecutor: (any SchedulingExecutor)? {
117133
return self as? SchedulingExecutor
118134
}
119135
#endif
@@ -136,7 +152,7 @@ extension Executor {
136152

137153
#if !$Embedded
138154
@available(StdlibDeploymentTarget 6.2, *)
139-
var isMainExecutor: Bool { false }
155+
public var isMainExecutor: Bool { false }
140156
#endif
141157

142158
}
@@ -147,20 +163,22 @@ extension SchedulingExecutor {
147163

148164
#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
149165

150-
func enqueue<C: Clock>(_ job: consuming ExecutorJob,
151-
after delay: C.Duration,
152-
tolerance: C.Duration? = nil,
153-
clock: C) {
166+
@available(StdlibDeploymentTarget 6.2, *)
167+
public func enqueue<C: Clock>(_ job: consuming ExecutorJob,
168+
after delay: C.Duration,
169+
tolerance: C.Duration? = nil,
170+
clock: C) {
154171
// If you crash here with a mutual recursion, it's because you didn't
155172
// implement one of these two functions
156173
enqueue(job, at: clock.now.advanced(by: delay),
157174
tolerance: tolerance, clock: clock)
158175
}
159176

160-
func enqueue<C: Clock>(_ job: consuming ExecutorJob,
161-
at instant: C.Instant,
162-
tolerance: C.Duration? = nil,
163-
clock: C) {
177+
@available(StdlibDeploymentTarget 6.2, *)
178+
public func enqueue<C: Clock>(_ job: consuming ExecutorJob,
179+
at instant: C.Instant,
180+
tolerance: C.Duration? = nil,
181+
clock: C) {
164182
// If you crash here with a mutual recursion, it's because you didn't
165183
// implement one of these two functions
166184
enqueue(job, after: clock.now.duration(to: instant),
@@ -355,7 +373,7 @@ extension SerialExecutor {
355373

356374
#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
357375
@available(StdlibDeploymentTarget 6.2, *)
358-
var isMainExecutor: Bool { return MainActor.executor._isSameExecutor(self) }
376+
public var isMainExecutor: Bool { return MainActor.executor._isSameExecutor(self) }
359377
#endif
360378

361379
@available(StdlibDeploymentTarget 6.0, *)
@@ -510,7 +528,8 @@ extension SerialExecutor where Self: Equatable {
510528
/// The idea here is that some executors may work by running a loop
511529
/// that processes events of some sort; we want a way to enter that loop,
512530
/// and we would also like a way to trigger the loop to exit.
513-
protocol RunLoopExecutor: Executor {
531+
@available(StdlibDeploymentTarget 6.2, *)
532+
public protocol RunLoopExecutor: Executor {
514533
/// Run the executor's run loop.
515534
///
516535
/// This method will synchronously block the calling thread. Nested calls to
@@ -540,9 +559,10 @@ protocol RunLoopExecutor: Executor {
540559
func stop()
541560
}
542561

562+
@available(StdlibDeploymentTarget 6.2, *)
543563
extension RunLoopExecutor {
544564

545-
func runUntil(_ condition: () -> Bool) throws {
565+
public func runUntil(_ condition: () -> Bool) throws {
546566
fatalError("run(until condition:) not supported on this executor")
547567
}
548568

@@ -551,14 +571,15 @@ extension RunLoopExecutor {
551571

552572
/// The main executor must conform to these two protocols; we have to
553573
/// make this a protocol for compatibility with Embedded Swift.
554-
protocol MainExecutor: RunLoopExecutor, SerialExecutor {
574+
@available(StdlibDeploymentTarget 6.2, *)
575+
public protocol MainExecutor: RunLoopExecutor, SerialExecutor {
555576
}
556577

557578

558579
/// An ExecutorFactory is used to create the default main and task
559580
/// executors.
560581
@available(StdlibDeploymentTarget 6.2, *)
561-
protocol ExecutorFactory {
582+
public protocol ExecutorFactory {
562583
#if !$Embedded
563584
/// Constructs and returns the main executor, which is started implicitly
564585
/// by the `async main` entry point and owns the "main" thread.
@@ -575,7 +596,7 @@ typealias DefaultExecutorFactory = PlatformExecutorFactory
575596

576597
@available(StdlibDeploymentTarget 6.2, *)
577598
@_silgen_name("swift_createExecutors")
578-
func _createExecutors<F: ExecutorFactory>(factory: F.Type) {
599+
public func _createExecutors<F: ExecutorFactory>(factory: F.Type) {
579600
#if !$Embedded && !SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
580601
MainActor._executor = factory.mainExecutor
581602
#endif
@@ -600,7 +621,8 @@ extension MainActor {
600621
///
601622
/// Attempting to set this after the first `enqueue` on the main
602623
/// executor is a fatal error.
603-
static var executor: any MainExecutor {
624+
@available(StdlibDeploymentTarget 6.2, *)
625+
public static var executor: any MainExecutor {
604626
// It would be good if there was a Swift way to do this
605627
_createDefaultExecutorsOnce()
606628
return _executor!
@@ -624,11 +646,19 @@ extension Task where Success == Never, Failure == Never {
624646
///
625647
/// Attempting to set this after the first `enqueue` on the global
626648
/// executor is a fatal error.
627-
static var defaultExecutor: any TaskExecutor {
649+
@available(StdlibDeploymentTarget 6.2, *)
650+
public static var defaultExecutor: any TaskExecutor {
628651
// It would be good if there was a Swift way to do this
629652
_createDefaultExecutorsOnce()
630653
return _defaultExecutor!
631654
}
655+
656+
/// An unowned version of the above, for performance
657+
@available(StdlibDeploymentTarget 6.2, *)
658+
static var unownedDefaultExecutor: UnownedTaskExecutor {
659+
_createDefaultExecutorsOnce()
660+
return unsafe UnownedTaskExecutor(_defaultExecutor!)
661+
}
632662
}
633663

634664
@available(StdlibDeploymentTarget 6.2, *)
@@ -644,8 +674,9 @@ extension Task where Success == Never, Failure == Never {
644674
/// 3. The task executor for the current thread
645675
///
646676
/// If none of these exist, returns the default executor.
677+
@available(StdlibDeploymentTarget 6.2, *)
647678
@_unavailableInEmbedded
648-
static var currentExecutor: any Executor {
679+
public static var currentExecutor: any Executor {
649680
if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor() {
650681
return activeExecutor
651682
} else if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() {
@@ -657,7 +688,8 @@ extension Task where Success == Never, Failure == Never {
657688
}
658689

659690
/// Get the preferred executor for the current `Task`, if any.
660-
static var preferredExecutor: (any TaskExecutor)? {
691+
@available(StdlibDeploymentTarget 6.2, *)
692+
public static var preferredExecutor: (any TaskExecutor)? {
661693
if let taskExecutor = unsafe _getPreferredTaskExecutor().asTaskExecutor() {
662694
return taskExecutor
663695
}
@@ -670,7 +702,7 @@ extension Task where Success == Never, Failure == Never {
670702
/// This follows the same logic as `currentExecutor`, except that it ignores
671703
/// any executor that isn't a `SchedulingExecutor`.
672704
@available(StdlibDeploymentTarget 6.2, *)
673-
static var currentSchedulingExecutor: (any SchedulingExecutor)? {
705+
public static var currentSchedulingExecutor: (any SchedulingExecutor)? {
674706
if let activeExecutor = unsafe _getActiveExecutor().asSerialExecutor(),
675707
let scheduling = activeExecutor.asSchedulingExecutor {
676708
return scheduling
@@ -762,7 +794,8 @@ public struct UnownedSerialExecutor: Sendable {
762794
unsafe _executor_isComplexEquality(self)
763795
}
764796

765-
func asSerialExecutor() -> (any SerialExecutor)? {
797+
@available(StdlibDeploymentTarget 6.2, *)
798+
public func asSerialExecutor() -> (any SerialExecutor)? {
766799
return unsafe unsafeBitCast(executor, to: (any SerialExecutor)?.self)
767800
}
768801
}
@@ -792,13 +825,14 @@ public struct UnownedTaskExecutor: Sendable {
792825
unsafe self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor)
793826
}
794827

795-
@available(SwiftStdlib 6.2, *)
828+
@available(StdlibDeploymentTarget 6.2, *)
796829
@inlinable
797830
public init<E: TaskExecutor>(_ executor: __shared E) {
798831
unsafe self.executor = Builtin.buildOrdinaryTaskExecutorRef(executor)
799832
}
800833

801-
func asTaskExecutor() -> (any TaskExecutor)? {
834+
@available(StdlibDeploymentTarget 6.2, *)
835+
public func asTaskExecutor() -> (any TaskExecutor)? {
802836
return unsafe unsafeBitCast(executor, to: (any TaskExecutor)?.self)
803837
}
804838
}

stdlib/public/Concurrency/ExecutorBridge.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ uint8_t swift_job_getPriority(Job *job) {
5454
return (uint8_t)(job->getPriority());
5555
}
5656

57+
extern "C" SWIFT_CC(swift)
58+
void swift_job_setPriority(Job *job, uint8_t priority) {
59+
job->setPriority(JobPriority(priority));
60+
}
61+
5762
extern "C" SWIFT_CC(swift)
5863
uint8_t swift_job_getKind(Job *job) {
5964
return (uint8_t)(job->Flags.getKind());

stdlib/public/Concurrency/ExecutorBridge.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ namespace swift {
2323
extern "C" SWIFT_CC(swift)
2424
SerialExecutorRef swift_getMainExecutor();
2525

26+
extern "C" SWIFT_CC(swift)
27+
TaskExecutorRef swift_getDefaultExecutor();
28+
2629
#if !SWIFT_CONCURRENCY_EMBEDDED
2730
extern "C" SWIFT_CC(swift)
2831
void *swift_createDispatchEventC(void (*handler)(void *), void *context);

stdlib/public/Concurrency/ExecutorBridge.swift

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ internal func _jobDeallocate(_ job: Builtin.Job,
7777
@_silgen_name("swift_job_getPriority")
7878
internal func _jobGetPriority(_ job: Builtin.Job) -> UInt8
7979

80+
@available(StdlibDeploymentTarget 6.2, *)
81+
@_silgen_name("swift_job_setPriority")
82+
internal func _jobSetPriority(_ job: Builtin.Job, _ priority: UInt8)
83+
8084
@available(StdlibDeploymentTarget 6.2, *)
8185
@_silgen_name("swift_job_getKind")
8286
internal func _jobGetKind(_ job: Builtin.Job) -> UInt8
@@ -102,6 +106,12 @@ internal func _getMainExecutorAsSerialExecutor() -> UnownedSerialExecutor
102106
#endif // SWIFT_STDLIB_TASK_TO_THREAD_MODEL_CONCURRENCY
103107
#endif // !$Embedded
104108

109+
@available(StdlibDeploymentTarget 6.2, *)
110+
@_silgen_name("swift_getDefaultExecutor")
111+
internal func _getDefaultExecutorAsTaskExecutor() -> UnownedTaskExecutor {
112+
return unsafe Task.unownedDefaultExecutor
113+
}
114+
105115
@available(StdlibDeploymentTarget 6.2, *)
106116
@_silgen_name("swift_dispatchMain")
107117
internal func _dispatchMain()
@@ -128,10 +138,10 @@ internal func _dispatchEnqueueWithDeadline(_ global: CBool,
128138
@_silgen_name("swift_dispatchAssertMainQueue")
129139
internal func _dispatchAssertMainQueue()
130140

141+
@_silgen_name("swift_createDefaultExecutorsOnce")
142+
func _createDefaultExecutorsOnce()
143+
131144
@_silgen_name("swift_getDispatchQueueForExecutor")
132145
internal func _getDispatchQueueForExecutor(
133146
_ executor: UnownedSerialExecutor
134147
) -> OpaquePointer?
135-
136-
@_silgen_name("swift_createDefaultExecutorsOnce")
137-
func _createDefaultExecutorsOnce()

0 commit comments

Comments
 (0)