@@ -10,7 +10,7 @@ use crate::Error;
10
10
use core:: {
11
11
num:: NonZeroU32 ,
12
12
ptr:: NonNull ,
13
- sync:: atomic:: { AtomicPtr , Ordering } ,
13
+ sync:: atomic:: { fence , AtomicPtr , Ordering } ,
14
14
} ;
15
15
use libc:: c_void;
16
16
@@ -113,17 +113,25 @@ impl Weak {
113
113
// dlsym function may be called multiple times.
114
114
pub fn ptr ( & self ) -> Option < NonNull < c_void > > {
115
115
// Despite having only a single atomic variable (self.addr), we still
116
- // need a "consume" ordering as we will generally be reading though
117
- // this value (by calling the function we dlsym'ed). Rust lacks this
118
- // ordering, so we have to go with the next strongest: Acquire/Release.
119
- // As noted in libstd, this might be unnecessary.
120
- let mut addr = self . addr . load ( Ordering :: Acquire ) ;
121
- if addr == Self :: UNINIT {
122
- let symbol = self . name . as_ptr ( ) as * const _ ;
123
- addr = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , symbol) } ;
124
- self . addr . store ( addr, Ordering :: Release ) ;
116
+ // cannot always use Ordering::Relaxed, as we need to make sure a
117
+ // successful call to dlsym() is "ordered before" any data read through
118
+ // the returned pointer (which occurs when the function is called).
119
+ // Our implementation mirrors that of the one in libstd, meaning that
120
+ // the use of non-Relaxed operations is probably unnecessary.
121
+ match self . addr . load ( Ordering :: Relaxed ) {
122
+ Self :: UNINIT => {
123
+ let symbol = self . name . as_ptr ( ) as * const _ ;
124
+ let addr = unsafe { libc:: dlsym ( libc:: RTLD_DEFAULT , symbol) } ;
125
+ // Synchronizes with the Acquire fence below
126
+ self . addr . store ( addr, Ordering :: Release ) ;
127
+ NonNull :: new ( addr)
128
+ }
129
+ addr => {
130
+ let func = NonNull :: new ( addr) ?;
131
+ fence ( Ordering :: Acquire ) ;
132
+ Some ( func)
133
+ }
125
134
}
126
- NonNull :: new ( addr)
127
135
}
128
136
}
129
137
0 commit comments