owned this note changed 2 years ago
Published Linked with GitHub

FUTURES CONCURRENCY III Reading notes

tags: reading-club

https://blog.yoshuawuyts.com/futures-concurrency-3/

Leave notes below


(eholk) Are there ever cases where we want to blend the different concurrency modes?

In other words, are we sure we don't need/want the extra flexibility afforded by select!?

Niko's blog post:

https://smallcultfollowing.com/babysteps/blog/2022/06/13/async-cancellation-a-case-study-of-pub-sub-in-mini-redis/#enter-select-our-dark-knight

select! {
    // accessess subscribers
    f = subscribers.next() => {
        
    }
    
    // accesses input
    m = input.next() => {
        match m {
            AddSubscriber => {
                // ok because we dropped
                // subscribers.next() before
                // entering the body
                subscribers.push(...);        
            }
        }
    }
}


scope(|s| {
    s.spawn(async move {
        // subscribers: tokio::StreamMap
        while let Some(s) = subscribers.next().await {
            
        }
    });
    
    s.spawn(async move {
        for i in inputs {
            subscribers.push(..);
        }
    });
})

nikomatsakis: what I want is something like a "token" where you both get an &mut but you share a single token that lets you do it. (language feature). You can't actually do this with locks unless the lock gets released on the await

Deferred borrows - (eholk's review)

tmandry: Is this the right shape for the code we want to support?

nikomatsakis: You could use actors. This shape seems useful, but not sure.

"Anyone who uses an async lock probably should've used an actor instead" - because this pattern doesn't work well.

nikomatsakis: These tasks are conceptually related and happen to share state. Using actors would be to organize your code around the data.

Rust has strong support for spatial separation (different data) but relatively weak for temporal data (same data, different times).

tmandry: this is a pretty common rust thing, right? to organize around data?

nikomatsakis: yes. it is, and I do think that's part of what makes Rust reliable.

nikomatsakis: why does Rayon work? I guess because you just can't do this sort of thing, and because when you have locks you have the ability to release the lock when waiting.

I would like it if when you "open" these &mut

eholk: merge merge types, select does a better job interleaving streams of different types, but you can do it with an enum.

fn foo() -> impl Debug {
    if {
        return Either::Left(22); 
    } else {
        return Either::Right("22");
    }
}

tmandry: anonymous enums could help

nikomatsakis: the problem I see is that anonymous enums, you have to match on what comes out? I guess if all the inputs are different types. the other thing i've thought about is some API that lets you build up the stream + the code to handle the stream (a closure), but

nikomatsakis: ormaybe anonymous do work, if the match is that each arm has to have a distinct type.

let stream: impl AsyncIterator<Item = u32> = ...;
let stream1: impl AsyncIterator<Item = f32> = ...;
let stream2: impl AsyncIterator<Item = u32> = ...;

merge!(stream, stream1, stream2).foreach /* (u32|f32) */ match {
    x: u32 => {
        
    }
    
    x: f32 => {
        
    }
}

struct MergedStream<sink type T> {
    
}

Matching with closures in C++

Select a repo