nu_protocol/engine/
sequence.rs1use crate::ShellError;
2use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
3
4#[derive(Debug, Default)]
6pub struct Sequence(AtomicUsize);
7
8impl Sequence {
9 #[track_caller]
11 pub fn next(&self) -> Result<usize, ShellError> {
12 self.0
18 .fetch_update(Relaxed, Relaxed, |current| current.checked_add(1))
19 .map_err(|_| ShellError::NushellFailedHelp {
20 msg: "an accumulator for identifiers overflowed".into(),
21 help: format!("see {}", std::panic::Location::caller()),
22 })
23 }
24}
25
26#[test]
27fn output_is_sequential() {
28 let sequence = Sequence::default();
29
30 for (expected, generated) in (0..1000).zip(std::iter::repeat_with(|| sequence.next())) {
31 assert_eq!(expected, generated.expect("error in sequence"));
32 }
33}
34
35#[test]
36fn output_is_unique_even_under_contention() {
37 let sequence = Sequence::default();
38
39 std::thread::scope(|scope| {
40 let threads = (0..4)
42 .map(|_| {
43 scope.spawn(|| {
44 (0..100000)
45 .map(|_| sequence.next())
46 .collect::<Result<Vec<_>, _>>()
47 })
48 })
49 .collect::<Vec<_>>();
50
51 let mut results = threads
53 .into_iter()
54 .flat_map(|thread| thread.join().expect("panicked").expect("error"))
55 .collect::<Vec<usize>>();
56
57 results.sort();
59 let initial_length = results.len();
60 results.dedup();
61 let deduplicated_length = results.len();
62 assert_eq!(initial_length, deduplicated_length);
63 })
64}