Explicit future construction, implicit await


#102

I don’t see how you could guarantee (in a typesafe sense) that a future will be run to completion since the executor not only must not drop it, but also must continue polling it for it to make progress

I haven’t thought about it, but I honestly think it doesn’t matter too much. Executors are rare. There’s maybe a handfull of them required. It’s ok for them to contain unsafe code that holds up certain guarantees. They already do that today, by implementing wake() in an unsafe way. Being hold up to the contract to poll each task to completion is just one more addition to that.

And while its not guaranteed that a hard cancelled future won’t be leaked all well behaved combinators and executors will drop futures they stop executing, allowing some amount of clean up on cancellation in correctly implemented code.

It’s not about combinators behaving correctly. It’s about reasoning in async code, and understanding transaction levels. E.g. I wanted to write some kind of rate limiting in async code during the last days. I really wanted to write that in the simple fashion that is equivalent to:

await!(self.async_semaphore.aquire(amount));
{
    /** Do something very long that might yield multiple times */
}
await!(self.async_semaphore.release(amount));

That doesn’t work, since the code might never arrive at release. In a similar fashion an unsafe malloc and a free after a yield point would not be guaranteed to work. In those cases it can be worked around by custom RAII guards. But it’s important to know that those are required, and it doesn’t necessarily work well for other code path where transactional semantics are required but there is not such a notion of guard that can be dropped unconditionally - e.g. if one has to execute situation-dependent cleanup code that also needs to be async.


#103

of course not!


#104

I don’t think that’s “worked around”; I think that’s “done properly”. It’s also important that it be done in an RAII guard to be resilient to panics that happen in the middle.


#105

The main problem is having an async drop, which cramertj has definitely talked about. Not a near term thing but an interesting extension in the long term.


#106

To be clear, the malloc was a simplified example that of course should be wrapped in a proper Rust type. However there are lots of cases where it wouldn’t be that obvious. As @glaebhoerl pointed out in the other thread, making an async task yield/drop-safe is quite similar to making code panic safe. Maybe a bit better highlighted (by await!s), but in comparison to panics also happening in non-broken code, and therefore super-important to get right.