Skip to content

Commit 18c1781

Browse files
committed
Auto merge of #30834 - reem:rwlock-read-guard-map, r=alexcrichton
This is very useful when the RwLock is synchronizing access to a data structure and you would like to return or store guards which contain references to data inside the data structure instead of the data structure itself.
2 parents 8fc73c7 + fc875b0 commit 18c1781

File tree

2 files changed

+228
-57
lines changed

2 files changed

+228
-57
lines changed

src/libstd/sync/mutex.rs

+85-22
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ pub struct MutexGuard<'a, T: ?Sized + 'a> {
172172
// funny underscores due to how Deref/DerefMut currently work (they
173173
// disregard field privacy).
174174
__lock: &'a StaticMutex,
175-
__data: &'a UnsafeCell<T>,
175+
__data: &'a mut T,
176176
__poison: poison::Guard,
177177
}
178178

@@ -211,8 +211,10 @@ impl<T: ?Sized> Mutex<T> {
211211
/// this call will return an error once the mutex is acquired.
212212
#[stable(feature = "rust1", since = "1.0.0")]
213213
pub fn lock(&self) -> LockResult<MutexGuard<T>> {
214-
unsafe { self.inner.lock.lock() }
215-
MutexGuard::new(&*self.inner, &self.data)
214+
unsafe {
215+
self.inner.lock.lock();
216+
MutexGuard::new(&*self.inner, &self.data)
217+
}
216218
}
217219

218220
/// Attempts to acquire this lock.
@@ -230,10 +232,12 @@ impl<T: ?Sized> Mutex<T> {
230232
/// acquired.
231233
#[stable(feature = "rust1", since = "1.0.0")]
232234
pub fn try_lock(&self) -> TryLockResult<MutexGuard<T>> {
233-
if unsafe { self.inner.lock.try_lock() } {
234-
Ok(try!(MutexGuard::new(&*self.inner, &self.data)))
235-
} else {
236-
Err(TryLockError::WouldBlock)
235+
unsafe {
236+
if self.inner.lock.try_lock() {
237+
Ok(try!(MutexGuard::new(&*self.inner, &self.data)))
238+
} else {
239+
Err(TryLockError::WouldBlock)
240+
}
237241
}
238242
}
239243

@@ -338,17 +342,21 @@ impl StaticMutex {
338342
/// Acquires this lock, see `Mutex::lock`
339343
#[inline]
340344
pub fn lock(&'static self) -> LockResult<MutexGuard<()>> {
341-
unsafe { self.lock.lock() }
342-
MutexGuard::new(self, &DUMMY.0)
345+
unsafe {
346+
self.lock.lock();
347+
MutexGuard::new(self, &DUMMY.0)
348+
}
343349
}
344350

345351
/// Attempts to grab this lock, see `Mutex::try_lock`
346352
#[inline]
347353
pub fn try_lock(&'static self) -> TryLockResult<MutexGuard<()>> {
348-
if unsafe { self.lock.try_lock() } {
349-
Ok(try!(MutexGuard::new(self, &DUMMY.0)))
350-
} else {
351-
Err(TryLockError::WouldBlock)
354+
unsafe {
355+
if self.lock.try_lock() {
356+
Ok(try!(MutexGuard::new(self, &DUMMY.0)))
357+
} else {
358+
Err(TryLockError::WouldBlock)
359+
}
352360
}
353361
}
354362

@@ -369,32 +377,72 @@ impl StaticMutex {
369377

370378
impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
371379

372-
fn new(lock: &'mutex StaticMutex, data: &'mutex UnsafeCell<T>)
380+
unsafe fn new(lock: &'mutex StaticMutex, data: &'mutex UnsafeCell<T>)
373381
-> LockResult<MutexGuard<'mutex, T>> {
374382
poison::map_result(lock.poison.borrow(), |guard| {
375383
MutexGuard {
376384
__lock: lock,
377-
__data: data,
385+
__data: &mut *data.get(),
378386
__poison: guard,
379387
}
380388
})
381389
}
390+
391+
/// Transform this guard to hold a sub-borrow of the original data.
392+
///
393+
/// Applies the supplied closure to the data, returning a new lock
394+
/// guard referencing the borrow returned by the closure.
395+
///
396+
/// # Examples
397+
///
398+
/// ```rust
399+
/// # #![feature(guard_map)]
400+
/// # use std::sync::{Mutex, MutexGuard};
401+
/// let x = Mutex::new(vec![1, 2]);
402+
///
403+
/// {
404+
/// let mut y = MutexGuard::map(x.lock().unwrap(), |v| &mut v[0]);
405+
/// *y = 3;
406+
/// }
407+
///
408+
/// assert_eq!(&*x.lock().unwrap(), &[3, 2]);
409+
/// ```
410+
#[unstable(feature = "guard_map",
411+
reason = "recently added, needs RFC for stabilization",
412+
issue = "27746")]
413+
pub fn map<U: ?Sized, F>(this: Self, cb: F) -> MutexGuard<'mutex, U>
414+
where F: FnOnce(&'mutex mut T) -> &'mutex mut U
415+
{
416+
// Compute the new data while still owning the original lock
417+
// in order to correctly poison if the callback panics.
418+
let data = unsafe { ptr::read(&this.__data) };
419+
let new_data = cb(data);
420+
421+
// We don't want to unlock the lock by running the destructor of the
422+
// original lock, so just read the fields we need and forget it.
423+
let (poison, lock) = unsafe {
424+
(ptr::read(&this.__poison), ptr::read(&this.__lock))
425+
};
426+
mem::forget(this);
427+
428+
MutexGuard {
429+
__lock: lock,
430+
__data: new_data,
431+
__poison: poison
432+
}
433+
}
382434
}
383435

384436
#[stable(feature = "rust1", since = "1.0.0")]
385437
impl<'mutex, T: ?Sized> Deref for MutexGuard<'mutex, T> {
386438
type Target = T;
387439

388-
fn deref(&self) -> &T {
389-
unsafe { &*self.__data.get() }
390-
}
440+
fn deref(&self) -> &T {self.__data }
391441
}
392442

393443
#[stable(feature = "rust1", since = "1.0.0")]
394444
impl<'mutex, T: ?Sized> DerefMut for MutexGuard<'mutex, T> {
395-
fn deref_mut(&mut self) -> &mut T {
396-
unsafe { &mut *self.__data.get() }
397-
}
445+
fn deref_mut(&mut self) -> &mut T { self.__data }
398446
}
399447

400448
#[stable(feature = "rust1", since = "1.0.0")]
@@ -421,7 +469,7 @@ mod tests {
421469
use prelude::v1::*;
422470

423471
use sync::mpsc::channel;
424-
use sync::{Arc, Mutex, StaticMutex, Condvar};
472+
use sync::{Arc, Mutex, StaticMutex, Condvar, MutexGuard};
425473
use sync::atomic::{AtomicUsize, Ordering};
426474
use thread;
427475

@@ -665,4 +713,19 @@ mod tests {
665713
let comp: &[i32] = &[4, 2, 5];
666714
assert_eq!(&*mutex.lock().unwrap(), comp);
667715
}
716+
717+
#[test]
718+
fn test_mutex_guard_map_panic() {
719+
let mutex = Arc::new(Mutex::new(vec![1, 2]));
720+
let mutex2 = mutex.clone();
721+
722+
thread::spawn(move || {
723+
let _ = MutexGuard::map::<usize, _>(mutex2.lock().unwrap(), |_| panic!());
724+
}).join().unwrap_err();
725+
726+
match mutex.lock() {
727+
Ok(r) => panic!("Lock on poisioned Mutex is Ok: {:?}", &*r),
728+
Err(_) => {}
729+
};
730+
}
668731
}

0 commit comments

Comments
 (0)