Skip to content

Commit bf416e7

Browse files
committed
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
2 parents 2733b18 + 1d19ad9 commit bf416e7

File tree

3 files changed

+33
-10
lines changed

3 files changed

+33
-10
lines changed

src/libstd/unstable/dynamic_lib.rs

+19-10
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
1313
Dynamic library facilities.
1414
15-
A simple wrapper over the platforms dynamic library facilities
15+
A simple wrapper over the platform's dynamic library facilities
1616
1717
*/
1818
use c_str::ToCStr;
@@ -80,7 +80,6 @@ impl DynamicLibrary {
8080
}
8181
}
8282

83-
8483
#[cfg(test)]
8584
mod test {
8685
use super::*;
@@ -90,8 +89,7 @@ mod test {
9089
use libc;
9190

9291
#[test]
93-
// #[ignore(cfg(windows))] // FIXME #8818
94-
#[ignore] // FIXME #9137 this library isn't thread-safe
92+
#[ignore(cfg(windows))] // FIXME #8818
9593
fn test_loading_cosine() {
9694
// The math library does not need to be loaded since it is already
9795
// statically linked in
@@ -100,8 +98,6 @@ mod test {
10098
Ok(libm) => libm
10199
};
102100

103-
// Unfortunately due to issue #6194 it is not possible to call
104-
// this as a C function
105101
let cosine: extern fn(libc::c_double) -> libc::c_double = unsafe {
106102
match libm.symbol("cos") {
107103
Err(error) => fail2!("Could not load function cos: {}", error),
@@ -114,15 +110,14 @@ mod test {
114110
let result = cosine(argument);
115111
if result != expected_result {
116112
fail2!("cos({:?}) != {:?} but equaled {:?} instead", argument,
117-
expected_result, result)
113+
expected_result, result)
118114
}
119115
}
120116

121117
#[test]
122118
#[cfg(target_os = "linux")]
123119
#[cfg(target_os = "macos")]
124120
#[cfg(target_os = "freebsd")]
125-
#[ignore] // FIXME #9137 this library isn't thread-safe
126121
fn test_errors_do_not_crash() {
127122
// Open /dev/null as a library to get an error, and make sure
128123
// that only causes an error, and not a crash.
@@ -164,17 +159,25 @@ pub mod dl {
164159
#[fixed_stack_segment]; #[inline(never)];
165160

166161
unsafe {
162+
// dlerror isn't thread safe, so we need to lock around this entire
163+
// sequence. `atomically` asserts that we don't do anything that
164+
// would cause this task to be descheduled, which could deadlock
165+
// the scheduler if it happens while the lock is held.
166+
// FIXME #9105 use a Rust mutex instead of C++ mutexes.
167167
do atomically {
168+
rust_take_dlerror_lock();
168169
let _old_error = dlerror();
169170

170171
let result = f();
171172

172173
let last_error = dlerror();
173-
if ptr::null() == last_error {
174+
let ret = if ptr::null() == last_error {
174175
Ok(result)
175176
} else {
176177
Err(str::raw::from_c_str(last_error))
177-
}
178+
};
179+
rust_drop_dlerror_lock();
180+
ret
178181
}
179182
}
180183
}
@@ -197,6 +200,11 @@ pub mod dl {
197200
Local = 0,
198201
}
199202

203+
extern {
204+
fn rust_take_dlerror_lock();
205+
fn rust_drop_dlerror_lock();
206+
}
207+
200208
#[link_name = "dl"]
201209
extern {
202210
fn dlopen(filename: *libc::c_char, flag: libc::c_int) -> *libc::c_void;
@@ -246,6 +254,7 @@ pub mod dl {
246254
}
247255
}
248256
}
257+
249258
pub unsafe fn symbol(handle: *libc::c_void, symbol: *libc::c_char) -> *libc::c_void {
250259
#[fixed_stack_segment]; #[inline(never)];
251260
GetProcAddress(handle, symbol)

src/rt/rust_builtin.cpp

+12
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,18 @@ rust_drop_linenoise_lock() {
609609
linenoise_lock.unlock();
610610
}
611611

612+
static lock_and_signal dlerror_lock;
613+
614+
extern "C" CDECL void
615+
rust_take_dlerror_lock() {
616+
dlerror_lock.lock();
617+
}
618+
619+
extern "C" CDECL void
620+
rust_drop_dlerror_lock() {
621+
dlerror_lock.unlock();
622+
}
623+
612624
extern "C" CDECL unsigned int
613625
rust_valgrind_stack_register(void *start, void *end) {
614626
return VALGRIND_STACK_REGISTER(start, end);

src/rt/rustrt.def.in

+2
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,5 @@ sd_markdown_render
206206
sd_markdown_free
207207
bufrelease
208208
bufnew
209+
rust_take_dlerror_lock
210+
rust_drop_dlerror_lock

0 commit comments

Comments
 (0)