Skip to content

Commit a4343e9

Browse files
committed
Add guard map methods for transforming guards to contain sub-borrows.
This is very useful when the lock 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.
1 parent 074f49a commit a4343e9

File tree

2 files changed

+139
-5
lines changed

2 files changed

+139
-5
lines changed

src/libstd/sync/mutex.rs

+53-1
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,43 @@ impl<'mutex, T: ?Sized> MutexGuard<'mutex, T> {
379379
}
380380
})
381381
}
382+
383+
/// Transform this guard to hold a sub-borrow of the original data.
384+
///
385+
/// Applies the supplied closure to the data, returning a new lock
386+
/// guard referencing the borrow returned by the closure.
387+
///
388+
/// ```rust
389+
/// # use std::sync::{Mutex, MutexGuard};
390+
/// let x = Mutex::new(vec![1, 2]);
391+
///
392+
/// {
393+
/// let y = MutexGuard::map(x.lock().unwrap(), |v| &mut v[0]);
394+
/// *y = 3;
395+
/// }
396+
///
397+
/// assert_eq!(&*x.lock(), &[3, 2]);
398+
/// ```
399+
#[unstable(feature = "guard_map",
400+
reason = "recently added, needs RFC for stabilization",
401+
issue = "0")]
402+
pub fn map<U: ?Sized, F>(this: Self, cb: F) -> MutexGuard<'mutex, U>
403+
where F: FnOnce(&'mutex mut T) -> &'mutex mut U {
404+
let new_data = unsafe {
405+
let data = cb(&mut *this.__data.get());
406+
mem::transmute::<&'mutex mut U, &'mutex UnsafeCell<U>>(data)
407+
};
408+
409+
let lock = unsafe { ptr::read(&this.__lock) };
410+
let poison = unsafe { ptr::read(&this.__poison) };
411+
mem::forget(this);
412+
413+
MutexGuard {
414+
__lock: lock,
415+
__data: new_data,
416+
__poison: poison
417+
}
418+
}
382419
}
383420

384421
#[stable(feature = "rust1", since = "1.0.0")]
@@ -421,7 +458,7 @@ mod tests {
421458
use prelude::v1::*;
422459

423460
use sync::mpsc::channel;
424-
use sync::{Arc, Mutex, StaticMutex, Condvar};
461+
use sync::{Arc, Mutex, StaticMutex, Condvar, MutexGuard};
425462
use sync::atomic::{AtomicUsize, Ordering};
426463
use thread;
427464

@@ -665,4 +702,19 @@ mod tests {
665702
let comp: &[i32] = &[4, 2, 5];
666703
assert_eq!(&*mutex.lock().unwrap(), comp);
667704
}
705+
706+
#[test]
707+
fn test_mutex_guard_map_panic() {
708+
let mutex = Arc::new(Mutex::new(vec![1, 2]));
709+
let mutex2 = mutex.clone();
710+
711+
thread::spawn(move || {
712+
let _ = MutexGuard::map::<usize, _>(mutex2.lock().unwrap(), |_| panic!());
713+
}).join().unwrap_err();
714+
715+
match mutex.lock() {
716+
Ok(r) => panic!("Lock on poisioned Mutex is Ok: {:?}", &*r),
717+
Err(_) => {}
718+
};
719+
}
668720
}

src/libstd/sync/rwlock.rs

+86-4
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ pub const RW_LOCK_INIT: StaticRwLock = StaticRwLock::new();
121121
#[stable(feature = "rust1", since = "1.0.0")]
122122
pub struct RwLockReadGuard<'a, T: ?Sized + 'a> {
123123
__lock: &'a StaticRwLock,
124-
__data: &'a UnsafeCell<T>,
124+
__data: &'a T,
125125
}
126126

127127
#[stable(feature = "rust1", since = "1.0.0")]
@@ -417,10 +417,37 @@ impl<'rwlock, T: ?Sized> RwLockReadGuard<'rwlock, T> {
417417
poison::map_result(lock.poison.borrow(), |_| {
418418
RwLockReadGuard {
419419
__lock: lock,
420-
__data: data,
420+
__data: unsafe { &*data.get() },
421421
}
422422
})
423423
}
424+
425+
/// Transform this guard to hold a sub-borrow of the original data.
426+
///
427+
/// Applies the supplied closure to the data, returning a new lock
428+
/// guard referencing the borrow returned by the closure.
429+
///
430+
/// ```rust
431+
/// # use std::sync::{RwLockReadGuard, RwLock};
432+
/// let x = RwLock::new(vec![1, 2]);
433+
///
434+
/// let y = RwLockReadGuard::map(x.read().unwrap(), |v| &v[0]);
435+
/// assert_eq!(*y, 1);
436+
/// ```
437+
#[unstable(feature = "guard_map",
438+
reason = "recently added, needs RFC for stabilization",
439+
issue = "0")]
440+
pub fn map<U: ?Sized, F>(this: Self, cb: F) -> RwLockReadGuard<'rwlock, U>
441+
where F: FnOnce(&'rwlock T) -> &'rwlock U {
442+
let new = RwLockReadGuard {
443+
__lock: this.__lock,
444+
__data: cb(this.__data)
445+
};
446+
447+
mem::forget(this);
448+
449+
new
450+
}
424451
}
425452

426453
impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> {
@@ -434,13 +461,52 @@ impl<'rwlock, T: ?Sized> RwLockWriteGuard<'rwlock, T> {
434461
}
435462
})
436463
}
464+
465+
/// Transform this guard to hold a sub-borrow of the original data.
466+
///
467+
/// Applies the supplied closure to the data, returning a new lock
468+
/// guard referencing the borrow returned by the closure.
469+
///
470+
/// ```rust
471+
/// # use std::sync::{RwLockWriteGuard, RwLock};
472+
/// let x = RwLock::new(vec![1, 2]);
473+
///
474+
/// {
475+
/// let y = RwLockWriteGuard::map(x.write().unwrap(), |v| &mut v[0]);
476+
/// assert_eq!(*y, 1);
477+
///
478+
/// *y = 10;
479+
/// }
480+
///
481+
/// assert_eq!(&**x.read().unwrap(), &[10, 2]);
482+
/// ```
483+
#[unstable(feature = "guard_map",
484+
reason = "recently added, needs RFC for stabilization",
485+
issue = "0")]
486+
pub fn map<U: ?Sized, F>(this: Self, cb: F) -> RwLockWriteGuard<'rwlock, U>
487+
where F: FnOnce(&'rwlock mut T) -> &'rwlock mut U {
488+
let new_data = unsafe {
489+
let data: &'rwlock mut T = &mut *this.__data.get();
490+
mem::transmute::<&'rwlock mut U, &'rwlock UnsafeCell<U>>(cb(data))
491+
};
492+
493+
let poison = unsafe { ptr::read(&this.__poison) };
494+
let lock = unsafe { ptr::read(&this.__lock) };
495+
mem::forget(this);
496+
497+
RwLockWriteGuard {
498+
__lock: lock,
499+
__data: new_data,
500+
__poison: poison
501+
}
502+
}
437503
}
438504

439505
#[stable(feature = "rust1", since = "1.0.0")]
440506
impl<'rwlock, T: ?Sized> Deref for RwLockReadGuard<'rwlock, T> {
441507
type Target = T;
442508

443-
fn deref(&self) -> &T { unsafe { &*self.__data.get() } }
509+
fn deref(&self) -> &T { self.__data }
444510
}
445511

446512
#[stable(feature = "rust1", since = "1.0.0")]
@@ -481,7 +547,7 @@ mod tests {
481547
use rand::{self, Rng};
482548
use sync::mpsc::channel;
483549
use thread;
484-
use sync::{Arc, RwLock, StaticRwLock, TryLockError};
550+
use sync::{Arc, RwLock, StaticRwLock, TryLockError, RwLockWriteGuard};
485551
use sync::atomic::{AtomicUsize, Ordering};
486552

487553
#[derive(Eq, PartialEq, Debug)]
@@ -729,4 +795,20 @@ mod tests {
729795
Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {:?}", x),
730796
}
731797
}
798+
799+
#[test]
800+
fn test_rwlock_write_map_poison() {
801+
let rwlock = Arc::new(RwLock::new(vec![1, 2]));
802+
let rwlock2 = rwlock.clone();
803+
804+
thread::spawn(move || {
805+
let _ = RwLockWriteGuard::map::<usize, _>(rwlock2.write().unwrap(), |_| panic!());
806+
}).join().unwrap_err();
807+
808+
match rwlock.read() {
809+
Ok(r) => panic!("Read lock on poisioned RwLock is Ok: {:?}", &*r),
810+
Err(_) => {}
811+
};
812+
}
732813
}
814+

0 commit comments

Comments
 (0)