str0m icon indicating copy to clipboard operation
str0m copied to clipboard

[Docs] Document any blocking / non-blocking behavior

Open teohhanhui opened this issue 4 months ago • 2 comments

The claim at https://github.com/algesten/str0m#can-i-utilize-str0m-with-any-rust-async-runtime:

Can I utilize str0m with any Rust async runtime?

Absolutely! str0m is fully sync, ensuring that it integrates seamlessly with any Rust async runtime you opt for.

However, calling a sync function from within an async task is only safe to do when it's non-blocking.

From https://ryhl.io/blog/async-what-is-blocking/:

To give a sense of scale of how much time is too much, a good rule of thumb is no more than 10 to 100 microseconds between each .await. That said, this depends on the kind of application you are writing.

Perhaps this should be documented for functions / methods where it might be relevant? Or at least, the claim on the README should be modified to make it clear that it's not generally safe to call any sync function from async code, unless it won't block on I/O (which is fine, this library is sans I/O) and won't perform any CPU-heavy computation.

teohhanhui avatar Aug 27 '25 18:08 teohhanhui

Or at least, the claim on the README should be modified to make it clear that it's not generally safe to call any sync function from async code, unless it won't block on I/O

I think how you build a real world app with str0m is more nuanced than that. Tokio is one async runtime with specific tradeoffs.

WebRTC inevitably means encryption/decryption – a CPU bound task. Whether that requires spawn_blocking or a separate thread or is fine as is within an async task is an architecture decision that is both dependent on circumstance and choice of runtime.

That seems a too complex subject for str0m's README, where the primary aim is to teach this Sans IO pattern and getting people going.

I'm however happy to review PRs for altering the wording and maybe even expand somewhat of what's there.

Notice we use cargo readme

algesten avatar Aug 27 '25 19:08 algesten

I've been working on a sans-io desgined networking application for two years now (https://github.com/firezone/firezone) and my learning from that is, you usually want to decouple your IO from your state explicitely with threads. For example, we spawn dedicated threads with single-threaded tokio runtimes that only do UDP IO. The "main" thread has the state logic (incl. str0m), which is CPU-bound. The threads are connected via bounded channels which use buffer pools to be efficient.

Explicitly controlling these threads means you won't ever have the problem of a CPU-bound task to "block" an async task.

One idea of sans-IO is that you access your state with &mut and avoid synchronisation. This means you are most likely not sending that across threads so having just one that processes that makes sense. You can still use async there to easily multiplex multiple things, like waiting on different channels.

thomaseizinger avatar Aug 28 '25 07:08 thomaseizinger