kopia lustrzana https://github.com/peterhinch/micropython-samples
162 wiersze
5.4 KiB
Markdown
162 wiersze
5.4 KiB
Markdown
# 1. Changes to usayncio
|
|
|
|
This archive contains suggestions for changes to new `uasyncio`.
|
|
|
|
## 1.1 Changes implemented
|
|
|
|
1. Implement as a Python package.
|
|
2. Implement synchronisation primitives as package modules to conserve RAM.
|
|
3. `Primitive` class has methods common to most synchronisation primitives.
|
|
Avoids the need for primitives to access the task queue directly.
|
|
4. Add `.priority` method to `Stream` class. Enables I/O to be handled at high
|
|
priority on a per-device basis.
|
|
5. Rename task queue class `TQueue` to avoid name clash with Queue primitive.
|
|
|
|
### Minor changes
|
|
|
|
1. Move `StreamReader` and `StreamWriter` assignments out of legacy section of
|
|
code: these classes exist in `asyncio` 3.8.
|
|
2. `.CreateTask` produces an assertion fail if called with a generator function.
|
|
Avoids obscure traceback if someone omits the parens.
|
|
3. Add machine readable version info. Useful in testing.
|
|
|
|
## 1.2 Suggested changes
|
|
|
|
I haven't implemented these.
|
|
|
|
1. Make `Event.set` capable of being triggered from an ISR.
|
|
2. Implement `wait_for_ms` as per V2.
|
|
|
|
# 2. CPython-compatible synchronisation primitives
|
|
|
|
These aim to work efficiently with the new version. All are separate modules to
|
|
conserve RAM. Items 1-4 subclass `uasyncio.Primitive`.
|
|
|
|
1. `Event`: Moved to separate module.
|
|
2. `Lock`: Kevin Köck's solution.
|
|
3. `Queue`: Paul's solution adapted for efficiency.
|
|
4. `Semaphore`: Also implements `BoundedSemaphore`.
|
|
5. `Condition`.
|
|
|
|
# 3. Other primitives
|
|
|
|
Included as examples of user-contributed primitives - see final section.
|
|
|
|
1. `Message`: Awaitable `Event` subclass with a data payload.
|
|
2. `Barrier`: Multiple tasks wait until all are either waiting on a Barrier
|
|
instance or have triggered the instance without waiting. Similar to `gather`
|
|
without the controlling coro: a barrier is shared between peers and may be
|
|
used in loops.
|
|
|
|
# 4. Test scripts
|
|
|
|
Hopefully these are self-documenting on import.
|
|
|
|
1. `prim_test.py` Tests for synchronisation primitives. Runs on MicroPython and
|
|
CPython V3.5-3.8. Demonstrates that MicroPython primitives behave similarly to
|
|
native CPython ones.
|
|
2. `test_fast_scheduling.py` Demonstrates difference between normal and priority
|
|
I/O scheduling. Runs on Pyboard.
|
|
3. `ms_timer.py` and `ms_timer_test.py` A practical use of priority scheduling to
|
|
implement a timer with higher precision than `asyncio.sleep_ms`. Runs on Pyboard.
|
|
4. `test_can.py` Demonstrates differences in behaviour between CPython 3.8 and
|
|
MicroPython. See code comments.
|
|
|
|
# 5. CPython compatibility of user primitives
|
|
|
|
`Message` is common to CPython and MicroPython.
|
|
There are two implementations of `Barrier` with the same functionality: a CPython
|
|
version and a MicroPython version with specific optimisations. The `Barrier` class
|
|
is loosely based on
|
|
[a Microsoft concept](https://docs.microsoft.com/en-us/windows/win32/sync/synchronization-barriers).
|
|
|
|
## 5.1 Directory structure of primitives
|
|
|
|
MicroPython optimised primitives are in `uasyncio/`. Primitives compatible with
|
|
`asyncio` are in `primitives/`.
|
|
|
|
# 6. Future uasyncio implementations
|
|
|
|
If part of `uasyncio` is to be implemented in C, it would be good if the following
|
|
capabilities were retained:
|
|
1. The ability to subclass the `asyncio` compatible primitives.
|
|
2. The ability to subclass `uasyncio.Primitive` (or provide other access to that
|
|
functionality).
|
|
3. A means of replacing the timebase by one based on the RTC for low power
|
|
applications.
|
|
4. A means of creating awaitable classes (e.g. `__iter__`).
|
|
|
|
# 7. Revisiting topics discussed via email
|
|
|
|
I am revising my tutorial to promote Python 3.8 syntax and to verify that code
|
|
samples run under MicroPython and CPython 3.8. I'm removing references to event
|
|
loop methods except for one minor section. This describes how to code for
|
|
compatibility with CPython versions 3.5-3.7.
|
|
|
|
Here are my observations on issues previously discussed.
|
|
|
|
## 7.1 Awaitable classes
|
|
|
|
I now have portable code which produces no syntax errors under CPython 3.8. It
|
|
is arguably hacky but a similar hack is required for V2. Nobody has complained.
|
|
```python
|
|
up = False # Running under MicroPython?
|
|
try:
|
|
import uasyncio as asyncio
|
|
up = True # Or can use sys.implementation.name
|
|
except ImportError:
|
|
import asyncio
|
|
|
|
async def times_two(n): # Coro to await
|
|
await asyncio.sleep(1)
|
|
return 2 * n
|
|
|
|
class Foo():
|
|
def __await__(self):
|
|
res = 1
|
|
for n in range(5):
|
|
print('__await__ called')
|
|
if up: # MicroPython
|
|
res = yield from times_two(res)
|
|
else: # CPython
|
|
res = yield from times_two(res).__await__()
|
|
return res
|
|
|
|
__iter__ = __await__ # MicroPython compatibility
|
|
|
|
async def bar():
|
|
foo = Foo() # foo is awaitable
|
|
print('waiting for foo')
|
|
res = await foo # Retrieve value
|
|
print('done', res)
|
|
|
|
asyncio.run(bar())
|
|
```
|
|
|
|
## 7.2 run_forever() behaviour
|
|
|
|
In an email I commented that the following code sample never terminates under
|
|
CPython 3.8, whereas under MicroPython it does:
|
|
```python
|
|
try:
|
|
import asyncio
|
|
except ImportError:
|
|
import uasyncio as asyncio
|
|
|
|
async def test():
|
|
print("test")
|
|
for _ in range(2):
|
|
await asyncio.sleep(0)
|
|
print('test2')
|
|
await asyncio.sleep(0.5)
|
|
print('Done')
|
|
|
|
loop=asyncio.get_event_loop()
|
|
loop.create_task(test())
|
|
loop.run_forever()
|
|
# asyncio.run(test())
|
|
```
|
|
While the observation is true, using the preferred (commented out) syntax it
|
|
terminates in CPython 3.8 and in MicroPython. My view is that it's not worth
|
|
fixing.
|