@@ -10,7 +10,7 @@ import sync;
10
10
import sync:: { mutex, rwlock} ;
11
11
12
12
export arc, clone, get;
13
- export condvar, mutex_arc, rw_arc;
13
+ export condvar, mutex_arc, rw_arc, rw_write_mode , rw_read_mode ;
14
14
15
15
/// As sync::condvar, a mechanism for unlock-and-descheduling and signalling.
16
16
struct condvar { is_mutex : bool ; failed: & mut bool; cond: & sync:: condvar; }
@@ -136,10 +136,11 @@ impl<T: send> &mutex_arc<T> {
136
136
&condvar { is_mutex: true, failed: &mut state.failed,
137
137
cond: cond })
138
138
*/
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 ) } ;
141
142
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) } )
143
144
}
144
145
}
145
146
}
@@ -227,10 +228,12 @@ impl<T: const send> &rw_arc<T> {
227
228
&condvar { is_mutex: false, failed: &mut state.failed,
228
229
cond: cond })
229
230
*/
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) };
232
235
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) } )
234
237
}
235
238
}
236
239
/**
@@ -249,6 +252,52 @@ impl<T: const send> &rw_arc<T> {
249
252
blk(&state.data)
250
253
}
251
254
}
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
+ }
252
301
}
253
302
254
303
// 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 {
258
307
unsafe { unsafe::reinterpret_cast(&state.lock) }
259
308
}
260
309
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
+
261
362
/****************************************************************************
262
363
* Tests
263
364
****************************************************************************/
@@ -374,6 +475,23 @@ mod tests {
374
475
assert * one == 1 ;
375
476
}
376
477
}
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
+ }
377
495
#[ test] #[ ignore( cfg( windows) ) ]
378
496
fn test_rw_arc_no_poison_rr ( ) {
379
497
let arc = ~rw_arc ( 1 ) ;
@@ -400,7 +518,24 @@ mod tests {
400
518
assert * one == 1 ;
401
519
}
402
520
}
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
+ }
404
539
#[ test]
405
540
fn test_rw_arc ( ) {
406
541
let arc = ~rw_arc ( 0 ) ;
@@ -434,4 +569,84 @@ mod tests {
434
569
p. recv( ) ;
435
570
do arc. read |num| { assert * num == 10 ; }
436
571
}
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
+ }
437
652
}
0 commit comments