Async/await interop suggestion
I wrote a small crate for using async/await syntax with actix. It also makes the "AtomicResponse" type obsolete by providing a more ergonomic mechanism to achieve the same thing. I think it would be nice to have as part of actix itself.
The basic idea is to use scoped TLS (at least until custom future contexts are supported) to store the actor and its context.
There are three functions:
FutureInterop::interop_actor()This is equivalent to the
into_actor()method except that it populates the TLS slots with the actor and the context before polling the inner future. There's also a boxed variant of this function.with_ctxThis takes a closure and immediately calls it, passing in mutable references to the actor and context obtained from the TLS slots to that closure.
critical_sectionThis takes a future/async block and uses
ctx.wait()to make sure it has exclusive access to the actor state while it is running. It returns a future itself so you can seamlessly demarcate blocks of code within an async/await block as being critical sections.
Example:
impl Handler<Request> for MyActor {
type Result = ResponseActFuture<Self, Result<Response, ()>>;
fn handle(&mut self, msg: Request, _ctx: &mut Context<Self>) -> Self::Result {
async move {
// Modify the actor state in some way
with_ctx(|a: &mut Self, _| a.mutate());
... <some async code> ...
// Run some code with exclusive access to the actor
let result = critical_section::<Self, _>(async {
// Put the actor into an inconsistent state.
// We don't want this visible outside the critical section.
with_ctx(|a: &mut Self, _| a.begin_mutation());
... <some async code> ...
// Make everything consistent again
Ok(with_ctx(|a: &mut Self, _| a.end_mutation()))
}).await;
... <some async code> ...
// Return a result
result
}.interop_actor_boxed(self)
}
}Some things which are very hard to do right now are made much easier in this pattern (for example, when only half of a message handler needs to run atomically). Also, all of the normal futures combinators can be used, rather than the more limited set exposed under actix::fut.
Currently the crate relies on a patch to scoped_thread_local which hasn't been merged yet, which is why I haven't published it. However, except for scoped-tls this crate itself does not require any unsafe code at all.
Source: actix/actix