Skip to content

Commit f69b615

Browse files
committed
UNDER_THE_HOOD.md reviewed and updated.
1 parent bfdb2e2 commit f69b615

File tree

1 file changed

+30
-18
lines changed

1 file changed

+30
-18
lines changed

UNDER_THE_HOOD.md

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22

33
This document aims to explain the operation of `uasyncio` as I understand it. I
44
did not write the library so the information presented is a result of using it,
5-
also studying the code, experiment and inference. There may be errors, in which
6-
case please raise an issue. None of the information here is required to use the
7-
library.
5+
studying the code, experiment and inference. There may be errors, in which case
6+
please raise an issue. None of the information here is required to use the
7+
library: it is intended to satisfy the curiosity of scheduler geeks.
88

9-
It assumes a good appreciation of the use of `uasyncio`. Familiarity with
9+
Where the versions differ, the explanation relates to the `fast_io` version.
10+
Differences are largely in `__init__.py`: the scheduling algorithm in `core.py`
11+
is little changed.
12+
13+
This doc assumes a good appreciation of the use of `uasyncio`. Familiarity with
1014
Python generators is also recommended, in particular the use of `yield from`
1115
and appreciating the difference between a generator and a generator function:
1216

@@ -19,12 +23,11 @@ def gen_func(n): # gen_func is a generator function
1923
my_gen = gen_func(7) # my_gen is a generator
2024
```
2125

22-
The code for `uasyncio` may be found in micropython-lib in the following
23-
directories:
26+
The code for the `fast_io` variant of `uasyncio` may be found in:
2427

2528
```
26-
uasyncio/uasyncio/__init__.py
27-
uasyncio.core/uasyncio/core.py
29+
fast_io/__init__.py
30+
fast_io/core.py
2831
```
2932

3033
# Generators and coroutines
@@ -101,7 +104,7 @@ The following subclasses exist:
101104
interface.
102105
* `IOWrite` Causes an interface to be polled for ready to accept data. `.arg`
103106
is the interface.
104-
* `IOReadDone` These stop polling of an interface.
107+
* `IOReadDone` These stop polling of an interface (in `.arg`).
105108
* `IOWriteDone`
106109

107110
The `IO*` classes are for the exclusive use of `StreamReader` and `StreamWriter`
@@ -113,11 +116,14 @@ The file `core.py` defines an `EventLoop` class which is subclassed by
113116
`PollEventLoop` in `__init__.py`. The latter extends the base class to support
114117
stream I/O. In particular `.wait()` is overridden in the subclass.
115118

116-
The `EventLoop` maintains two queues, `.runq` and `.waitq`. Tasks are appended
117-
to the bottom of the run queue and retrieved from the top; in other words it is
118-
a First In First Out (FIFO) queue. Tasks on the wait queue are sorted in order
119-
of the time when they are to run, the task having the soonest time to run at
120-
the top.
119+
The `fast_io` `EventLoop` maintains three queues, `.runq`, `.waitq` and `.ioq`,
120+
although `.ioq` is only instantiated if it is specified. Official `uasyncio`
121+
does not have `.ioq`.
122+
123+
Tasks are appended to the bottom of the run queue and retrieved from the top;
124+
in other words it is a First In First Out (FIFO) queue. The I/O queue is
125+
similar. Tasks on the wait queue are sorted in order of the time when they are
126+
to run, the task having the soonest time to run at the top.
121127

122128
When a task issues `await asyncio.sleep(t)` or `await asyncio.sleep_ms(t)` and
123129
t > 0 the task is placed on the wait queue. If t == 0 it is placed on the run
@@ -140,7 +146,8 @@ iterates to the next entry. If it is a task, it runs then either yields or
140146
raises an exception. If it yields the return type is examined as described
141147
above. If the task yields with a zero delay it will be appended to the run
142148
queue, but as described above it will not be rescheduled in this pass through
143-
the queue.
149+
the queue. If it yields a nonzero delay it will be added to `.waitq` (it has
150+
already been removed from `.runq`).
144151

145152
Once every task which was initially on the run queue has been scheduled, the
146153
queue may or may not be empty depending on whether tasks yielded a zero delay.
@@ -164,10 +171,15 @@ the run queue - the task is simply not rescheduled.
164171
If an unhandled exception occurs in a task this will be propagated to the
165172
caller of `run_forever()` or `run_until_complete` a explained in the tutorial.
166173

167-
# Stream I/O
174+
## Task Cancellation
168175

169-
This description of stream I/O is based on my code rather than the official
170-
version.
176+
The `cancel` function uses `pend_throw` to pass a `CancelledError` to the coro
177+
to be cancelled. The generator's `.throw` and `.close` methods cause the coro
178+
to execute code immediately. This is incorrect behaviour for a de-scheduled
179+
coro. The `.pend_throw` method causes the exception to be processed the next
180+
time the coro is scheduled.
181+
182+
# Stream I/O
171183

172184
Stream I/O is an efficient way of polling stream devices using `select.poll`.
173185
Device drivers for this mechanism must provide an `ioctl` method which reports

0 commit comments

Comments
 (0)