• Stars
    star
    265
  • Rank 154,577 (Top 4 %)
  • Language
    Rust
  • License
    MIT License
  • Created over 2 years ago
  • Updated 7 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Experiments with structured concurrency in Rust

moro

Experiments with structured concurrency in Rust

TL;DR

Similar to rayon or std::thread::scope, moro let's you create a scope using a moro::async_scope! macro. Within this scope, you can spawn jobs that can access stack data defined outside the scope:

let value = 22;
let result = moro::async_scope!(|scope| {
    let future1 = scope.spawn(async {
        let future2 = scope.spawn(async {
            value // access stack values that outlive scope
        });

        let v = future2.await * 2;
        v
    });

    let v = future1.await * 2;
    v
})
.await;
eprintln!("{result}"); // prints 88

Stack access, core scope API

Invoke moro::async_scope!(|scope| ...) and you get back a future for the overall scope that you can await to start up the scope.

Within the scope body (...) you can invoke scope.spawn(async { ... }) to spawn a job. This job must terminate before the scope itself is considered completed. The result of scope.spawn is a future whose result is the result returned by the job.

Early termination and cancellation

Moro scopes support early termination or cancellation. You can invoke scope.terminate(v).await and all spawned threads within the scope will immediately stop executing. Termination is commonly used when v is a Result to make Err values cancel (we offer helper methods like unwrap_or_cancel for this in the prelude).

An example that uses cancellation is shown in monitor -- in this example, several jobs are spawned which all examine one integer from the input. If any integers are negative, the entire scope is canceled.

Future work: Integrating with rayon-like iterators

I want to do this. :)

Frequently asked questions

Where does the name moro come from?

It's from the Greek word for "baby" (μωρό). The popular "trio" library uses the term "nursery" to refer to a scope, so I wanted to honor that lineage.

Apparently though "moros" is also the 'hateful' spirit of impending doom, which I didn't know, but is kinda' awesome.

Are there other async nursery projects available, and how does moro compare?

Yes! I'm aware of...

  • async_nursery, which is similar to moro but provides parallel execution (not just concurrent), but -- as a result -- requires a 'static bound.
  • FuturesUnordered, which can be used as a kind of nursery, but which also has a number of known footguns. This type is currently used in the moro implementation, but moro's API prevents those footguns from happening.
  • select operations are commonly used to "model" parallel streams; like with FuturesUnordered, this is an errorprone approach, and moro evolved in part as an alternative to select-like APIs.

Why do moro spawns only run concurrently, not parallel?

Parallel moro tasks cannot, with Rust as it is today, be done safely. The full details are in a later question, but the tl;dr is that when a moro scope yields to its caller, the scope is "giving up control" to its caller, and that caller can -- if it chooses -- just forget the scope entirely and stop executing it. This means that if the moro scope has started parallel threads, those threads will go on accessing the caller's data, which can create data races. Not good.

Isn't running concurrently a huge limitation?

Sort of? Parallel would definitely be nice, but for many async servers, you get parallelism between connections and you don't need to have parallelism within a connection. You can also use other mechanisms to get parallelism, but 'static bounds are required.

OK, but why do moro spawns only run concurrently, not parallel? Give me the details!

The Future::poll method permits safe code to "partially advance" a future and then, because a future is an ordinary Rust value, "forget" it (e.g., via std::mem::forget, though there are other ways). This would allow you to create a scope, execute it a few times, and then discard it without running any destructor:

async fn method() {
    let data = vec![1, 2, 3];
    let some_future = moro::async_scope!(|scope| {
        scope.spawn(async { 
            for d in &data {
                tokio::task::yield_now().await;
            }
        });
    });

    // pseudo-code, we'd have to gin up a context etc:
    std::future::Future::poll(some_future);
    std::future::Future::poll(some_future);
    std::mem::forget(some_future);
    return;
}

If moro tasks were running in parallel, there would be no way for us to ensure that the parallel threads spawned inside the scope are stopped before method returns. As a result, they would go on accessing the data from data even after the stack frame was popped and the data was freed. Bad.

But because moro is limited to concurrency, this is fine. Tasks in the scope only advance when they are polled (they're not parallel) -- so when you "forget" the scope, you simply stop executing the tasks too.

Note that this problem doesn't occur in libraries like rayon or the new std::thread::scope call. This is because sync code has a capability that async code lacks: a sync function can block its caller (this is because safe Rust forbids longjmp). But in async code, under Rust's current model, so long as you "await" something, you are giving up control to your caller and they are free to never poll you again. This means, I believe, that it is not possible to have a "scope" like moro's that safely refers to data outside of the scope, since that data is owned by your callers, and you cannot force them not to return. Put another way, async code can, by cooperating with the executor, ensure that some future runs to completion. Any call to tokio::spawn will do that. But you cannot ensure that your future is embedded in something else that runs to completion.

I do not believe parallel execution can be safely enabled without modifying the Future trait or Rust in some way. There are various proposals to change the Future trait to permit moro to support parallel execution (those same proposals would help for supporting io-uring, DMA, and other features), but the exact path forward hasn't been settled.

More Repositories

1

intorust

Learn Rust real good.
HTML
306
star
2

how-to-rust

A collection of blog posts and links that talk about how to successfully use Rust.
285
star
3

rustacean-principles

Handlebars
176
star
4

skill-tree

Skill-tree rendering
Rust
145
star
5

office-hours

Help me help you!
110
star
6

borrowck

Modeling NLL and the Rust borrowck
Rust
67
star
7

rust-design-axioms

Discovering and describing Rust's design axioms
61
star
8

fields-in-traits-rfc

An (experimental) RFC repo devoted to the "fields in traits" RFC.
59
star
9

gnome-class

Some experimental macros for GNOME integration
Rust
54
star
10

nll-rfc

Non-lexical lifetimes RFC.
46
star
11

bidir-type-infer

Implementing the type system described in the paper "Complete and Easy Bidirectional Type Inference" in Rust
Rust
43
star
12

dogged

Experimental persistent collections in Rust
Rust
42
star
13

perf-focus

A perf script for helping to answer queries about how much time a given function occupies, where it is called from, and so forth.
Rust
35
star
14

rustypop

A not-yet-functional Rust parser written in LALRPOP.
Rust
33
star
15

cargo-incremental

A fuzzing tool for incremental compilation that walks git history
Rust
33
star
16

dyner

Experimenting with ergonomic dyn types
Rust
29
star
17

babysteps

Babysteps blog
JavaScript
27
star
18

rust-tutorials-keynote

Keynote versions of my Rust tutorials.
HTML
26
star
19

rust-etags

Exuberant ctags language definition for Rust.
Shell
22
star
20

plmw-2022

Talk to be given at PLMW 2022
CSS
20
star
21

polonius.next

experimental datalog rules for a next gen polonius
Rust
19
star
22

concurrency-tutorial

Rust concurrency tutorial examples
Rust
19
star
23

rust-ctags

ctags definition for Rust
Shell
17
star
24

rust-skill-tree

A (WIP) skill tree for Rust
HTML
17
star
25

mutable

Tinkering with a more ergonomic cell abstraction
Rust
16
star
26

rusty-peg

Rust
16
star
27

great-pl-papers

Exploring awesome PL papers in redex
Racket
16
star
28

rust-tutorializer

A very basic framework for publishing and experimenting with sample code for Rust tutorials. Also includes a collection of lecture plans. Currently for my own personal use, but maybe evolvable into something byeond that.
Rust
14
star
29

rustnl-2024

Type theory for busy engineers
Rust
14
star
30

rust-belt-rust-2019

Polonius talk at RBR 2019
CSS
13
star
31

context-capabilities-initiative

12
star
32

ppl-2023

Talk for PPL2023
CSS
12
star
33

lsrtm

Concurrency and parallelism in async Rust
CSS
12
star
34

rustc-on-demand-incremental-design-doc

A draft design document / RFC describing the plan for incremental and "on-demand"
12
star
35

rust-name-resolution-algorithm

A prototype of my proposed name resolution algorithm for Rust.
Rust
12
star
36

nll-souffle

experimental variant of nll analysis based on datalog
Rust
11
star
37

rustconf-2021

Talk for RustConf 2021
CSS
11
star
38

pliss-2019

Slides for PLISS 2019
CSS
10
star
39

typing-haskell-in-rust

Port of the code from Typing Haskell in Haskell to Rust
Rust
10
star
40

eurorust-2023

Talk for EuroRust 2023
CSS
9
star
41

mathema

An experimental flashcard program.
Rust
9
star
42

learn-some-rust-this-month

Code snippets for Rust discussions
Rust
9
star
43

chalk-it-up

Chalk 2.0: a design document and planning repository
JavaScript
8
star
44

simple-graph

A (very) simple graph library.
Rust
8
star
45

rust-runtime-benchmarks

Some benchmarks for testing rustc performance.
Rust
8
star
46

rust-iter

An iteration library for Rust
Rust
7
star
47

rustc-parallelization

A repo to house plans to parallelize rustc.
7
star
48

lsrtm-2022-05

Talking about async Rust
CSS
7
star
49

rust-formal

Featherweight Java for Rust
6
star
50

rustconf2019-solana

Talk for RustConf 2019 at Solana event
CSS
5
star
51

rustc-mux

A rustup-compatible rustc toolchain that selects which version of `rustc` to use based on your local directory.
Shell
5
star
52

rust-latam-2019

Keynote speech for Rust LATAM 2019
CSS
5
star
53

graph-compress

A little algorithm used in incremental compilation to compress graphs. Will move into rustc eventually.
Rust
5
star
54

rust-recursive-descent-regex-parser

A very simple demonstration of how a recursice descent parser works.
Rust
5
star
55

cargo-chrono

a cargo benchmarking tool
Rust
5
star
56

pjs-polyfill

Prototype of PJS API based on typed objects.
JavaScript
5
star
57

rustnation-24

CSS
5
star
58

rusty-rete

An implementation of the Rete algorithm in Rust.
Rust
5
star
59

2022-06-15-blogpost

Rust
4
star
60

rebnf

"Rebnf" -- an extended form of EBNF, intended primarily for specifying Rust grammar
Rust
4
star
61

boston-rust-artwork

Citgo sign forever
4
star
62

rusty-wam

A Rust implementation of the classic Warren Abstract Machine (WAM) for Prolog
Rust
4
star
63

iFood-Tech-Day-2022

CSS
4
star
64

dwim

Who *doesn't* want the computer to just do what they mean?!
4
star
65

squared

Rust
4
star
66

yaccrpop

A hacky and quite incomplete program to convert YACC files into skeleton LALRPOP files.
Rust
4
star
67

crabcake

Rust
4
star
68

plar-rs

Exploring John Harrison's Handbook of Practical Logic and Automated Reasoning, in Rust
Rust
4
star
69

Popsicle

A utility for writing type rules that are emitted as LaTeX.
Python
3
star
70

async-fn-in-traits

Playground for async fn in traits
3
star
71

rust-trait-object-upcast

In-progress RFC around trait object upcasting
3
star
72

rayon-plot

Plotting tool for Rayon logs (WIP)
3
star
73

rustverify2022

Presentation for Rust Verify 2022
CSS
3
star
74

rust-lifetime-errors

A collection of rust files with lifetime errors
3
star
75

Lathos

A generic debug log server.
Java
3
star
76

enimerosi

github notification manager (super megaduper experimental)
TypeScript
2
star
77

rust-grammar-toy

Toy model of Rust grammar, specifically targeted at exploring RFC 2250
Rust
2
star
78

SpanakopitaPlugin

A plugin for TextMate to create a Wiki-like environment.
Objective-C
2
star
79

genasynciter

sketching a possible approach to generator/iterator traits
Rust
2
star
80

nll-crater-run-results

tracking NLL crater run results
2
star
81

intorust.vscode

A VSCode extension for learning Rust
CSS
2
star
82

PopsicleBundle

A TextMate bundle for Popsicle.
2
star
83

Harmonic

Harmonic Programming Language
2
star
84

suspend

Experiments with suspension
Rust
2
star
85

vision-squared

Vision² -- the vision doc for vision docs.
2
star
86

patpar

Java prototype of the Patient Parent model for parallel programming
Java
2
star
87

pjs-old

C++
2
star
88

skillsmatter2022

Skills Matter 2022
CSS
2
star
89

SpanakopitaBundle

A TextMate bundle for Spanakopita.
Python
2
star
90

coq-a-doodle-do

messing around with "Certified Programming with Dependent Types"
Coq
2
star
91

teyjus-rust-region-solver

Messing about with the "redction" of higher-ranked regions to simpler constraints in teyjus
AMPL
2
star
92

talk-template

A template for talks I give
CSS
2
star
93

americano

Experimental calendaring software
SCSS
1
star
94

rust-safe-robinhood-map

An experimental safe version of robin hood map in Rust
Rust
1
star
95

me

Various pictures of myself that I have used as avatar pics.
1
star
96

dyn-vision-doc

Notes on dyn in Rust
1
star
97

parpipeline

Experimental parallel pipeline API for JS
JavaScript
1
star
98

rusty-regex

A regex macro that creates compile-time macros.
Rust
1
star
99

nikomatsakis-babysteps-theme

Theme for my babysteps blog
SCSS
1
star
100

pjs-pipeline

Experimental API.
JavaScript
1
star