typst_utils/deferred.rs
1use std::sync::Arc;
2
3use once_cell::sync::OnceCell;
4
5/// A value that is lazily executed on another thread.
6///
7/// Execution will be started in the background and can be waited on.
8pub struct Deferred<T>(Arc<OnceCell<T>>);
9
10impl<T: Send + Sync + 'static> Deferred<T> {
11 /// Creates a new deferred value.
12 ///
13 /// The closure will be called on a secondary thread such that the value
14 /// can be initialized in parallel.
15 pub fn new<F>(f: F) -> Self
16 where
17 F: FnOnce() -> T + Send + Sync + 'static,
18 {
19 let inner = Arc::new(OnceCell::new());
20 let cloned = Arc::clone(&inner);
21 rayon::spawn(move || {
22 // Initialize the value if it hasn't been initialized yet.
23 // We do this to avoid panicking in case it was set externally.
24 cloned.get_or_init(f);
25 });
26 Self(inner)
27 }
28
29 /// Waits on the value to be initialized.
30 ///
31 /// If the value has already been initialized, this will return
32 /// immediately. Otherwise, this will block until the value is
33 /// initialized in another thread.
34 pub fn wait(&self) -> &T {
35 // Fast path if the value is already available. We don't want to yield
36 // to rayon in that case.
37 if let Some(value) = self.0.get() {
38 return value;
39 }
40
41 // Ensure that we yield to give the deferred value a chance to compute
42 // single-threaded platforms (for WASM compatibility).
43 while let Some(rayon::Yield::Executed) = rayon::yield_now() {}
44
45 self.0.wait()
46 }
47}
48
49impl<T> Clone for Deferred<T> {
50 fn clone(&self) -> Self {
51 Self(Arc::clone(&self.0))
52 }
53}