Skip to content

Commit 589ee65

Browse files
committed
Add rw_arc.downgrade() + std and cfail tests. Tons of region FIXMEs... (cf #2282, #3154)
1 parent 1779ab4 commit 589ee65

12 files changed

+365
-28
lines changed

src/libcore/ptr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ fn to_uint<T>(thing: &T) -> uint unsafe {
152152

153153
/// Determine if two borrowed pointers point to the same thing.
154154
#[inline(always)]
155-
fn ref_eq<T>(thing: &T, other: &T) -> bool {
155+
fn ref_eq<T>(thing: &a/T, other: &b/T) -> bool {
156156
to_uint(thing) == to_uint(other)
157157
}
158158

src/libcore/unsafe.rs

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
//! Unsafe operations
22
33
export reinterpret_cast, forget, bump_box_refcount, transmute;
4+
export transmute_mut, transmute_immut, transmute_region, transmute_mut_region;
45

56
export SharedMutableState, shared_mutable_state, clone_shared_mutable_state;
67
export get_shared_mutable_state, get_shared_immutable_state;
@@ -53,6 +54,17 @@ unsafe fn transmute<L, G>(-thing: L) -> G {
5354
return newthing;
5455
}
5556

57+
/// Coerce an immutable reference to be mutable.
58+
unsafe fn transmute_mut<T>(+ptr: &T) -> &mut T { transmute(ptr) }
59+
/// Coerce a mutable reference to be immutable.
60+
unsafe fn transmute_immut<T>(+ptr: &mut T) -> &T { transmute(ptr) }
61+
/// Coerce a borrowed pointer to have an arbitrary associated region.
62+
unsafe fn transmute_region<T>(+ptr: &a/T) -> &b/T { transmute(ptr) }
63+
/// Coerce a borrowed mutable pointer to have an arbitrary associated region.
64+
unsafe fn transmute_mut_region<T>(+ptr: &a/mut T) -> &b/mut T {
65+
transmute(ptr)
66+
}
67+
5668
/****************************************************************************
5769
* Shared state & exclusive ARC
5870
****************************************************************************/

src/libstd/arc.rs

+223-8
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import sync;
1010
import sync::{mutex, rwlock};
1111

1212
export arc, clone, get;
13-
export condvar, mutex_arc, rw_arc;
13+
export condvar, mutex_arc, rw_arc, rw_write_mode, rw_read_mode;
1414

1515
/// As sync::condvar, a mechanism for unlock-and-descheduling and signalling.
1616
struct condvar { is_mutex: bool; failed: &mut bool; cond: &sync::condvar; }
@@ -136,10 +136,11 @@ impl<T: send> &mutex_arc<T> {
136136
&condvar { is_mutex: true, failed: &mut state.failed,
137137
cond: cond })
138138
*/
139-
// XXX: Working around two seeming region bugs here
140-
let fref = unsafe { unsafe::reinterpret_cast(&mut state.failed) };
139+
// FIXME(#2282) region variance
140+
let fref =
141+
unsafe { unsafe::transmute_mut_region(&mut state.failed) };
141142
let cvar = condvar { is_mutex: true, failed: fref, cond: cond };
142-
blk(&mut state.data, unsafe { unsafe::reinterpret_cast(&cvar) } )
143+
blk(&mut state.data, unsafe { unsafe::transmute_region(&cvar) } )
143144
}
144145
}
145146
}
@@ -227,10 +228,12 @@ impl<T: const send> &rw_arc<T> {
227228
&condvar { is_mutex: false, failed: &mut state.failed,
228229
cond: cond })
229230
*/
230-
// XXX: Working around two seeming region bugs here
231-
let fref = unsafe { unsafe::reinterpret_cast(&mut state.failed) };
231+
// FIXME(#2282): Need region variance to use the commented-out
232+
// code above instead of this casting mess
233+
let fref =
234+
unsafe { unsafe::transmute_mut_region(&mut state.failed) };
232235
let cvar = condvar { is_mutex: false, failed: fref, cond: cond };
233-
blk(&mut state.data, unsafe { unsafe::reinterpret_cast(&cvar) } )
236+
blk(&mut state.data, unsafe { unsafe::transmute_region(&cvar) } )
234237
}
235238
}
236239
/**
@@ -249,6 +252,52 @@ impl<T: const send> &rw_arc<T> {
249252
blk(&state.data)
250253
}
251254
}
255+
256+
/**
257+
* As write(), but with the ability to atomically 'downgrade' the lock.
258+
* See sync::rwlock.write_downgrade(). The rw_write_mode token must be
259+
* used to obtain the &mut T, and can be transformed into a rw_read_mode
260+
* token by calling downgrade(), after which a &T can be obtained instead.
261+
* ~~~
262+
* do arc.write_downgrade |write_mode| {
263+
* do (&write_mode).write_cond |state, condvar| {
264+
* ... exclusive access with mutable state ...
265+
* }
266+
* let read_mode = arc.downgrade(write_mode);
267+
* do (&read_mode).read |state| {
268+
* ... shared access with immutable state ...
269+
* }
270+
* }
271+
* ~~~
272+
*/
273+
fn write_downgrade<U>(blk: fn(+rw_write_mode<T>) -> U) -> U {
274+
let state = unsafe { get_shared_mutable_state(&self.x) };
275+
do borrow_rwlock(state).write_downgrade |write_mode| {
276+
check_poison(false, state.failed);
277+
// FIXME(#2282) need region variance to avoid having to cast here
278+
let (data,failed) =
279+
unsafe { (unsafe::transmute_mut_region(&mut state.data),
280+
unsafe::transmute_mut_region(&mut state.failed)) };
281+
blk(rw_write_mode((data, write_mode, poison_on_fail(failed))))
282+
}
283+
}
284+
285+
/// To be called inside of the write_downgrade block.
286+
fn downgrade(+token: rw_write_mode<T>) -> rw_read_mode<T> {
287+
// The rwlock should assert that the token belongs to us for us.
288+
let state = unsafe { get_shared_immutable_state(&self.x) };
289+
let rw_write_mode((data, t, _poison)) = token;
290+
// Let readers in
291+
let new_token = (&state.lock).downgrade(t);
292+
// Whatever region the input reference had, it will be safe to use
293+
// the same region for the output reference. (The only 'unsafe' part
294+
// of this cast is removing the mutability.)
295+
let new_data = unsafe { unsafe::transmute_immut(data) };
296+
// Downgrade ensured the token belonged to us. Just a sanity check.
297+
assert ptr::ref_eq(&state.data, new_data);
298+
// Produce new token
299+
rw_read_mode((new_data, new_token))
300+
}
252301
}
253302
254303
// Borrowck rightly complains about immutably aliasing the rwlock in order to
@@ -258,6 +307,58 @@ fn borrow_rwlock<T: const send>(state: &mut rw_arc_inner<T>) -> &rwlock {
258307
unsafe { unsafe::reinterpret_cast(&state.lock) }
259308
}
260309
310+
// FIXME (#3154) ice with struct/&<T> prevents these from being structs.
311+
312+
/// The "write permission" token used for rw_arc.write_downgrade().
313+
enum rw_write_mode<T: const send> =
314+
(&mut T, sync::rwlock_write_mode, poison_on_fail);
315+
/// The "read permission" token used for rw_arc.write_downgrade().
316+
enum rw_read_mode<T:const send> = (&T, sync::rwlock_read_mode);
317+
318+
impl<T: const send> &rw_write_mode<T> {
319+
/// Access the pre-downgrade rw_arc in write mode.
320+
fn write<U>(blk: fn(x: &mut T) -> U) -> U {
321+
match *self {
322+
rw_write_mode((data, ref token, _)) => {
323+
// FIXME(#2282) cast to avoid region invariance
324+
let mode = unsafe { unsafe::transmute_region(token) };
325+
do mode.write {
326+
blk(data)
327+
}
328+
}
329+
}
330+
}
331+
/// Access the pre-downgrade rw_arc in write mode with a condvar.
332+
fn write_cond<U>(blk: fn(x: &x/mut T, c: &c/condvar) -> U) -> U {
333+
match *self {
334+
rw_write_mode((data, ref token, ref poison)) => {
335+
// FIXME(#2282) cast to avoid region invariance
336+
let mode = unsafe { unsafe::transmute_region(token) };
337+
do mode.write_cond |cond| {
338+
let cvar = condvar {
339+
is_mutex: false, failed: poison.failed,
340+
cond: unsafe { unsafe::reinterpret_cast(cond) } };
341+
// FIXME(#2282) region variance would avoid having to cast
342+
blk(data, &cvar)
343+
}
344+
}
345+
}
346+
}
347+
}
348+
349+
impl<T: const send> &rw_read_mode<T> {
350+
/// Access the post-downgrade rwlock in read mode.
351+
fn read<U>(blk: fn(x: &T) -> U) -> U {
352+
match *self {
353+
rw_read_mode((data, ref token)) => {
354+
// FIXME(#2282) cast to avoid region invariance
355+
let mode = unsafe { unsafe::transmute_region(token) };
356+
do mode.read { blk(data) }
357+
}
358+
}
359+
}
360+
}
361+
261362
/****************************************************************************
262363
* Tests
263364
****************************************************************************/
@@ -374,6 +475,23 @@ mod tests {
374475
assert *one == 1;
375476
}
376477
}
478+
#[test] #[should_fail] #[ignore(cfg(windows))]
479+
fn test_rw_arc_poison_dw() {
480+
let arc = ~rw_arc(1);
481+
let arc2 = ~arc.clone();
482+
do task::try {
483+
do arc2.write_downgrade |write_mode| {
484+
// FIXME(#2282)
485+
let mode = unsafe { unsafe::transmute_region(&write_mode) };
486+
do mode.write |one| {
487+
assert *one == 2;
488+
}
489+
}
490+
};
491+
do arc.write |one| {
492+
assert *one == 1;
493+
}
494+
}
377495
#[test] #[ignore(cfg(windows))]
378496
fn test_rw_arc_no_poison_rr() {
379497
let arc = ~rw_arc(1);
@@ -400,7 +518,24 @@ mod tests {
400518
assert *one == 1;
401519
}
402520
}
403-
521+
#[test] #[ignore(cfg(windows))]
522+
fn test_rw_arc_no_poison_dr() {
523+
let arc = ~rw_arc(1);
524+
let arc2 = ~arc.clone();
525+
do task::try {
526+
do arc2.write_downgrade |write_mode| {
527+
let read_mode = arc2.downgrade(write_mode);
528+
// FIXME(#2282)
529+
let mode = unsafe { unsafe::transmute_region(&read_mode) };
530+
do mode.read |one| {
531+
assert *one == 2;
532+
}
533+
}
534+
};
535+
do arc.write |one| {
536+
assert *one == 1;
537+
}
538+
}
404539
#[test]
405540
fn test_rw_arc() {
406541
let arc = ~rw_arc(0);
@@ -434,4 +569,84 @@ mod tests {
434569
p.recv();
435570
do arc.read |num| { assert *num == 10; }
436571
}
572+
#[test]
573+
fn test_rw_downgrade() {
574+
// (1) A downgrader gets in write mode and does cond.wait.
575+
// (2) A writer gets in write mode, sets state to 42, and does signal.
576+
// (3) Downgrader wakes, sets state to 31337.
577+
// (4) tells writer and all other readers to contend as it downgrades.
578+
// (5) Writer attempts to set state back to 42, while downgraded task
579+
// and all reader tasks assert that it's 31337.
580+
let arc = ~rw_arc(0);
581+
582+
// Reader tasks
583+
let mut reader_convos = ~[];
584+
for 10.times {
585+
let ((rc1,rp1),(rc2,rp2)) = (pipes::stream(),pipes::stream());
586+
vec::push(reader_convos, (rc1,rp2));
587+
let arcn = ~arc.clone();
588+
do task::spawn {
589+
rp1.recv(); // wait for downgrader to give go-ahead
590+
do arcn.read |state| {
591+
assert *state == 31337;
592+
rc2.send(());
593+
}
594+
}
595+
}
596+
597+
// Writer task
598+
let arc2 = ~arc.clone();
599+
let ((wc1,wp1),(wc2,wp2)) = (pipes::stream(),pipes::stream());
600+
do task::spawn {
601+
wp1.recv();
602+
do arc2.write_cond |state, cond| {
603+
assert *state == 0;
604+
*state = 42;
605+
cond.signal();
606+
}
607+
wp1.recv();
608+
do arc2.write |state| {
609+
// This shouldn't happen until after the downgrade read
610+
// section, and all other readers, finish.
611+
assert *state == 31337;
612+
*state = 42;
613+
}
614+
wc2.send(());
615+
}
616+
617+
// Downgrader (us)
618+
do arc.write_downgrade |write_mode| {
619+
// FIXME(#2282)
620+
let mode = unsafe { unsafe::transmute_region(&write_mode) };
621+
do mode.write_cond |state, cond| {
622+
wc1.send(()); // send to another writer who will wake us up
623+
while *state == 0 {
624+
cond.wait();
625+
}
626+
assert *state == 42;
627+
*state = 31337;
628+
// send to other readers
629+
for vec::each(reader_convos) |x| {
630+
match x {
631+
(rc, _) => rc.send(()),
632+
}
633+
}
634+
}
635+
let read_mode = arc.downgrade(write_mode);
636+
// FIXME(#2282)
637+
let mode = unsafe { unsafe::transmute_region(&read_mode) };
638+
do mode.read |state| {
639+
// complete handshake with other readers
640+
for vec::each(reader_convos) |x| {
641+
match x {
642+
(_, rp) => rp.recv(),
643+
}
644+
}
645+
wc1.send(()); // tell writer to try again
646+
assert *state == 31337;
647+
}
648+
}
649+
650+
wp2.recv(); // complete handshake with writer
651+
}
437652
}

0 commit comments

Comments
 (0)