#API suggestion: `spawn_maybe_blocking()` / `spawn_uncooperative()`

17 messages · Page 1 of 1 (latest)

kind vine
#

W.r.t. the sempiternal probem of "I have a spawned async piece of work (a task), but with a potentially-blocking synchronous piece of code in it; how do I handle it?"

  • Note: let's assume a non-current_thread executor.
    As of now, this has 2/2.5 answers:
  • Straight-forward: wrap the blocking piece of code in spawn_blocking(), should it be 'static + Send (minor overhead);
    • When the latter is not true, you can also use block_in_place() (higher overhead);
  • Spawn your "task" as a blocking one (spawn_blocking(), or using your own thread-pool or whatnot), and then use block_on() around the async part(s) in it.

But it turns out there may be a third option, based on the observation that a block_on() thread is not a core/worker_thread, and thus, a block_on() thread need not abide by async-coöperation rules.

Hence:

  1. spawn a block_on() thread:
  2. spawn some separate thread, either through spawn_blocking(), or using your own thread-pool or whatnot.
  3. call Handle::block_on() in it
  4. Run your async "task" in it, inlined,
  5. with the sync-blocking code inlined in it as well.
/// Could also be named `spawn_uncooperative()`.
fn spawn_maybe_blocking<R: Send>(
    handle: &Handle,
    task_that_blocks_inside: impl Send + Future<Output = R>,
) -> JoinHandle<R>
{
    handle.clone().spawn_blocking(move || {
        handle.block_on(task_that_blocks_inside)
    })
}
```Usage:```rs
spawn_maybe_blocking(Handle::current(), async move {
    while let Some(item) = stream.next().await {
        do_blocking_work(item);
    }
}).await
#

From this idea, stem two questions:

#

1) could (and should) such an API be added to ::tokio?

#

And the subtler one:

2) If so, could the notion of "acceptable to call block_on()", in ::tokio, be extended to such cases wherein reëntrant- block_on() ought to be fine?

#

@mellow forum mentions that reëntrant block_on() ought to be fine in general, with it only being an issue w.r.t. tokio driver shenanigans. But if the block_on()s stem from Handle rather than Runtime, such driver idiosyncrasies ought to be averted:

mellow forum
#

there is still the issue of join!/select! being sub-executors that can be blocked by this style of code

kind vine
limber latch
#

You can re-entrantly call block_on in your dedicated thread via block_in_place

kind vine
limber latch
#

I think it just changes a boolean in a thread-local varibale

kind vine
#

Ah neat, so way more lightweight indeed

#

Thanks for the tip!

limber latch
#

Unlike when you call it from within the runtime, it does not spawn new threads.

#

because when in block_on(), there is no need to

kind vine