@@ -33,6 +33,7 @@ pub fn new() -> ~Task {
33
33
let mut task = ~Task :: new ( ) ;
34
34
task. put_runtime ( ~Ops {
35
35
lock : unsafe { Mutex :: new ( ) } ,
36
+ awoken : false ,
36
37
} as ~rt:: Runtime ) ;
37
38
return task;
38
39
}
@@ -85,7 +86,8 @@ pub fn spawn_opts(opts: TaskOpts, f: proc()) {
85
86
// This structure is the glue between channels and the 1:1 scheduling mode. This
86
87
// structure is allocated once per task.
87
88
struct Ops {
88
- lock : Mutex , // native synchronization
89
+ lock : Mutex , // native synchronization
90
+ awoken : bool , // used to prevent spurious wakeups
89
91
}
90
92
91
93
impl rt:: Runtime for Ops {
@@ -139,25 +141,38 @@ impl rt::Runtime for Ops {
139
141
// reasoning for this is the same logic as above in that the task silently
140
142
// transfers ownership via the `uint`, not through normal compiler
141
143
// semantics.
144
+ //
145
+ // On a mildly unrelated note, it should also be pointed out that OS
146
+ // condition variables are susceptible to spurious wakeups, which we need to
147
+ // be ready for. In order to accomodate for this fact, we have an extra
148
+ // `awoken` field which indicates whether we were actually woken up via some
149
+ // invocation of `reawaken`. This flag is only ever accessed inside the
150
+ // lock, so there's no need to make it atomic.
142
151
fn deschedule ( mut ~self , times : uint , mut cur_task : ~Task ,
143
152
f: |BlockedTask | -> Result < ( ) , BlockedTask > ) {
144
- let my_lock : * mut Mutex = & mut self . lock as * mut Mutex ;
153
+ let me = & mut * self as * mut Ops ;
145
154
cur_task. put_runtime ( self as ~rt:: Runtime ) ;
146
155
147
156
unsafe {
148
157
let cur_task_dupe = * cast:: transmute :: < & ~Task , & uint > ( & cur_task) ;
149
158
let task = BlockedTask :: block ( cur_task) ;
150
159
151
160
if times == 1 {
152
- ( * my_lock) . lock ( ) ;
161
+ ( * me) . lock . lock ( ) ;
162
+ ( * me) . awoken = false ;
153
163
match f ( task) {
154
- Ok ( ( ) ) => ( * my_lock) . wait ( ) ,
164
+ Ok ( ( ) ) => {
165
+ while !( * me) . awoken {
166
+ ( * me) . lock . wait ( ) ;
167
+ }
168
+ }
155
169
Err ( task) => { cast:: forget ( task. wake ( ) ) ; }
156
170
}
157
- ( * my_lock ) . unlock ( ) ;
171
+ ( * me ) . lock . unlock ( ) ;
158
172
} else {
159
173
let mut iter = task. make_selectable ( times) ;
160
- ( * my_lock) . lock ( ) ;
174
+ ( * me) . lock . lock ( ) ;
175
+ ( * me) . awoken = false ;
161
176
let success = iter. all ( |task| {
162
177
match f ( task) {
163
178
Ok ( ( ) ) => true ,
@@ -167,10 +182,10 @@ impl rt::Runtime for Ops {
167
182
}
168
183
}
169
184
} ) ;
170
- if success {
171
- ( * my_lock ) . wait ( ) ;
185
+ while success && ! ( * me ) . awoken {
186
+ ( * me ) . lock . wait ( ) ;
172
187
}
173
- ( * my_lock ) . unlock ( ) ;
188
+ ( * me ) . lock . unlock ( ) ;
174
189
}
175
190
// re-acquire ownership of the task
176
191
cur_task = cast:: transmute :: < uint , ~Task > ( cur_task_dupe) ;
@@ -184,12 +199,13 @@ impl rt::Runtime for Ops {
184
199
// why it's valid to do so.
185
200
fn reawaken ( mut ~self , mut to_wake : ~Task , _can_resched : bool ) {
186
201
unsafe {
187
- let lock : * mut Mutex = & mut self . lock as * mut Mutex ;
202
+ let me = & mut * self as * mut Ops ;
188
203
to_wake. put_runtime ( self as ~rt:: Runtime ) ;
189
204
cast:: forget ( to_wake) ;
190
- ( * lock) . lock ( ) ;
191
- ( * lock) . signal ( ) ;
192
- ( * lock) . unlock ( ) ;
205
+ ( * me) . lock . lock ( ) ;
206
+ ( * me) . awoken = true ;
207
+ ( * me) . lock . signal ( ) ;
208
+ ( * me) . lock . unlock ( ) ;
193
209
}
194
210
}
195
211
0 commit comments