Skip to main content

wry_bindgen_core/
thread_local.rs

1//! Lazily-initialized, cached runtime-local JavaScript values.
2
3use alloc::boxed::Box;
4
5use crate::runtime::with_backend;
6
7/// A runtime-local accessor for lazily initialized JavaScript values.
8///
9/// This type provides safe access to cached JavaScript global values, ensuring
10/// the value is initialized on first access in the active runtime.
11///
12/// # Example
13///
14/// ```ignore
15/// #[wasm_bindgen]
16/// extern "C" {
17///     #[wasm_bindgen(thread_local_v2, js_name = window)]
18///     pub static WINDOW: Window;
19/// }
20///
21/// WINDOW.with(|window| {
22///     let doc = window.document();
23/// });
24/// ```
25pub struct LazyCell<T: 'static> {
26    init: fn() -> T,
27}
28
29impl<T> LazyCell<T> {
30    /// Create a new `LazyCell`.
31    #[doc(hidden)]
32    pub const fn new(init: fn() -> T) -> Self {
33        Self { init }
34    }
35
36    /// Return the cached value, initializing it if needed.
37    pub fn force(&'static self) -> &'static T {
38        let init = self.init;
39        // Take the value out of the runtime, initializing it if it isn't there
40        // yet. We initialize outside the runtime borrow because init() may
41        // re-enter the runtime to access other thread locals.
42        //
43        // We never drop js thread locals because:
44        // 1. The destructor only has an effect when the webview still exists and it should now be gone
45        // 2. It would rely on the thread local being dropped before the runtime is dropped, which relies on the drop order of
46        // different thread locals
47        let value = match with_backend(|runtime| runtime.take_thread_local_box(self)) {
48            Some(value) => *value.downcast::<&'static T>().expect("type mismatch"),
49            None => Box::leak(Box::new(init())) as &'static T,
50        };
51        with_backend(|runtime| {
52            runtime.insert_thread_local_box(self, Box::new(value));
53        });
54        value
55    }
56}
57
58/// Backwards-compatible name used by generated `thread_local_v2` bindings.
59pub struct JsThreadLocal<T: 'static> {
60    inner: LazyCell<T>,
61}
62
63impl<T> JsThreadLocal<T> {
64    /// Create a new `JsThreadLocal`.
65    #[doc(hidden)]
66    pub const fn new(init: fn() -> T) -> Self {
67        Self {
68            inner: LazyCell::new(init),
69        }
70    }
71
72    /// Run a closure with access to the cached value.
73    pub fn with<F, R>(&'static self, f: F) -> R
74    where
75        F: FnOnce(&T) -> R,
76    {
77        f(LazyCell::force(&self.inner))
78    }
79}