summaryrefslogtreecommitdiffstats
path: root/third_party/rust/scoped-tls/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /third_party/rust/scoped-tls/src
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/scoped-tls/src')
-rw-r--r--third_party/rust/scoped-tls/src/lib.rs267
1 files changed, 267 insertions, 0 deletions
diff --git a/third_party/rust/scoped-tls/src/lib.rs b/third_party/rust/scoped-tls/src/lib.rs
new file mode 100644
index 0000000000..d4567c2bcb
--- /dev/null
+++ b/third_party/rust/scoped-tls/src/lib.rs
@@ -0,0 +1,267 @@
+// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Scoped thread-local storage
+//!
+//! This module provides the ability to generate *scoped* thread-local
+//! variables. In this sense, scoped indicates that thread local storage
+//! actually stores a reference to a value, and this reference is only placed
+//! in storage for a scoped amount of time.
+//!
+//! There are no restrictions on what types can be placed into a scoped
+//! variable, but all scoped variables are initialized to the equivalent of
+//! null. Scoped thread local storage is useful when a value is present for a known
+//! period of time and it is not required to relinquish ownership of the
+//! contents.
+//!
+//! # Examples
+//!
+//! ```
+//! #[macro_use]
+//! extern crate scoped_tls;
+//!
+//! scoped_thread_local!(static FOO: u32);
+//!
+//! # fn main() {
+//! // Initially each scoped slot is empty.
+//! assert!(!FOO.is_set());
+//!
+//! // When inserting a value, the value is only in place for the duration
+//! // of the closure specified.
+//! FOO.set(&1, || {
+//! FOO.with(|slot| {
+//! assert_eq!(*slot, 1);
+//! });
+//! });
+//! # }
+//! ```
+
+#![deny(missing_docs, warnings)]
+
+use std::cell::Cell;
+use std::marker;
+use std::thread::LocalKey;
+
+/// The macro. See the module level documentation for the description and examples.
+#[macro_export]
+macro_rules! scoped_thread_local {
+ ($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => (
+ $(#[$attrs])*
+ $vis static $name: $crate::ScopedKey<$ty> = $crate::ScopedKey {
+ inner: {
+ ::std::thread_local!(static FOO: ::std::cell::Cell<*const ()> = const {
+ ::std::cell::Cell::new(::std::ptr::null())
+ });
+ &FOO
+ },
+ _marker: ::std::marker::PhantomData,
+ };
+ )
+}
+
+/// Type representing a thread local storage key corresponding to a reference
+/// to the type parameter `T`.
+///
+/// Keys are statically allocated and can contain a reference to an instance of
+/// type `T` scoped to a particular lifetime. Keys provides two methods, `set`
+/// and `with`, both of which currently use closures to control the scope of
+/// their contents.
+pub struct ScopedKey<T> {
+ #[doc(hidden)]
+ pub inner: &'static LocalKey<Cell<*const ()>>,
+ #[doc(hidden)]
+ pub _marker: marker::PhantomData<T>,
+}
+
+unsafe impl<T> Sync for ScopedKey<T> {}
+
+impl<T> ScopedKey<T> {
+ /// Inserts a value into this scoped thread local storage slot for a
+ /// duration of a closure.
+ ///
+ /// While `f` is running, the value `t` will be returned by `get` unless
+ /// this function is called recursively inside of `f`.
+ ///
+ /// Upon return, this function will restore the previous value, if any
+ /// was available.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #[macro_use]
+ /// extern crate scoped_tls;
+ ///
+ /// scoped_thread_local!(static FOO: u32);
+ ///
+ /// # fn main() {
+ /// FOO.set(&100, || {
+ /// let val = FOO.with(|v| *v);
+ /// assert_eq!(val, 100);
+ ///
+ /// // set can be called recursively
+ /// FOO.set(&101, || {
+ /// // ...
+ /// });
+ ///
+ /// // Recursive calls restore the previous value.
+ /// let val = FOO.with(|v| *v);
+ /// assert_eq!(val, 100);
+ /// });
+ /// # }
+ /// ```
+ pub fn set<F, R>(&'static self, t: &T, f: F) -> R
+ where F: FnOnce() -> R
+ {
+ struct Reset {
+ key: &'static LocalKey<Cell<*const ()>>,
+ val: *const (),
+ }
+ impl Drop for Reset {
+ fn drop(&mut self) {
+ self.key.with(|c| c.set(self.val));
+ }
+ }
+ let prev = self.inner.with(|c| {
+ let prev = c.get();
+ c.set(t as *const T as *const ());
+ prev
+ });
+ let _reset = Reset { key: self.inner, val: prev };
+ f()
+ }
+
+ /// Gets a value out of this scoped variable.
+ ///
+ /// This function takes a closure which receives the value of this
+ /// variable.
+ ///
+ /// # Panics
+ ///
+ /// This function will panic if `set` has not previously been called.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// #[macro_use]
+ /// extern crate scoped_tls;
+ ///
+ /// scoped_thread_local!(static FOO: u32);
+ ///
+ /// # fn main() {
+ /// FOO.with(|slot| {
+ /// // work with `slot`
+ /// # drop(slot);
+ /// });
+ /// # }
+ /// ```
+ pub fn with<F, R>(&'static self, f: F) -> R
+ where F: FnOnce(&T) -> R
+ {
+ let val = self.inner.with(|c| c.get());
+ assert!(!val.is_null(), "cannot access a scoped thread local \
+ variable without calling `set` first");
+ unsafe {
+ f(&*(val as *const T))
+ }
+ }
+
+ /// Test whether this TLS key has been `set` for the current thread.
+ pub fn is_set(&'static self) -> bool {
+ self.inner.with(|c| !c.get().is_null())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::cell::Cell;
+ use std::sync::mpsc::{channel, Sender};
+ use std::thread;
+
+ scoped_thread_local!(static FOO: u32);
+
+ #[test]
+ fn smoke() {
+ scoped_thread_local!(static BAR: u32);
+
+ assert!(!BAR.is_set());
+ BAR.set(&1, || {
+ assert!(BAR.is_set());
+ BAR.with(|slot| {
+ assert_eq!(*slot, 1);
+ });
+ });
+ assert!(!BAR.is_set());
+ }
+
+ #[test]
+ fn cell_allowed() {
+ scoped_thread_local!(static BAR: Cell<u32>);
+
+ BAR.set(&Cell::new(1), || {
+ BAR.with(|slot| {
+ assert_eq!(slot.get(), 1);
+ });
+ });
+ }
+
+ #[test]
+ fn scope_item_allowed() {
+ assert!(!FOO.is_set());
+ FOO.set(&1, || {
+ assert!(FOO.is_set());
+ FOO.with(|slot| {
+ assert_eq!(*slot, 1);
+ });
+ });
+ assert!(!FOO.is_set());
+ }
+
+ #[test]
+ fn panic_resets() {
+ struct Check(Sender<u32>);
+ impl Drop for Check {
+ fn drop(&mut self) {
+ FOO.with(|r| {
+ self.0.send(*r).unwrap();
+ })
+ }
+ }
+
+ let (tx, rx) = channel();
+ let t = thread::spawn(|| {
+ FOO.set(&1, || {
+ let _r = Check(tx);
+
+ FOO.set(&2, || {
+ panic!()
+ });
+ });
+ });
+
+ assert_eq!(rx.recv().unwrap(), 1);
+ assert!(t.join().is_err());
+ }
+
+ #[test]
+ fn attrs_allowed() {
+ scoped_thread_local!(
+ /// Docs
+ static BAZ: u32
+ );
+
+ scoped_thread_local!(
+ #[allow(non_upper_case_globals)]
+ static quux: u32
+ );
+
+ let _ = BAZ;
+ let _ = quux;
+ }
+}