@@ -139,19 +139,17 @@ and rebuilding.
139
139
140
140
6 . [ Hints and tips] ( ./TUTORIAL.md#6-hints-and-tips )
141
141
142
- 6.1 [ Coroutines are generators ] ( ./TUTORIAL.md#61-coroutines-are-generators )
142
+ 6.1 [ Program hangs ] ( ./TUTORIAL.md#61-program-hangs )
143
143
144
- 6.2 [ Program hangs ] ( ./TUTORIAL.md#62-program-hangs )
144
+ 6.2 [ uasyncio retains state ] ( ./TUTORIAL.md#62-uasyncio-retains-state )
145
145
146
- 6.3 [ uasyncio retains state ] ( ./TUTORIAL.md#63-uasyncio-retains-state )
146
+ 6.3 [ Garbage Collection ] ( ./TUTORIAL.md#63-garbage-collection )
147
147
148
- 6.4 [ Garbage Collection ] ( ./TUTORIAL.md#64-garbage-collection )
148
+ 6.4 [ Testing ] ( ./TUTORIAL.md#64-testing )
149
149
150
- 6.5 [ Testing ] ( ./TUTORIAL.md#65-testing )
150
+ 6.5 [ A common error ] ( ./TUTORIAL.md#65-a-common-error ) This can be hard to find.
151
151
152
- 6.6 [ A common hard to find error] ( ./TUTORIAL.md#66-a-common-error )
153
-
154
- 6.7 [ Socket programming] ( ./TUTORIAL.md#67-socket-programming )
152
+ 6.6 [ Socket programming] ( ./TUTORIAL.md#66-socket-programming )
155
153
156
154
7 . [ Notes for beginners] ( ./TUTORIAL.md#7-notes-for-beginners )
157
155
@@ -169,8 +167,6 @@ and rebuilding.
169
167
170
168
7.7 [ Polling] ( ./TUTORIAL.md#77-polling )
171
169
172
- 8 . [ Modifying uasyncio] ( ./TUTORIAL.md#8-modifying-uasyncio )
173
-
174
170
# 1. Cooperative scheduling
175
171
176
172
The technique of cooperative multi-tasking is widely used in embedded systems.
@@ -1042,6 +1038,10 @@ Python's `select.poll` system: because the polling is done in C it is faster
1042
1038
and more efficient than explicit polling. The use of ` stream I/O ` is discussed
1043
1039
[ here] ( ./TUTORIAL.md#53-using-the-stream-mechanism ) .
1044
1040
1041
+ Owing to its efficiency implicit polling benefits most fast I/O device drivers:
1042
+ streaming drivers can be written for many devices not normally considered as
1043
+ streaming devices [ section 5.4] ( ./TUTORIAL.md#54-writing-streaming-device-drivers ) .
1044
+
1045
1045
###### [ Contents] ( ./TUTORIAL.md#contents )
1046
1046
1047
1047
## 5.1 Timing issues
@@ -1075,10 +1075,9 @@ and `sleep_ms()` functions. The worst-case value for this overrun may be
1075
1075
calculated by summing, for every other coro, the worst-case execution time
1076
1076
between yielding to the scheduler.
1077
1077
1078
- There is an experimental version of uasyncio presented [ here] ( ./FASTPOLL.md ) .
1079
- This provides for callbacks which run on every iteration of the scheduler
1080
- enabling a coro to wait on an event with much reduced latency. It is hoped
1081
- that improvements to ` uasyncio ` will remove the need for this in future.
1078
+ The [ fast_io] ( ./FASTPOLL.md ) version of ` uasyncio ` in this repo provides a way
1079
+ to ensure that stream I/O is polled on every iteration of the scheduler. It is
1080
+ hoped that official ` uasyncio ` will adopt code to this effect in due course.
1082
1081
1083
1082
###### [ Contents] ( ./TUTORIAL.md#contents )
1084
1083
@@ -1121,7 +1120,7 @@ class RecordOrientedUart():
1121
1120
self .uart = UART(4 , 9600 )
1122
1121
self .data = b ' '
1123
1122
1124
- def __await__ (self ):
1123
+ def __iter__ (self ): # Not __await__ issue #2678
1125
1124
data = b ' '
1126
1125
while not data.endswith(self .DELIMITER ):
1127
1126
yield from asyncio.sleep(0 ) # Neccessary because:
@@ -1130,8 +1129,6 @@ class RecordOrientedUart():
1130
1129
data = b ' ' .join((data, self .uart.read(self .uart.any())))
1131
1130
self .data = data
1132
1131
1133
- __iter__ = __await__ # workround for issue #2678
1134
-
1135
1132
async def send_record (self , data ):
1136
1133
data = b ' ' .join((data, self .DELIMITER ))
1137
1134
self .uart.write(data)
@@ -1251,8 +1248,7 @@ data as is available.
1251
1248
` readline() ` Return as many characters as are available up to and including any
1252
1249
newline character. Required if you intend to use ` StreamReader.readline() `
1253
1250
` read(n) ` Return as many characters as are available but no more than ` n ` .
1254
- Required if you plan to use ` StreamReader.read() ` or
1255
- ` StreamReader.readexactly() `
1251
+ Required to use ` StreamReader.read() ` or ` StreamReader.readexactly() `
1256
1252
1257
1253
A writeable driver must provide this synchronous method:
1258
1254
` write ` Args ` buf ` , ` off ` , ` sz ` . Arguments:
@@ -1332,8 +1328,8 @@ async def timer_test(n):
1332
1328
```
1333
1329
1334
1330
With official ` uasyncio ` this confers no benefit over ` await asyncio.sleep_ms() ` .
1335
- With the [ priority version ] ( ./FASTPOLL.md ) it offers much more precise delays
1336
- under a common usage scenario .
1331
+ Using [ fast_io ] ( ./FASTPOLL.md ) it offers much more precise delays under the
1332
+ common usage pattern where coros await a zero delay .
1337
1333
1338
1334
It is possible to use I/O scheduling to associate an event with a callback.
1339
1335
This is more efficient than a polling loop because the coro doing the polling
@@ -1385,20 +1381,19 @@ class PinCall(io.IOBase):
1385
1381
```
1386
1382
1387
1383
Once again with official ` uasyncio ` latency can be high. Depending on
1388
- application design the [ priority version ] ( ./FASTPOLL.md ) can greatly reduce
1384
+ application design the [ fast_io ] ( ./FASTPOLL.md ) version can greatly reduce
1389
1385
this.
1390
1386
1391
1387
The demo program ` iorw.py ` illustrates a complete example. Note that, at the
1392
1388
time of writing there is a bug in ` uasyncio ` which prevents this from woking.
1393
1389
See [ this GitHub thread] ( https://github.com/micropython/micropython/pull/3836#issuecomment-397317408 ) .
1394
1390
There are two solutions. A workround is to write two separate drivers, one
1395
- read-only and the other write-only. Alternatively an experimental version
1396
- of ` uasyncio ` is [ documented here] ( ./FASTPOLL.md ) which addresses this and
1397
- also enables the priority of I/O to be substantially raised.
1391
+ read-only and the other write-only. Alternatively the
1392
+ [ fast_io] ( ./FASTPOLL.md ) addresses this.
1398
1393
1399
- In the official ` uasyncio ` is scheduled quite infrequently. See
1394
+ In the official ` uasyncio ` I/O is scheduled quite infrequently. See
1400
1395
[ see this GitHub RFC] ( https://github.com/micropython/micropython/issues/2664 ) .
1401
- The experimental version addresses this issue.
1396
+ The ` fast_io ` version addresses this issue.
1402
1397
1403
1398
###### [ Contents] ( ./TUTORIAL.md#contents )
1404
1399
@@ -1407,15 +1402,15 @@ The experimental version addresses this issue.
1407
1402
This may be found in the ` nec_ir ` directory. Its use is documented
1408
1403
[ here] ( ./nec_ir/README.md ) . The demo provides a complete device driver example:
1409
1404
a receiver/decoder for an infra red remote controller. The following notes are
1410
- salient points regarding its asyncio usage.
1405
+ salient points regarding its ` asyncio ` usage.
1411
1406
1412
1407
A pin interrupt records the time of a state change (in us) and sets an event,
1413
1408
passing the time when the first state change occurred. A coro waits on the
1414
1409
event, yields for the duration of a data burst, then decodes the stored data
1415
1410
before calling a user-specified callback.
1416
1411
1417
1412
Passing the time to the ` Event ` instance enables the coro to compensate for
1418
- any asyncio latency when setting its delay period.
1413
+ any ` asyncio ` latency when setting its delay period.
1419
1414
1420
1415
###### [ Contents] ( ./TUTORIAL.md#contents )
1421
1416
@@ -1433,26 +1428,6 @@ run while acquisition is in progress.
1433
1428
1434
1429
# 6 Hints and tips
1435
1430
1436
- ## 6.1 Coroutines are generators
1437
-
1438
- In MicroPython coroutines are generators. This is not the case in CPython.
1439
- Issuing ` yield ` in a coro will provoke a syntax error in CPython, whereas in
1440
- MicroPython it has the same effect as ` await asyncio.sleep(0) ` . The surest way
1441
- to write error free code is to use CPython conventions and assume that coros
1442
- are not generators.
1443
-
1444
- The following will work. If you use them, be prepared to test your code against
1445
- each uasyncio release because the behaviour is not necessarily guaranteed.
1446
-
1447
- ``` python
1448
- yield from coro # Equivalent to await coro: continue when coro terminates.
1449
- yield # Reschedule current coro in round-robin fashion.
1450
- yield 100 # Pause 100ms - equivalent to above
1451
- ```
1452
-
1453
- Issuing ` yield ` or ` yield 100 ` is slightly faster than the equivalent ` await `
1454
- statements.
1455
-
1456
1431
###### [ Contents] ( ./TUTORIAL.md#contents )
1457
1432
1458
1433
## 6.1 Program hangs
@@ -1532,6 +1507,7 @@ the outer loop:
1532
1507
1533
1508
It is perhaps worth noting that this error would not have been apparent had
1534
1509
data been sent to the UART at a slow rate rather than via a loopback test.
1510
+ Welcome to the joys of realtime programming.
1535
1511
1536
1512
###### [ Contents] ( ./TUTORIAL.md#contents )
1537
1513
@@ -1540,11 +1516,18 @@ data been sent to the UART at a slow rate rather than via a loopback test.
1540
1516
If a function or method is defined with ` async def ` and subsequently called as
1541
1517
if it were a regular (synchronous) callable, MicroPython does not issue an
1542
1518
error message. This is [ by design] ( https://github.com/micropython/micropython/issues/3241 ) .
1543
- It typically leads to a program silently failing to run correctly.
1519
+ It typically leads to a program silently failing to run correctly:
1520
+
1521
+ ``` python
1522
+ async def foo ():
1523
+ # code
1524
+ loop.create_task(foo) # Case 1: foo will never run
1525
+ foo() # Case 2: Likewise.
1526
+ ```
1544
1527
1545
1528
I have [ a PR] ( https://github.com/micropython/micropython-lib/pull/292 ) which
1546
- proposes a fix for this . The [ experimental fast_io] ( ./FASTPOLL.md ) version
1547
- implements this fix .
1529
+ proposes a fix for case 1 . The [ fast_io] ( ./FASTPOLL.md ) version implements
1530
+ this.
1548
1531
1549
1532
The script ` check_async_code.py ` attempts to locate instances of questionable
1550
1533
use of coros. It is intended to be run on a PC and uses Python3. It takes a
@@ -1569,11 +1552,14 @@ bar(foo) # These lines will warn but may or may not be correct
1569
1552
bar(foo())
1570
1553
z = (foo,)
1571
1554
z = (foo(),)
1555
+ foo() # Will warn: is surely wrong.
1572
1556
```
1573
1557
1574
1558
I find it useful as-is but improvements are always welcome.
1575
1559
1576
- ## 6.7 Socket programming
1560
+ ###### [ Contents] ( ./TUTORIAL.md#contents )
1561
+
1562
+ ## 6.6 Socket programming
1577
1563
1578
1564
The use of nonblocking sockets requires some attention to detail. If a
1579
1565
nonblocking read is performed, because of server latency, there is no guarantee
@@ -1587,7 +1573,7 @@ practice a timeout is likely to be required to cope with server outages.
1587
1573
A further complication is that, at the time of writing, the ESP32 port has
1588
1574
issues which require rather unpleasant hacks for error-free operation.
1589
1575
1590
- The file ` sock_nonblock.py ` illustrates the sort of techniques required. It is
1576
+ The file [ sock_nonblock.py] ( ./sock_nonblock.py ) illustrates the sort of techniques required. It is
1591
1577
not a working demo, and solutions are likely to be application dependent.
1592
1578
1593
1579
An alternative approach is to use blocking sockets with ` StreamReader ` and
@@ -1884,35 +1870,3 @@ services the hardware and sets a flag. A coro polls the flag: if it's set it
1884
1870
handles the data and clears the flag. A better approach is to use an ` Event ` .
1885
1871
1886
1872
###### [ Contents] ( ./TUTORIAL.md#contents )
1887
-
1888
- # 8 Modifying uasyncio
1889
-
1890
- The library is designed to be extensible. By following these guidelines a
1891
- module can be constructed which alters the functionality of asyncio without the
1892
- need to change the official library. Such a module may be used where ` uasyncio `
1893
- is implemented as frozen bytecode.
1894
-
1895
- Assume that the aim is to alter the event loop. The module should issue
1896
-
1897
- ``` python
1898
- from uasyncio import *
1899
- ```
1900
-
1901
- The event loop should be subclassed from ` PollEventLoop ` (defined in
1902
- ` __init__.py ` ).
1903
-
1904
- The event loop is instantiated by the first call to ` get_event_loop() ` : this
1905
- creates a singleton instance. This is returned by every call to
1906
- ` get_event_loop() ` . On the assumption that the constructor arguments for the
1907
- new class differ from those of the base class, the module will need to redefine
1908
- ` get_event_loop() ` along the following lines:
1909
-
1910
- ``` python
1911
- _event_loop = None # The singleton instance
1912
- _event_loop_class = MyNewEventLoopClass # The class, not an instance
1913
- def get_event_loop (args ):
1914
- global _event_loop
1915
- if _event_loop is None :
1916
- _event_loop = _event_loop_class(args) # Instantiate once only
1917
- return _event_loop
1918
- ```
0 commit comments