Rollup merge of #125696 - workingjubilee:please-dont-say-you-are-lazy… · model-checking/verify-rust-std@e38c13a (original) (raw)

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

`

8

8

`/// A synchronization primitive which can nominally 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

`///

`

16

``

`` -

/// Using OnceLock to store a function’s previously computed value (a.k.a.

``

17

``

`-

/// ‘lazy static’ or ‘memoizing’):

`

18

``

`-

///

`

19

``


/// ```

20

``

`-

/// use std::sync::OnceLock;

`

21

``

`-

///

`

22

``

`-

/// struct DeepThought {

`

23

``

`-

/// answer: String,

`

24

``

`-

/// }

`

25

``

`-

///

`

26

``

`-

/// impl DeepThought {

`

27

``

`-

/// # fn great_question() -> String {

`

28

``

`-

/// # "42".to_string()

`

29

``

`-

/// # }

`

30

``

`-

/// #

`

31

``

`-

/// fn new() -> Self {

`

32

``

`-

/// Self {

`

33

``

`-

/// // M3 Ultra takes about 16 million years in --release config

`

34

``

`-

/// answer: Self::great_question(),

`

35

``

`-

/// }

`

36

``

`-

/// }

`

37

``

`-

/// }

`

38

``

`-

///

`

39

``

`-

/// fn computation() -> &'static DeepThought {

`

40

``

`` -

/// // n.b. static items do not call [Drop] on program termination, so if

``

41

``

`` -

/// // [DeepThought] impls Drop, that will not be used for this instance.

``

42

``

`-

/// static COMPUTATION: OnceLock = OnceLock::new();

`

43

``

`-

/// COMPUTATION.get_or_init(|| DeepThought::new())

`

44

``

`-

/// }

`

45

``

`-

///

`

46

``

`` -

/// // The DeepThought is built, stored in the OnceLock, and returned.

``

47

``

`-

/// let _ = computation().answer;

`

48

``

`` -

/// // The DeepThought is retrieved from the OnceLock and returned.

``

49

``

`-

/// let _ = computation().answer;

`

50

``


/// ```

51

``

`-

///

`

52

22

`` /// Writing to a OnceLock from a separate thread:

``

53

23

`///

`

54

24

```` /// ```


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

`

`73`

`43`

`/// Some(&12345),

`

`74`

`44`

`/// );

`

`75`

`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


/// ```

76

95

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

`

77

96

`pub struct OnceLock {

`

78

97

`once: Once,

`