diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:05:33 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-07-07 01:05:33 +0000 |
commit | d4c142a400bd07c6efcf232a0679110462c6b002 (patch) | |
tree | 03ff332f00013788b5fbe9b552f3d285627f560c | |
parent | d592436652a8c33f843c6df71b2af7db470fe739 (diff) | |
parent | a04647f49289e252243698da97cb54487c4baccf (diff) | |
download | thread_local-android14-mainline-wifi-release.tar.gz |
Snap for 10447354 from a04647f49289e252243698da97cb54487c4baccf to mainline-wifi-releaseaml_wif_341610000aml_wif_341510000aml_wif_341410080aml_wif_341310010aml_wif_341110010aml_wif_341011010aml_wif_340913010android14-mainline-wifi-release
Change-Id: Ic01a707e6abf23f819a9fe7d3f5bcaad05a42215
-rw-r--r-- | .cargo_vcs_info.json | 2 | ||||
-rw-r--r-- | .github/workflows/ci.yml | 16 | ||||
-rw-r--r-- | .travis.yml | 30 | ||||
-rw-r--r-- | Android.bp | 17 | ||||
-rw-r--r-- | Cargo.toml | 26 | ||||
-rw-r--r-- | Cargo.toml.orig | 17 | ||||
-rw-r--r-- | METADATA | 14 | ||||
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | benches/thread_local.rs | 3 | ||||
-rw-r--r-- | src/lib.rs | 56 | ||||
-rw-r--r-- | src/thread_id.rs | 112 |
11 files changed, 195 insertions, 100 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index f17b5e4..98f2b9b 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,6 +1,6 @@ { "git": { - "sha1": "4a54e5702e0968bdda77366738ba646f646044e8" + "sha1": "d9c6ff3e751408c3f2e58b15dcc047e3669b9292" }, "path_in_vcs": "" }
\ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1d0e51b --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,16 @@ +on: [push, pull_request] + +name: Continuous integration + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dtolnay/rust-toolchain@1.59.0 + with: + components: rustfmt + - run: cargo fmt -- --check + - run: cargo test + - run: cargo bench diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 16bf2d2..0000000 --- a/.travis.yml +++ /dev/null @@ -1,30 +0,0 @@ -language: rust -sudo: false - -rust: -- nightly -- beta -- stable -- 1.36.0 - -before_script: -- | - pip install 'travis-cargo<0.2' --user && - export PATH=$HOME/.local/bin:$PATH - -script: -- travis-cargo build -- travis-cargo test -- travis-cargo bench -- --features criterion -# Criterion may drop support for 1.36.0 in the future. If it does, replace the above line with this: -# - travis-cargo --skip 1.36.0 bench -- --features criterion -- travis-cargo doc -- --no-deps - -after_success: -- travis-cargo --only nightly doc-upload -env: - global: - - TRAVIS_CARGO_NIGHTLY_FEATURE="" - -notifications: - email: false @@ -42,12 +42,19 @@ rust_library { host_supported: true, crate_name: "thread_local", cargo_env_compat: true, - cargo_pkg_version: "1.1.4", + cargo_pkg_version: "1.1.7", srcs: ["src/lib.rs"], - edition: "2018", + edition: "2021", rustlibs: [ + "libcfg_if", "libonce_cell", ], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + product_available: true, + vendor_available: true, } rust_test { @@ -55,15 +62,17 @@ rust_test { host_supported: true, crate_name: "thread_local", cargo_env_compat: true, - cargo_pkg_version: "1.1.4", + cargo_pkg_version: "1.1.7", srcs: ["src/lib.rs"], test_suites: ["general-tests"], auto_gen_config: true, test_options: { unit_test: true, }, - edition: "2018", + edition: "2021", rustlibs: [ + "libcfg_if", + "libcriterion", "libonce_cell", ], } @@ -10,28 +10,36 @@ # See Cargo.toml.orig for the original contents. [package] -edition = "2018" +edition = "2021" name = "thread_local" -version = "1.1.4" +version = "1.1.7" authors = ["Amanieu d'Antras <amanieu@gmail.com>"] description = "Per-object thread-local storage" documentation = "https://docs.rs/thread_local/" readme = "README.md" -keywords = ["thread_local", "concurrent", "thread"] -license = "Apache-2.0/MIT" +keywords = [ + "thread_local", + "concurrent", + "thread", +] +license = "MIT OR Apache-2.0" repository = "https://github.com/Amanieu/thread_local-rs" [[bench]] name = "thread_local" harness = false -required-features = ["criterion"] -[dependencies.criterion] -version = "0.3.3" -optional = true + +[dependencies.cfg-if] +version = "1.0.0" [dependencies.once_cell] version = "1.5.2" -[dev-dependencies] +[dev-dependencies.criterion] +version = "0.4.0" + +[features] +nightly = [] + [badges.travis-ci] repository = "Amanieu/thread_local-rs" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index f2e8ff0..56d301c 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,27 +1,30 @@ [package] name = "thread_local" -version = "1.1.4" +version = "1.1.7" authors = ["Amanieu d'Antras <amanieu@gmail.com>"] description = "Per-object thread-local storage" documentation = "https://docs.rs/thread_local/" -license = "Apache-2.0/MIT" +license = "MIT OR Apache-2.0" repository = "https://github.com/Amanieu/thread_local-rs" readme = "README.md" keywords = ["thread_local", "concurrent", "thread"] -edition = "2018" +edition = "2021" + +[features] +# this feature provides performance improvements using nightly features +nightly = [] [badges] travis-ci = { repository = "Amanieu/thread_local-rs" } [dependencies] once_cell = "1.5.2" - -# This is actually a dev-dependency, see https://github.com/rust-lang/cargo/issues/1596 -criterion = { version = "0.3.3", optional = true } +# this is required to gate `nightly` related code paths +cfg-if = "1.0.0" [dev-dependencies] +criterion = "0.4.0" [[bench]] name = "thread_local" -required-features = ["criterion"] harness = false @@ -1,3 +1,7 @@ +# This project was upgraded with external_updater. +# Usage: tools/external_updater/updater.sh update rust/crates/thread_local +# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md + name: "thread_local" description: "Per-object thread-local storage" third_party { @@ -7,13 +11,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/thread_local/thread_local-1.1.4.crate" + value: "https://static.crates.io/crates/thread_local/thread_local-1.1.7.crate" } - version: "1.1.4" + version: "1.1.7" license_type: NOTICE last_upgrade_date { - year: 2022 - month: 3 - day: 1 + year: 2023 + month: 2 + day: 17 } } @@ -21,7 +21,7 @@ thread_local = "1.1" ## Minimum Rust version -This crate's minimum supported Rust version (MSRV) is 1.36.0. +This crate's minimum supported Rust version (MSRV) is 1.59.0. ## License diff --git a/benches/thread_local.rs b/benches/thread_local.rs index ccad665..dd4716d 100644 --- a/benches/thread_local.rs +++ b/benches/thread_local.rs @@ -1,6 +1,3 @@ -extern crate criterion; -extern crate thread_local; - use criterion::{black_box, BatchSize}; use thread_local::ThreadLocal; @@ -65,6 +65,7 @@ #![warn(missing_docs)] #![allow(clippy::mutex_atomic)] +#![cfg_attr(feature = "nightly", feature(thread_local))] mod cached; mod thread_id; @@ -81,7 +82,6 @@ use std::mem::MaybeUninit; use std::panic::UnwindSafe; use std::ptr; use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; -use std::sync::Mutex; use thread_id::Thread; use unreachable::UncheckedResultExt; @@ -107,11 +107,6 @@ pub struct ThreadLocal<T: Send> { /// The number of values in the thread local. This can be less than the real number of values, /// but is never more. values: AtomicUsize, - - /// Lock used to guard against concurrent modifications. This is taken when - /// there is a possibility of allocating a new bucket, which only occurs - /// when inserting values. - lock: Mutex<()>, } struct Entry<T> { @@ -155,7 +150,7 @@ impl<T: Send> Drop for ThreadLocal<T> { continue; } - unsafe { Box::from_raw(std::slice::from_raw_parts_mut(bucket_ptr, this_bucket_size)) }; + unsafe { deallocate_bucket(bucket_ptr, this_bucket_size) }; } } } @@ -190,14 +185,12 @@ impl<T: Send> ThreadLocal<T> { // representation as a sequence of their inner type. buckets: unsafe { mem::transmute(buckets) }, values: AtomicUsize::new(0), - lock: Mutex::new(()), } } /// Returns the element for the current thread, if it exists. pub fn get(&self) -> Option<&T> { - let thread = thread_id::get(); - self.get_inner(thread) + self.get_inner(thread_id::get()) } /// Returns the element for the current thread, or creates it if it doesn't @@ -220,10 +213,11 @@ impl<T: Send> ThreadLocal<T> { F: FnOnce() -> Result<T, E>, { let thread = thread_id::get(); - match self.get_inner(thread) { - Some(x) => Ok(x), - None => Ok(self.insert(thread, create()?)), + if let Some(val) = self.get_inner(thread) { + return Ok(val); } + + Ok(self.insert(create()?)) } fn get_inner(&self, thread: Thread) -> Option<&T> { @@ -244,24 +238,34 @@ impl<T: Send> ThreadLocal<T> { } #[cold] - fn insert(&self, thread: Thread, data: T) -> &T { - // Lock the Mutex to ensure only a single thread is allocating buckets at once - let _guard = self.lock.lock().unwrap(); - + fn insert(&self, data: T) -> &T { + let thread = thread_id::get(); let bucket_atomic_ptr = unsafe { self.buckets.get_unchecked(thread.bucket) }; - let bucket_ptr: *const _ = bucket_atomic_ptr.load(Ordering::Acquire); + + // If the bucket doesn't already exist, we need to allocate it let bucket_ptr = if bucket_ptr.is_null() { - // Allocate a new bucket - let bucket_ptr = allocate_bucket(thread.bucket_size); - bucket_atomic_ptr.store(bucket_ptr, Ordering::Release); - bucket_ptr + let new_bucket = allocate_bucket(thread.bucket_size); + + match bucket_atomic_ptr.compare_exchange( + ptr::null_mut(), + new_bucket, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => new_bucket, + // If the bucket value changed (from null), that means + // another thread stored a new bucket before we could, + // and we can free our bucket and use that one instead + Err(bucket_ptr) => { + unsafe { deallocate_bucket(new_bucket, thread.bucket_size) } + bucket_ptr + } + } } else { bucket_ptr }; - drop(_guard); - // Insert the new element into the bucket let entry = unsafe { &*bucket_ptr.add(thread.index) }; let value_ptr = entry.value.get(); @@ -525,6 +529,10 @@ fn allocate_bucket<T>(size: usize) -> *mut Entry<T> { ) as *mut _ } +unsafe fn deallocate_bucket<T>(bucket: *mut Entry<T>, size: usize) { + let _ = Box::from_raw(std::slice::from_raw_parts_mut(bucket, size)); +} + #[cfg(test)] mod tests { use super::ThreadLocal; diff --git a/src/thread_id.rs b/src/thread_id.rs index 6eb0f61..aa4f2d6 100644 --- a/src/thread_id.rs +++ b/src/thread_id.rs @@ -7,6 +7,7 @@ use crate::POINTER_WIDTH; use once_cell::sync::Lazy; +use std::cell::Cell; use std::cmp::Reverse; use std::collections::BinaryHeap; use std::sync::Mutex; @@ -73,24 +74,103 @@ impl Thread { } } -/// Wrapper around `Thread` that allocates and deallocates the ID. -struct ThreadHolder(Thread); -impl ThreadHolder { - fn new() -> ThreadHolder { - ThreadHolder(Thread::new(THREAD_ID_MANAGER.lock().unwrap().alloc())) - } -} -impl Drop for ThreadHolder { - fn drop(&mut self) { - THREAD_ID_MANAGER.lock().unwrap().free(self.0.id); - } -} +cfg_if::cfg_if! { + if #[cfg(feature = "nightly")] { + // This is split into 2 thread-local variables so that we can check whether the + // thread is initialized without having to register a thread-local destructor. + // + // This makes the fast path smaller. + #[thread_local] + static mut THREAD: Option<Thread> = None; + thread_local! { static THREAD_GUARD: ThreadGuard = const { ThreadGuard { id: Cell::new(0) } }; } + + // Guard to ensure the thread ID is released on thread exit. + struct ThreadGuard { + // We keep a copy of the thread ID in the ThreadGuard: we can't + // reliably access THREAD in our Drop impl due to the unpredictable + // order of TLS destructors. + id: Cell<usize>, + } -thread_local!(static THREAD_HOLDER: ThreadHolder = ThreadHolder::new()); + impl Drop for ThreadGuard { + fn drop(&mut self) { + // Release the thread ID. Any further accesses to the thread ID + // will go through get_slow which will either panic or + // initialize a new ThreadGuard. + unsafe { + THREAD = None; + } + THREAD_ID_MANAGER.lock().unwrap().free(self.id.get()); + } + } -/// Get the current thread. -pub(crate) fn get() -> Thread { - THREAD_HOLDER.with(|holder| holder.0) + /// Returns a thread ID for the current thread, allocating one if needed. + #[inline] + pub(crate) fn get() -> Thread { + if let Some(thread) = unsafe { THREAD } { + thread + } else { + get_slow() + } + } + + /// Out-of-line slow path for allocating a thread ID. + #[cold] + fn get_slow() -> Thread { + let new = Thread::new(THREAD_ID_MANAGER.lock().unwrap().alloc()); + unsafe { + THREAD = Some(new); + } + THREAD_GUARD.with(|guard| guard.id.set(new.id)); + new + } + } else { + // This is split into 2 thread-local variables so that we can check whether the + // thread is initialized without having to register a thread-local destructor. + // + // This makes the fast path smaller. + thread_local! { static THREAD: Cell<Option<Thread>> = const { Cell::new(None) }; } + thread_local! { static THREAD_GUARD: ThreadGuard = const { ThreadGuard { id: Cell::new(0) } }; } + + // Guard to ensure the thread ID is released on thread exit. + struct ThreadGuard { + // We keep a copy of the thread ID in the ThreadGuard: we can't + // reliably access THREAD in our Drop impl due to the unpredictable + // order of TLS destructors. + id: Cell<usize>, + } + + impl Drop for ThreadGuard { + fn drop(&mut self) { + // Release the thread ID. Any further accesses to the thread ID + // will go through get_slow which will either panic or + // initialize a new ThreadGuard. + let _ = THREAD.try_with(|thread| thread.set(None)); + THREAD_ID_MANAGER.lock().unwrap().free(self.id.get()); + } + } + + /// Returns a thread ID for the current thread, allocating one if needed. + #[inline] + pub(crate) fn get() -> Thread { + THREAD.with(|thread| { + if let Some(thread) = thread.get() { + thread + } else { + get_slow(thread) + } + }) + } + + /// Out-of-line slow path for allocating a thread ID. + #[cold] + fn get_slow(thread: &Cell<Option<Thread>>) -> Thread { + let new = Thread::new(THREAD_ID_MANAGER.lock().unwrap().alloc()); + thread.set(Some(new)); + THREAD_GUARD.with(|guard| guard.id.set(new.id)); + new + } + } } #[test] |