kopia lustrzana https://github.com/peterhinch/micropython-samples
76 wiersze
2.5 KiB
Python
76 wiersze
2.5 KiB
Python
# barrier.py MicroPython optimised version
|
|
|
|
# A Barrier synchronises N coros. In normal use each issues await barrier.
|
|
# Execution pauses until all other participant coros are waiting on it.
|
|
# At that point the callback is executed. Then the barrier is 'opened' and
|
|
# execution of all participants resumes.
|
|
# .trigger enables a coro to signal it has passed the barrier without waiting.
|
|
|
|
try:
|
|
import asyncio
|
|
raise RuntimeError('This version of barrier is MicroPython specific')
|
|
except ImportError:
|
|
import uasyncio
|
|
|
|
async def _g():
|
|
pass
|
|
type_coro = type(_g())
|
|
|
|
# If a callback is passed, run it and return.
|
|
# If a coro is passed initiate it and return.
|
|
# coros are passed by name i.e. not using function call syntax.
|
|
def launch(func, tup_args):
|
|
res = func(*tup_args)
|
|
if isinstance(res, type_coro):
|
|
uasyncio.create_task(res)
|
|
|
|
|
|
class Barrier(uasyncio.Primitive):
|
|
def __init__(self, participants, func=None, args=()):
|
|
super().__init__()
|
|
self._participants = participants
|
|
self._func = func
|
|
self._args = args
|
|
self._reset(True)
|
|
|
|
def trigger(self):
|
|
self._update()
|
|
if self._at_limit(): # All other coros are also at limit
|
|
if self._func is not None:
|
|
launch(self._func, self._args)
|
|
self._reset(not self._down) # Toggle direction and release others
|
|
self.run_all()
|
|
|
|
def __iter__(self): # MicroPython
|
|
self._update()
|
|
if self._at_limit(): # All other coros are also at limit
|
|
if self._func is not None:
|
|
launch(self._func, self._args)
|
|
self._reset(not self._down) # Toggle direction and release others
|
|
self.run_all()
|
|
return
|
|
direction = self._down
|
|
# Other tasks have not reached barrier, put the calling task on the barrier's waiting queue
|
|
self.save_current()
|
|
yield
|
|
|
|
def _reset(self, down):
|
|
self._down = down
|
|
self._count = self._participants if down else 0
|
|
|
|
def busy(self):
|
|
if self._down:
|
|
done = self._count == self._participants
|
|
else:
|
|
done = self._count == 0
|
|
return not done
|
|
|
|
def _at_limit(self): # Has count reached up or down limit?
|
|
limit = 0 if self._down else self._participants
|
|
return self._count == limit
|
|
|
|
def _update(self):
|
|
self._count += -1 if self._down else 1
|
|
if self._count < 0 or self._count > self._participants:
|
|
raise ValueError('Too many tasks accessing Barrier')
|