Futures-timer: reusing a fused timer with reset in loop?

Hi

I want to wait for messages on a future's channel with variable timeout and have something like this:

async fn process(mut recv: UnboundedReceiver<Event>) {
    let mut timer = Delay::new(Duration::from_millis(10)).fuse();
    loop {
        select! {
            ev = recv.next() => {
		// handle event
            }
            _ = timer => {
		// handle timeout
            }
        }
        // want to do this now but can't since timer is fused
        timer.reset(Instant::now() + new_calculated_timeout);
        // have to do it this (wasteful) way instead
        timer = Delay::new(new_calculated_timeout).fuse();
    }
}

I'm thinking there must be some way to do this without having to create a new timer each time.. Am I trying to solve this with the wrong timer api?

Why do you need to fuse it?

select! requires it

Can you not use the select function instead?

Wrap Delay and implement FusedFuture. (and Future)
In what you show can probably get away with is_terminated just return false.

1 Like

Thanks! I'll give it a try and post my solution/results here after.

I'm also considering wrapping future's mpsc receiver to add a next_deadline(deadline: Instant) function...

Hmm, I don't see how I can implement future for Delay without modifying the source code. How would I forward the poll call to Delay (which is private)? For my purpose I'm ok with just implementing FusedFuture for Delay directly and keep a custom crate for futures-timer.

I think your not understanding what I mean be wrapping. You seem to be describing an extension trait. I am referring to a new structure composed of the original with similar methods/traits.

struct FusedDelay(Delay);

Yes this is what I have but struct FusedDelay { delay: Delay }. Implementing FusedFuture is easy, however I also need to implement the Future trait for FusedDelay. From FusedDelay I can't access any of Delay's private functions or fields so there's no easy way of forwarding the poll() call from FusedDelay to Delay.

This should be good:

impl Future for FusedDelay {
    type Output = Instant;
    fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Instant> {
        unsafe {
            self.map_unchecked_mut(|fd| &mut fd.delay).poll(ctx)
        }
    }
}

You can avoid unsafe with a crate such as pin-project.

1 Like

That's beautiful. Thanks!