@@ -52,13 +52,17 @@ guides to this may be found online.
52
52
53
53
3.5 [ Queue] ( ./TUTORIAL.md#35-queue )
54
54
55
+ 3.6 [ Task cancellation] ( ./TUTORIAL.md#36-task-cancellation )
56
+
55
57
4 . [ Designing classes for asyncio] ( ./TUTORIAL.md#4-designing-classes-for-asyncio )
56
58
57
59
4.1 [ Awaitable classes] ( ./TUTORIAL.md#41-awaitable-classes )
58
60
59
61
4.2 [ Asynchronous iterators] ( ./TUTORIAL.md#42-asynchronous-iterators )
60
62
61
63
4.3 [ Asynchronous context managers] ( ./TUTORIAL.md#43-asynchronous-context-managers )
64
+
65
+ 4.4 [ Coroutines with timeouts] ( ./TUTORIAL.md#44-coroutines-with-timeouts )
62
66
63
67
5 . [ Device driver examples] ( ./TUTORIAL.md#5-device-driver-examples )
64
68
@@ -440,6 +444,13 @@ multiple instances of ``report`` print their result and pause until the other
440
444
instances are also complete. At that point the callback runs. On its completion
441
445
the coros resume.
442
446
447
+ A special case of ` Barrier ` usage is where some coros are allowed to pass the
448
+ barrier, registering the fact that they have done so. At least one coro must
449
+ wait on the barrier. It will continue execution when all non-waiting coros have
450
+ passed the barrier, and all other waiting coros have reached it. This can be of
451
+ use when cancelling coros. A coro which cancels others might wait until all
452
+ cancelled coros have passed the barrier as they quit.
453
+
443
454
###### [ Jump to Contents] ( ./TUTORIAL.md#contents )
444
455
445
456
## 3.4 Semaphore
@@ -498,6 +509,71 @@ An example of its use is provided in ``aqtest.py``.
498
509
499
510
###### [ Jump to Contents] ( ./TUTORIAL.md#contents )
500
511
512
+ ## 3.6 Task cancellation
513
+
514
+ At the time of writing (12th Nov 2017) this requires PR #3380 and
515
+ micropython-lib PR #221 which are yet to be merged.
516
+
517
+ The ` uasyncio ` library supports task cancellation by throwing an exception to
518
+ the coro which is to be cancelled. The latter must trap the exception and
519
+ (after performing any cleanup) terminate. The use of this mechanism is
520
+ facilitated by the ` Cancellable ` class which enables a coro to be associated
521
+ with a user-defined name for cancellation. Examples of its usage may be found
522
+ in ` asyntest.py ` .
523
+
524
+ A cancellable coro is instantiated from a normal coro ` foo() ` by means of
525
+ ` Cancellable(foo(5), 'foo') ` . Note the passing of an argument to the coro. A
526
+ coro can ` await ` such a task with ` await Cancellable(foo(5), 'foo') ` .
527
+ Alternatively a cancellable task can be scheduled for execution with
528
+ ` loop.create_task(Cancellable(foo(5), 'foo').task) `
529
+
530
+ In either case the coro with the user-defined name 'foo' is cancelled with
531
+ ` Cancellable.cancel('foo') ` . The coro ` foo ` will receive the ` CancelError ` when
532
+ it next runs. This means that in real time, and from the point of view of the
533
+ coro which has cancelled it, cancellation may not be immediate. In some
534
+ situations this may matter. Synchronisation may be achieved using the ` Barrier `
535
+ class, with the cancelling task pausing until all the coros it has cancelled
536
+ have processed the exception. The following - adapted from ` asyntest.py ` -
537
+ illustrates this.
538
+
539
+ ``` python
540
+ import uasyncio as asyncio
541
+ from asyn import Barrier, Cancellable, CancelError
542
+
543
+ async def forever (n ):
544
+ print (' Started forever() instance' , n)
545
+ while True : # Run until cancelled. Error propagates to caller.
546
+ await asyncio.sleep(7 + n)
547
+ print (' Running instance' , n)
548
+
549
+ barrier = Barrier(3 ) # 3 tasks share the barrier
550
+
551
+ async def rats (n ):
552
+ # Cancellable coros must trap the CancelError
553
+ try :
554
+ await forever(n) # Error propagates up from forever()
555
+ except CancelError:
556
+ await barrier(nowait = True ) # Quit immediately
557
+ print (' Instance' , n, ' was cancelled' )
558
+
559
+ async def run_cancel_test2 ():
560
+ loop = asyncio.get_event_loop()
561
+ loop.create_task(Cancellable(rats(1 ), ' rats_1' ).task)
562
+ loop.create_task(Cancellable(rats(2 ), ' rats_2' ).task)
563
+ print (' Running two tasks' )
564
+ await asyncio.sleep(10 )
565
+ print (' About to cancel tasks' )
566
+ Cancellable.cancel(' rats_1' )
567
+ Cancellable.cancel(' rats_2' )
568
+ await barrier # Continue when dependent tasks have quit
569
+ print (' tasks were cancelled' )
570
+
571
+ loop = asyncio.get_event_loop()
572
+ loop.run_until_complete(run_cancel_test2())
573
+ ```
574
+
575
+ ###### [ Jump to Contents] ( ./TUTORIAL.md#contents )
576
+
501
577
# 4 Designing classes for asyncio
502
578
503
579
In the context of device drivers the aim is to ensure nonblocking operation.
@@ -628,6 +704,43 @@ to completion. The error appears to be in PEP492. See
628
704
629
705
###### [ Jump to Contents] ( ./TUTORIAL.md#contents )
630
706
707
+ ## 4.4 Coroutines with timeouts
708
+
709
+ At the time of writing (12th Nov 2017) this requires PR #3380 and
710
+ micropython-lib PR #221 which are yet to be merged.
711
+
712
+ Timeouts are implemented by means of ` uasyncio.wait_for() ` . This takes as
713
+ arguments a coroutine and a timeout in seconds. If the timeout expires a
714
+ ` TimeoutError ` will be thrown to the coro. The next time the coro is scheduled
715
+ for execution the exception will be raised: the coro should trap this and quit.
716
+
717
+ ``` python
718
+ import uasyncio as asyncio
719
+
720
+ async def forever ():
721
+ print (' Starting' )
722
+ try :
723
+ while True :
724
+ await asyncio.sleep_ms(300 )
725
+ print (' Got here' )
726
+ except asyncio.TimeoutError:
727
+ print (' Got timeout' )
728
+
729
+ async def foo ():
730
+ await asyncio.wait_for(forever(), 5 )
731
+ await asyncio.sleep(2 )
732
+
733
+ loop = asyncio.get_event_loop()
734
+ loop.run_until_complete(foo())
735
+ ```
736
+
737
+ Note that if the coro awaits a long delay, it will not be rescheduled until the
738
+ time has elapsed. The ` TimeoutError ` will occur as soon as it is scheduled. But
739
+ in real time and from the point of view of the calling coro, its response to
740
+ the ` TimeoutError ` will be correspondingly delayed.
741
+
742
+ ###### [ Jump to Contents] ( ./TUTORIAL.md#contents )
743
+
631
744
# 5 Device driver examples
632
745
633
746
Many devices such as sensors are read-only in nature and need to be polled to
0 commit comments