move brainstorm doc to readme

Changed files
+344 -333
futures
+1
.envrc
··· 1 + use flake
+7
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "futures" 7 + version = "0.1.0"
+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
··· 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
··· 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)]