Set timeout for synchronous function

Hi, I have an algorithm that takes a potentially really long time to execute. I want to call that function with a set timeout and if that timeout expires, just return a default. I read about async await with tokio, but that requires me to make both the caller and the receiver async. If possible I want to avoid that or hide it away, so that the function can be called from a sync context. Is there a way to do that and if so, where can I look?

Basically I have:
fn might_take_too_long(params) -> SomeReturn and I want fn might_take_too_long_with_timeout(params, timeout: Duration) -> Option<SomeReturn>
that calls the former function but returns None if it exceeds the timeout.

The only reliable way to stop synchronous job without modifying its code is to kill its process. If you can modify its code, check the time periodically and return error or default on timeout.

1 Like

Ah, I see. I unsuccessfully tried to work around it like this:

pub async fn might_take_too_long_with_timeout(params, max_time: Duration) -> Option<SomeReturn> {
    if let Ok(result) = tokio::time::timeout(might_take_too_long_async_wrapper(params), max_time).await {
        result
    } else {
        None
    }
}

async fn might_take_too_long_async_wrapper(params) -> SomeReturn {
    fn might_take_too_long(params)
}

pub fn might_take_too_long(params) {
    // calculate result
   result
}

That way I could still call it from a sync context but switch to async if I require the timeout. It compiles and runs but the timeout doesn't actually work. Do you perhaps know why my naive approach doesn't work and if there's something I can do to make it work?

I don't wanna check the function periodically, there's a lot of recursive calls (I even had to use the crate stacker to extend the stack) and the overhead would be too large.

Wrapping async task with timeout make the task canceled on next .await point after the timeout. Usually network services has short computations and long IO waits, so "next .await point" becomes low or zero. But in your case "next .await point" happens after the heavy computation is done.

1 Like

I've written a blog post that explains why the timeout doesn't work:

Basically, the timeout doesn't take effect until the next time it reaches an await that yields.

1 Like

Is the timing of your blog post just impeccable or did you write that just because of my question? Either way, I'll read that later when I have more time. Thanks!

I started on the blog post in november :wink:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.