Add "OnceList" example to motivate OnceLock · model-checking/verify-rust-std@06d9b0e (original) (raw)

`@@ -8,8 +8,14 @@ use crate::sync::Once;

`

8

8

`/// A synchronization primitive which can be written to only once.

`

9

9

`///

`

10

10

`` /// This type is a thread-safe [OnceCell], and can be used in statics.

``

``

11

`` +

/// In many simple cases, you can use [LazyLock<T, F>] instead to get the benefits of this type

``

``

12

`` +

/// with less effort: LazyLock<T, F> "looks like" &T because it initializes with F on deref!

``

``

13

`+

/// Where OnceLock shines is when LazyLock is too simple to support a given case, as LazyLock

`

``

14

`` +

/// doesn't allow additional inputs to its function after you call [LazyLock::new(|| ...)].

``

11

15

`///

`

12

16

`` /// [OnceCell]: crate::cell::OnceCell

``

``

17

`` +

/// [LazyLock<T, F>]: crate::sync::LazyLock

``

``

18

`` +

/// [LazyLock::new(|| ...)]: crate::sync::LazyLock::new

``

13

19

`///

`

14

20

`/// # Examples

`

15

21

`///

`

`@@ -37,6 +43,55 @@ use crate::sync::Once;

`

37

43

`/// Some(&12345),

`

38

44

`/// );

`

39

45

```` /// ```


``

`46`

`+

///

`

``

`47`

`` +

/// You can use `OnceLock` to implement a type that requires "append-only" logic:

``

``

`48`

`+

///

`

``

`49`

```` +

/// ```

``

50

`+

/// use std::sync::{OnceLock, atomic::{AtomicU32, Ordering}};

`

``

51

`+

/// use std::thread;

`

``

52

`+

///

`

``

53

`+

/// struct OnceList {

`

``

54

`+

/// data: OnceLock,

`

``

55

`+

/// next: OnceLock<Box<OnceList>>,

`

``

56

`+

/// }

`

``

57

`+

/// impl OnceList {

`

``

58

`+

/// const fn new() -> OnceList {

`

``

59

`+

/// OnceList { data: OnceLock::new(), next: OnceLock::new() }

`

``

60

`+

/// }

`

``

61

`+

/// fn push(&self, value: T) {

`

``

62

`+

/// // FIXME: this impl is concise, but is also slow for long lists or many threads.

`

``

63

`+

/// // as an exercise, consider how you might improve on it while preserving the behavior

`

``

64

`+

/// if let Err(value) = self.data.set(value) {

`

``

65

`+

/// let next = self.next.get_or_init(|| Box::new(OnceList::new()));

`

``

66

`+

/// next.push(value)

`

``

67

`+

/// };

`

``

68

`+

/// }

`

``

69

`+

/// fn contains(&self, example: &T) -> bool

`

``

70

`+

/// where

`

``

71

`+

/// T: PartialEq,

`

``

72

`+

/// {

`

``

73

`+

/// self.data.get().map(|item| item == example).filter(|v| *v).unwrap_or_else(|| {

`

``

74

`+

/// self.next.get().map(|next| next.contains(example)).unwrap_or(false)

`

``

75

`+

/// })

`

``

76

`+

/// }

`

``

77

`+

/// }

`

``

78

`+

///

`

``

79

`+

/// // Let's exercise this new Sync append-only list by doing a little counting

`

``

80

`+

/// static LIST: OnceList = OnceList::new();

`

``

81

`+

/// static COUNTER: AtomicU32 = AtomicU32::new(0);

`

``

82

`+

///

`

``

83

`+

/// let vec = (0..thread::available_parallelism().unwrap().get()).map(|_| thread::spawn(|| {

`

``

84

`+

/// while let i @ 0..=1000 = COUNTER.fetch_add(1, Ordering::Relaxed) {

`

``

85

`+

/// LIST.push(i);

`

``

86

`+

/// }

`

``

87

`+

/// })).collect::<Vec<thread::JoinHandle<_>>>();

`

``

88

`+

/// vec.into_iter().for_each(|handle| handle.join().unwrap());

`

``

89

`+

///

`

``

90

`+

/// for i in 0..=1000 {

`

``

91

`+

/// assert!(LIST.contains(&i));

`

``

92

`+

/// }

`

``

93

`+

///

`

``

94


/// ```

40

95

`#[stable(feature = "once_cell", since = "1.70.0")]

`

41

96

`pub struct OnceLock {

`

42

97

`once: Once,

`