@@ -271,11 +271,11 @@ protocol RunLoopExecutor: Executor {
271
271
}
272
272
```
273
273
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) :
276
276
277
277
``` swift
278
- protocol SerialRunLoopExecutor : RunLoopExecutor & SerialExecutor {
278
+ protocol MainExecutor : RunLoopExecutor & SerialExecutor & EventableExecutor {
279
279
}
280
280
```
281
281
@@ -289,7 +289,7 @@ extension MainActor {
289
289
///
290
290
/// Attempting to set this after the first `enqueue` on the main
291
291
/// executor is a fatal error.
292
- public static var executor: any SerialRunLoopExecutor { get set }
292
+ public static var executor: any MainExecutor { get set }
293
293
}
294
294
295
295
extension Task {
@@ -298,7 +298,7 @@ extension Task {
298
298
///
299
299
/// Attempting to set this after the first `enqueue` on the global
300
300
/// executor is a fatal error.
301
- public static var defaultExecutor: any Executor { get set }
301
+ public static var defaultExecutor: any TaskExecutor { get set }
302
302
}
303
303
```
304
304
@@ -307,12 +307,12 @@ exposed with the names below:
307
307
308
308
``` swift
309
309
/// The default main executor implementation for the current platform.
310
- public struct PlatformMainExecutor : SerialRunLoopExecutor {
310
+ public struct PlatformMainExecutor : MainExecutor {
311
311
...
312
312
}
313
313
314
314
/// The default global executor implementation for the current platform.
315
- public struct PlatformDefaultExecutor : Executor {
315
+ public struct PlatformDefaultExecutor : TaskExecutor {
316
316
...
317
317
}
318
318
```
@@ -325,27 +325,27 @@ the `Executor` protocols:
325
325
struct ExecutorJob {
326
326
...
327
327
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 )
330
330
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
335
335
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
340
338
341
- /// The raw job kind value.
342
- public var rawValue: RawValue
339
+ /// A task
340
+ public static let task = RawValue ( 0 )
343
341
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
+ }
346
345
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
+ ...
349
349
}
350
350
```
351
351
@@ -422,6 +422,59 @@ extension Task {
422
422
If this option is enabled, an Embedded Swift program that wishes to
423
423
customize executor behaviour will have to use the C API.
424
424
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
+
425
478
## Detailed design
426
479
427
480
### ` async ` main code generation
0 commit comments