Leak pthreax_mutex_t when it's dropped while locked.

This commit is contained in:
Mara Bos 2022-06-16 11:23:31 +02:00
parent 392d272868
commit d72294491c
2 changed files with 35 additions and 4 deletions

View File

@ -1,5 +1,5 @@
use crate::cell::UnsafeCell;
use crate::mem::MaybeUninit;
use crate::mem::{forget, MaybeUninit};
use crate::sys::cvt_nz;
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
@ -23,6 +23,24 @@ impl LazyInit for Mutex {
unsafe { mutex.init() };
mutex
}
fn destroy(mutex: Box<Self>) {
// We're not allowed to pthread_mutex_destroy a locked mutex,
// so check first if it's unlocked.
if unsafe { mutex.try_lock() } {
unsafe { mutex.unlock() };
drop(mutex);
} else {
// The mutex is locked. This happens if a MutexGuard is leaked.
// In this case, we just leak the Mutex too.
forget(mutex);
}
}
fn cancel_init(_: Box<Self>) {
// In this case, we can just drop it without any checks,
// since it cannot have been locked yet.
}
}
impl Mutex {

View File

@ -21,8 +21,21 @@ pub(crate) trait LazyInit {
///
/// It might be called more than once per LazyBox, as multiple threads
/// might race to initialize it concurrently, each constructing and initializing
/// their own box. (All but one of them will be destroyed right after.)
/// their own box. All but one of them will be passed to `cancel_init` right after.
fn init() -> Box<Self>;
/// Any surplus boxes from `init()` that lost the initialization race
/// are passed to this function for disposal.
///
/// The default implementation calls destroy().
fn cancel_init(x: Box<Self>) {
Self::destroy(x);
}
/// This is called to destroy a used box.
///
/// The default implementation just drops it.
fn destroy(_: Box<Self>) {}
}
impl<T: LazyInit> LazyBox<T> {
@ -45,7 +58,7 @@ impl<T: LazyInit> LazyBox<T> {
Err(ptr) => {
// Lost the race to another thread.
// Drop the box we created, and use the one from the other thread instead.
drop(unsafe { Box::from_raw(new_ptr) });
T::cancel_init(unsafe { Box::from_raw(new_ptr) });
ptr
}
}
@ -71,7 +84,7 @@ impl<T: LazyInit> Drop for LazyBox<T> {
fn drop(&mut self) {
let ptr = *self.ptr.get_mut();
if !ptr.is_null() {
drop(unsafe { Box::from_raw(ptr) });
T::destroy(unsafe { Box::from_raw(ptr) });
}
}
}