1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
// SPDX-License-Identifier: MIT OR Apache-2.0
/*!
# some_executor

Rust made the terrible mistake of not having an async executor in std. And worse: there is no
trait for executors to implement, nor a useful API for users to expect. The result is everyone has to write
their code to a specific executor, and it's always tokio. But tokio has too many drawbacks to make
it the universal choice, and the other executors are too cumbersome to be practical. As a result,
async rust is stuck in limbo.
There are many proposals to fix this. This one's mine. Here's how it works:
**If you want to execute futures**, this crate provides a simple, obvious trait to spawn your future onto "some" executor.
The rich APIs are thoughtfully designed to support typical applications, be fast, compatible with many executors, and future-proof. For example, you
can take an executor as a generic argument, giving the compiler the opportunity to specialize your code for the specific executor.
Or, you can spawn a task onto a global executor via dynamic dispatch. You can provide rich scheduling information that
can be used by the executor to prioritize tasks. You can do all this in a modular and futureproof way.
Oh, and we built an executor into the crate. It's not the greatest, but it is a baseline that's always available.
These builtin executors print warnings when used to alert you they're not production-quality. If you want them to panic instead
(useful for catching missing executor configuration during development), set the environment variable `SOME_EXECUTOR_BUILTIN_SHOULD_PANIC=1`.
**If you want to implement an executor**, this crate provides a simple, obvious trait to receive futures
and execute them, and plug into the ecosystem. Moreover, advanced features like cancellation are
implemented for you, so you get them for free and can focus on the core logic of your executor.
**If you want to write async code**, this crate provides a **standard, robust featureset** that (in my opinion) is
table-stakes for writing async rust in 2024. This includes cancellation, task locals, priorities, execution hints, and much more.
These features are portable and dependable across any executor.
Here are deeper dives on each topic.
# For those spawning tasks
some_executor provides many different API options for many usecases.
1. The [SomeExecutorExt] trait provides an interface to spawn onto an executor. You can take it as a generic argument, specializing your code/types against
the executor you want to use.
2. The [LocalExecutorExt] trait provides the analogous interface for local executors (for your futures which are `!Send`).
3. The object-safe versions, [SomeExecutor] and [SomeLocalExecutor], are for when you want to store your executor in a struct by erasing their type. This has the usual tradeoffs around boxing types.
4. You can spawn onto the "current" executor, at task level [current_executor] or thread level [thread_executor]. This is useful in case you don't want to take an executor as an argument, but your caller probably has one, and you can borrow that.
5. You can spawn onto a program-wide [global_executor]. This is useful in case you don't want to take it as an argument, you aren't sure what your caller is doing (for example you might be handling a signal), and you nonetheless want to spawn a task.
Spawning a task is as simple as calling `spawn` on any of the executor types. Then you get an [TypedObserver] object that you can use to get the results of the task, if interested, or cancel the task.
## Reference executors:
* [test_executors](https://sealedabstract.com/code/test_executors) provides a set of toy executors good enough for unit tests.
* [some_local_executor](https://sealedabstract.com/code/some_local_executor) provides a local executor that runs its task on the current thread, and can also receive tasks from other threads.
* A reference thread-per-core executor is planned.
# For those implementing executors
Here are your APIs:
1. Implement the [SomeExecutorExt] trait. This supports a wide variety of callers and patterns.
2. Alternatively, or in addition, if your executor is local to a thread, implement the [LocalExecutorExt] trait. This type can spawn futures that are `!Send`.
3. Optionally, respond to notifications by implementing the [ExecutorNotified] trait. This is optional, but can provide some efficiency.
4. For static executors (non-Send), use the [static_support] module to erase notifier types and create unified interfaces via [OwnedSomeStaticExecutorErasingNotifier](static_support::OwnedSomeStaticExecutorErasingNotifier).
The main gotcha of this API is that you must wait to poll tasks until after [Task::poll_after]. You can
accomplish this any way you like, such as suspending the task, sleeping, etc. For more details, see the documentation.
# For those writing async code
Mostly, write the code you want to write. But here are some benefits you can get from this crate:
1. If you need to spawn [Task]s from your async code, see above.
2. The crate adds the [task_local] macro, which is comparable to `thread_local` or tokio's version. It provides a way to store data that is local to the task.
3. The provides various particular task locals, such as [task::TASK_ID] and [task::TASK_LABEL], which are useful for debugging and logging information about the current task.
4. The crate propagates some locals, such as [task::TASK_PRIORITY], which can be used to provide useful downstream information about how the task is executing.
5. The crate provides the [task::IS_CANCELLED] local, which can be used to check if the task has been cancelled. This allows you to return early and avoid unnecessary work.
6. The crate provides [hint::Hint] to communicate expected task behavior (I/O-bound vs CPU-bound) to executors, enabling better scheduling decisions.
7. In the future, support for task groups and parent-child cancellation may be added.
# Alternative to `executor-trait`
One way to understand this crate is as an alternative to the [executor-trait](https://crates.io/crates/executor-trait/) project. While I like it a lot,
here's why I made this instead:
1. To support futures with output types that are not `()`.
2. To avoid boxing futures in cases where it isn't really necessary.
3. To provide hints and priorities to the executor.
4. To support task locals and other features that are useful for async code.
5. To support task cancellation much more robustly.
Philosophically, the difference is that `executor-trait` ships the lowest-common denominator API that all executors can support. While this
crate ships the **highest-common denominator API that all async code can use**, together with **polyfills and fallbacks so all executors
can use them** even if they don't support them natively. The result is rich, fast, easy, and portable async rust.
It is straightforward to implement the API of this crate in terms of `executor-trait`, as well as the reverse. So it is possible
to use both projects together.
# Development status
This interface is unstable and may change.
# wasm32 support
This crate has full support for wasm32-unknown-unknown.
*/
/// Observer traits and notification types used by spawned task handles.
/// Platform-appropriate instant type for time measurements.
///
/// This is a re-export of either `std::time::Instant` (on native platforms) or
/// `web_time::Instant` (on wasm32), providing a unified interface for time-related
/// operations across all supported platforms.
///
/// Use this type for task scheduling and timing operations within the executor framework.
pub use Instant;
/// Task priority for scheduling hints.
///
/// This is a re-export of the priority crate's Priority type,
/// allowing executors to make scheduling decisions based on task priority.
pub type Priority = Priority;
use crate;
use crateTask;
use Any;
use Infallible;
use Debug;
use Future;
use Pin;
// Type aliases for complex types to satisfy clippy::type_complexity warnings
/// Type alias for a boxed future that outputs boxed Any and is Send + 'static.
///
/// This type is used for type-erased futures that can be sent between threads.
/// It's primarily used internally for object-safe trait implementations.
pub type BoxedSendFuture =
;
/// Type alias for a boxed observer notifier that handles Send Any values.
///
/// This type provides notifications when a type-erased Send task completes.
pub type BoxedSendObserverNotifier = ;
/// Type alias for a Task that can be used with object-safe spawning.
///
/// This allows spawning type-erased tasks through trait objects,
/// enabling dynamic dispatch when concrete types aren't known at compile time.
pub type ObjSafeTask = ;
/// Type alias for a boxed observer that handles Send Any values.
///
/// This observer can be used to track the status and result of type-erased Send tasks.
pub type BoxedSendObserver = ;
/// Type alias for a future that returns a boxed observer for Send Any values.
///
/// Used for async spawning methods that return observers asynchronously.
pub type BoxedSendObserverFuture<'s> = ;
/// Type alias for a boxed future that outputs boxed Any (non-Send).
///
/// This type is used for type-erased futures that are local to a thread.
pub type BoxedLocalFuture = ;
/// Type alias for a boxed observer notifier that handles Any values (non-Send).
///
/// This type provides notifications when a type-erased local task completes.
pub type BoxedLocalObserverNotifier = ;
/// Type alias for a Task that can be used with local object-safe spawning.
///
/// This allows spawning type-erased !Send tasks through trait objects.
pub type ObjSafeLocalTask = ;
/// Type alias for a boxed observer that handles Any values (non-Send).
///
/// This observer can be used to track the status and result of type-erased local tasks.
pub type BoxedLocalObserver =
;
/// Type alias for a future that returns a boxed observer for local Any values.
///
/// Used for async spawning methods that return local observers asynchronously.
pub type BoxedLocalObserverFuture<'s> = ;
/// Type alias for a boxed future that outputs boxed Any and is 'static but not Send.
///
/// This type is used for type-erased futures with static lifetime but no Send requirement.
pub type BoxedStaticFuture = ;
/// Type alias for a boxed observer notifier that handles 'static Any values (non-Send).
///
/// This type provides notifications when a type-erased static task completes.
pub type BoxedStaticObserverNotifier = ;
/// Type alias for a Task that can be used with static object-safe spawning.
///
/// This allows spawning type-erased 'static tasks without Send requirement.
pub type ObjSafeStaticTask = ;
/// Type alias for a boxed observer that handles 'static Any values (non-Send).
///
/// This observer can be used to track the status and result of type-erased static tasks.
pub type BoxedStaticObserver =
;
/// Type alias for a future that returns a boxed observer for static Any values.
///
/// Used for async spawning methods that return static observers asynchronously.
pub type BoxedStaticObserverFuture<'s> = ;
/*
Design notes.
Send is required because we often want to take this trait object and port it to another thread, etc.
Sync is required to have a global executor.
Clone is required so that we can get copies for sending.
PartialEq could be used to compare runtimes, but I can't imagine anyone needs it
PartialOrd, Ord what does it mean?
Hash might make sense if we support eq but again, I can't imagine anyone needs it.
Debug
I think all the rest are nonsense.
*/
/// A trait targeting 'some' executor.
///
/// Code targeting this trait can spawn tasks on an executor without knowing which executor it is.
/// This is the core abstraction that allows writing executor-agnostic async code.
///
/// If possible, use the [SomeExecutorExt] trait instead for a more ergonomic API.
/// This trait is primarily useful when you need an object-safe trait for dynamic dispatch.
///
/// # Example
///
/// ```
/// # use some_executor::{SomeExecutor, task::Task};
/// # use std::any::Any;
/// # use std::pin::Pin;
/// # use std::future::Future;
/// # use std::convert::Infallible;
/// # fn example(exec: &mut dyn SomeExecutor<ExecutorNotifier = Infallible>) {
/// let future = Box::new(async { Box::new(42) as Box<dyn Any + Send> });
/// let task = Task::new_objsafe(
/// "example".to_string(),
/// future,
/// Default::default(),
/// None
/// );
/// let observer = exec.spawn_objsafe(task);
/// // Can track task completion via observer
/// # }
/// ```
/// A non-objsafe descendant of [SomeExecutor].
///
/// This trait provides a more ergonomic interface for executors, but is not object-safe
/// due to the Clone requirement. This is the preferred trait for most use cases where
/// you know the executor type at compile time.
///
/// # Example
///
/// ```
/// # use some_executor::{SomeExecutorExt, task::Task};
/// # use std::future::Future;
/// # use std::convert::Infallible;
/// # fn example<E: SomeExecutorExt>(mut exec: E) {
/// let task = Task::<_, Infallible>::without_notifications(
/// "example".to_string(),
/// Default::default(),
/// async { 42 }
/// );
/// let observer = exec.spawn(task);
/// // Type-safe observer for the return value
/// # }
/// ```
/**
A trait for executors that can spawn tasks onto the local thread.
This type can spawn futures that are `!Send`.
# About the lifetime parameter
The lifetime parameter defines the lifetime of the executor itself, which is really the longest lifetime of any future it may be executing.
To understand this, it is helpful to consider 3 cases.
## Multithreaded executors
Multithreaded executors along the lines of [SomeExecutor] generally require their futures to be `'static`.
This is vaguely intuitive in the "it is nice to be able to move the future to another thread" sense,
but in full detail it is less intuitive than it seems.
If a future refers to data on the local stack frame, then the future may dangle if:
1. The user returns upstack before the future completes, this could maybe be resolved with clever lifetimes?
2. The thread panics before the future completes, this is a hard problem.
3. The "thread" is really an async context, which is cancelled before the future completes, this is a hard problem.
4. The thread is terminated by the OS for some reason, this is a hard problem.
For at least reasons 2-4, [SomeExecutor] implicitly requires `'static` futures.
## Local executors, globally-scoped
Now let us consider a local executor with global scope (such as a main thread executor). These executors
disptach onto the local thread but exist for a long time, such as the lifetime of the program. These
types of executors have requirements not so dissimilar from multithreaded executors:
1. The user returns upstack before the future completes. This is probably fine, if we poison the
executor in some way (includingstatically), although care must be taken to ensure that the future's memory does not escape.
For example, in a DMA-type operation where the OS is writing to a buffer independently,
that buffer must not be located on the stack.
2. If the thread panics before the future completes, that's probably fine as well since the executor
is inherently poisoned by the panic. See the DMA-style caveat above.
3. If the async context is cancelled before the future completes, that's a big problem. It is a hard one
to solve because it's not obvious how to poison the executor deterministically.
4. If the thread is terminated by the OS, the executor is poisoned so that's ok.
Due to reason 3, local executors with global scope generally require `'static` futures.
## Local executors, locally scoped
Alternatively we may spin up an executor e.g. on a stack frame, for a specific task. In that case:
1. Returning upstack inherently poisons the executor, with sensible lifetime design/analysis.
2. Panicking poisons the executor.
3. Cancellation posions the executor since the executor is on the same stackframe
4. The thread is terminated by the OS, the executor is poisoned.
In this case, the executor can support non-`'static` futures.
## Overall
In summary, for executors with global scope, `'static' should be chosen as the lifetime parameter.
For executors with local scope, this trait can be implemented for any lifetime.
When in doubt, the `'static` lifetime can be chosen and upgraded later.
*/