From 9704a083d042113986d1e0fdd2c25792c0d38cd4 Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Thu, 26 May 2022 12:34:55 -0700 Subject: Update TEST_MAPPING Test: None Bug: 233924440 Change-Id: I89e561d6ecde587acf0ebc20f354085819f308fd --- TEST_MAPPING | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/TEST_MAPPING b/TEST_MAPPING index 7974396..8e3b8fd 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,5 +1,10 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { + "postsubmit": [ + { + "name": "thread_local_test_src_lib" + } + ], "presubmit": [ { "name": "thread_local_test_src_lib" -- cgit v1.2.3 From b918e3bc33d508b9b19fd987ac54942ceb9d23ef Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 14 Jun 2022 17:17:07 -0700 Subject: Update TEST_MAPPING Test: None Bug: 236006683 Change-Id: I44a7628611e85e0ee38b89323c9ee6225798cead --- TEST_MAPPING | 5 ----- 1 file changed, 5 deletions(-) diff --git a/TEST_MAPPING b/TEST_MAPPING index 8e3b8fd..7974396 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -1,10 +1,5 @@ // Generated by update_crate_tests.py for tests that depend on this crate. { - "postsubmit": [ - { - "name": "thread_local_test_src_lib" - } - ], "presubmit": [ { "name": "thread_local_test_src_lib" -- cgit v1.2.3 From 4039605b26d3bf0eec4a91b19b2470efe16b29c8 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Fri, 17 Feb 2023 09:43:40 +0100 Subject: Upgrade thread_local to 1.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 Test: TreeHugger Change-Id: I2678d456c0db7df97cd68cd6cab2edde6500bd08 --- .cargo_vcs_info.json | 2 +- .github/workflows/ci.yml | 16 +++++++ .travis.yml | 30 ------------- Android.bp | 15 +++++-- Cargo.toml | 26 +++++++---- Cargo.toml.orig | 17 ++++--- METADATA | 14 +++--- README.md | 2 +- benches/thread_local.rs | 3 -- src/lib.rs | 56 ++++++++++++++---------- src/thread_id.rs | 112 ++++++++++++++++++++++++++++++++++++++++------- 11 files changed, 193 insertions(+), 100 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml 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 diff --git a/Android.bp b/Android.bp index 223fd47..d6736e4 100644 --- a/Android.bp +++ b/Android.bp @@ -42,12 +42,17 @@ 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", + ], } rust_test { @@ -55,15 +60,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", ], } diff --git a/Cargo.toml b/Cargo.toml index 90e5319..d773a90 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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 "] 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 "] 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 diff --git a/METADATA b/METADATA index 4c77c6a..0466287 100644 --- a/METADATA +++ b/METADATA @@ -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 } } diff --git a/README.md b/README.md index 6560356..914451c 100644 --- a/README.md +++ b/README.md @@ -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; diff --git a/src/lib.rs b/src/lib.rs index 33b79d6..12d25f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 { /// 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 { @@ -155,7 +150,7 @@ impl Drop for ThreadLocal { 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 ThreadLocal { // 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 ThreadLocal { F: FnOnce() -> Result, { 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 ThreadLocal { } #[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(size: usize) -> *mut Entry { ) as *mut _ } +unsafe fn deallocate_bucket(bucket: *mut Entry, 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 = 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, + } -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> = 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, + } + + 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>) -> 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] -- cgit v1.2.3 From fbe5c1db95fd272365d8c7d8b1f71759a887ac4c Mon Sep 17 00:00:00 2001 From: Matthew Maurer Date: Tue, 7 Mar 2023 17:24:59 -0800 Subject: Make thread_local available to product and vendor Bug: 270690570 Test: mma in external/rust/crates Change-Id: I276782fc977794b37b17d4701f0d2ad77b5096ef --- Android.bp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Android.bp b/Android.bp index d6736e4..33cd835 100644 --- a/Android.bp +++ b/Android.bp @@ -53,6 +53,8 @@ rust_library { "//apex_available:platform", "//apex_available:anyapex", ], + product_available: true, + vendor_available: true, } rust_test { -- cgit v1.2.3