https://github.com/anglesideangle/bcsc
borrow checked structured concurrency - experimental primitives from swiper
https://github.com/anglesideangle/bcsc
Last synced: 11 months ago
JSON representation
borrow checked structured concurrency - experimental primitives from swiper
- Host: GitHub
- URL: https://github.com/anglesideangle/bcsc
- Owner: AngleSideAngle
- Created: 2025-07-13T20:26:50.000Z (11 months ago)
- Default Branch: main
- Last Pushed: 2025-07-16T01:42:18.000Z (11 months ago)
- Last Synced: 2025-07-17T03:10:16.924Z (11 months ago)
- Language: Rust
- Size: 13.7 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
I will make no_std no_alloc stack allocated async work.
> What does bcsc stand for?
Borrow checked structured concurrency
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.
> What is wrong with you asa??
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.
> Ok(())
[multi-task vs intra-task concurrency](https://without.boats/blog/futures-unordered/)
multi-task concurrency extends better to parallelism
- in my case, you can also implement thread affinity for hardware
`FuturesUnordered` requires Arc (std)
[scoped/non-'static futures](https://github.com/rmanoka/async-scoped)
why this (nostd structured concurrency) is unsound/impossible to do safetly
- https://tmandry.gitlab.io/blog/posts/2023-03-01-scoped-tasks/ !!
- https://without.boats/blog/the-scoped-task-trilemma/ !!
- https://conradludgate.com/posts/async-stack !!
- https://cglab.ca/~abeinges/blah/everyone-poops/
- https://sabrinajewson.org/blog/async-drop
- https://blog.yoshuawuyts.com/the-waker-allocation-problem/
- https://faultlore.com/blah/linear-rust/
- https://blog.yoshuawuyts.com/linear-types-one-pager/
problem: i really want this feature, and am fine with unsound code
sound options:
[async nursery](https://github.com/najamelan/async_nursery) - still 'static and not ergonomic api, wraps `FuturesUnordered`
[async-scoped](https://github.com/rmanoka/async-scoped) - wraps `FuturesUnordered`, stores in executor
better?
https://github.com/maroider/async_scoped_task/blob/master/src/lib.rs
unsafe `async_scoped::scope_and_collect` is perfect (unsafe) but uses heap alloc
[moro](https://github.com/nikomatsakis/moro) - wraps `FuturesUnordered`, relies on single threaded for invariants
[task scope](https://docs.rs/task_scope/0.1.1/task_scope/) - scoped tasks but no drop guarantees unless blocking
relevant rfc for Forget
https://github.com/rust-lang/rfcs/pull/3782 !!
outdated tracking issue
https://github.com/rust-lang/compiler-team/issues/727
other similar proposal for Leak
https://zetanumbers.github.io/book/myosotis.html
alternate way of fixing drop issue
https://github.com/Matthias247/rfcs/pull/1
other relevant work/rfc tracking pr
https://github.com/rust-lang/rfcs/pull/2958
why drop?
https://without.boats/blog/wakers-i/
https://without.boats/blog/wakers-ii/
wakers are references to a Task/whatever the executor uses to wrap and enqueue Futures
safe api: [Wake](https://doc.rust-lang.org/beta/std/task/trait.Wake.html)
where `Task: Wake`, wakers are essentially `Weak` 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)
why can't there be a safe api with `Arc`?
`&dyn Wake` doesn't work because concurrency (think: joins) involves multiple wakers for the same task (unless everything is spawned instead of joined!??)
wakers must be cloned, but clone -> Self (Self vtable is unknown through `&dyn Wake` pointer)
ok that explains *const (), but why remove the lifetimes?
not sure?? it seems like it wouldn't make a difference, most futures are static anyway for afformentioned soundness reasons
- what if wakers are an intrusive linked list that the task traverses to cancel when dropped? (requires `!Forget`)/leak safety
- what if wakers were `&dyn Task` with no cloning, and all intra-task concurrency was moved to join handles for scoped spawns
- 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
- intra-task concurrency is evil??
- 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
there is no way to make existing `RawWaker`/`AtomicWaker` api safe because it cannot be "invalidated"?
## What is this project?
New async primitives that disallow intra-task concurrency, clone of `futures` and `futures-concurrency` for the new primitives.
## TODO:
- [x] ScopedFuture
- [ ] static combinators (Join Race etc), see futures-concurrency
- [ ] `#[bsync]` or some compiler ScopedFuture generation
- [ ] growable combinators (eg. `FutureGroup`, `FuturesUnordered`) (require alloc?)
- [ ] unsound (needs `Forget`) multithreading
- [ ] "rethinking async rust"
- [ ] all of the above for streams
- [ ] rfc?
channels: need lifetimed receievers, probably needs `Forget` (arc-like channels would be unsafe)