Description
These will serve as enhanced versions of arc::exclusive
. The plan is for mutex_arc
to be the same as exclusive
, but scheduler enabled, and come equipped with a working condition variable. It is marked unsafe because you can still create cycles with it (benefit: you can nest them).
rw_arc
is somewhat more advanced, and notably more safe. The const
serves two purposes - it allows handing out immutable references while in read mode (just like arc::arc
does today), and it also prevents nesting them inside each other, since they won't be const themselves.
I am also thinking of exposing semaphores, with acquire
and release
and potentially also cond_wait
and cond_signal
. These wouldn't protect anything themselves, but could perhaps be used to synchronise beyond-rust shared resources, such as the filesystem.
Proposed interface:
trait condvar {
fn signal();
fn wait();
}
impl <T: send> for &mutex_arc<T> {
unsafe fn access<U>(blk: fn(&mut T) -> U) -> U;
unsafe fn access_cond<U>(blk: fn(&mut T, condvar) -> U) -> U;
}
impl <T: const send> for &rw_arc<T> {
// Read mode
fn access_read<U>(blk: fn(&T) -> U) -> U;
// Write mode
fn access<U>(blk: fn(&mut T) -> U) -> U;
fn access_cond<U>(blk: fn(&mut T, &condvar) -> U) -> U;
fn access_downgrade<U>(blk: fn(+write_mode<T>) -> U) -> U;
}
fn downgrade<T: const send>(+write_mode<T>) -> read_mode<T>;
impl <T: const send> for &write_mode<T> {
fn access<U>(blk: fn(&mut T) -> U) -> U;
fn access_cond<U>(blk: fn(&mut T, &condvar) -> U) -> U;
}
impl <T: const send> for &read_mode<T> {
fn access<U>(blk: fn(&T) -> U) -> U;
}
A couple points:
- The condvar is just like
rust_cond_lock
very-unsafely provided in the past.
cvar example:
do mutex_arc.access_cond |state, cond| {
...
while not_satisfied(state) {
cond.wait();
}
...
}
- I don't think it's meaningful to hand out a condition variable for rws in read-mode. And it'd be a bunch nastier to implement. Feel free to argue, but I think that (a) signalling on one is meaningless, since you can't have changed anything inside, and (b) waiting on one is meaningless, because either you forbid writers from going before you wake (in which case what are you waiting for?) or writers can go (in which case you might as well have dropped the lock wholesale).
- Downgrade is really neat (hopefully). The
read_mode
andwrite_mode
are linear tokens that allow you to access the state, and downgrade consumes the write mode. This allows you atomically downgrade without releasing the lock, while statically enforcing no mutation after the downgrade.
Downgrade example:
do rw_arc.access_downgrade |write_mode| {
do write_mode.access |state| {
... mutate state ...
}
let read_mode = downgrade(write_mode);
do read_mode.access |state| {
... state is immutable ...
}
}
In particular, I would like confirmation that my understanding of region pointers will enforce the im/mutability properties.