Skip to content

Jade ureq#124

Closed
pythcoiner wants to merge 2 commits intowizardsardine:masterfrom
pythcoiner:jade_ureq
Closed

Jade ureq#124
pythcoiner wants to merge 2 commits intowizardsardine:masterfrom
pythcoiner:jade_ureq

Conversation

@pythcoiner
Copy link
Copy Markdown
Contributor

@pythcoiner pythcoiner commented Mar 15, 2026

this PR replace reqwest by ureq, it was use in a single call to the Jade ping server

dropping dep count from 278 => 254:

~/ws/async-hwi master ⇡1 *9 ❯ cargo tree --prefix none | sort -u | wc -l                                                           02:12:12 AM
278
~/ws/async-hwi master ⇡1 *9 ❯ git checkout jade_ureq && cargo tree --prefix none | sort -u | wc -l                                 02:12:16 AM
Switched to branch 'jade_ureq'
254

@edouardparis
Copy link
Copy Markdown
Member

edouardparis commented Mar 16, 2026

thank you but we will keep async-hwi using if possible async I/O, we should even migrate our hid libs to async-hid.

Claude is better to explain than me also:

ureq vs reqwest (async) in a Tokio Runtime

ureq is synchronous — it blocks the thread

ureq makes blocking I/O calls. In a Tokio async context, this is the core problem:

  • Tokio uses a thread pool of worker threads (typically equal to CPU cores)
  • A blocking call with ureq will park the entire OS thread for the duration of the HTTP request
  • While that thread is blocked, Tokio cannot schedule other async tasks on it
  • If you have many concurrent requests, you can starve the runtime — all worker threads become blocked waiting on I/O, and nothing else progresses

Concretely, what breaks

Scenario With reqwest (async) With ureq (blocking)
100 parallel HTTP requests Fine — all run concurrently on a few threads Up to 100 threads blocked simultaneously
Mixed async tasks (timers, DB, etc.) Interleaved normally Starved while HTTP threads are blocked
Tokio worker thread count (e.g. 4) 4 threads handle thousands of tasks 4 threads = max 4 concurrent HTTP calls

The "correct" workaround if you must use ureq

Wrap blocking calls in tokio::task::spawn_blocking:

let response = tokio::task::spawn_blocking(|| {
    ureq::get("https://example.com").call()
}).await?;

This offloads the blocking work to a dedicated blocking thread pool (separate from Tokio's async workers), so you don't starve the runtime. However:

  • Tokio's blocking pool has a default cap of 512 threads — under heavy load, you'll spawn many OS threads
  • Each thread has overhead (~8MB stack by default on Linux), so memory usage can spike significantly
  • You lose the elegant backpressure and connection pooling that reqwest + Tokio gives you for free

Summary recommendation

Stick with reqwest async if you're already on Tokio. It's purpose-built for this:

  • Non-blocking, integrates natively with the Tokio reactor
  • Connection pooling out of the box
  • No thread-per-request overhead

Use ureq only in purely synchronous codebases where you have no async runtime at all. Mixing it with Tokio is technically possible via spawn_blocking, but it trades elegant async concurrency for heavyweight thread-based concurrency, which defeats much of the point of using Tokio in the first place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants