24
24
use crate :: ffi:: CStr ;
25
25
use crate :: marker;
26
26
use crate :: mem;
27
- use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
27
+ use crate :: sync:: atomic:: { self , AtomicUsize , Ordering } ;
28
28
29
29
macro_rules! weak {
30
30
( fn $name: ident( $( $t: ty) ,* ) -> $ret: ty) => (
@@ -47,15 +47,49 @@ impl<F> Weak<F> {
47
47
pub fn get ( & self ) -> Option < F > {
48
48
assert_eq ! ( mem:: size_of:: <F >( ) , mem:: size_of:: <usize >( ) ) ;
49
49
unsafe {
50
- if self . addr . load ( Ordering :: SeqCst ) == 1 {
51
- self . addr . store ( fetch ( self . name ) , Ordering :: SeqCst ) ;
52
- }
53
- match self . addr . load ( Ordering :: SeqCst ) {
50
+ // Relaxed is fine here because we fence before reading through the
51
+ // pointer (see the comment below).
52
+ match self . addr . load ( Ordering :: Relaxed ) {
53
+ 1 => self . initialize ( ) ,
54
54
0 => None ,
55
- addr => Some ( mem:: transmute_copy :: < usize , F > ( & addr) ) ,
55
+ addr => {
56
+ let func = mem:: transmute_copy :: < usize , F > ( & addr) ;
57
+ // The caller is presumably going to read through this value
58
+ // (by calling the function we've dlsymed). This means we'd
59
+ // need to have loaded it with at least C11's consume
60
+ // ordering in order to be guaranteed that the data we read
61
+ // from the pointer isn't from before the pointer was
62
+ // stored. Rust has no equivalent to memory_order_consume,
63
+ // so we use an acquire fence (sorry, ARM).
64
+ //
65
+ // Now, in practice this likely isn't needed even on CPUs
66
+ // where relaxed and consume mean different things. The
67
+ // symbols we're loading are probably present (or not) at
68
+ // init, and even if they aren't the runtime dynamic loader
69
+ // is extremely likely have sufficient barriers internally
70
+ // (possibly implicitly, for example the ones provided by
71
+ // invoking `mprotect`).
72
+ //
73
+ // That said, none of that's *guaranteed*, and so we fence.
74
+ atomic:: fence ( Ordering :: Acquire ) ;
75
+ Some ( func)
76
+ }
56
77
}
57
78
}
58
79
}
80
+
81
+ // Cold because it should only happen during first-time initalization.
82
+ #[ cold]
83
+ unsafe fn initialize ( & self ) -> Option < F > {
84
+ let val = fetch ( self . name ) ;
85
+ // This synchronizes with the acquire fence in `get`.
86
+ self . addr . store ( val, Ordering :: Release ) ;
87
+
88
+ match val {
89
+ 0 => None ,
90
+ addr => Some ( mem:: transmute_copy :: < usize , F > ( & addr) ) ,
91
+ }
92
+ }
59
93
}
60
94
61
95
unsafe fn fetch ( name : & str ) -> usize {
0 commit comments