1
- use core:: sync:: atomic:: { AtomicUsize , Ordering :: Relaxed } ;
1
+ #![ allow( dead_code) ]
2
+ use core:: {
3
+ ffi:: c_void,
4
+ sync:: atomic:: { AtomicPtr , AtomicUsize , Ordering } ,
5
+ } ;
2
6
3
7
// This structure represents a lazily initialized static usize value. Useful
4
8
// when it is preferable to just rerun initialization instead of locking.
@@ -21,22 +25,22 @@ use core::sync::atomic::{AtomicUsize, Ordering::Relaxed};
21
25
pub ( crate ) struct LazyUsize ( AtomicUsize ) ;
22
26
23
27
impl LazyUsize {
28
+ // The initialization is not completed.
29
+ const UNINIT : usize = usize:: max_value ( ) ;
30
+
24
31
pub const fn new ( ) -> Self {
25
32
Self ( AtomicUsize :: new ( Self :: UNINIT ) )
26
33
}
27
34
28
- // The initialization is not completed.
29
- pub const UNINIT : usize = usize:: max_value ( ) ;
30
-
31
35
// Runs the init() function at most once, returning the value of some run of
32
36
// init(). Multiple callers can run their init() functions in parallel.
33
37
// init() should always return the same value, if it succeeds.
34
38
pub fn unsync_init ( & self , init : impl FnOnce ( ) -> usize ) -> usize {
35
39
// Relaxed ordering is fine, as we only have a single atomic variable.
36
- let mut val = self . 0 . load ( Relaxed ) ;
40
+ let mut val = self . 0 . load ( Ordering :: Relaxed ) ;
37
41
if val == Self :: UNINIT {
38
42
val = init ( ) ;
39
- self . 0 . store ( val, Relaxed ) ;
43
+ self . 0 . store ( val, Ordering :: Relaxed ) ;
40
44
}
41
45
val
42
46
}
@@ -54,3 +58,53 @@ impl LazyBool {
54
58
self . 0 . unsync_init ( || init ( ) as usize ) != 0
55
59
}
56
60
}
61
+
62
+ // This structure represents a lazily initialized static pointer value.
63
+ ///
64
+ /// It's intended to be used for weak linking of a C function that may
65
+ /// or may not be present at runtime.
66
+ ///
67
+ /// Based off of the DlsymWeak struct in libstd:
68
+ /// https://github.com/rust-lang/rust/blob/1.61.0/library/std/src/sys/unix/weak.rs#L84
69
+ /// except that the caller must manually cast self.ptr() to a function pointer.
70
+ pub struct LazyPtr {
71
+ addr : AtomicPtr < c_void > ,
72
+ }
73
+
74
+ impl LazyPtr {
75
+ /// A non-null pointer value which indicates we are uninitialized.
76
+ ///
77
+ /// This constant should ideally not be a valid pointer. However,
78
+ /// if by chance initialization function passed to the `unsync_init`
79
+ /// method does return UNINIT, there will not be undefined behavior.
80
+ /// The initialization function will just be called each time `get()`
81
+ /// is called. This would be inefficient, but correct.
82
+ const UNINIT : * mut c_void = !0usize as * mut c_void ;
83
+
84
+ /// Construct new `LazyPtr` in uninitialized state.
85
+ pub const fn new ( ) -> Self {
86
+ Self {
87
+ addr : AtomicPtr :: new ( Self :: UNINIT ) ,
88
+ }
89
+ }
90
+
91
+ // Runs the init() function at most once, returning the value of some run of
92
+ // init(). Multiple callers can run their init() functions in parallel.
93
+ // init() should always return the same value, if it succeeds.
94
+ pub fn unsync_init ( & self , init : impl Fn ( ) -> * mut c_void ) -> * mut c_void {
95
+ // Despite having only a single atomic variable (self.addr), we still
96
+ // cannot always use Ordering::Relaxed, as we need to make sure a
97
+ // successful call to `init` is "ordered before" any data read through
98
+ // the returned pointer (which occurs when the function is called).
99
+ // Our implementation mirrors that of the one in libstd, meaning that
100
+ // the use of non-Relaxed operations is probably unnecessary.
101
+ match self . addr . load ( Ordering :: Acquire ) {
102
+ Self :: UNINIT => {
103
+ let addr = init ( ) ;
104
+ self . addr . store ( addr, Ordering :: Release ) ;
105
+ addr
106
+ }
107
+ addr => addr,
108
+ }
109
+ }
110
+ }
0 commit comments