ursa_engine/blocking.rs
1//! CPU-bound and blocking work, kept off the async workers where there are any.
2//!
3//! On native targets [`run_blocking`] is `tokio::task::spawn_blocking`: the
4//! graph kernels are CPU-bound (and parallel, through rayon), and reading a
5//! stored index blocks on the file system, so neither runs on a worker that
6//! drives other queries' futures.
7//!
8//! In the browser (wasm32) there is one thread and no blocking pool, so the
9//! closure runs inline, on the task that awaits it. The work and its result
10//! are the same; only where it runs differs (#278).
11
12use std::fmt;
13
14/// Run `f` off the async workers (native), or inline (wasm32).
15pub async fn run_blocking<F, R>(f: F) -> Result<R, BlockingError>
16where
17 F: FnOnce() -> R + Send + 'static,
18 R: Send + 'static,
19{
20 #[cfg(not(target_arch = "wasm32"))]
21 {
22 tokio::task::spawn_blocking(f).await.map_err(BlockingError)
23 }
24 #[cfg(target_arch = "wasm32")]
25 {
26 Ok(f())
27 }
28}
29
30/// Why blocking work produced no result: it panicked or was cancelled. Only a
31/// native build can see one; inline work on wasm32 cannot fail this way.
32#[derive(Debug)]
33pub struct BlockingError(#[cfg(not(target_arch = "wasm32"))] tokio::task::JoinError);
34
35impl fmt::Display for BlockingError {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 #[cfg(not(target_arch = "wasm32"))]
38 {
39 self.0.fmt(f)
40 }
41 #[cfg(target_arch = "wasm32")]
42 {
43 f.write_str("blocking work failed")
44 }
45 }
46}
47
48impl std::error::Error for BlockingError {}