@@ -271,11 +271,11 @@ protocol RunLoopExecutor: Executor {
271271}
272272```
273273
274- We will also add a protocol for ` RunLoopExecutor ` s that are also
275- ` SerialExecutors ` :
274+ We will also add a protocol for the main actor's executor (see later
275+ for details of ` EventableExecutor ` and why it exists) :
276276
277277``` swift
278- protocol SerialRunLoopExecutor : RunLoopExecutor & SerialExecutor {
278+ protocol MainExecutor : RunLoopExecutor & SerialExecutor & EventableExecutor {
279279}
280280```
281281
@@ -289,7 +289,7 @@ extension MainActor {
289289 ///
290290 /// Attempting to set this after the first `enqueue` on the main
291291 /// executor is a fatal error.
292- public static var executor: any SerialRunLoopExecutor { get set }
292+ public static var executor: any MainExecutor { get set }
293293}
294294
295295extension Task {
@@ -298,7 +298,7 @@ extension Task {
298298 ///
299299 /// Attempting to set this after the first `enqueue` on the global
300300 /// executor is a fatal error.
301- public static var defaultExecutor: any Executor { get set }
301+ public static var defaultExecutor: any TaskExecutor { get set }
302302}
303303```
304304
@@ -307,12 +307,12 @@ exposed with the names below:
307307
308308``` swift
309309/// The default main executor implementation for the current platform.
310- public struct PlatformMainExecutor : SerialRunLoopExecutor {
310+ public struct PlatformMainExecutor : MainExecutor {
311311 ...
312312}
313313
314314/// The default global executor implementation for the current platform.
315- public struct PlatformDefaultExecutor : Executor {
315+ public struct PlatformDefaultExecutor : TaskExecutor {
316316 ...
317317}
318318```
@@ -325,27 +325,27 @@ the `Executor` protocols:
325325struct ExecutorJob {
326326 ...
327327
328- /// Storage reserved for the scheduler (exactly two UInts in size)
329- var schedulerPrivate: some Collection < UInt >
328+ /// Storage reserved for the executor
329+ var executorPrivate: ( UInt , UInt )
330330
331- /// What kind of job this is
332- var kind: ExecutorJobKind
333- ...
334- }
331+ /// Kinds of schedulable jobs.
332+ @frozen
333+ public struct Kind : Sendable {
334+ public typealias RawValue = UInt8
335335
336- /// Kinds of schedulable jobs.
337- @frozen
338- public struct ExecutorJobKind : Sendable {
339- public typealias RawValue = UInt8
336+ /// The raw job kind value.
337+ public var rawValue: RawValue
340338
341- /// The raw job kind value.
342- public var rawValue: RawValue
339+ /// A task
340+ public static let task = RawValue ( 0 )
343341
344- /// A task
345- public static let task = RawValue (0 )
342+ // Job kinds >= 192 are private to the implementation
343+ public static let firstReserved = RawValue (192 )
344+ }
346345
347- // Job kinds >= 192 are private to the implementation
348- public static let firstReserved = RawValue (192 )
346+ /// What kind of job this is
347+ var kind: Kind
348+ ...
349349}
350350```
351351
@@ -422,6 +422,59 @@ extension Task {
422422If this option is enabled, an Embedded Swift program that wishes to
423423customize executor behaviour will have to use the C API.
424424
425+ ### Coalesced Event Interface
426+
427+ We would like custom main executors to be able to integrate with other
428+ libraries, without tying the implementation to a specific library; in
429+ practice, this means that the executor will need to be able to trigger
430+ processing from some external event.
431+
432+ ``` swift
433+ protocol EventableExecutor {
434+
435+ /// An opaque, executor-dependent type used to represent an event.
436+ associatedtype Event
437+
438+ /// Register a new event with a given handler.
439+ ///
440+ /// Notifying the executor of the event will cause the executor to
441+ /// execute the handler, however the executor is free to coalesce multiple
442+ /// event notifications, and is also free to execute the handler at a time
443+ /// of its choosing.
444+ ///
445+ /// Parameters
446+ ///
447+ /// - handler: The handler to call when the event fires.
448+ ///
449+ /// Returns a new opaque `Event`.
450+ public func registerEvent (handler : @escaping () -> ()) -> Event
451+
452+ /// Deregister the given event.
453+ ///
454+ /// After this function returns, there will be no further executions of the
455+ /// handler for the given event.
456+ public func deregister (event : Event)
457+
458+ /// Notify the executor of an event.
459+ ///
460+ /// This will trigger, at some future point, the execution of the associated
461+ /// event handler. Prior to that time, multiple calls to `notify` may be
462+ /// coalesced and result in a single invocation of the event handler.
463+ public func notify (event : Event)
464+
465+ }
466+ ```
467+
468+ Our expectation is that a library that wishes to integrate with the
469+ main executor will register an event with the main executor, and can
470+ then notify the main executor of that event, which will trigger the
471+ executor to run the associated handler at an appropriate time.
472+
473+ The point of this interface is that a library can rely on the executor
474+ to coalesce these events, such that the handler will be triggered once
475+ for a potentially long series of ` MainActor.executor.notify(event:) `
476+ invocations.
477+
425478## Detailed design
426479
427480### ` async ` main code generation
0 commit comments