@@ -33,6 +33,7 @@ pub fn new() -> ~Task {
3333 let mut task = ~Task :: new ( ) ;
3434 task. put_runtime ( ~Ops {
3535 lock : unsafe { Mutex :: new ( ) } ,
36+ awoken : false ,
3637 } as ~rt:: Runtime ) ;
3738 return task;
3839}
@@ -85,7 +86,8 @@ pub fn spawn_opts(opts: TaskOpts, f: proc()) {
8586// This structure is the glue between channels and the 1:1 scheduling mode. This
8687// structure is allocated once per task.
8788struct Ops {
88- lock : Mutex , // native synchronization
89+ lock : Mutex , // native synchronization
90+ awoken : bool , // used to prevent spurious wakeups
8991}
9092
9193impl rt:: Runtime for Ops {
@@ -139,25 +141,38 @@ impl rt::Runtime for Ops {
139141 // reasoning for this is the same logic as above in that the task silently
140142 // transfers ownership via the `uint`, not through normal compiler
141143 // 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.
142151 fn deschedule ( mut ~self , times : uint , mut cur_task : ~Task ,
143152 f: |BlockedTask | -> Result < ( ) , BlockedTask > ) {
144- let my_lock : * mut Mutex = & mut self . lock as * mut Mutex ;
153+ let me = & mut * self as * mut Ops ;
145154 cur_task. put_runtime ( self as ~rt:: Runtime ) ;
146155
147156 unsafe {
148157 let cur_task_dupe = * cast:: transmute :: < & ~Task , & uint > ( & cur_task) ;
149158 let task = BlockedTask :: block ( cur_task) ;
150159
151160 if times == 1 {
152- ( * my_lock) . lock ( ) ;
161+ ( * me) . lock . lock ( ) ;
162+ ( * me) . awoken = false ;
153163 match f ( task) {
154- Ok ( ( ) ) => ( * my_lock) . wait ( ) ,
164+ Ok ( ( ) ) => {
165+ while !( * me) . awoken {
166+ ( * me) . lock . wait ( ) ;
167+ }
168+ }
155169 Err ( task) => { cast:: forget ( task. wake ( ) ) ; }
156170 }
157- ( * my_lock ) . unlock ( ) ;
171+ ( * me ) . lock . unlock ( ) ;
158172 } else {
159173 let mut iter = task. make_selectable ( times) ;
160- ( * my_lock) . lock ( ) ;
174+ ( * me) . lock . lock ( ) ;
175+ ( * me) . awoken = false ;
161176 let success = iter. all ( |task| {
162177 match f ( task) {
163178 Ok ( ( ) ) => true ,
@@ -167,10 +182,10 @@ impl rt::Runtime for Ops {
167182 }
168183 }
169184 } ) ;
170- if success {
171- ( * my_lock ) . wait ( ) ;
185+ while success && ! ( * me ) . awoken {
186+ ( * me ) . lock . wait ( ) ;
172187 }
173- ( * my_lock ) . unlock ( ) ;
188+ ( * me ) . lock . unlock ( ) ;
174189 }
175190 // re-acquire ownership of the task
176191 cur_task = cast:: transmute :: < uint , ~Task > ( cur_task_dupe) ;
@@ -184,12 +199,13 @@ impl rt::Runtime for Ops {
184199 // why it's valid to do so.
185200 fn reawaken ( mut ~self , mut to_wake : ~Task , _can_resched : bool ) {
186201 unsafe {
187- let lock : * mut Mutex = & mut self . lock as * mut Mutex ;
202+ let me = & mut * self as * mut Ops ;
188203 to_wake. put_runtime ( self as ~rt:: Runtime ) ;
189204 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 ( ) ;
193209 }
194210 }
195211
0 commit comments