Struct ThreadMap

Source
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>

Source

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.

Source

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?
examples/doc_thread_map.rs (line 28)
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
Hide additional examples
examples/doc_comparative.rs (line 52)
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
    }
}
Source

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.
Source

pub fn get(&self) -> V
where V: Clone,

Returns a clone of the value associated with the current thread.

§Panics
  • If self’s lock is poisoned.
Examples found in repository?
examples/doc_thread_map.rs (line 30)
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
Hide additional examples
examples/doc_comparative.rs (line 60)
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
    }
}
Source

pub fn set(&self, v: V)

§Panics
  • If self’s lock is poisoned.
Source

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
Examples found in repository?
examples/doc_thread_map.rs (line 57)
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
Hide additional examples
examples/doc_comparative.rs (line 118)
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
    }
}
Source

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
Source

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
Examples found in repository?
examples/doc_thread_map.rs (line 53)
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
Hide additional examples
examples/doc_comparative.rs (line 114)
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
    }
}
Source

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
Examples found in repository?
examples/doc_thread_map.rs (line 36)
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
Hide additional examples
examples/doc_comparative.rs (line 75)
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
    }
}

Trait Implementations§

Source§

impl<V: Debug> Debug for ThreadMap<V>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<V: Default> Default for ThreadMap<V>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl<V> !Freeze for ThreadMap<V>

§

impl<V> RefUnwindSafe for ThreadMap<V>

§

impl<V> Send for ThreadMap<V>
where V: Send,

§

impl<V> Sync for ThreadMap<V>
where V: Send,

§

impl<V> Unpin for ThreadMap<V>
where V: Unpin,

§

impl<V> UnwindSafe for ThreadMap<V>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.