Skip to content

Commit a602aa8

Browse files
committed
Update primitives for new uasyncio version. README provides introduction.
1 parent 9a1939c commit a602aa8

File tree

3 files changed

+116
-18
lines changed

3 files changed

+116
-18
lines changed

README.md

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ code size and high performance on bare metal targets. This repository provides
66
documentation, tutorial material and code to aid in its effective use. It also
77
contains an optional `fast_io` variant of `uasyncio`.
88

9+
Damien has completely rewritten `uasyncio`. Its release is likely to be
10+
imminent, see
11+
[PR5332](https://github.com/micropython/micropython/pull/5332) and [section 3.1](./README.md##31-the-new_version).
12+
913
## The fast_io variant
1014

1115
This comprises two parts.
@@ -70,6 +74,78 @@ installation instructions where `uasyncio` is not pre-installed.
7074

7175
# 3. uasyncio development state
7276

77+
## 3.1 The new version
78+
79+
This complete rewrite of `uasyncio` supports CPython 3.8 syntax. A design aim
80+
is that it should be be a compatible subset of `asyncio`. Many applications
81+
using the coding style advocated in the tutorial will work unchanged. The
82+
following features will involve minor changes to application code:
83+
84+
* Task cancellation: `cancel` is now a method of a `Task` instance.
85+
* Event loop methods: `call_at`, `call_later`, `call_later_ms` and
86+
`call_soon` are no longer supported. In CPython docs these are
87+
[lightly deprecated](https://docs.python.org/3/library/asyncio-eventloop.html#preface)
88+
in application code; there are simple workrounds.
89+
* `yield` in coroutines should be replaced by `await asyncio.sleep_ms(0)`:
90+
this is in accord with CPython where `yield` will produce a syntax error.
91+
* Awaitable classes: currently under discussion. The `__iter__` method works
92+
but `yield` should be replaced by `await asyncio.sleep_ms(0)`. As yet I have
93+
found no way to write an awaitable classes compatible with the new `uasyncio`
94+
and which does not throw syntax errors under CPython 3.8/`asyncio`.
95+
96+
### 3.1.1 Implications for this repository
97+
98+
It is planned to retain V2 under a different name. The new version fixes bugs
99+
which have been outstanding for a long time. In my view V2 is best viewed as
100+
deprecated. I will retain V2-specific code and docs in a separate directory,
101+
with the rest of this repo being adapted for the new version.
102+
103+
#### 3.1.1.1 Tutorial
104+
105+
This requires only minor changes.
106+
107+
#### 3.1.1.2 Fast I/O
108+
109+
The `fast_io` fork is incompatible and will be relegated to the V2 directory.
110+
111+
The new version's design greatly simplifies the implementation of fast I/O:
112+
I therefore hope the new `uasyncio` will include it. The other principal aims
113+
were to provide workrounds for bugs now fixed. If `uasyncio` includes fast I/O
114+
there is no reason to fork the new version; other `fast_io` features will be
115+
lost unless Damien sees fit to implement them. The low priority task option is
116+
little used and arguably is ill-conceived: I will not be advocating for its
117+
inclusion.
118+
119+
#### 3.1.1.3 Synchronisation Primitives
120+
121+
The CPython `asyncio` library supports these synchronisation primitives:
122+
* `Lock` - already incorporated in new `uasyncio`.
123+
* `Event` - already incorporated.
124+
* `gather` - already incorporated.
125+
* `Semaphore` and `BoundedSemaphore`. My classes work under new version.
126+
* `Condition`. Works under new version.
127+
* `Queue`. This was implemented by Paul Sokolvsky in `uasyncio.queues`.
128+
129+
Incorporating these will produce more efficient implementations; my solutions
130+
were designed to work with stock `uasyncio` V2.
131+
132+
The `Event` class in `asyn.py` provides a nonstandard option to supply a data
133+
value to the `.set` method and to retrieve this with `.value`. It is also an
134+
awaitable class. I will support these by subclassing the native `Event`.
135+
136+
The following work under new and old versions:
137+
* `Barrier` (now adapted).
138+
* `Delay_ms` (this and the following in aswitch.py)
139+
* `Switch`
140+
* `Pushbutton`
141+
142+
The following were workrounds for bugs and omissions in V2 which are now fixed.
143+
They will be removed.
144+
* The cancellation decorators and classes (cancellation works as per CPython).
145+
* The nonstandard support for `gather` (now properly supported).
146+
147+
## 3.2 The current version V2.0
148+
73149
These notes are intended for users familiar with `asyncio` under CPython.
74150

75151
The MicroPython language is based on CPython 3.4. The `uasyncio` library
@@ -99,13 +175,13 @@ It supports millisecond level timing with the following:
99175

100176
Classes `Task` and `Future` are not supported.
101177

102-
## 3.1 Asynchronous I/O
178+
## 3.2.1 Asynchronous I/O
103179

104180
Asynchronous I/O (`StreamReader` and `StreamWriter` classes) support devices
105181
with streaming drivers, such as UARTs and sockets. It is now possible to write
106182
streaming device drivers in Python.
107183

108-
## 3.2 Time values
184+
## 3.2.2 Time values
109185

110186
For timing asyncio uses floating point values of seconds. The `uasyncio.sleep`
111187
method accepts floats (including sub-second values) or integers. Note that in

asyn.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def __await__(self):
154154
while True: # Wait until last waiting thread changes the direction
155155
if direction != self._down:
156156
return
157-
yield
157+
await asyncio.sleep_ms(0)
158158

159159
__iter__ = __await__
160160

@@ -201,7 +201,7 @@ async def __aexit__(self, *args):
201201

202202
async def acquire(self):
203203
while self._count == 0:
204-
yield
204+
await asyncio.sleep_ms(0)
205205
self._count -= 1
206206

207207
def release(self):

asyntest.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def print_tests():
4040
event_test() Test Event and Lock objects.
4141
barrier_test() Test the Barrier class.
4242
semaphore_test(bounded=False) Test Semaphore or BoundedSemaphore.
43-
condition_test() Test the Condition class.
43+
condition_test(new=False) Test the Condition class. Set arg True for new uasyncio.
4444
gather_test() Test the Gather class
4545
4646
Recommended to issue ctrl-D after running each test.
@@ -287,13 +287,6 @@ async def cond01():
287287
with await cond:
288288
cond.notify(2) # Notify 2 tasks
289289

290-
async def cond02(n, barrier):
291-
with await cond:
292-
print('cond02', n, 'Awaiting notification.')
293-
await cond.wait()
294-
print('cond02', n, 'triggered. tim =', tim)
295-
barrier.trigger()
296-
297290
@asyn.cancellable
298291
async def cond03(): # Maintain a count of seconds
299292
global tim
@@ -302,6 +295,26 @@ async def cond03(): # Maintain a count of seconds
302295
await asyncio.sleep(1)
303296
tim += 1
304297

298+
async def cond01_new():
299+
while True:
300+
await asyncio.sleep(2)
301+
with await cond:
302+
cond.notify(2) # Notify 2 tasks
303+
304+
async def cond03_new(): # Maintain a count of seconds
305+
global tim
306+
await asyncio.sleep(0.5)
307+
while True:
308+
await asyncio.sleep(1)
309+
tim += 1
310+
311+
async def cond02(n, barrier):
312+
with await cond:
313+
print('cond02', n, 'Awaiting notification.')
314+
await cond.wait()
315+
print('cond02', n, 'triggered. tim =', tim)
316+
barrier.trigger()
317+
305318
def predicate():
306319
return tim >= 8 # 12
307320

@@ -312,11 +325,15 @@ async def cond04(n, barrier):
312325
print('cond04', n, 'triggered. tim =', tim)
313326
barrier.trigger()
314327

315-
async def cond_go(loop):
328+
async def cond_go(loop, new):
316329
ntasks = 7
317330
barrier = asyn.Barrier(ntasks + 1)
318-
loop.create_task(asyn.Cancellable(cond01)())
319-
loop.create_task(asyn.Cancellable(cond03)())
331+
if new:
332+
t1 = asyncio.create_task(cond01_new())
333+
t3 = asyncio.create_task(cond03_new())
334+
else:
335+
loop.create_task(asyn.Cancellable(cond01)())
336+
loop.create_task(asyn.Cancellable(cond03)())
320337
for n in range(ntasks):
321338
loop.create_task(cond02(n, barrier))
322339
await barrier # All instances of cond02 have completed
@@ -325,10 +342,15 @@ async def cond_go(loop):
325342
loop.create_task(cond04(99, barrier))
326343
await barrier
327344
# cancel continuously running coros.
328-
await asyn.Cancellable.cancel_all()
345+
if new:
346+
t1.cancel()
347+
t3.cancel()
348+
await asyncio.sleep_ms(0)
349+
else:
350+
await asyn.Cancellable.cancel_all()
329351
print('Done.')
330352

331-
def condition_test():
353+
def condition_test(new=False):
332354
printexp('''cond02 0 Awaiting notification.
333355
cond02 1 Awaiting notification.
334356
cond02 2 Awaiting notification.
@@ -348,7 +370,7 @@ def condition_test():
348370
Done.
349371
''', 13)
350372
loop = asyncio.get_event_loop()
351-
loop.run_until_complete(cond_go(loop))
373+
loop.run_until_complete(cond_go(loop, new))
352374

353375
# ************ Gather test ************
354376

0 commit comments

Comments
 (0)