@@ -10,7 +10,7 @@ use crate::Error;
1010use core:: {
1111 num:: NonZeroU32 ,
1212 ptr:: NonNull ,
13- sync:: atomic:: { AtomicPtr , Ordering } ,
13+ sync:: atomic:: { fence , AtomicPtr , Ordering } ,
1414} ;
1515use libc:: c_void;
1616
@@ -113,17 +113,25 @@ impl Weak {
113113 // dlsym function may be called multiple times.
114114 pub fn ptr ( & self ) -> Option < NonNull < c_void > > {
115115 // 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+ }
125134 }
126- NonNull :: new ( addr)
127135 }
128136}
129137
0 commit comments