Future Never Sleeps

Recently I’ve been reading this book:  “Network Programming with Rust” by Abhishek Chanda. I found this book bit problematic. It’s just collection of many unrelated examples (often taken from crates documentation), with just little of background and concepts explanation and in some parts this book is just wrong, in other parts it’s using too much simplifications, so the result does not make much sense or worst it introduces some dangerous ideas. One of these  places is part about futures and streams – let’s look at one example:

As you can see on line 37 above the poll method is blocked by thread::sleep – the author explanation is :

“We simulate a delay in returning the result by sleeping for a random amount of time between 1 and 5 seconds.”

But this is wrong, very wrong, going against very basic principle of asynchronous  programing and Futures in Rust.  Because what is happening here is that whole event loop is blocked and no other Future can progress. Future’s poll method should never block, rather it should return Async::NotReady to indicate that it cannot proceed and also schedule next poll for later time, when it expects it could proceed.

So as an exercise I tried to create proper implementation, probably bit simplistic and suboptimal (for proper delay in futures look for instance at tokio-timer crate).

So here is the code:

 

It’s indeed much longer then previous example and majority of code in there is about asynchronous delay.  We have there two streams – CollatzStream – same as in previous example (with random delays before producing value) and then another stream Ticker, which provides unit value every 100 ms – just to demonstrate that both streams are running asynchronously.

Then we have two supporting structures Sleeper and Waker – to enable proper asynchronous sleeps in our streams.  Sleeper sleeps for given delay, returning Async::NotReady when sleeping, then Async::Ready(()) after sleep period and then going to sleep again. Waker registers sleeping futures and wakes them in an appropriate time. Waker is using binary heap to keep references to sleeping futures ordered by their planned wake up time (futures are here represented by their tasks) and runs background thread to wake always nearest task  – schedule it for next poll (with task.notify method).

So as can be seen by comparing original example with fixed one, too much simplification is sometimes dangerous, it can introduce false ideas and confuse the underlying principles rather then to clarify them.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">