Skip to content

std: Always check for EDEADLK in rwlocks on unix #25015

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 2, 2015
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 29 additions & 12 deletions src/libstd/sys/unix/rwlock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

use prelude::v1::*;

use libc;
use cell::UnsafeCell;
use sys::sync as ffi;

Expand All @@ -26,7 +27,23 @@ impl RWLock {
#[inline]
pub unsafe fn read(&self) {
let r = ffi::pthread_rwlock_rdlock(self.inner.get());
debug_assert_eq!(r, 0);

// According to the pthread_rwlock_rdlock spec, this function **may**
// fail with EDEADLK if a deadlock is detected. On the other hand
// pthread mutexes will *never* return EDEADLK if they are initialized
// as the "fast" kind (which ours always are). As a result, a deadlock
// situation may actually return from the call to pthread_rwlock_rdlock
// instead of blocking forever (as mutexes and Windows rwlocks do). Note
// that not all unix implementations, however, will return EDEADLK for
// their rwlocks.
//
// We roughly maintain the deadlocking behavior by panicking to ensure
// that this lock acquisition does not succeed.
if r == libc::EDEADLK {
panic!("rwlock read lock would result in deadlock");
} else {
debug_assert_eq!(r, 0);
}
}
#[inline]
pub unsafe fn try_read(&self) -> bool {
Expand All @@ -35,7 +52,12 @@ impl RWLock {
#[inline]
pub unsafe fn write(&self) {
let r = ffi::pthread_rwlock_wrlock(self.inner.get());
debug_assert_eq!(r, 0);
// see comments above for why we check for EDEADLK
if r == libc::EDEADLK {
panic!("rwlock write lock would result in deadlock");
} else {
debug_assert_eq!(r, 0);
}
}
#[inline]
pub unsafe fn try_write(&self) -> bool {
Expand All @@ -49,21 +71,16 @@ impl RWLock {
#[inline]
pub unsafe fn write_unlock(&self) { self.read_unlock() }
#[inline]
#[cfg(not(target_os = "dragonfly"))]
pub unsafe fn destroy(&self) {
let r = ffi::pthread_rwlock_destroy(self.inner.get());
debug_assert_eq!(r, 0);
}

#[inline]
#[cfg(target_os = "dragonfly")]
pub unsafe fn destroy(&self) {
use libc;
let r = ffi::pthread_rwlock_destroy(self.inner.get());
// On DragonFly pthread_rwlock_destroy() returns EINVAL if called on a
// rwlock that was just initialized with
// ffi::PTHREAD_RWLOCK_INITIALIZER. Once it is used (locked/unlocked)
// or pthread_rwlock_init() is called, this behaviour no longer occurs.
debug_assert!(r == 0 || r == libc::EINVAL);
if cfg!(target_os = "dragonfly") {
debug_assert!(r == 0 || r == libc::EINVAL);
} else {
debug_assert_eq!(r, 0);
}
}
}