aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:05:33 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-07-07 01:05:33 +0000
commitd4c142a400bd07c6efcf232a0679110462c6b002 (patch)
tree03ff332f00013788b5fbe9b552f3d285627f560c
parentd592436652a8c33f843c6df71b2af7db470fe739 (diff)
parenta04647f49289e252243698da97cb54487c4baccf (diff)
downloadthread_local-android14-mainline-wifi-release.tar.gz
Change-Id: Ic01a707e6abf23f819a9fe7d3f5bcaad05a42215
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--.github/workflows/ci.yml16
-rw-r--r--.travis.yml30
-rw-r--r--Android.bp17
-rw-r--r--Cargo.toml26
-rw-r--r--Cargo.toml.orig17
-rw-r--r--METADATA14
-rw-r--r--README.md2
-rw-r--r--benches/thread_local.rs3
-rw-r--r--src/lib.rs56
-rw-r--r--src/thread_id.rs112
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
diff --git a/Android.bp b/Android.bp
index 223fd47..33cd835 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
],
}
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 <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
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<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]