+1
.envrc
+1
.envrc
···
1
+
use flake
+7
Cargo.lock
+7
Cargo.lock
+95
README.md
+95
README.md
···
1
+
I will make no_std no_alloc stack allocated async work.
2
+
3
+
> What does bcsc stand for?
4
+
5
+
Borrow checked structured concurrency
6
+
7
+
Normal rust async is borrow checked, but this uses lifetimes (as opposed to spamming ref counting) to enforce structured concurrency in a more elegant, performant, and nostd compatible manner.
8
+
9
+
> What is wrong with you asa??
10
+
11
+
I set out to build the best robotics framework in every aspect. I will do that. It's unfortunate that rust doesn't currently make it easy, but I will gladly fix the problems in rust first.
12
+
13
+
> Ok(())
14
+
15
+
[multi-task vs intra-task concurrency](https://without.boats/blog/futures-unordered/)
16
+
17
+
multi-task concurrency extends better to parallelism
18
+
- in my case, you can also implement thread affinity for hardware
19
+
20
+
`FuturesUnordered` requires Arc (std)
21
+
22
+
[scoped/non-'static futures](https://github.com/rmanoka/async-scoped)
23
+
24
+
why this (nostd structured concurrency) is unsound/impossible to do safetly
25
+
26
+
- https://tmandry.gitlab.io/blog/posts/2023-03-01-scoped-tasks/ !!
27
+
- https://without.boats/blog/the-scoped-task-trilemma/ !!
28
+
- https://conradludgate.com/posts/async-stack !!
29
+
- https://cglab.ca/~abeinges/blah/everyone-poops/
30
+
- https://sabrinajewson.org/blog/async-drop
31
+
- https://blog.yoshuawuyts.com/the-waker-allocation-problem/
32
+
- https://faultlore.com/blah/linear-rust/
33
+
- https://blog.yoshuawuyts.com/linear-types-one-pager/
34
+
35
+
problem: i really want this feature, and am fine with unsound code
36
+
37
+
sound options:
38
+
[async nursery](https://github.com/najamelan/async_nursery) - still 'static and not ergonomic api, wraps `FuturesUnordered`
39
+
[async-scoped](https://github.com/rmanoka/async-scoped) - wraps `FuturesUnordered`, stores in executor
40
+
better?
41
+
https://github.com/maroider/async_scoped_task/blob/master/src/lib.rs
42
+
unsafe `async_scoped::scope_and_collect` is perfect (unsafe) but uses heap alloc
43
+
44
+
[moro](https://github.com/nikomatsakis/moro) - wraps `FuturesUnordered`, relies on single threaded for invariants
45
+
[task scope](https://docs.rs/task_scope/0.1.1/task_scope/) - scoped tasks but no drop guarantees unless blocking
46
+
relevant rfc for Forget
47
+
https://github.com/rust-lang/rfcs/pull/3782 !!
48
+
49
+
outdated tracking issue
50
+
https://github.com/rust-lang/compiler-team/issues/727
51
+
52
+
other similar proposal for Leak
53
+
https://zetanumbers.github.io/book/myosotis.html
54
+
55
+
alternate way of fixing drop issue
56
+
https://github.com/Matthias247/rfcs/pull/1
57
+
58
+
other relevant work/rfc tracking pr
59
+
https://github.com/rust-lang/rfcs/pull/2958
60
+
61
+
why drop?
62
+
https://without.boats/blog/wakers-i/
63
+
https://without.boats/blog/wakers-ii/
64
+
65
+
wakers are references to a Task/whatever the executor uses to wrap and enqueue Futures
66
+
67
+
safe api: [Wake](https://doc.rust-lang.org/beta/std/task/trait.Wake.html)
68
+
where `Task: Wake`, wakers are essentially `Weak<Task>` so they can wake the task while it exists (Weak won't get upgraded once the task goes out of scope, so this is safe)
69
+
why can't there be a safe api with `Arc`?
70
+
`&dyn Wake` doesn't work because concurrency (think: joins) involves multiple wakers for the same task (unless everything is spawned instead of joined!??)
71
+
wakers must be cloned, but clone -> Self (Self vtable is unknown through `&dyn Wake` pointer)
72
+
ok that explains *const (), but why remove the lifetimes?
73
+
not sure?? it seems like it wouldn't make a difference, most futures are static anyway for afformentioned soundness reasons
74
+
- what if wakers are an intrusive linked list that the task traverses to cancel when dropped? (requires `!Forget`)/leak safety
75
+
- what if wakers were `&dyn Task` with no cloning, and all intra-task concurrency was moved to join handles for scoped spawns
76
+
- also note that stuff like join!() doesn't actually execute the specific future, the outermost task gets woken and then executes all subtasks, which return Pending if they aren't ready
77
+
- intra-task concurrency is evil??
78
+
- still have to wait on concurrent join handles? -> join handles are part of nursery/scope, which stores its own waker-per-task -> subwakers/scope's wakers get called -> scope queues relevant tasks -> call higher level task waker
79
+
there is no way to make existing `RawWaker`/`AtomicWaker` api safe because it cannot be "invalidated"?
80
+
81
+
## What is this project?
82
+
83
+
New async primitives that disallow intra-task concurrency, clone of `futures` and `futures-concurrency` for the new primitives.
84
+
85
+
## TODO:
86
+
- [x] ScopedFuture
87
+
- [ ] static combinators (Join Race etc), see futures-concurrency
88
+
- [ ] `#[bsync]` or some compiler ScopedFuture generation
89
+
- [ ] growable combinators (eg. `FutureGroup`, `FuturesUnordered`) (require alloc?)
90
+
- [ ] unsound (needs `Forget`) multithreading
91
+
- [ ] "rethinking async rust"
92
+
- [ ] all of the above for streams
93
+
- [ ] rfc?
94
+
95
+
channels: need lifetimed receievers, probably needs `Forget` (arc-like channels would be unsafe)
+14
futures/src/future.rs
+14
futures/src/future.rs
···
1
+
pub trait Wake {
2
+
fn wake(&mut self);
3
+
}
4
+
5
+
/// ScopedFuture represents a unit of asynchronous computation that must be
6
+
/// polled by an external actor.
7
+
///
8
+
///
9
+
pub trait ScopedFuture<'scope> {
10
+
type Output;
11
+
12
+
// TODO make new Context with &'a mut dyn Wake field
13
+
fn poll(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Self::Output>;
14
+
}
+227
-333
futures/src/lib.rs
+227
-333
futures/src/lib.rs
···
1
-
// what issue does this solve??
2
-
// first, literature review:
3
-
//
4
-
// multi-task vs intra-task concurrency
5
-
// https://without.boats/blog/futures-unordered/
6
-
// multi-task concurrency extends better to parallelism
7
-
// - in my case, you can also implement thread affinity for hardware
8
-
//
9
-
// `FuturesUnordered` requires Arc (std)
10
-
//
11
-
// scoped/non-'static futures https://github.com/rmanoka/async-scoped
12
-
//
13
-
// why this is unsound/impossible to do safetly
14
-
// https://tmandry.gitlab.io/blog/posts/2023-03-01-scoped-tasks/ !!
15
-
// https://without.boats/blog/the-scoped-task-trilemma/ !!
16
-
// https://conradludgate.com/posts/async-stack !!
17
-
// https://cglab.ca/~abeinges/blah/everyone-poops/
18
-
// https://sabrinajewson.org/blog/async-drop
19
-
// https://blog.yoshuawuyts.com/the-waker-allocation-problem/
20
-
// https://faultlore.com/blah/linear-rust/
21
-
// https://blog.yoshuawuyts.com/linear-types-one-pager/
22
-
// problem: i really want this feature, and am fine with unsound code
23
-
//
24
-
// sound options:
25
-
// async nursery - still 'static and not ergonomic api, wraps `FuturesUnordered`
26
-
// https://github.com/najamelan/async_nursery
27
-
//
28
-
// async-scoped - wraps `FuturesUnordered`, stores in executor
29
-
// https://github.com/rmanoka/async-scoped
30
-
// better?
31
-
// https://github.com/maroider/async_scoped_task/blob/master/src/lib.rs
32
-
// unsafe `async_scoped::scope_and_collect` is perfect (unsafe) but uses heap alloc
33
-
//
34
-
// moro - wraps `FuturesUnordered`, relies on single threaded for invariants
35
-
// https://github.com/nikomatsakis/moro
36
-
//
37
-
// task scope - scoped tasks but no drop guarantees unless blocking
38
-
// https://docs.rs/task_scope/0.1.1/task_scope/
39
-
//
40
-
// relevant rfc for Forget
41
-
// https://github.com/rust-lang/rfcs/pull/3782
42
-
// outdated tracking issue
43
-
// https://github.com/rust-lang/compiler-team/issues/727
44
-
// other similar proposal for Leak
45
-
// https://zetanumbers.github.io/book/myosotis.html
46
-
// alternate way of fixing drop issue
47
-
// https://github.com/Matthias247/rfcs/pull/1
48
-
// other relevant work
49
-
// https://github.com/rust-lang/rfcs/pull/2958
50
-
//
51
-
// why drop?
52
-
// https://without.boats/blog/wakers-i/
53
-
// https://without.boats/blog/wakers-ii/
54
-
//
55
-
// wakers are references to a Task/whatever the executor uses to wrap and enqueue Futures
56
-
//
57
-
// safe api: [Wake](https://doc.rust-lang.org/beta/std/task/trait.Wake.html)
58
-
// where `Task: Wake`, wakers are essentially `Weak<Task>` so they can wake the task while it exists (Weak won't get upgraded once the task goes out of scope, so this is safe)
59
-
//
60
-
// why can't there be a safe api with `Arc`?
61
-
// `&dyn Wake` doesn't work because concurrency (think: joins) involves multiple wakers for the same task (unless everything is spawned instead of joined!??)
62
-
// wakers must be cloned, but clone -> Self (Self vtable is unknown through `&dyn Wake` pointer)
63
-
//
64
-
// ok that explains *const (), but why remove the lifetimes?
65
-
// not sure?? it seems like it wouldn't make a difference, most futures are static anyway for afformentioned soundness reasons
66
-
//
67
-
// - what if wakers are an intrusive linked list that the task traverses to cancel when dropped? (requires `!Forget`)/leak safety
68
-
// - what if wakers were `&dyn Task` with no cloning, and all intra-task concurrency was moved to join handles for scoped spawns
69
-
// - also note that stuff like join!() doesn't actually execute the specific future, the outermost task gets woken and then executes all subtasks, which return Pending if they aren't ready
70
-
// - intra-task concurrency is evil??
71
-
// - still have to wait on concurrent join handles? -> join handles are part of nursery/scope, which stores its own waker-per-task -> subwakers/scope's wakers get called -> scope queues relevant tasks -> call higher level task waker
72
-
// there is no way to make existing `RawWaker`/`AtomicWaker` api safe because it cannot be "invalidated"
1
+
mod future;
73
2
74
-
mod mental_illness {
75
-
use std::{
76
-
cell::Cell,
77
-
pin::{self, Pin},
78
-
ptr::NonNull,
79
-
task::Poll,
80
-
};
3
+
use std::{
4
+
cell::Cell,
5
+
pin::{self, Pin},
6
+
ptr::NonNull,
7
+
task::Poll,
8
+
};
81
9
82
-
use cordyceps::{list, Linked};
10
+
use cordyceps::{Linked, list};
83
11
84
-
pub trait Wake {
85
-
fn wake(&mut self);
86
-
}
12
+
/// from yoshuawuyts/futures-concurrency
13
+
/// Wait for all futures to complete.
14
+
///
15
+
/// Awaits multiple futures simultaneously, returning the output of the futures
16
+
/// in the same container type they were created once all complete.
17
+
pub trait Join<'scope> {
18
+
/// The resulting output type.
19
+
type Output;
87
20
88
-
pub trait ScopedFuture<'scope> {
89
-
type Output;
90
-
91
-
// TODO make new Context with &'a mut dyn Wake field
92
-
fn poll(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Self::Output>;
93
-
}
21
+
/// The [`Future`] implementation returned by this method.
22
+
type Future: ScopedFuture<'scope, Output = Self::Output>;
94
23
95
-
/// from yoshuawuyts/futures-concurrency
96
-
/// Wait for all futures to complete.
24
+
/// Waits for multiple futures to complete.
97
25
///
98
26
/// Awaits multiple futures simultaneously, returning the output of the futures
99
-
/// in the same container type they were created once all complete.
100
-
pub trait Join<'scope> {
101
-
/// The resulting output type.
102
-
type Output;
27
+
/// in the same container type they we're created once all complete.
28
+
///
29
+
/// # Examples
30
+
///
31
+
/// Awaiting multiple futures of the same type can be done using either a vector
32
+
/// or an array.
33
+
/// ```rust
34
+
/// # futures::executor::block_on(async {
35
+
/// use futures_concurrency::prelude::*;
36
+
///
37
+
/// // all futures passed here are of the same type
38
+
/// let fut1 = core::future::ready(1);
39
+
/// let fut2 = core::future::ready(2);
40
+
/// let fut3 = core::future::ready(3);
41
+
///
42
+
/// let outputs = [fut1, fut2, fut3].join().await;
43
+
/// assert_eq!(outputs, [1, 2, 3]);
44
+
/// # })
45
+
/// ```
46
+
///
47
+
/// In practice however, it's common to want to await multiple futures of
48
+
/// different types. For example if you have two different `async {}` blocks,
49
+
/// you want to `.await`. To do that, you can call `.join` on tuples of futures.
50
+
/// ```rust
51
+
/// # futures::executor::block_on(async {
52
+
/// use futures_concurrency::prelude::*;
53
+
///
54
+
/// async fn some_async_fn() -> usize { 3 }
55
+
///
56
+
/// // the futures passed here are of different types
57
+
/// let fut1 = core::future::ready(1);
58
+
/// let fut2 = async { 2 };
59
+
/// let fut3 = some_async_fn();
60
+
/// // ^ NOTE: no `.await` here!
61
+
///
62
+
/// let outputs = (fut1, fut2, fut3).join().await;
63
+
/// assert_eq!(outputs, (1, 2, 3));
64
+
/// # })
65
+
/// ```
66
+
///
67
+
/// <br><br>
68
+
/// This function returns a new future which polls all futures concurrently.
69
+
fn join(self) -> Self::Future;
70
+
}
103
71
104
-
/// The [`Future`] implementation returned by this method.
105
-
type Future: ScopedFuture<'scope, Output = Self::Output>;
72
+
// "look at what they need for a fraction of our power" (more efficient join impl is regular join here)
73
+
// https://github.com/yoshuawuyts/futures-concurrency/blob/main/src/utils/wakers/array/waker.rs
74
+
// possibly copy large portions of futures-concurrency over here
106
75
107
-
/// Waits for multiple futures to complete.
108
-
///
109
-
/// Awaits multiple futures simultaneously, returning the output of the futures
110
-
/// in the same container type they we're created once all complete.
111
-
///
112
-
/// # Examples
113
-
///
114
-
/// Awaiting multiple futures of the same type can be done using either a vector
115
-
/// or an array.
116
-
/// ```rust
117
-
/// # futures::executor::block_on(async {
118
-
/// use futures_concurrency::prelude::*;
119
-
///
120
-
/// // all futures passed here are of the same type
121
-
/// let fut1 = core::future::ready(1);
122
-
/// let fut2 = core::future::ready(2);
123
-
/// let fut3 = core::future::ready(3);
124
-
///
125
-
/// let outputs = [fut1, fut2, fut3].join().await;
126
-
/// assert_eq!(outputs, [1, 2, 3]);
127
-
/// # })
128
-
/// ```
129
-
///
130
-
/// In practice however, it's common to want to await multiple futures of
131
-
/// different types. For example if you have two different `async {}` blocks,
132
-
/// you want to `.await`. To do that, you can call `.join` on tuples of futures.
133
-
/// ```rust
134
-
/// # futures::executor::block_on(async {
135
-
/// use futures_concurrency::prelude::*;
136
-
///
137
-
/// async fn some_async_fn() -> usize { 3 }
138
-
///
139
-
/// // the futures passed here are of different types
140
-
/// let fut1 = core::future::ready(1);
141
-
/// let fut2 = async { 2 };
142
-
/// let fut3 = some_async_fn();
143
-
/// // ^ NOTE: no `.await` here!
144
-
///
145
-
/// let outputs = (fut1, fut2, fut3).join().await;
146
-
/// assert_eq!(outputs, (1, 2, 3));
147
-
/// # })
148
-
/// ```
149
-
///
150
-
/// <br><br>
151
-
/// This function returns a new future which polls all futures concurrently.
152
-
fn join(self) -> Self::Future;
153
-
}
76
+
// contains a future that may be finished, safe to poll after ready
77
+
enum MaybeReady<'scope, F: ScopedFuture<'scope>> {
78
+
Polling(F),
79
+
Ready(F::Output),
80
+
}
154
81
155
-
// "look at what they need for a fraction of our power" (more efficient join impl is regular join here)
156
-
// https://github.com/yoshuawuyts/futures-concurrency/blob/main/src/utils/wakers/array/waker.rs
157
-
// possibly copy large portions of futures-concurrency over here
82
+
impl<'scope, F: ScopedFuture<'scope>> ScopedFuture<'scope> for MaybeReady<'scope, F> {
83
+
type Output = F::Output;
158
84
159
-
// contains a future that may be finished, safe to poll after ready
160
-
enum MaybeReady<'scope, F: ScopedFuture<'scope>> {
161
-
Polling(F),
162
-
Ready(F::Output),
85
+
fn poll(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Self::Output> {
86
+
todo!()
163
87
}
88
+
}
164
89
165
-
impl<'scope, F: ScopedFuture<'scope>> ScopedFuture<'scope> for MaybeReady<'scope, F> {
166
-
type Output = F::Output;
90
+
// TODO bit packing
91
+
struct WakeStore {
92
+
ready: bool,
93
+
}
167
94
168
-
fn poll(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Self::Output> {
169
-
todo!()
170
-
}
95
+
impl WakeStore {
96
+
fn read_ready(&mut self) -> bool {
97
+
let out = self.ready;
98
+
self.ready = false;
99
+
out
171
100
}
101
+
}
172
102
173
-
// TODO bit packing
174
-
struct WakeStore {
175
-
ready: bool,
103
+
impl Wake for WakeStore {
104
+
fn wake(&mut self) {
105
+
self.ready = true;
176
106
}
107
+
}
177
108
178
-
impl WakeStore {
179
-
fn read_ready(&mut self) -> bool {
180
-
let out = self.ready;
181
-
self.ready = false;
182
-
out
183
-
}
184
-
}
109
+
// field for Join
110
+
struct Pollable<'scope, F: ScopedFuture<'scope>> {
111
+
future: MaybeReady<'scope, F>,
112
+
waker: WakeStore,
113
+
}
185
114
186
-
impl Wake for WakeStore {
187
-
fn wake(&mut self) {
188
-
self.ready = true;
115
+
impl<'scope, F: ScopedFuture<'scope>> Pollable<'scope, F> {
116
+
fn new(fut: F) -> Self {
117
+
Self {
118
+
future: MaybeReady::Polling(fut),
119
+
waker: WakeStore { ready: true },
189
120
}
190
121
}
122
+
}
191
123
192
-
// field for Join
193
-
struct Pollable<'scope, F: ScopedFuture<'scope>> {
194
-
future: MaybeReady<'scope, F>,
195
-
waker: WakeStore,
196
-
}
197
-
198
-
impl<'scope, F: ScopedFuture<'scope>> Pollable<'scope, F> {
199
-
fn new(fut: F) -> Self {
200
-
Self {
201
-
future: MaybeReady::Polling(fut),
202
-
waker: WakeStore { ready: true },
203
-
}
124
+
// heavily based on https://github.com/yoshuawuyts/futures-concurrency
125
+
macro_rules! impl_join_tuple {
126
+
($mod_name:ident $StructName:ident $($F:ident)+) => {
127
+
pub struct $StructName<'scope, $($F: ScopedFuture<'scope>),+> {
128
+
$($F: Pollable<'scope, $F>,)*
204
129
}
205
-
}
206
130
207
-
// heavily based on https://github.com/yoshuawuyts/futures-concurrency
208
-
macro_rules! impl_join_tuple {
209
-
($mod_name:ident $StructName:ident $($F:ident)+) => {
210
-
pub struct $StructName<'scope, $($F: ScopedFuture<'scope>),+> {
211
-
$($F: Pollable<'scope, $F>,)*
212
-
}
131
+
impl<'scope, $($F: ScopedFuture<'scope>),+> ScopedFuture<'scope> for $StructName<'scope, $($F),+> {
132
+
type Output = ($($F::Output),+);
213
133
214
-
impl<'scope, $($F: ScopedFuture<'scope>),+> ScopedFuture<'scope> for $StructName<'scope, $($F),+> {
215
-
type Output = ($($F::Output),+);
216
134
217
-
218
-
fn poll(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Self::Output> {
219
-
let this = unsafe { self.get_unchecked_mut() };
135
+
fn poll(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Self::Output> {
136
+
let this = unsafe { self.get_unchecked_mut() };
220
137
221
-
let ready = true;
222
-
223
-
// "loop" through all futures, poll if ready
224
-
$(
225
-
match this.$F.future {
226
-
MaybeReady::Polling(fut) => {
227
-
let out = unsafe { Pin::new_unchecked(&mut fut) }.poll(&mut this.$F.waker);
228
-
if let Poll::Ready(result) = out {
229
-
// violate pin but that's ok because the future completed
230
-
this.$F.future = MaybeReady::Ready(result);
231
-
}
232
-
},
233
-
MaybeReady::Ready(_) => {
138
+
let ready = true;
234
139
140
+
// "loop" through all futures, poll if ready
141
+
$(
142
+
match this.$F.future {
143
+
MaybeReady::Polling(fut) => {
144
+
let out = unsafe { Pin::new_unchecked(&mut fut) }.poll(&mut this.$F.waker);
145
+
if let Poll::Ready(result) = out {
146
+
// violate pin but that's ok because the future completed
147
+
this.$F.future = MaybeReady::Ready(result);
235
148
}
149
+
},
150
+
MaybeReady::Ready(_) => {
151
+
236
152
}
237
-
)
238
-
239
-
todo!()
240
153
}
241
-
}
154
+
)
242
155
243
-
impl<'scope, $($F: ScopedFuture<'scope>),+> Join<'scope> for ($($F),+) {
244
-
type Output = ($($F::Output),*);
245
-
type Future = $StructName<'scope, $($F),+>;
246
-
247
-
fn join(self) -> Self::Future {
248
-
let ($($F),+): ($($F),+) = self;
249
-
$StructName { $($F: Pollable::new($F),)* }
250
-
}
156
+
todo!()
251
157
}
252
-
253
-
// // Implementation block for the generated struct.
254
-
// impl<$(F),+> $StructName<$(F),+> {
255
-
// /// Returns the number of generic types the struct was created with.
256
-
// /// This uses a common macro trick to "count" repetitions by creating
257
-
// /// an array of stringified identifiers and getting its length at compile time.
258
-
// const fn generic_type_count() -> usize {
259
-
// [$(stringify!(F)),*].len()
260
-
// }
158
+
}
261
159
262
-
// /// Checks if the `count` field is greater than the number of generic types.
263
-
// pub fn is_count_greater_than_len(&self) -> bool {
264
-
// self.count as usize > Self::generic_type_count()
265
-
// }
266
-
// }
267
-
};
268
-
}
160
+
impl<'scope, $($F: ScopedFuture<'scope>),+> Join<'scope> for ($($F),+) {
161
+
type Output = ($($F::Output),*);
162
+
type Future = $StructName<'scope, $($F),+>;
269
163
270
-
impl_join_tuple!(join2 Join2 A B);
164
+
fn join(self) -> Self::Future {
165
+
let ($($F),+): ($($F),+) = self;
166
+
$StructName { $($F: Pollable::new($F),)* }
167
+
}
168
+
}
271
169
272
-
// scoped future combinators:
273
-
//
274
-
// Join<N>
275
-
// TryJoin
276
-
// Race
277
-
// RaceOk
278
-
//
279
-
// add Deadline(a, rest) (deadline_against())
280
-
// also functionality like (a, b, c).join().race_against(d, e, f)
281
-
//
282
-
// UnorderedJoinQueueStream? is this VecJoinStream?
283
-
// OrderedJoinQueueStream
284
-
285
-
pub trait ScopedStream<'scope> {
286
-
type Item;
287
-
288
-
fn poll_next(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Option<Self::Item>>;
289
-
}
170
+
// // Implementation block for the generated struct.
171
+
// impl<$(F),+> $StructName<$(F),+> {
172
+
// /// Returns the number of generic types the struct was created with.
173
+
// /// This uses a common macro trick to "count" repetitions by creating
174
+
// /// an array of stringified identifiers and getting its length at compile time.
175
+
// const fn generic_type_count() -> usize {
176
+
// [$(stringify!(F)),*].len()
177
+
// }
290
178
291
-
// represents an active task, to be used by UnorderedJoinHandle
292
-
pub struct Task<'scope, Output, F: ScopedFuture<'scope, Output = Output>> {
293
-
inner: F,
294
-
scope: &'scope UnorderedJoinHandle<'scope, Output>,
295
-
next_active: list::Links<Self>,
296
-
}
179
+
// /// Checks if the `count` field is greater than the number of generic types.
180
+
// pub fn is_count_greater_than_len(&self) -> bool {
181
+
// self.count as usize > Self::generic_type_count()
182
+
// }
183
+
// }
184
+
};
185
+
}
297
186
298
-
impl<'scope, Output, F: ScopedFuture<'scope, Output = Output>> Wake for Task<'scope, Output, F> {
299
-
fn wake(&self) {
300
-
// TODO add self to running queue
301
-
// propogate wake up scope
302
-
self.scope.enqueue();
303
-
}
304
-
}
187
+
impl_join_tuple!(join2 Join2 A B);
305
188
306
-
// impl<'scope, Output, F: ScopedFuture<'scope, Output = Output>> TaskErasure
307
-
// for Task<'scope, Output, F>
308
-
// {
309
-
// }
189
+
// scoped future combinators:
190
+
//
191
+
// Join<N>
192
+
// TryJoin
193
+
// Race
194
+
// RaceOk
195
+
//
196
+
// add Deadline(a, rest) (deadline_against())
197
+
// also functionality like (a, b, c).join().race_against(d, e, f)
198
+
//
199
+
// UnorderedJoinQueueStream? is this VecJoinStream?
200
+
// OrderedJoinQueueStream
310
201
311
-
// !Forget
312
-
// this is the most annoying data structure ever:
313
-
// should it own the tasks?? maybe
314
-
//
315
-
// a)
316
-
//
317
-
// Task { &Future, *mut Task }
318
-
//
319
-
// b)
320
-
//
321
-
// b is better, use proc macros, no data structures!
322
-
//
323
-
// <n1, n2, n3 > (o1, o2, o3) etc
324
-
// pub struct UnorderedJoinHandle<'scope, Output> {
325
-
// parent_waker: &'scope mut dyn Wake,
326
-
// active_head: Pin<*const dyn TaskErasure>,
327
-
// inactive_head: Pin<*const dyn TaskErasure>,
328
-
// // tasks: [&'scope dyn ScopedFuture<'scope, Output = Output>; N],
329
-
// }
202
+
pub trait ScopedStream<'scope> {
203
+
type Item;
330
204
331
-
// impl<'scope, Output> UnorderedJoinHandle<'scope, Output> {
332
-
// /// adds task to running queue, wakes task
333
-
// pub fn enqueue(&self) {
334
-
// self.parent_waker.wake();
335
-
// todo!()
336
-
// }
205
+
fn poll_next(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Option<Self::Item>>;
206
+
}
337
207
338
-
// pub fn spawn(&self) {
339
-
// todo!()
340
-
// }
341
-
// }
208
+
// represents an active task, to be used by UnorderedJoinHandle
209
+
pub struct Task<'scope, Output, F: ScopedFuture<'scope, Output = Output>> {
210
+
inner: F,
211
+
scope: &'scope UnorderedJoinHandle<'scope, Output>,
212
+
next_active: list::Links<Self>,
213
+
}
342
214
343
-
// should be mandated by !Forget
344
-
/// # Soundness
345
-
///
346
-
/// This is unsound!! Don't use my code.
347
-
impl<'scope, const N: usize, Output> Drop for UnorderedJoinHandle<'scope, N, Output> {
348
-
fn drop(&mut self) {
349
-
// TODO sever linked list
350
-
}
215
+
impl<'scope, Output, F: ScopedFuture<'scope, Output = Output>> Wake for Task<'scope, Output, F> {
216
+
fn wake(&self) {
217
+
// TODO add self to running queue
218
+
// propogate wake up scope
219
+
self.scope.enqueue();
351
220
}
221
+
}
352
222
353
-
impl<'scope, const N: usize, Output> ScopedStream<'scope>
354
-
for UnorderedJoinHandle<'scope, N, Output>
355
-
{
356
-
type Item = Output;
223
+
// impl<'scope, Output, F: ScopedFuture<'scope, Output = Output>> TaskErasure
224
+
// for Task<'scope, Output, F>
225
+
// {
226
+
// }
357
227
358
-
fn poll_next(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Option<Self::Item>> {
359
-
// update parent waker to latest waker
360
-
// unsafe { self.get_mut(|f| &mut f.parent_waker) }.set(cx);
361
-
self.get_mut().parent_waker = cx;
228
+
// !Forget
229
+
// this is the most annoying data structure ever:
230
+
// should it own the tasks?? maybe
231
+
//
232
+
// a)
233
+
//
234
+
// Task { &Future, *mut Task }
235
+
//
236
+
// b)
237
+
//
238
+
// b is better, use proc macros, no data structures!
239
+
//
240
+
// <n1, n2, n3 > (o1, o2, o3) etc
241
+
// pub struct UnorderedJoinHandle<'scope, Output> {
242
+
// parent_waker: &'scope mut dyn Wake,
243
+
// active_head: Pin<*const dyn TaskErasure>,
244
+
// inactive_head: Pin<*const dyn TaskErasure>,
245
+
// // tasks: [&'scope dyn ScopedFuture<'scope, Output = Output>; N],
246
+
// }
362
247
363
-
todo!()
364
-
}
365
-
}
248
+
// impl<'scope, Output> UnorderedJoinHandle<'scope, Output> {
249
+
// /// adds task to running queue, wakes task
250
+
// pub fn enqueue(&self) {
251
+
// self.parent_waker.wake();
252
+
// todo!()
253
+
// }
366
254
367
-
mod tests {
368
-
use super::*;
255
+
// pub fn spawn(&self) {
256
+
// todo!()
257
+
// }
258
+
// }
369
259
370
-
#[test]
371
-
fn hmm() {
372
-
// struct Task {}
373
-
// impl Wake for Task {
374
-
// fn wake(&self) {
375
-
// todo!()
376
-
// }
377
-
// }
378
-
}
260
+
// should be mandated by !Forget
261
+
/// # Soundness
262
+
///
263
+
/// This is unsound!! Don't use my code.
264
+
impl<'scope, const N: usize, Output> Drop for UnorderedJoinHandle<'scope, N, Output> {
265
+
fn drop(&mut self) {
266
+
// TODO sever linked list
379
267
}
380
268
}
381
269
382
-
use std::{pin::Pin, task::Poll};
270
+
impl<'scope, const N: usize, Output> ScopedStream<'scope>
271
+
for UnorderedJoinHandle<'scope, N, Output>
272
+
{
273
+
type Item = Output;
383
274
384
-
use futures::stream::FuturesUnordered;
275
+
fn poll_next(self: Pin<&mut Self>, cx: &'scope mut dyn Wake) -> Poll<Option<Self::Item>> {
276
+
// update parent waker to latest waker
277
+
// unsafe { self.get_mut(|f| &mut f.parent_waker) }.set(cx);
278
+
self.get_mut().parent_waker = cx;
385
279
386
-
pub fn add(left: u64, right: u64) -> u64 {
387
-
left + right
280
+
todo!()
281
+
}
388
282
}
389
283
390
284
#[cfg(test)]