pub struct ThreadMap<V> { /* private fields */ }Expand description
This type encapsulates the association of ThreadIds to values of type V. It is a simple and easy-to-use alternative
to the std::thread_local macro and the thread_local crate.
§Example
use std::{
sync::Arc,
thread::{self},
time::Duration,
};
use thread_map::ThreadMap;
const NTHREADS: i32 = 20;
const NITER: i32 = 10;
const SLEEP_MICROS: u64 = 10;
#[test]
fn test() {
main();
}
fn main() {
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm: Arc<ThreadMap<i32>> = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
tm.with_mut(move |i0: &mut i32| *i0 += i);
}
let value = tm.get();
assert_eq!(i * NITER, value);
});
}
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
for _ in 0..NITER {
tm.with_mut(move |i0: &mut i32| *i0 += NTHREADS)
}
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
});
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}Implementations§
Source§impl<V> ThreadMap<V>
impl<V> ThreadMap<V>
Sourcepub fn new(value_init: fn() -> V) -> Self
pub fn new(value_init: fn() -> V) -> Self
Creates a new ThreadMap instance, with value_init used to create the initial value for each thread.
Sourcepub fn with_mut<W>(&self, f: impl FnOnce(&mut V) -> W) -> W
pub fn with_mut<W>(&self, f: impl FnOnce(&mut V) -> W) -> W
Invokes f mutably on the value associated with the ThreadId of the current thread and returns the invocation result.
If there is no value associated with the current thread then the value_init argument of Self::new is used
to instantiate an initial associated value before f is applied.
§Panics
- If
self’s lock is poisoned.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn main() {
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm: Arc<ThreadMap<i32>> = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
tm.with_mut(move |i0: &mut i32| *i0 += i);
}
let value = tm.get();
assert_eq!(i * NITER, value);
});
}
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
for _ in 0..NITER {
tm.with_mut(move |i0: &mut i32| *i0 += NTHREADS)
}
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
});
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}More examples
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
fn main() {
//**ThreadMap**
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
//**ThreadMap**
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
//**ThreadMap**
tm.with_mut(move |i0: &mut i32| *i0 += i);
//**std::thread_local**
TL.with(move |i0: &Cell<i32>| i0.replace(i0.get() + i));
}
//**ThreadMap**
{
let value = tm.get();
assert_eq!(i * NITER, value);
}
//**std::thread_local**
{
let value = TL.with(Cell::get);
assert_eq!(i * NITER, value);
}
});
}
//**ThreadMap**
{
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
//**ThreadMap**
for _ in 0..NITER {
tm.with_mut(|i0: &mut i32| *i0 += NTHREADS)
}
//**std::thread_local**
for _ in 0..NITER {
TL.with(|i0: &Cell<i32>| i0.replace(i0.get() + NTHREADS));
}
//**ThreadMap**
{
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
});
//**ThreadMap**
{
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
}Sourcepub fn with<W>(&self, f: impl FnOnce(&V) -> W) -> W
pub fn with<W>(&self, f: impl FnOnce(&V) -> W) -> W
Invokes f on the value associated with the ThreadId of the current thread and returns the invocation result.
If there is no value associated with the current thread then the value_init argument of Self::new is used
to instantiate an initial associated value before f is applied.
§Panics
- If
self’s lock is poisoned.
Sourcepub fn get(&self) -> Vwhere
V: Clone,
pub fn get(&self) -> Vwhere
V: Clone,
Returns a clone of the value associated with the current thread.
§Panics
- If
self’s lock is poisoned.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn main() {
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm: Arc<ThreadMap<i32>> = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
tm.with_mut(move |i0: &mut i32| *i0 += i);
}
let value = tm.get();
assert_eq!(i * NITER, value);
});
}
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
for _ in 0..NITER {
tm.with_mut(move |i0: &mut i32| *i0 += NTHREADS)
}
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
});
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}More examples
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
fn main() {
//**ThreadMap**
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
//**ThreadMap**
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
//**ThreadMap**
tm.with_mut(move |i0: &mut i32| *i0 += i);
//**std::thread_local**
TL.with(move |i0: &Cell<i32>| i0.replace(i0.get() + i));
}
//**ThreadMap**
{
let value = tm.get();
assert_eq!(i * NITER, value);
}
//**std::thread_local**
{
let value = TL.with(Cell::get);
assert_eq!(i * NITER, value);
}
});
}
//**ThreadMap**
{
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
//**ThreadMap**
for _ in 0..NITER {
tm.with_mut(|i0: &mut i32| *i0 += NTHREADS)
}
//**std::thread_local**
for _ in 0..NITER {
TL.with(|i0: &Cell<i32>| i0.replace(i0.get() + NTHREADS));
}
//**ThreadMap**
{
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
});
//**ThreadMap**
{
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
}Sourcepub fn drain(&self) -> Result<HashMap<ThreadId, V>, ThreadMapLockError>
pub fn drain(&self) -> Result<HashMap<ThreadId, V>, ThreadMapLockError>
Returns a HashMap with the values associated with each ThreadId key and clears self’s state.
§Errors
ThreadMapLockErrorif the internal lock is poisoned.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn main() {
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm: Arc<ThreadMap<i32>> = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
tm.with_mut(move |i0: &mut i32| *i0 += i);
}
let value = tm.get();
assert_eq!(i * NITER, value);
});
}
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
for _ in 0..NITER {
tm.with_mut(move |i0: &mut i32| *i0 += NTHREADS)
}
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
});
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}More examples
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
fn main() {
//**ThreadMap**
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
//**ThreadMap**
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
//**ThreadMap**
tm.with_mut(move |i0: &mut i32| *i0 += i);
//**std::thread_local**
TL.with(move |i0: &Cell<i32>| i0.replace(i0.get() + i));
}
//**ThreadMap**
{
let value = tm.get();
assert_eq!(i * NITER, value);
}
//**std::thread_local**
{
let value = TL.with(Cell::get);
assert_eq!(i * NITER, value);
}
});
}
//**ThreadMap**
{
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
//**ThreadMap**
for _ in 0..NITER {
tm.with_mut(|i0: &mut i32| *i0 += NTHREADS)
}
//**std::thread_local**
for _ in 0..NITER {
TL.with(|i0: &Cell<i32>| i0.replace(i0.get() + NTHREADS));
}
//**ThreadMap**
{
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
});
//**ThreadMap**
{
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
}Sourcepub fn fold<W>(
&self,
z: W,
f: impl FnMut(W, (ThreadId, &V)) -> W,
) -> Result<W, ThreadMapLockError>
pub fn fold<W>( &self, z: W, f: impl FnMut(W, (ThreadId, &V)) -> W, ) -> Result<W, ThreadMapLockError>
Folds every association in self into an accumulator (with initial value z) by applying an operation f,
returning the final result.
§Errors
ThreadMapLockErrorif the internal lock is poisoned.
Sourcepub fn fold_values<W>(
&self,
z: W,
f: impl FnMut(W, &V) -> W,
) -> Result<W, ThreadMapLockError>
pub fn fold_values<W>( &self, z: W, f: impl FnMut(W, &V) -> W, ) -> Result<W, ThreadMapLockError>
Folds every value in self into an accumulator (with initial value z) by applying an operation f,
returning the final result.
§Errors
ThreadMapLockErrorif the internal lock is poisoned.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn main() {
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm: Arc<ThreadMap<i32>> = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
tm.with_mut(move |i0: &mut i32| *i0 += i);
}
let value = tm.get();
assert_eq!(i * NITER, value);
});
}
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
for _ in 0..NITER {
tm.with_mut(move |i0: &mut i32| *i0 += NTHREADS)
}
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
});
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}More examples
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
fn main() {
//**ThreadMap**
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
//**ThreadMap**
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
//**ThreadMap**
tm.with_mut(move |i0: &mut i32| *i0 += i);
//**std::thread_local**
TL.with(move |i0: &Cell<i32>| i0.replace(i0.get() + i));
}
//**ThreadMap**
{
let value = tm.get();
assert_eq!(i * NITER, value);
}
//**std::thread_local**
{
let value = TL.with(Cell::get);
assert_eq!(i * NITER, value);
}
});
}
//**ThreadMap**
{
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
//**ThreadMap**
for _ in 0..NITER {
tm.with_mut(|i0: &mut i32| *i0 += NTHREADS)
}
//**std::thread_local**
for _ in 0..NITER {
TL.with(|i0: &Cell<i32>| i0.replace(i0.get() + NTHREADS));
}
//**ThreadMap**
{
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
});
//**ThreadMap**
{
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
}Sourcepub fn probe(&self) -> Result<HashMap<ThreadId, V>, ThreadMapLockError>where
V: Clone,
pub fn probe(&self) -> Result<HashMap<ThreadId, V>, ThreadMapLockError>where
V: Clone,
Returns a HashMap with clones of the values associated with each ThreadId key at the time the probe
was executed.
§Errors
ThreadMapLockErrorif the internal lock is poisoned.
Examples found in repository?
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
fn main() {
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm: Arc<ThreadMap<i32>> = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
tm.with_mut(move |i0: &mut i32| *i0 += i);
}
let value = tm.get();
assert_eq!(i * NITER, value);
});
}
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
for _ in 0..NITER {
tm.with_mut(move |i0: &mut i32| *i0 += NTHREADS)
}
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
});
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}More examples
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
fn main() {
//**ThreadMap**
// There is no real need to wrap in `Arc` here because references can be used in scoped threads instead
// of clones, but the `Arc` wrapper would be required if non-scoped threads were used.
let tm = Arc::new(ThreadMap::default());
thread::scope(|s| {
for i in 0..NTHREADS {
//**ThreadMap**
let tm = tm.clone();
s.spawn(move || {
for _ in 0..NITER {
thread::sleep(Duration::from_micros(SLEEP_MICROS));
//**ThreadMap**
tm.with_mut(move |i0: &mut i32| *i0 += i);
//**std::thread_local**
TL.with(move |i0: &Cell<i32>| i0.replace(i0.get() + i));
}
//**ThreadMap**
{
let value = tm.get();
assert_eq!(i * NITER, value);
}
//**std::thread_local**
{
let value = TL.with(Cell::get);
assert_eq!(i * NITER, value);
}
});
}
//**ThreadMap**
{
// Snapshot before thread-local value in main thread is updated.
let probed = tm.probe().unwrap();
println!("probed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
//**ThreadMap**
for _ in 0..NITER {
tm.with_mut(|i0: &mut i32| *i0 += NTHREADS)
}
//**std::thread_local**
for _ in 0..NITER {
TL.with(|i0: &Cell<i32>| i0.replace(i0.get() + NTHREADS));
}
//**ThreadMap**
{
// Snapshot before all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
});
//**ThreadMap**
{
// Snapshot after all scoped threads terminate.
let probed = tm.probe().unwrap();
println!("\nprobed={probed:?}");
let expected_sum = (0..=NTHREADS).map(|i| i * NITER).sum::<i32>();
let sum = tm.fold_values(0, |z, v| z + v).unwrap();
assert_eq!(expected_sum, sum);
// Extracted values after all scoped threads terminate.
let dumped = tm.drain().unwrap();
println!("\ndumped={dumped:?}");
}
//**std::thread_local**
{
// Can't do something similar to the above block
}
}