Skip to content

Commit 12ed0a3

Browse files
committed
asyn.py NamedTask supports cancel_all classmethod.
1 parent c20d5a2 commit 12ed0a3

File tree

2 files changed

+69
-61
lines changed

2 files changed

+69
-61
lines changed

PRIMITIVES.md

Lines changed: 64 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22

33
There is often a need to provide synchronisation between coros. A common
44
example is to avoid what are known as "race conditions" where multiple coros
5-
compete to access a single resource. An example is provided in the ``aswitch.py``
5+
compete to access a single resource. An example is provided in the `aswitch.py`
66
program and discussed in [the docs](./DRIVERS.md). Another hazard is the "deadly
77
embrace" where two coros wait on the other's completion.
88

99
In simple applications these are often addressed with global flags. A more
10-
elegant approach is to use synchronisation primitives. The module ``asyn.py``
11-
offers "micro" implementations of ``Lock``, ``Event``, ``Barrier`` and ``Semaphore``
10+
elegant approach is to use synchronisation primitives. The module `asyn.py`
11+
offers "micro" implementations of `Lock`, `Event`, `Barrier` and `Semaphore`
1212
primitives.
1313

1414
Another synchronisation issue arises with producer and consumer coros. The
15-
producer generates data which the consumer uses. Asyncio provides the ``Queue``
15+
producer generates data which the consumer uses. Asyncio provides the `Queue`
1616
object. The producer puts data onto the queue while the consumer waits for its
17-
arrival (with other coros getting scheduled for the duration). The ``Queue``
17+
arrival (with other coros getting scheduled for the duration). The `Queue`
1818
guarantees that items are removed in the order in which they were received. As
1919
this is a part of the uasyncio library its use is described in the [tutorial](./TUTORIAL.md).
2020

@@ -23,13 +23,13 @@ this is a part of the uasyncio library its use is described in the [tutorial](./
2323
# 2. Modules
2424

2525
The following modules are provided:
26-
* ``asyn.py`` The main library.
27-
* ``asyntest.py`` Test/demo programs for the library.
26+
* `asyn.py` The main library.
27+
* `asyntest.py` Test/demo programs for the library.
2828

2929
These modules support CPython 3.5 and MicroPython on Unix and microcontroller
30-
targets. The library is for use only with asyncio. They are ``micro`` in design
30+
targets. The library is for use only with asyncio. They are `micro` in design
3131
and are presented as simple, concise examples of asyncio code. They are not
32-
thread safe. Hence they are incompatible with the ``_thread`` module and with
32+
thread safe. Hence they are incompatible with the `_thread` module and with
3333
interrupt handlers.
3434

3535
# 3. asyn.py
@@ -41,9 +41,9 @@ args. If the function is a callback it is executed with the supplied argumets.
4141
If it is a coro, it is scheduled for execution.
4242

4343
args:
44-
* ``func`` Mandatory. a function or coro. These are provided 'as-is' i.e. not
44+
* `func` Mandatory. a function or coro. These are provided 'as-is' i.e. not
4545
using function call syntax.
46-
* ``tup_args`` Optional. A tuple of arguments, default ``()``. The args are
46+
* `tup_args` Optional. A tuple of arguments, default `()`. The args are
4747
upacked when provided to the function.
4848

4949
## 3.2 Lock
@@ -52,12 +52,12 @@ This has now been superceded by the more efficient official version. See the
5252
[test program](https://github.com/micropython/micropython-lib/blob/master/uasyncio.synchro/example_lock.py).
5353
For an example of how to use the preferred official version see [this](./TUTORIAL.md#31-lock).
5454

55-
I have retained this version in ``asyn.py`` merely as an example of uasyncio
55+
I have retained this version in `asyn.py` merely as an example of uasyncio
5656
coding. The remainder of this section applies to this version.
5757

5858
This guarantees unique access to a shared resource. The preferred way to use it
59-
is via an asynchronous context manager. In the following code sample a ``Lock``
60-
instance ``lock`` has been created and is passed to all coros wishing to access
59+
is via an asynchronous context manager. In the following code sample a `Lock`
60+
instance `lock` has been created and is passed to all coros wishing to access
6161
the shared resource. Each coro issues the following:
6262

6363
```python
@@ -66,30 +66,30 @@ async def bar(lock):
6666
# Access resource
6767
```
6868

69-
While the coro ``bar`` is accessing the resource, other coros will pause at the
70-
``async with lock`` statement until the context manager in ``bar()`` is
69+
While the coro `bar` is accessing the resource, other coros will pause at the
70+
`async with lock` statement until the context manager in `bar()` is
7171
complete.
7272

7373
### 3.2.1 Definition
7474

75-
Constructor: Optional argument ``delay_ms`` default 0. Sets a delay between
75+
Constructor: Optional argument `delay_ms` default 0. Sets a delay between
7676
attempts to acquire the lock. In applications with coros needing frequent
7777
scheduling a nonzero value will facilitate this at the expense of latency.
7878
Methods:
7979

80-
* ``locked`` No args. Returns ``True`` if locked.
81-
* ``release`` No args. Releases the lock.
82-
* ``acquire`` No args. Coro which pauses until the lock has been acquired. Use
83-
by executing ``await lock.acquire()``.
80+
* `locked` No args. Returns `True` if locked.
81+
* `release` No args. Releases the lock.
82+
* `acquire` No args. Coro which pauses until the lock has been acquired. Use
83+
by executing `await lock.acquire()`.
8484

8585
## 3.3 Event
8686

8787
This provides a way for one or more coros to pause until another one flags them
88-
to continue. An ``Event`` object is instantiated and passed to all coros using
89-
it. Coros waiting on the event issue ``await event``. Execution pauses
90-
until a coro issues ``event.set()``. ``event.clear()`` must then be issued. An
91-
optional data argument may be passed to ``event.set()`` and retrieved by
92-
``event.value()``.
88+
to continue. An `Event` object is instantiated and passed to all coros using
89+
it. Coros waiting on the event issue `await event`. Execution pauses
90+
until a coro issues `event.set()`. `event.clear()` must then be issued. An
91+
optional data argument may be passed to `event.set()` and retrieved by
92+
`event.value()`.
9393

9494
In the usual case where a single coro is awaiting the event this can be done
9595
immediately after it is received:
@@ -111,7 +111,7 @@ async def foo(event):
111111
event.set()
112112
```
113113

114-
If multiple coros are to wait on a single event, consider using a ``Barrier``
114+
If multiple coros are to wait on a single event, consider using a `Barrier`
115115
object described below. This is because the coro which raised the event has no
116116
way to determine whether all others have received it; determining when to clear
117117
it down requires further synchronisation. One way to achieve this is with an
@@ -123,49 +123,49 @@ async def eventwait(event, ack_event):
123123
ack_event.set()
124124
```
125125

126-
Example of this are in ``event_test`` and ``ack_test`` in asyntest.py.
126+
Example of this are in `event_test` and `ack_test` in asyntest.py.
127127

128128
### 3.3.1 Definition
129129

130130
Constructor: takes one optional boolean argument, defaulting False.
131-
* ``lp`` If ``True`` and the experimental low priority core.py is installed,
131+
* `lp` If `True` and the experimental low priority core.py is installed,
132132
low priority scheduling will be used while awaiting the event. If the standard
133133
version of uasyncio is installed the arg will have no effect.
134134

135135
Synchronous Methods:
136-
* ``set`` Initiates the event. Optional arg ``data``: may be of any type,
137-
sets the event's value. Default ``None``.
138-
* ``clear`` No args. Clears the event, sets the value to ``None``.
139-
* ``is_set`` No args. Returns ``True`` if the event is set.
140-
* ``value`` No args. Returns the value passed to ``set``.
136+
* `set` Initiates the event. Optional arg `data`: may be of any type,
137+
sets the event's value. Default `None`.
138+
* `clear` No args. Clears the event, sets the value to `None`.
139+
* `is_set` No args. Returns `True` if the event is set.
140+
* `value` No args. Returns the value passed to `set`.
141141

142142
The optional data value may be used to compensate for the latency in awaiting
143-
the event by passing ``loop.time()``.
143+
the event by passing `loop.time()`.
144144

145145
## 3.4 Barrier
146146

147147
This enables multiple coros to rendezvous at a particular point. For example
148148
producer and consumer coros can synchronise at a point where the producer has
149149
data available and the consumer is ready to use it. At that point in time the
150-
``Barrier`` can optionally run a callback before releasing the barrier and
150+
`Barrier` can optionally run a callback before releasing the barrier and
151151
allowing all waiting coros to continue.
152152

153153
Constructor.
154154
Mandatory arg:
155-
``participants`` The number of coros which will wait on the barrier.
155+
`participants` The number of coros which will wait on the barrier.
156156
Optional args:
157-
``func`` Callback to run. Default ``None``.
158-
``args`` Tuple of args for the callback. Default ``()``.
157+
`func` Callback to run. Default `None`.
158+
`args` Tuple of args for the callback. Default `()`.
159159

160160
The callback can be a function or a coro. In most applications a function is
161161
likely to be used: this can be guaranteed to run to completion beore the
162162
barrier is released.
163163

164-
The ``Barrier`` has no properties or methods for user access. Participant
165-
coros issue ``await my_barrier`` whereupon execution pauses until all other
164+
The `Barrier` has no properties or methods for user access. Participant
165+
coros issue `await my_barrier` whereupon execution pauses until all other
166166
participants are also waiting on it. At this point any callback will run and
167-
then each participant will re-commence execution. See ``barrier_test`` and
168-
``semaphore_test`` in asyntest.py for example usage.
167+
then each participant will re-commence execution. See `barrier_test` and
168+
`semaphore_test` in asyntest.py for example usage.
169169

170170
A special case of `Barrier` usage is where some coros are allowed to pass the
171171
barrier, registering the fact that they have done so. At least one coro must
@@ -204,14 +204,14 @@ used to limit the number of instances of a particular coro which can run
204204
concurrently. It performs this using an access counter which is initialised by
205205
the constructor and decremented each time a coro acquires the semaphore.
206206

207-
Constructor: Optional arg ``value`` default 1. Number of permitted concurrent
207+
Constructor: Optional arg `value` default 1. Number of permitted concurrent
208208
accesses.
209209

210210
Synchronous method:
211-
* ``release`` No args. Increments the access counter.
211+
* `release` No args. Increments the access counter.
212212

213213
Asynchronous method:
214-
* ``acquire`` No args. If the access counter is greater than 0, decrements it
214+
* `acquire` No args. If the access counter is greater than 0, decrements it
215215
and terminates. Otherwise waits for it to become greater than 0 before
216216
decrementing it and terminating.
217217

@@ -223,25 +223,27 @@ async def foo(sema):
223223
# Limited access here
224224
```
225225

226-
There is a difference between a ``Semaphore`` and a ``Lock``. A ``Lock``
226+
There is a difference between a `Semaphore` and a `Lock`. A `Lock`
227227
instance is owned by the coro which locked it: only that coro can release it. A
228-
``Semaphore`` can be released by any coro which acquired it.
228+
`Semaphore` can be released by any coro which acquired it.
229229

230230
### 3.5.1 BoundedSemaphore
231231

232-
This works identically to the ``Semaphore`` class except that if the ``release``
233-
method causes the access counter to exceed its initial value, a ``ValueError``
232+
This works identically to the `Semaphore` class except that if the `release`
233+
method causes the access counter to exceed its initial value, a `ValueError`
234234
is raised.
235235

236236
## 3.6 NamedCoro
237237

238238
This provides for coros to be readily identified by associating them with a
239239
user-defined name, to enable them to be cancelled or to have an exception
240-
thrown to them. The ``NamedCoro`` class maintains a dict of coros indexed by
240+
thrown to them. The `NamedCoro` class maintains a dict of coros indexed by
241241
the name.
242242

243+
The `NamedCoro` class is an awaitable class.
244+
243245
Constructor mandatory args:
244-
* ``task`` A coro.
246+
* `task` A coro.
245247
* `name` Names may be any valid dictionary index. A `ValueError` will be
246248
raised if the name already exists.
247249

@@ -251,6 +253,7 @@ Class methods:
251253
scheduled. The coro should trap this and quit ASAP. `cancel` will return
252254
`True` if the coro was cancelled or if it had already terminated. It will
253255
return `False` if the coro is not in the dict or has already been killed.
256+
* `cancel_all` No args. Cancel all running `NamedCoro` instances.
254257
* `pend_throw` Args: 1. A coro name 2. An exception.
255258
The named coro will receive the exception the next time it is scheduled.
256259

@@ -266,15 +269,15 @@ existing applications but it will soon be removed.
266269

267270
# 4 asyntest.py
268271

269-
This provides the following test/demo programs. Because ``uasyncio`` retains
272+
This provides the following test/demo programs. Because `uasyncio` retains
270273
state between runs, a soft reset (ctrl-D) should be issued after running a test
271274
and before running another.
272275

273-
* ``ack_test()`` Use of ``Event`` objects. Runs for 10s.
274-
* ``event_test()`` Use of ``Lock`` and ``Event`` objects.
275-
* ``barrier_test()`` Use of the ``Barrier`` class.
276-
* ``semaphore_test()`` Use of ``Semaphore`` objects. Call with a ``True`` arg
277-
to demonstrate the ``BoundedSemaphore`` error exception.
278-
* ``cancel_test1()`` Basic task cancellation.
279-
* ``cancel_test2()`` Task cacellation with a ``Barrier``.
280-
* ``cancel_test3()`` Cancellation of a task which has terminated.
276+
* `ack_test()` Use of `Event` objects. Runs for 10s.
277+
* `event_test()` Use of `Lock` and `Event` objects.
278+
* `barrier_test()` Use of the `Barrier` class.
279+
* `semaphore_test()` Use of `Semaphore` objects. Call with a `True` arg
280+
to demonstrate the `BoundedSemaphore` error exception.
281+
* `cancel_test1()` Basic task cancellation.
282+
* `cancel_test2()` Task cacellation with a `Barrier`.
283+
* `cancel_test3()` Cancellation of a task which has terminated.

asyn.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@ def pend_throw(cls, taskname, ClsException):
255255
return True
256256
return False
257257

258+
@classmethod
259+
def cancel_all(cls):
260+
for task in cls.tasks:
261+
cls.cancel(task)
262+
258263
def __init__(self, task, name):
259264
if name in self.tasks:
260265
raise ValueError('Task name "{}" already exists.'.format(name))

0 commit comments

Comments
 (0)