Struct corundum::sync::PMutex [−][src]
pub struct PMutex<T, A: MemPool> { /* fields omitted */ }
Expand description
A transaction-wide recursive mutual exclusion primitive useful for protecting shared data while transaction is open. Further locking in the same thread is non-blocking. Any access to data is serialized. Borrow rules are checked dynamically to prevent multiple mutable dereferencing.
This mutex will block threads/transactions waiting for the lock to become
available. The difference between Mutex
and std
::
sync
::
Mutex
is that it will hold the lock until the transaction commits. For example,
consider the following code snippet in which a shared object is protected
with std
::
sync
::
Mutex
. In this case, data might be lost.
use corundum::default::*; use std::sync::Mutex; type P = BuddyAlloc; let obj = P::open::<Parc<Mutex<i32>>>("foo.pool", O_CF).unwrap(); // ^ std::sync::Mutex is not PSafe transaction(|j| { { let obj = obj.lock().unwrap(); // Some statements ... } // <-- release the lock here // Another thread can work with obj { let obj = obj.lock().unwrap(); // Some statements ... } // <-- release the lock here // A crash may happen here after another thread has used updated data // which leads to an inconsistent state });
The safest way to have a shared object protected from both data-race and
data-loss is to wrap it with a transaction-wide Mutex
as in the following
example:
use corundum::default::*; type P = BuddyAlloc; // PMutex<T> = corundum::sync::Mutex<T,P> let obj = P::open::<Parc<PMutex<i32>>>("foo.pool", O_CF).unwrap(); transaction(|j| { { let obj = obj.lock(j); // Some statements ... } // data is still locked. { let obj = obj.lock(j); // <-- does not block the current thread // Some statements ... } }); // <-- release the lock here after committing or rolling back the transaction
Implementations
Acquires a mutex, blocking the current thread until it is able to do so.
This function will block the local thread until it is available to
acquire the mutex. Upon returning, the thread is the only thread with
the lock held. An RAII guard is returned to keep track of borrowing
data. It creates an UnlockOnCommit
log to unlock the mutex when
transaction is done.
If the local thread already holds the lock, lock()
does not block it.
The mutex remains locked until the transaction is committed.
Alternatively, PMutex
can be used as a compact form of Mutex
.
Examples
use corundum::default::*; use std::thread; type P = BuddyAlloc; let obj = P::open::<Parc<PMutex<i32>>>("foo.pool", O_CF).unwrap(); // Using short forms in the pool module, there is no need to specify the // pool type, as follows: // let obj = P::open::<Parc<PMutex<i32>>>("foo.pool", O_CF).unwrap(); let obj = Parc::demote(&obj); thread::spawn(move || { transaction(move |j| { if let Some(obj) = obj.promote(j) { *obj.lock(j) += 1; } }).unwrap(); }).join().expect("thread::spawn failed");
Attempts to acquire this lock.
If the lock could not be acquired at this time, then Err
is returned.
Otherwise, an RAII guard is returned. The lock will be unlocked when the
owner transaction ends.
This function does not block.
Errors
If another user of this mutex holds a guard, then this call will return failure if the mutex would otherwise be acquired.
Examples
use corundum::default::*; use std::thread; type P = BuddyAlloc; let obj = P::open::<Parc<PMutex<i32>>>("foo.pool", O_CF).unwrap(); let a = Parc::demote(&obj); thread::spawn(move || { transaction(|j| { if let Some(obj) = a.promote(j) { let mut lock = obj.try_lock(j); if let Ok(ref mut mutex) = lock { **mutex = 10; } else { println!("try_lock failed"); } } }).unwrap(); }).join().expect("thread::spawn failed"); transaction(|j| { assert_eq!(*obj.lock(j), 10); }).unwrap();