owned this note changed 2 years ago
Published Linked with GitHub

Reading notes: Context reactor hook

Link to document: https://jblog.andbit.net/2022/12/28/context-reactor-hook/

tags: reading-club

Leave questions, observations, discussion topics below.


Topic

vincenzo: how it is possible use the reactor concept to acheive an asyn function that do
not depend from the runtime? (I am missing soemthing)
vincenzo: in the described case the reactor is an observer patter? that will notify when a future is ready to be pool?


Rough Draft

A while back Justin posted a link to a rough draft of what the reactor hooks would look like:

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=486b97ac681c5d6923a066ef9bcae1df


eholk: Is there any potential for deadlocks when futures on different reactor systems have dependencies between them?

If an application instantiates multiple futures that each require different reactors, and each reactor requires special behavior in the execution thread, then only one reactor would “win”. Futures requiring any of the other reactors would be expected to fail, for example by resolving to an error or panicking.

That solves the problem but doesn't seem that desirable. Failing at runtime not ideal. OTOH, probably going to fail pretty fast if you are mixing different reactors.

tmandry: Agreed that I would like to find some static way of talking about this. (Contexts and capabilities?)

eholk: Or at least composable.


async fn main

tmandry

There could be a single built-in implementation of async fn main able to execute any content.

Is this desirable from a performance/customization perspective?

eholk: You could imagine having a crate that is the io-uring reactor, everyone ends up using that.

tmandry: Not convinced we want to make everyone standardize on one reactor per tech. Maybe if it's a really thin layer.

eholk: But if we have composable reactors we don't have to force you to pick one.

tmandry: But how?

eholk: Only way would be to spawn a thread.

tmandry: I might be okay with a world where the first reactor runs on-thread, and other reactors spawn thread if they get used.

eholk: Also curious about the fd-chaining mentioned in the footnote.

eholk: Maybe the constraint is you need an OS that allows you to compose arbitrary blocking calls somehow.

tmandry: You might have e.g. the epoll crate behave differently if the uring reactor is active on the same thread. Use an optional cargo dep and ask the crate if it's active, then set up fd-chaining or whatever, if that's more optimal than spawning a thread.

eholk: Is there always a way to block? spawn a thread

tmandry: But like in the thread-per-core case you need a Waker that's woken on the same thread, so you have to work that in. Probably just don't want to spawn a thread at all in that case.

eholk: Spawning as a fallback can work. Seems bad for predictability.

tmandry: In

eholk: Could we do something in the type system? Like e.g. parameterize Future on what kind of blocking it can do.

vincenzo: Useful for futures that expect to be singlethreaded too.


vincenzo: Examples of needing multiple reactors?

tmandry:

  • Interop, have TcpStream from two different kinds (uring, epoll)
  • UI Frameworks and event loops

justin: Stacking glib on top of Qt on top of Mac event loop


justin: Imagining some kind of "mega-reactor" that lets you compose reactors later. So there's some value in working this out now even without composability yet.


(back to type parameter)

justin: Status quo is e.g. futures will panic if the runtime isn't running.

tmandry: Interested if we can express in the trait system that e.g. this reactor knows how to adapt to this other reactor. (Maybe some/all can fall back to spawning a thread)

eholk: Can you express reactor constraints with contexts?

tmandry: Yeah, something like

impl Future for MyTcpStream
with epoll_reactor::reactor_ctx {
    ...
}

but I'm not sure how to work the adaptability part in. Actually, maybe it's like

impl Future for MyTcpStream
with std::async::reactor_ctx: CompatibleWithEpollReactor {
    ...
}

eholk: ^ that's what I had in mind, I assume reactor_ctx has some requirement like impl Reactor but then you can add additional bounds where you need them. Then when you call the future it's up to you to set a context that impls all the necessary traits.

tmandry: +1 :)

Select a repo