Implement sync::rwlock::write_cond (and task::rekillable)

This commit is contained in:
Ben Blum 2012-08-09 23:15:30 -04:00
parent 4c9f168372
commit e1086b0175
2 changed files with 74 additions and 19 deletions

View file

@ -162,7 +162,6 @@ impl condvar {
// Release lock, 'atomically' enqueuing ourselves in so doing.
do (**self.sem).with |state| {
// Drop the lock.
// FIXME(#3145) investigate why factoring doesn't compile.
state.count += 1;
if state.count <= 0 {
signal_waitqueue(&state.waiters);
@ -343,34 +342,41 @@ impl &rwlock {
unsafe {
do task::unkillable {
(&self.order_lock).acquire();
(&self.access_lock).acquire();
(&self.order_lock).release();
do (&self.access_lock).access {
(&self.order_lock).release();
task::rekillable(blk)
}
}
}
let _z = rwlock_release_write(self);
blk()
}
/**
* As write(), but also with a handle to a condvar. Waiting on this
* condvar will allow readers and writers alike to take the rwlock before
* the waiting task is signalled.
* the waiting task is signalled. (Note: a writer that waited and then
* was signalled might reacquire the lock before other waiting writers.)
*/
fn write_cond<U>(_blk: fn(condvar) -> U) -> U {
fail ~"Need implement lock order lock before access lock";
fn write_cond<U>(blk: fn(condvar) -> U) -> U {
// NB: You might think I should thread the order_lock into the cond
// wait call, so that it gets waited on before access_lock gets
// reacquired upon being woken up. However, (a) this would be not
// pleasant to implement (and would mandate a new 'rw_cond' type) and
// (b) I think violating no-starvation in that case is appropriate.
unsafe {
do task::unkillable {
(&self.order_lock).acquire();
do (&self.access_lock).access_cond |cond| {
(&self.order_lock).release();
do task::rekillable { blk(cond) }
}
}
}
}
// to-do implement downgrade
}
// FIXME(#3136) should go inside of write() and read() respectively
struct rwlock_release_write {
lock: &rwlock;
new(lock: &rwlock) { self.lock = lock; }
drop unsafe {
do task::unkillable { (&self.lock.access_lock).release(); }
}
}
// FIXME(#3136) should go inside of read()
struct rwlock_release_read {
lock: &rwlock;
new(lock: &rwlock) { self.lock = lock; }
@ -708,6 +714,41 @@ mod tests {
let _ = p1.recv();
}
}
#[test]
fn test_rwlock_cond_wait() {
// As test_mutex_cond_wait above.
let x = ~rwlock();
// Child wakes up parent
do x.write_cond |cond| {
let x2 = ~x.clone();
do task::spawn {
do x2.write_cond |cond| {
let woken = cond.signal();
assert woken;
}
}
cond.wait();
}
// Parent wakes up child
let (chan,port) = pipes::stream();
let x3 = ~x.clone();
do task::spawn {
do x3.write_cond |cond| {
chan.send(());
cond.wait();
chan.send(());
}
}
let _ = port.recv(); // Wait until child gets in the rwlock
do x.read { } // Must be able to get in as a reader in the meantime
do x.write_cond |cond| { // Or as another writer
let woken = cond.signal();
assert woken;
}
let _ = port.recv(); // Wait until child wakes up
do x.read { } // Just for good measure
}
#[cfg(test)] #[ignore(cfg(windows))]
fn rwlock_kill_helper(reader1: bool, reader2: bool) {
// Mutex must get automatically unlocked if failed/killed within.

View file

@ -56,7 +56,7 @@ export try;
export yield;
export failing;
export get_task;
export unkillable;
export unkillable, rekillable;
export atomically;
export local_data_key;
@ -572,7 +572,7 @@ fn get_task() -> task {
* }
* ~~~
*/
unsafe fn unkillable(f: fn()) {
unsafe fn unkillable<U>(f: fn() -> U) -> U {
class allow_failure {
let t: *rust_task;
new(t: *rust_task) { self.t = t; }
@ -582,7 +582,21 @@ unsafe fn unkillable(f: fn()) {
let t = rustrt::rust_get_task();
let _allow_failure = allow_failure(t);
rustrt::rust_task_inhibit_kill(t);
f();
f()
}
/// The inverse of unkillable. Only ever to be used nested in unkillable().
unsafe fn rekillable<U>(f: fn() -> U) -> U {
class disallow_failure {
let t: *rust_task;
new(t: *rust_task) { self.t = t; }
drop { rustrt::rust_task_inhibit_kill(self.t); }
}
let t = rustrt::rust_get_task();
let _allow_failure = disallow_failure(t);
rustrt::rust_task_allow_kill(t);
f()
}
/**