Problem
RateLimiter::sleep() in src/rate_limiter.rs uses std::thread::sleep:
fn sleep(&self) {
match self.period {
TimePeriod::Second => sleep(Duration::from_secs(1)),
TimePeriod::Minute => sleep(Duration::from_secs(60)),
// ...
}
}
This is called from request() inside Request::send(), which is an async context running on a tokio executor thread. std::thread::sleep blocks the OS thread entirely, preventing tokio from scheduling any other task on that thread for the duration of the sleep. Under a default multi-thread tokio runtime this stalls one worker thread; under a single-thread runtime it stalls the entire application.
Expected behaviour
The rate limiter should yield the executor thread back to tokio while waiting, allowing other tasks to make progress.
Fix
Replace std::thread::sleep with tokio::time::sleep (async) and make request() an async function:
pub async fn request(&mut self) {
loop {
// ...
if self.remaining > 0 {
self.remaining -= 1;
return;
} else {
self.is_asleep = true;
log::info!("Rate limit exceeded, sleeping for {:?}", self.period);
tokio::time::sleep(self.period.clone().into()).await;
self.is_asleep = false;
}
}
}
TimePeriod::into::<std::time::Duration> already exists; tokio::time::sleep accepts std::time::Duration directly.
Impact
Every user of reqt in an async context is affected. Under high concurrency or long rate-limit periods (minute/hour/day), this bug can starve the entire tokio runtime.
Problem
RateLimiter::sleep()insrc/rate_limiter.rsusesstd::thread::sleep:This is called from
request()insideRequest::send(), which is an async context running on a tokio executor thread.std::thread::sleepblocks the OS thread entirely, preventing tokio from scheduling any other task on that thread for the duration of the sleep. Under a default multi-thread tokio runtime this stalls one worker thread; under a single-thread runtime it stalls the entire application.Expected behaviour
The rate limiter should yield the executor thread back to tokio while waiting, allowing other tasks to make progress.
Fix
Replace
std::thread::sleepwithtokio::time::sleep(async) and makerequest()an async function:TimePeriod::into::<std::time::Duration>already exists;tokio::time::sleepacceptsstd::time::Durationdirectly.Impact
Every user of
reqtin an async context is affected. Under high concurrency or long rate-limit periods (minute/hour/day), this bug can starve the entire tokio runtime.