@@ -248,19 +248,19 @@ impl MiniTokio {
248
248
tasks : VecDeque :: new (),
249
249
}
250
250
}
251
-
251
+
252
252
/// Spawn a future onto the mini-tokio instance.
253
253
fn spawn <F >(& mut self , future : F )
254
254
where
255
255
F : Future <Output = ()> + Send + 'static ,
256
256
{
257
257
self . tasks. push_back (Box :: pin (future ));
258
258
}
259
-
259
+
260
260
fn run (& mut self ) {
261
261
let waker = task :: noop_waker ();
262
262
let mut cx = Context :: from_waker (& waker );
263
-
263
+
264
264
while let Some (mut task ) = self . tasks. pop_front () {
265
265
if task . as_mut (). poll (& mut cx ). is_pending () {
266
266
self . tasks. push_back (task );
@@ -453,22 +453,33 @@ Wakers are `Sync` and can be cloned. When `wake` is called, the task must be
453
453
scheduled for execution. To implement this, we have a channel. When the ` wake() `
454
454
is called on the waker, the task is pushed into the send half of the channel.
455
455
Our ` Task ` structure will implement the wake logic. To do this, it needs to
456
- contain both the spawned future and the channel send half.
456
+ contain both the spawned future and the channel send half. We place the future
457
+ in a ` TaskFuture ` struct alongside a ` Poll ` enum to keep track of the latest
458
+ ` Future::poll() ` result, which is needed to handle spurious wake-ups. More
459
+ details are given in the implementation of the ` poll() ` method in ` TaskFuture ` .
457
460
458
461
``` rust
459
462
# use std :: future :: Future ;
460
463
# use std :: pin :: Pin ;
461
464
# use std :: sync :: mpsc;
465
+ # use std :: task :: Poll ;
462
466
use std :: sync :: {Arc , Mutex };
463
467
468
+ /// A structure holding a future and the result of
469
+ /// the latest call to its `poll` method.
470
+ struct TaskFuture {
471
+ future : Pin <Box <dyn Future <Output = ()> + Send >>,
472
+ poll : Poll <()>,
473
+ }
474
+
464
475
struct Task {
465
476
// The `Mutex` is to make `Task` implement `Sync`. Only
466
- // one thread accesses `future ` at any given time. The
467
- // `Mutex` is not required for correctness. Real Tokio
477
+ // one thread accesses `task_future ` at any given time.
478
+ // The `Mutex` is not required for correctness. Real Tokio
468
479
// does not use a mutex here, but real Tokio has
469
480
// more lines of code than can fit in a single tutorial
470
481
// page.
471
- future : Mutex <Pin < Box < dyn Future < Output = ()> + Send >> >,
482
+ task_future : Mutex <TaskFuture >,
472
483
executor : mpsc :: Sender <Arc <Task >>,
473
484
}
474
485
@@ -520,13 +531,17 @@ channel. Next, we implement receiving and executing the tasks in the
520
531
# use std :: future :: Future ;
521
532
# use std :: pin :: Pin ;
522
533
# use std :: sync :: {Arc , Mutex };
523
- # use std :: task :: {Context };
534
+ # use std :: task :: {Context , Poll };
524
535
# struct MiniTokio {
525
536
# scheduled : mpsc :: Receiver <Arc <Task >>,
526
537
# sender : mpsc :: Sender <Arc <Task >>,
527
538
# }
539
+ # struct TaskFuture {
540
+ # future : Pin <Box <dyn Future <Output = ()> + Send >>,
541
+ # poll : Poll <()>,
542
+ # }
528
543
# struct Task {
529
- # future : Mutex <Pin < Box < dyn Future < Output = ()> + Send >> >,
544
+ # task_future : Mutex <TaskFuture >,
530
545
# executor : mpsc :: Sender <Arc <Task >>,
531
546
# }
532
547
# impl ArcWake for Task {
@@ -558,18 +573,38 @@ impl MiniTokio {
558
573
}
559
574
}
560
575
576
+ impl TaskFuture {
577
+ fn new (future : impl Future <Output = ()> + Send + 'static ) -> TaskFuture {
578
+ TaskFuture {
579
+ future : Box :: pin (future ),
580
+ poll : Poll :: Pending ,
581
+ }
582
+ }
583
+
584
+ fn poll (& mut self , cx : & mut Context <'_ >) {
585
+ // Spurious wake-ups are allowed, even after a future has
586
+ // returned `Ready`. However, polling a future which has
587
+ // already returned `Ready` is *not* allowed. For this
588
+ // reason we need to check that the future is still pending
589
+ // before we call it. Failure to do so can lead to a panic.
590
+ if self . poll. is_pending () {
591
+ self . poll = self . future. as_mut (). poll (cx );
592
+ }
593
+ }
594
+ }
595
+
561
596
impl Task {
562
597
fn poll (self : Arc <Self >) {
563
598
// Create a waker from the `Task` instance. This
564
599
// uses the `ArcWake` impl from above.
565
600
let waker = task :: waker (self . clone ());
566
601
let mut cx = Context :: from_waker (& waker );
567
602
568
- // No other thread ever tries to lock the future
569
- let mut future = self . future . try_lock (). unwrap ();
603
+ // No other thread ever tries to lock the task_future
604
+ let mut task_future = self . task_future . try_lock (). unwrap ();
570
605
571
- // Poll the future
572
- let _ = future . as_mut () . poll (& mut cx );
606
+ // Poll the inner future
607
+ task_future . poll (& mut cx );
573
608
}
574
609
575
610
// Spawns a new task with the given future.
@@ -582,13 +617,12 @@ impl Task {
582
617
F : Future <Output = ()> + Send + 'static ,
583
618
{
584
619
let task = Arc :: new (Task {
585
- future : Mutex :: new (Box :: pin (future )),
620
+ task_future : Mutex :: new (TaskFuture :: new (future )),
586
621
executor : sender . clone (),
587
622
});
588
623
589
624
let _ = sender . send (task );
590
625
}
591
-
592
626
}
593
627
```
594
628
@@ -638,7 +672,7 @@ use std::pin::Pin;
638
672
# type Output = ();
639
673
# fn poll (self : Pin <& mut Self >, _cx : & mut Context <'_ >) -> Poll <()> {
640
674
# Poll :: Pending
641
- # }
675
+ # }
642
676
# }
643
677
644
678
#[tokio:: main]
0 commit comments