@@ -67,9 +67,13 @@ fn new_sem<Q: send>(count: int, +q: Q) -> sem<Q> {
67
67
waiters : waitqueue { head : wait_head, tail : wait_tail } ,
68
68
blocked : q } ) )
69
69
}
70
- fn new_sem_and_signal ( count : int ) -> sem < waitqueue > {
71
- let ( block_tail, block_head) = pipes:: stream ( ) ;
72
- new_sem ( count, waitqueue { head : block_head, tail : block_tail } )
70
+ fn new_sem_and_signal ( count : int , num_condvars : uint ) -> sem < ~[ waitqueue ] > {
71
+ let mut queues = ~[ ] ;
72
+ for num_condvars. times {
73
+ let ( block_tail, block_head) = pipes:: stream( ) ;
74
+ vec:: push( queues, waitqueue { head : block_head, tail : block_tail } ) ;
75
+ }
76
+ new_sem( count, queues)
73
77
}
74
78
75
79
impl < Q : send > & sem < Q > {
@@ -119,7 +123,7 @@ impl &sem<()> {
119
123
blk( )
120
124
}
121
125
}
122
- impl & sem < waitqueue > {
126
+ impl & sem < ~ [ waitqueue ] > {
123
127
fn access < U > ( blk : fn ( ) -> U ) -> U {
124
128
let mut release = none;
125
129
unsafe {
@@ -139,52 +143,75 @@ struct sem_release {
139
143
drop { self. sem . release ( ) ; }
140
144
}
141
145
struct sem_and_signal_release {
142
- sem : & sem < waitqueue > ;
143
- new ( sem : & sem < waitqueue > ) { self . sem = sem; }
146
+ sem : & sem < ~ [ waitqueue ] > ;
147
+ new ( sem : & sem < ~ [ waitqueue ] > ) { self . sem = sem; }
144
148
drop { self. sem . release ( ) ; }
145
149
}
146
150
147
151
/// A mechanism for atomic-unlock-and-deschedule blocking and signalling.
148
- struct condvar { priv sem: & sem < waitqueue > ; drop { } }
152
+ struct condvar { priv sem: & sem < ~ [ waitqueue ] > ; drop { } }
149
153
150
154
impl & condvar {
151
155
/// Atomically drop the associated lock, and block until a signal is sent.
152
- fn wait ( ) {
156
+ fn wait ( ) { self . wait_on ( 0 ) }
157
+ /**
158
+ * As wait(), but can specify which of multiple condition variables to
159
+ * wait on. Only a signal_on() or broadcast_on() with the same condvar_id
160
+ * will wake this thread.
161
+ *
162
+ * The associated lock must have been initialised with an appropriate
163
+ * number of condvars. The condvar_id must be between 0 and num_condvars-1
164
+ * or else this call will fail.
165
+ *
166
+ * wait() is equivalent to wait_on(0).
167
+ */
168
+ fn wait_on ( condvar_id : uint ) {
153
169
// Create waiter nobe.
154
170
let ( signal_end, wait_end) = pipes:: oneshot ( ) ;
171
+ let mut wait_end = some ( wait_end) ;
155
172
let mut signal_end = some ( signal_end) ;
156
173
let mut reacquire = none;
174
+ let mut out_of_bounds = none;
157
175
unsafe {
158
176
do task:: unkillable {
177
+ // Release lock, 'atomically' enqueuing ourselves in so doing.
178
+ do ( * * self . sem ) . with |state| {
179
+ if condvar_id < vec:: len ( state. blocked ) {
180
+ // Drop the lock.
181
+ state. count += 1 ;
182
+ if state. count <= 0 {
183
+ signal_waitqueue ( & state. waiters ) ;
184
+ }
185
+ // Enqueue ourself to be woken up by a signaller.
186
+ let signal_end = option:: swap_unwrap ( & mut signal_end) ;
187
+ state. blocked [ condvar_id] . tail . send ( signal_end) ;
188
+ } else {
189
+ out_of_bounds = some ( vec:: len ( state. blocked ) ) ;
190
+ }
191
+ }
192
+
159
193
// If yield checks start getting inserted anywhere, we can be
160
194
// killed before or after enqueueing. Deciding whether to
161
195
// unkillably reacquire the lock needs to happen atomically
162
196
// wrt enqueuing.
163
- reacquire = some ( sem_and_signal_reacquire ( self . sem ) ) ;
164
-
165
- // Release lock, 'atomically' enqueuing ourselves in so doing.
166
- do ( * * self . sem ) . with |state| {
167
- // Drop the lock.
168
- state. count += 1 ;
169
- if state. count <= 0 {
170
- signal_waitqueue ( & state. waiters ) ;
171
- }
172
- // Enqueue ourself to be woken up by a signaller.
173
- let signal_end = option:: swap_unwrap ( & mut signal_end) ;
174
- state. blocked . tail . send ( signal_end) ;
197
+ if out_of_bounds. is_none ( ) {
198
+ reacquire = some ( sem_and_signal_reacquire ( self . sem ) ) ;
175
199
}
176
200
}
177
201
}
178
- // Unconditionally "block". (Might not actually block if a signaller
179
- // did send -- I mean 'unconditionally' in contrast with acquire().)
180
- let _ = pipes:: recv_one ( wait_end) ;
202
+ do check_cvar_bounds ( out_of_bounds, condvar_id, "cond.wait_on()" ) {
203
+ // Unconditionally "block". (Might not actually block if a
204
+ // signaller already sent -- I mean 'unconditionally' in contrast
205
+ // with acquire().)
206
+ let _ = pipes:: recv_one ( option:: swap_unwrap ( & mut wait_end) ) ;
207
+ }
181
208
182
209
// This is needed for a failing condition variable to reacquire the
183
210
// mutex during unwinding. As long as the wrapper (mutex, etc) is
184
211
// bounded in when it gets released, this shouldn't hang forever.
185
212
struct sem_and_signal_reacquire {
186
- sem : & sem < waitqueue > ;
187
- new ( sem : & sem < waitqueue > ) { self . sem = sem ; }
213
+ sem : & sem < ~ [ waitqueue ] > ;
214
+ new ( sem : & sem < ~ [ waitqueue ] > ) { self . sem = sem; }
188
215
drop unsafe {
189
216
// Needs to succeed, instead of itself dying.
190
217
do task:: unkillable {
@@ -195,26 +222,64 @@ impl &condvar {
195
222
}
196
223
197
224
/// Wake up a blocked task. Returns false if there was no blocked task.
198
- fn signal ( ) -> bool {
225
+ fn signal ( ) -> bool { self . signal_on ( 0 ) }
226
+ /// As signal, but with a specified condvar_id. See wait_on.
227
+ fn signal_on ( condvar_id : uint ) -> bool {
228
+ let mut out_of_bounds = none;
229
+ let mut result = false ;
199
230
unsafe {
200
231
do ( * * self . sem ) . with |state| {
201
- signal_waitqueue ( & state. blocked )
232
+ if condvar_id < vec:: len ( state. blocked ) {
233
+ result = signal_waitqueue ( & state. blocked [ condvar_id] ) ;
234
+ } else {
235
+ out_of_bounds = some ( vec:: len ( state. blocked ) ) ;
236
+ }
202
237
}
203
238
}
239
+ do check_cvar_bounds ( out_of_bounds, condvar_id, "cond.signal_on()" ) {
240
+ result
241
+ }
204
242
}
205
243
206
244
/// Wake up all blocked tasks. Returns the number of tasks woken.
207
- fn broadcast ( ) -> uint {
245
+ fn broadcast ( ) -> uint { self . broadcast_on ( 0 ) }
246
+ /// As broadcast, but with a specified condvar_id. See wait_on.
247
+ fn broadcast_on ( condvar_id : uint ) -> uint {
248
+ let mut out_of_bounds = none;
249
+ let mut result = 0 ;
208
250
unsafe {
209
251
do ( * * self . sem ) . with |state| {
210
- // FIXME(#3145) fix :broadcast_heavy
211
- broadcast_waitqueue ( & state. blocked )
252
+ if condvar_id < vec:: len ( state. blocked ) {
253
+ // FIXME(#3145) fix :broadcast_heavy
254
+ result = broadcast_waitqueue ( & state. blocked [ condvar_id] )
255
+ } else {
256
+ out_of_bounds = some ( vec:: len ( state. blocked ) ) ;
257
+ }
212
258
}
213
259
}
260
+ do check_cvar_bounds ( out_of_bounds, condvar_id, "cond.signal_on()" ) {
261
+ result
262
+ }
263
+ }
264
+ }
265
+
266
+ // Checks whether a condvar ID was out of bounds, and fails if so, or does
267
+ // something else next on success.
268
+ #[ inline( always) ]
269
+ fn check_cvar_bounds < U > ( out_of_bounds : option < uint > , id : uint , act : & str ,
270
+ blk : fn ( ) -> U ) -> U {
271
+ match out_of_bounds {
272
+ some( 0 ) =>
273
+ fail fmt ! ( "%s with illegal ID %u - this lock has no condvars!" ,
274
+ act, id) ,
275
+ some( length) =>
276
+ fail fmt ! ( "%s with illegal ID %u - ID must be less than %u" ,
277
+ act, id, length) ,
278
+ none => blk ( )
214
279
}
215
280
}
216
281
217
- impl & sem < waitqueue > {
282
+ impl & sem < ~ [ waitqueue ] > {
218
283
// The only other place that condvars get built is rwlock_write_mode.
219
284
fn access_cond < U > ( blk : fn ( c : & condvar ) -> U ) -> U {
220
285
do self . access { blk ( & condvar { sem : self } ) }
@@ -263,10 +328,19 @@ impl &semaphore {
263
328
* FIFO condition variable.
264
329
* FIXME(#3145): document killability
265
330
*/
266
- struct mutex { priv sem: sem < waitqueue > ; }
331
+ struct mutex { priv sem: sem < ~ [ waitqueue ] > ; }
267
332
268
- /// Create a new mutex.
269
- fn mutex ( ) -> mutex { mutex { sem : new_sem_and_signal ( 1 ) } }
333
+ /// Create a new mutex, with one associated condvar.
334
+ fn mutex ( ) -> mutex { mutex_with_condvars ( 1 ) }
335
+ /**
336
+ * Create a new mutex, with a specified number of associated condvars. This
337
+ * will allow calling wait_on/signal_on/broadcast_on with condvar IDs between
338
+ * 0 and num_condvars-1. (If num_condvars is 0, lock_cond will be allowed but
339
+ * any operations on the condvar will fail.)
340
+ */
341
+ fn mutex_with_condvars ( num_condvars : uint ) -> mutex {
342
+ mutex { sem : new_sem_and_signal ( 1 , num_condvars) }
343
+ }
270
344
271
345
impl & mutex {
272
346
/// Create a new handle to the mutex.
@@ -295,13 +369,20 @@ struct rwlock_inner {
295
369
/// A blocking, no-starvation, reader-writer lock with an associated condvar.
296
370
struct rwlock {
297
371
/* priv */ order_lock : semaphore ;
298
- /* priv */ access_lock: sem<waitqueue>;
372
+ /* priv */ access_lock: sem<~ [ waitqueue] >;
299
373
/* priv */ state: Exclusive <rwlock_inner>;
300
374
}
301
375
302
- /// Create a new rwlock.
303
- fn rwlock ( ) -> rwlock {
304
- rwlock { order_lock : semaphore ( 1 ) , access_lock : new_sem_and_signal ( 1 ) ,
376
+ /// Create a new rwlock, with one associated condvar.
377
+ fn rwlock ( ) -> rwlock { rwlock_with_condvars ( 1 ) }
378
+
379
+ /**
380
+ * Create a new rwlock, with a specified number of associated condvars.
381
+ * Similar to mutex_with_condvars.
382
+ */
383
+ fn rwlock_with_condvars ( num_condvars : uint ) -> rwlock {
384
+ rwlock { order_lock : semaphore ( 1 ) ,
385
+ access_lock : new_sem_and_signal ( 1 , num_condvars) ,
305
386
state : exclusive ( rwlock_inner { read_mode : false ,
306
387
read_count : 0 } ) }
307
388
}
@@ -813,6 +894,59 @@ mod tests {
813
894
drop { self. c. send( ( ) ) ; }
814
895
}
815
896
}
897
+ #[ test]
898
+ fn test_mutex_cond_signal_on_0( ) {
899
+ // Tests that signal_on(0) is equivalent to signal().
900
+ let m = ~mutex( ) ;
901
+ do m. lock_cond |cond| {
902
+ let m2 = ~m. clone( ) ;
903
+ do task:: spawn {
904
+ do m2. lock_cond |cond| {
905
+ cond. signal_on( 0 ) ;
906
+ }
907
+ }
908
+ cond. wait( ) ;
909
+ }
910
+ }
911
+ #[ test] #[ ignore( cfg( windows) ) ]
912
+ fn test_mutex_different_conds ( ) {
913
+ let result = do task : : try {
914
+ let m = ~mutex_with_condvars( 2 ) ;
915
+ let m2 = ~m. clone( ) ;
916
+ let ( c, p) = pipes:: stream( ) ;
917
+ do task:: spawn {
918
+ do m2. lock_cond |cond| {
919
+ c. send( ( ) ) ;
920
+ cond. wait_on( 1 ) ;
921
+ }
922
+ }
923
+ let _ = p. recv( ) ;
924
+ do m. lock_cond |cond| {
925
+ if !cond. signal_on( 0 ) {
926
+ fail; // success; punt sibling awake.
927
+ }
928
+ }
929
+ } ;
930
+ assert result. is_err( ) ;
931
+ }
932
+ #[ test] #[ ignore( cfg( windows) ) ]
933
+ fn test_mutex_no_condvars( ) {
934
+ let result = do task:: try {
935
+ let m = ~mutex_with_condvars( 0 ) ;
936
+ do m. lock_cond |cond| { cond. wait( ) ; }
937
+ } ;
938
+ assert result. is_err( ) ;
939
+ let result = do task:: try {
940
+ let m = ~mutex_with_condvars( 0 ) ;
941
+ do m. lock_cond |cond| { cond. signal( ) ; }
942
+ } ;
943
+ assert result. is_err( ) ;
944
+ let result = do task:: try {
945
+ let m = ~mutex_with_condvars( 0 ) ;
946
+ do m. lock_cond |cond| { cond. broadcast( ) ; }
947
+ } ;
948
+ assert result. is_err( ) ;
949
+ }
816
950
/************************************************************************
817
951
* Reader/writer lock tests
818
952
************************************************************************/
0 commit comments