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-rtcompat | Tokio | MockExecutor |
---|---|---|
ToplevelBlockOn::block_on | Runtime::block_on | ToplevelBlockOn::block_on |
Blocking::spawn_blocking | task::spawn_blocking | subthread_spawn |
Blocking::reenter_block_on | Handle::block_on | subthread_block_on_future |
Blocking::blocking_io | block_in_place | subthread_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§
Sourcetype ThreadHandle<T: Send + 'static>: Future<Output = T>
type ThreadHandle<T: Send + 'static>: Future<Output = T>
Future from spawn_blocking
Required Methods§
Sourcefn spawn_blocking<F, T>(&self, f: F) -> Self::ThreadHandle<T>
fn spawn_blocking<F, T>(&self, f: F) -> Self::ThreadHandle<T>
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 viareenter_block_on
. - Return answers with async inter-task facilities, calling either
a non-blocking immediate send (eg
[try_send
]) or an async send call viareneter_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
).
Sourcefn reenter_block_on<F>(&self, future: F) -> F::Output
fn reenter_block_on<F>(&self, future: F) -> F::Output
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 Runtime
s, 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§
Sourcefn blocking_io<F, T>(&self, f: F) -> impl Future<Output = T>
fn blocking_io<F, T>(&self, f: F) -> impl Future<Output = T>
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 callBlocking::reenter_block_on
, so:f
cannot execute any futures. If this is needed, break upf
into smaller pieces so that the futures can be awaited outside the call toblocking_io
, or usespawn_blocking
for the whole activity.f
may be called on the calling thread whenblocking_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.
impl Blocking for AsyncStd
native-tls
or rustls
) and (crate features async-std
or tokio
) and crate feature async-std
only.type ThreadHandle<T: Send + 'static> = BlockingHandle<T>
fn spawn_blocking<F, T>(&self, f: F) -> BlockingHandle<T>
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.
impl Blocking for AsyncStdNativeTlsRuntime
native-tls
or rustls
) and crate feature async-std
only.type ThreadHandle<T: Send + 'static> = <CompoundRuntime<AsyncStd, AsyncStd, RealCoarseTimeProvider, AsyncStd, AsyncStd, NativeTlsProvider, AsyncStd> as Blocking>::ThreadHandle<T>
Source§impl Blocking for AsyncStdRustlsRuntime
Available on (crate features native-tls
or rustls
) and crate feature async-std
only.
impl Blocking for AsyncStdRustlsRuntime
native-tls
or rustls
) and crate feature async-std
only.type ThreadHandle<T: Send + 'static> = <CompoundRuntime<AsyncStd, AsyncStd, RealCoarseTimeProvider, AsyncStd, AsyncStd, RustlsProvider, AsyncStd> as Blocking>::ThreadHandle<T>
Source§impl Blocking for PreferredRuntime
impl Blocking for PreferredRuntime
type ThreadHandle<T: Send + 'static> = <TokioNativeTlsRuntime as Blocking>::ThreadHandle<T>
Source§impl Blocking for TokioNativeTlsRuntime
Available on (crate features native-tls
or rustls
) and crate feature tokio
only.
impl Blocking for TokioNativeTlsRuntime
native-tls
or rustls
) and crate feature tokio
only.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.
impl Blocking for TokioRustlsRuntime
native-tls
or rustls
) and crate feature tokio
only.