Trait Blocking

Source
pub trait Blocking:
    Clone
    + Send
    + Sync
    + 'static {
    type ThreadHandle<T: Send + 'static>: Future<Output = T>;

    // Required methods
    fn spawn_blocking<F, T>(&self, f: F) -> Self::ThreadHandle<T>
       where F: FnOnce() -> T + Send + 'static,
             T: Send + 'static;
    fn reenter_block_on<F>(&self, future: F) -> F::Output
       where F: Future,
             F::Output: Send + 'static;

    // Provided method
    fn blocking_io<F, T>(&self, f: F) -> impl Future<Output = T>
       where F: FnOnce() -> T + Send + 'static,
             T: Send + 'static { ... }
}
Expand description

Support for interacting with blocking (non-async) code

This supports two use cases: blocking IO and CPU-intensive activities. (In both of these cases, simply calling the functions within an async task is a bad idea, because that can block the whole async runtime.)

§Blocking IO

Blocking can be used to interact with libraries or OS primitives that only offer a synchronous, blocking, interface.

Use spawn_blocking when it is convenient to have a long-running thread, for these operations.

Use blocking_io when the blocking code is usually expected to complete quickly, and/or you will be switching back and forth a lot between sync and async contexts. Note that you cannot call back to async code from within blocking_io.

§CPU-intensive activities

Perform CPU-intensive work, that ought not to block the program’s main loop, via Blocking::spawn_blocking.

spawn_blocking does not apply any limiting or prioritisation; its threads simply compete for CPU with other threads in the program. That must be done by the caller; therefore:

Limit the number of cpu threads spawned in order to limit the total amount of CPU time consumed by any part of the program. For example, consider using one CPU thread per Tor Hidden Service.

It is most performant to spawn a long-running thread, rather than to repeatedly spawn short-lived threads for individual work items. This also makes it easier to limit the number of concurrente cpu threads. For the same reason, Blocking::blocking_io should be avoided for the CPU-intensive use case.

§Mapping to concrete functions from underlying libraries

The semantics of Blocking are heavily influenced by Tokio and by the desire to be able to use tor-rtmock’s MockExecutor to test Arti code.

tor-rtcompatTokioMockExecutor
ToplevelBlockOn::block_onRuntime::block_onToplevelBlockOn::block_on
Blocking::spawn_blockingtask::spawn_blockingsubthread_spawn
Blocking::reenter_block_onHandle::block_onsubthread_block_on_future
Blocking::blocking_ioblock_in_placesubthread_spawn
(not available)(not implemented)progress_until_stalled etc.

Re block_on, see also the docs for the underlying implementations in tokio and async-std.

Required Associated Types§

Source

type ThreadHandle<T: Send + 'static>: Future<Output = T>

Future from spawn_blocking

Required Methods§

Source

fn spawn_blocking<F, T>(&self, f: F) -> Self::ThreadHandle<T>
where F: FnOnce() -> T + Send + 'static, T: Send + 'static,

Spawn a thread for blocking IO or CPU-bound work.

This is used in two situations:

  • To perform blocking IO
  • For cpu-intensive work

See Blocking’s trait level docs for advice on choosing between spawn_blocking and Blocking::blocking_io.

Blocking::spawn_blocking is similar to std::thread::spawn but also makes any necessary arrangements so that reenter_block_on, can be called on the spawned thread.

However, Blocking::spawn_blocking does not guarantee to use a completely fresh thread. The implementation may have a thread pool, allowing it reuse an existing thread. Correspondingly, if a very large number of Blocking::spawn_blocking calls, are in progress at once, some of them may block. (For example, the implementation for Tokio uses tokio::task::spawn_blocking, which has both of these properties.)

§Typical use of spawn_blocking
  • Spawn the thread with SpawnThread::spawn_blocking.
  • On that thread, receive work items from from the async environment using async inter-task facilities (eg futures::channel::mpsc::channel), called via reenter_block_on.
  • Return answers with async inter-task facilities, calling either a non-blocking immediate send (eg [try_send]) or an async send call via reneter_block_on.
§CPU-intensive work

Limit the number of CPU-intensive concurrent threads spawned with spawn_blocking. See the trait-level docs for more details.

§Panics

Blocking::spawn_blocking may only be called from within either:

  • A task or future being polled by this Runtime; or
  • A thread itself spawned with Blocking::spawn_blocking on the this runtime.

Otherwise it may malfunction or panic. (tor_rtmock::MockExecutor’s implementation will usually detect violations.)

If f panics, ThreadHandle will also panic when polled (perhaps using resume_unwind).

Source

fn reenter_block_on<F>(&self, future: F) -> F::Output
where F: Future, F::Output: Send + 'static,

Block on a future, from within Blocking::spawn_blocking

Reenters the executor, blocking this thread until future is Ready.

See spawn_blocking and Blocking’s trait-level docs for more details.

It is not guaranteed what thread the future will be polled on. In production Runtimes, it will usually be the thread calling reenter_block_on.

§Panics

Must only be called on a thread made with Blocking::spawn_blocking. Not allowed within blocking_io.

Otherwise it may malfunction or panic. (tor_rtmock::MockExecutor’s implemnetation will usually detect violations.)

Provided Methods§

Source

fn blocking_io<F, T>(&self, f: F) -> impl Future<Output = T>
where F: FnOnce() -> T + Send + 'static, T: Send + 'static,

Perform some blocking IO from an async future

Call the blocking function f, informing the async executor that we are going to perform blocking IO.

This is a usually-faster, but simpler, alternative to Blocking::spawn_blocking.

Its API can be more convenient than spawn_blocking. blocking_io is intended to be more performant than spawn_blocking when called repeatedly (ie, when switching quickly between sync and async).

See Blocking’s trait-level docs for more information about the performance properties, and on choosing between blocking_io and spawn_blocking. (Avoid using blocking_io for CPU-intensive work.)

§Limitations
  • f may not call Blocking::reenter_block_on, so:
  • f cannot execute any futures. If this is needed, break up f into smaller pieces so that the futures can be awaited outside the call to blocking_io, or use spawn_blocking for the whole activity.
  • f may be called on the calling thread when blocking_io is called, on an executor thread when the returned future is polled, or a different thread.
  • Not suitable for CPU-intensive work (mostly because there is no practical way to ration or limit the amount of cpu time used). Use spawn_blocking for that.
  • Performance better than using spawn_blocking each time is not guaranteed.
§Panics

Blocking::block_in_place may only be called from within a task or future being polled by this Runtime.

Otherwise it may malfunction or panic. (tor_rtmock::MockExecutor’s implemnetation will usually detect violations.)

§Fallback (provided) implementation

The fallback implementation is currently used with async_std. It spawns a thread with spawn_blocking, once for each blocking_io call.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.

Implementations on Foreign Types§

Source§

impl Blocking for AsyncStd

Available on (crate features native-tls or rustls) and (crate features async-std or tokio) and crate feature async-std only.
Source§

type ThreadHandle<T: Send + 'static> = BlockingHandle<T>

Source§

fn spawn_blocking<F, T>(&self, f: F) -> BlockingHandle<T>
where F: FnOnce() -> T + Send + 'static, T: Send + 'static,

Source§

fn reenter_block_on<F: Future>(&self, f: F) -> F::Output

Implementors§

Source§

impl Blocking for AsyncStdNativeTlsRuntime

Available on (crate features native-tls or rustls) and crate feature async-std only.
Source§

impl Blocking for AsyncStdRustlsRuntime

Available on (crate features native-tls or rustls) and crate feature async-std only.
Source§

impl Blocking for PreferredRuntime

Source§

impl Blocking for TokioNativeTlsRuntime

Available on (crate features native-tls or rustls) and crate feature tokio only.
Source§

type ThreadHandle<T: Send + 'static> = <CompoundRuntime<TokioRuntimeHandle, TokioRuntimeHandle, RealCoarseTimeProvider, TokioRuntimeHandle, TokioRuntimeHandle, NativeTlsProvider, TokioRuntimeHandle> as Blocking>::ThreadHandle<T>

Source§

impl Blocking for TokioRustlsRuntime

Available on (crate features native-tls or rustls) and crate feature tokio only.
Source§

type ThreadHandle<T: Send + 'static> = <CompoundRuntime<TokioRuntimeHandle, TokioRuntimeHandle, RealCoarseTimeProvider, TokioRuntimeHandle, TokioRuntimeHandle, RustlsProvider, TokioRuntimeHandle> as Blocking>::ThreadHandle<T>

Source§

impl<TaskR, SleepR, CoarseTimeR, TcpR, UnixR, TlsR, UdpR> Blocking for CompoundRuntime<TaskR, SleepR, CoarseTimeR, TcpR, UnixR, TlsR, UdpR>
where TaskR: Blocking, SleepR: Clone + Send + Sync + 'static, CoarseTimeR: Clone + Send + Sync + 'static, TcpR: Clone + Send + Sync + 'static, UnixR: Clone + Send + Sync + 'static, TlsR: Clone + Send + Sync + 'static, UdpR: Clone + Send + Sync + 'static,

Source§

type ThreadHandle<T: Send + 'static> = <TaskR as Blocking>::ThreadHandle<T>