5
5
6
6
import unsafe:: { shared_mutable_state, clone_shared_mutable_state,
7
7
get_shared_mutable_state, get_shared_immutable_state} ;
8
- import sync:: { condvar, mutex, rwlock} ;
8
+ import sync;
9
+ import sync:: { mutex, rwlock} ;
9
10
10
11
export arc, clone, get;
11
- export mutex_arc, rw_arc;
12
+ export condvar, mutex_arc, rw_arc;
13
+
14
+ /// As sync::condvar, a mechanism for unlock-and-descheduling and signalling.
15
+ struct condvar { is_mutex : bool ; failed: & mut bool; cond: & sync:: condvar; }
16
+
17
+ impl & condvar {
18
+ /// Atomically exit the associated ARC and block until a signal is sent.
19
+ fn wait ( ) {
20
+ assert !* self . failed ;
21
+ self . cond . wait ( ) ;
22
+ // This is why we need to wrap sync::condvar.
23
+ check_poison ( self . is_mutex , * self . failed ) ;
24
+ }
25
+ /// Wake up a blocked task. Returns false if there was no blocked task.
26
+ fn signal ( ) -> bool {
27
+ assert !* self . failed ;
28
+ self . cond . signal ( )
29
+ }
30
+ /// Wake up all blocked tasks. Returns the number of tasks woken.
31
+ fn broadcast ( ) -> uint {
32
+ assert !* self . failed ;
33
+ self . cond . broadcast ( )
34
+ }
35
+ }
12
36
13
37
/****************************************************************************
14
38
* Immutable ARC
@@ -95,26 +119,28 @@ impl<T: send> &mutex_arc<T> {
95
119
// unsafe. See borrow_rwlock, far below.
96
120
do ( & state. lock ) . lock {
97
121
check_poison ( true , state. failed ) ;
98
- state. failed = true ;
99
- let result = blk ( & mut state. data ) ;
100
- state. failed = false ;
101
- result
122
+ let _z = poison_on_fail ( & mut state. failed ) ;
123
+ blk ( & mut state. data )
102
124
}
103
125
}
104
- /* FIXME(#3145): Make this compile; borrowck doesn't like it..?
105
126
/// As access(), but with a condvar, as sync::mutex.lock_cond().
106
127
#[ inline( always) ]
107
- unsafe fn access_cond<U>(blk: fn(x: &mut T, condvar) -> U) -> U {
128
+ unsafe fn access_cond < U > ( blk : fn ( x : & x/ mut T , c : & c/ condvar ) -> U ) -> U {
108
129
let state = unsafe { get_shared_mutable_state ( & self . x ) } ;
109
130
do ( & state. lock ) . lock_cond |cond| {
110
131
check_poison ( true , state. failed ) ;
111
- state.failed = true;
112
- let result = blk(&mut state.data, cond);
113
- state.failed = false;
114
- result
132
+ let _z = poison_on_fail ( & mut state. failed ) ;
133
+ /*
134
+ blk(&mut state.data,
135
+ &condvar { is_mutex: true, failed: &mut state.failed,
136
+ cond: cond })
137
+ */
138
+ // XXX: Working around two seeming region bugs here
139
+ let fref = unsafe { unsafe :: reinterpret_cast ( & mut state. failed ) } ;
140
+ let cvar = condvar { is_mutex : true , failed : fref, cond : cond } ;
141
+ blk ( & mut state. data , unsafe { unsafe :: reinterpret_cast ( & cvar) } )
115
142
}
116
143
}
117
- */
118
144
}
119
145
120
146
// Common code for {mutex.access,rwlock.write}{,_cond}.
@@ -129,6 +155,15 @@ fn check_poison(is_mutex: bool, failed: bool) {
129
155
}
130
156
}
131
157
158
+ struct poison_on_fail {
159
+ failed : & mut bool ;
160
+ new ( failed : & mut bool ) { self . failed = failed; }
161
+ drop {
162
+ /* assert !*self.failed; -- might be false in case of cond.wait() */
163
+ if task:: failing ( ) { * self . failed = true ; }
164
+ }
165
+ }
166
+
132
167
/****************************************************************************
133
168
* R/W lock protected ARC
134
169
****************************************************************************/
@@ -175,26 +210,28 @@ impl<T: const send> &rw_arc<T> {
175
210
let state = unsafe { get_shared_mutable_state ( & self . x ) } ;
176
211
do borrow_rwlock ( state) . write {
177
212
check_poison ( false , state. failed ) ;
178
- state. failed = true ;
179
- let result = blk ( & mut state. data ) ;
180
- state. failed = false ;
181
- result
213
+ let _z = poison_on_fail ( & mut state. failed ) ;
214
+ blk ( & mut state. data )
182
215
}
183
216
}
184
- /* FIXME(#3145): Make this compile; borrowck doesn't like it..?
185
217
/// As write(), but with a condvar, as sync::rwlock.write_cond().
186
218
#[ inline( always) ]
187
- fn write_cond<U>(blk: fn(x: &mut T, condvar) -> U) -> U {
219
+ fn write_cond < U > ( blk : fn ( x : & x/ mut T , c : & c/ condvar ) -> U ) -> U {
188
220
let state = unsafe { get_shared_mutable_state ( & self . x ) } ;
189
221
do borrow_rwlock ( state) . write_cond |cond| {
190
222
check_poison ( false , state. failed ) ;
191
- state.failed = true;
192
- let result = blk(&mut state.data, cond);
193
- state.failed = false;
194
- result
223
+ let _z = poison_on_fail ( & mut state. failed ) ;
224
+ /*
225
+ blk(&mut state.data,
226
+ &condvar { is_mutex: false, failed: &mut state.failed,
227
+ cond: cond })
228
+ */
229
+ // XXX: Working around two seeming region bugs here
230
+ let fref = unsafe { unsafe :: reinterpret_cast ( & mut state. failed ) } ;
231
+ let cvar = condvar { is_mutex : false , failed : fref, cond : cond } ;
232
+ blk ( & mut state. data , unsafe { unsafe :: reinterpret_cast ( & cvar) } )
195
233
}
196
234
}
197
- */
198
235
/**
199
236
* Access the underlying data immutably. May run concurrently with other
200
237
* reading tasks.
@@ -254,6 +291,49 @@ mod tests {
254
291
log ( info, arc_v) ;
255
292
}
256
293
294
+ #[ test]
295
+ fn test_mutex_arc_condvar ( ) {
296
+ let arc = ~mutex_arc ( false ) ;
297
+ let arc2 = ~arc. clone ( ) ;
298
+ let ( c, p) = pipes:: oneshot ( ) ;
299
+ let ( c, p) = ( ~mut some ( c) , ~mut some ( p) ) ;
300
+ do task:: spawn {
301
+ // wait until parent gets in
302
+ pipes : : recv_one ( option:: swap_unwrap ( p) ) ;
303
+ do arc2. access_cond |state, cond | {
304
+ * state = true ;
305
+ cond. signal ( ) ;
306
+ }
307
+ }
308
+ do arc. access_cond |state, cond| {
309
+ pipes:: send_one( option:: swap_unwrap( c) , ( ) ) ;
310
+ assert !* state;
311
+ while ! * state {
312
+ cond. wait( ) ;
313
+ }
314
+ }
315
+ }
316
+ #[ test] #[ should_fail] #[ ignore( cfg( windows) ) ]
317
+ fn test_arc_condvar_poison ( ) {
318
+ let arc = ~mutex_arc ( 1 ) ;
319
+ let arc2 = ~arc. clone ( ) ;
320
+ let ( c, p) = pipes:: stream ( ) ;
321
+
322
+ do task:: spawn_unlinked {
323
+ let _ = p. recv( ) ;
324
+ do arc2. access_cond |one, cond| {
325
+ cond. signal ( ) ;
326
+ assert * one == 0 ; // Parent should fail when it wakes up.
327
+ }
328
+ }
329
+
330
+ do arc. access_cond |one, cond| {
331
+ c. send ( ( ) ) ;
332
+ while * one == 1 {
333
+ cond. wait ( ) ;
334
+ }
335
+ }
336
+ }
257
337
#[ test] #[ should_fail] #[ ignore( cfg( windows) ) ]
258
338
fn test_mutex_arc_poison ( ) {
259
339
let arc = ~mutex_arc ( 1 ) ;
0 commit comments