about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-10-05 15:41:35 -0700
committerbors <bors@rust-lang.org>2013-10-05 15:41:35 -0700
commitbf416e7daf533fd4eb6f270da3ab965ac7422f05 (patch)
tree34399950e063772b48aa29ea18ff7f61e38bcf08 /src/libstd
parent2733b189ac60cea541fbf80e5839e5027ffc9fbf (diff)
parent1d19ad97871d22fb26a6ba0856d106546da8612d (diff)
downloadrust-bf416e7daf533fd4eb6f270da3ab965ac7422f05.tar.gz
rust-bf416e7daf533fd4eb6f270da3ab965ac7422f05.zip
auto merge of #9713 : sfackler/rust/dynamic_lib, r=alexcrichton
The root issue is that dlerror isn't reentrant or even thread safe.

The solution implemented here is to make a yielding spin lock over an
AtomicFlag. This is pretty hacky, but the best we can do at this point.
As far as I can tell, it isn't possible to create a global mutex without
having to initialize it in a single threaded context.

The Windows code isn't affected since errno is thread-local on Windows
and it's running in an atomically block to ensure there isn't a green
thread context switch.

Closes #8156
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/unstable/dynamic_lib.rs29
1 files changed, 19 insertions, 10 deletions
diff --git a/src/libstd/unstable/dynamic_lib.rs b/src/libstd/unstable/dynamic_lib.rs
index 0045aef06f1..62ff8c9fbc8 100644
--- a/src/libstd/unstable/dynamic_lib.rs
+++ b/src/libstd/unstable/dynamic_lib.rs
@@ -12,7 +12,7 @@
 
 Dynamic library facilities.
 
-A simple wrapper over the platforms dynamic library facilities
+A simple wrapper over the platform's dynamic library facilities
 
 */
 use c_str::ToCStr;
@@ -80,7 +80,6 @@ impl DynamicLibrary {
     }
 }
 
-
 #[cfg(test)]
 mod test {
     use super::*;
@@ -90,8 +89,7 @@ mod test {
     use libc;
 
     #[test]
-    // #[ignore(cfg(windows))] // FIXME #8818
-    #[ignore] // FIXME #9137 this library isn't thread-safe
+    #[ignore(cfg(windows))] // FIXME #8818
     fn test_loading_cosine() {
         // The math library does not need to be loaded since it is already
         // statically linked in
@@ -100,8 +98,6 @@ mod test {
             Ok(libm) => libm
         };
 
-        // Unfortunately due to issue #6194 it is not possible to call
-        // this as a C function
         let cosine: extern fn(libc::c_double) -> libc::c_double = unsafe {
             match libm.symbol("cos") {
                 Err(error) => fail2!("Could not load function cos: {}", error),
@@ -114,7 +110,7 @@ mod test {
         let result = cosine(argument);
         if result != expected_result {
             fail2!("cos({:?}) != {:?} but equaled {:?} instead", argument,
-                  expected_result, result)
+                   expected_result, result)
         }
     }
 
@@ -122,7 +118,6 @@ mod test {
     #[cfg(target_os = "linux")]
     #[cfg(target_os = "macos")]
     #[cfg(target_os = "freebsd")]
-    #[ignore] // FIXME #9137 this library isn't thread-safe
     fn test_errors_do_not_crash() {
         // Open /dev/null as a library to get an error, and make sure
         // that only causes an error, and not a crash.
@@ -164,17 +159,25 @@ pub mod dl {
         #[fixed_stack_segment]; #[inline(never)];
 
         unsafe {
+            // dlerror isn't thread safe, so we need to lock around this entire
+            // sequence. `atomically` asserts that we don't do anything that
+            // would cause this task to be descheduled, which could deadlock
+            // the scheduler if it happens while the lock is held.
+            // FIXME #9105 use a Rust mutex instead of C++ mutexes.
             do atomically {
+                rust_take_dlerror_lock();
                 let _old_error = dlerror();
 
                 let result = f();
 
                 let last_error = dlerror();
-                if ptr::null() == last_error {
+                let ret = if ptr::null() == last_error {
                     Ok(result)
                 } else {
                     Err(str::raw::from_c_str(last_error))
-                }
+                };
+                rust_drop_dlerror_lock();
+                ret
             }
         }
     }
@@ -197,6 +200,11 @@ pub mod dl {
         Local = 0,
     }
 
+    extern {
+        fn rust_take_dlerror_lock();
+        fn rust_drop_dlerror_lock();
+    }
+
     #[link_name = "dl"]
     extern {
         fn dlopen(filename: *libc::c_char, flag: libc::c_int) -> *libc::c_void;
@@ -246,6 +254,7 @@ pub mod dl {
             }
         }
     }
+
     pub unsafe fn symbol(handle: *libc::c_void, symbol: *libc::c_char) -> *libc::c_void {
         #[fixed_stack_segment]; #[inline(never)];
         GetProcAddress(handle, symbol)