kopia lustrzana https://github.com/peterhinch/micropython-samples
uasyncio: implement Primitive class.
rodzic
7b021ad645
commit
6853171823
|
@ -1,13 +1,15 @@
|
|||
# Changes to usayncio
|
||||
|
||||
This archive contains suggestions for changes to new `uasyncio`:
|
||||
This archive contains suggestions for changes to new `uasyncio`. Item 3 below
|
||||
added 2 Dec.
|
||||
|
||||
1. Implement as a Python package.
|
||||
2. Implement synchronisation primitives as package modules to conserve RAM.
|
||||
3. Add `.priority` method to `Stream` class. Enables I/O to be handled at high
|
||||
3. `Primitive` class has methods common to most synchronisation primitives.
|
||||
4. Add `.priority` method to `Stream` class. Enables I/O to be handled at high
|
||||
priority on a per-device basis.
|
||||
4. Rename task queue class `TQueue` to avoid name clash with Queue primitive.
|
||||
5. Rename task queue instance to `tqueue` as it is used by primitives.
|
||||
5. Rename task queue class `TQueue` to avoid name clash with Queue primitive.
|
||||
6. Rename task queue instance to `tqueue` as it might be used by primitives.
|
||||
|
||||
## Minor changes
|
||||
|
||||
|
|
|
@ -8,6 +8,26 @@ import sys, select
|
|||
|
||||
type_genf = type((lambda: (yield))) # Type of a generator function upy iss #3241
|
||||
|
||||
################################################################################
|
||||
# Primitive class embodies methods common to most synchronisation primitives
|
||||
|
||||
class Primitive:
|
||||
def __init__(self):
|
||||
self.waiting = TQueue() # Linked list of Tasks waiting on completion
|
||||
def run_next(self):
|
||||
awt = self.waiting.next
|
||||
if awt: # Schedule next task waiting on primitive
|
||||
tqueue.push_head(self.waiting.pop_head())
|
||||
return awt
|
||||
def run_all(self):
|
||||
while self.waiting.next: # Schedule all tasks waiting on primitive
|
||||
tqueue.push_head(self.waiting.pop_head())
|
||||
def save_current(self): # Postpone currently running task
|
||||
self.waiting.push_head(cur_task)
|
||||
# Set calling task's data to this event that it waits on, to double-link it
|
||||
cur_task.data = self
|
||||
|
||||
|
||||
################################################################################
|
||||
# Task Queue class renamed to avoid conflict with Queue class
|
||||
|
||||
|
@ -462,4 +482,4 @@ class Loop:
|
|||
def get_event_loop(runq_len=0, waitq_len=0):
|
||||
return Loop()
|
||||
|
||||
version = (3, 0, 0)
|
||||
version = (3, 0, 1)
|
||||
|
|
|
@ -25,12 +25,12 @@ def launch(func, tup_args):
|
|||
uasyncio.create_task(res)
|
||||
|
||||
|
||||
class Barrier():
|
||||
class Barrier(uasyncio.Primitive):
|
||||
def __init__(self, participants, func=None, args=()):
|
||||
super().__init__()
|
||||
self._participants = participants
|
||||
self._func = func
|
||||
self._args = args
|
||||
self.waiting = uasyncio.TQueue() # Linked list of Tasks waiting on completion of barrier
|
||||
self._reset(True)
|
||||
|
||||
def trigger(self):
|
||||
|
@ -39,8 +39,7 @@ class Barrier():
|
|||
if self._func is not None:
|
||||
launch(self._func, self._args)
|
||||
self._reset(not self._down) # Toggle direction and release others
|
||||
while self.waiting.next:
|
||||
uasyncio.tqueue.push_head(self.waiting.pop_head())
|
||||
self.run_all()
|
||||
|
||||
def __iter__(self): # MicroPython
|
||||
self._update()
|
||||
|
@ -48,14 +47,11 @@ class Barrier():
|
|||
if self._func is not None:
|
||||
launch(self._func, self._args)
|
||||
self._reset(not self._down) # Toggle direction and release others
|
||||
while self.waiting.next:
|
||||
uasyncio.tqueue.push_head(self.waiting.pop_head())
|
||||
self.run_all()
|
||||
return
|
||||
direction = self._down
|
||||
# Other tasks have not reached barrier, put the calling task on the barrier's waiting queue
|
||||
self.waiting.push_head(uasyncio.cur_task)
|
||||
# Set calling task's data to this barrier that it waits on, to double-link it
|
||||
uasyncio.cur_task.data = self
|
||||
self.save_current()
|
||||
yield
|
||||
|
||||
def _reset(self, down):
|
||||
|
|
|
@ -2,7 +2,7 @@ import uasyncio
|
|||
import uasyncio.lock
|
||||
import uasyncio.event
|
||||
|
||||
class Condition():
|
||||
class Condition:
|
||||
def __init__(self, lock=None):
|
||||
self.lock = uasyncio.Lock() if lock is None else lock
|
||||
self.events = []
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
import uasyncio
|
||||
|
||||
# Event class for primitive events that can be waited on, set, and cleared
|
||||
class Event:
|
||||
class Event(uasyncio.Primitive):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.state = 0 # 0=unset; 1=set
|
||||
self.waiting = uasyncio.TQueue() # Queue of Tasks waiting on completion of this event
|
||||
def set(self):
|
||||
# Event becomes set, schedule any tasks waiting on it
|
||||
while self.waiting.next:
|
||||
uasyncio.tqueue.push_head(self.waiting.pop_head())
|
||||
def set(self): # Event becomes set, schedule any tasks waiting on it
|
||||
self.run_all()
|
||||
self.state = 1
|
||||
def clear(self):
|
||||
self.state = 0
|
||||
|
@ -17,9 +15,7 @@ class Event:
|
|||
async def wait(self):
|
||||
if self.state == 0:
|
||||
# Event not set, put the calling task on the event's waiting queue
|
||||
self.waiting.push_head(uasyncio.cur_task)
|
||||
# Set calling task's data to this event that it waits on, to double-link it
|
||||
uasyncio.cur_task.data = self
|
||||
self.save_current()
|
||||
yield
|
||||
return True
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@ import uasyncio
|
|||
import uasyncio
|
||||
|
||||
|
||||
class Lock:
|
||||
class Lock(uasyncio.Primitive):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._locked = False
|
||||
self.waiting = uasyncio.TQueue() # Linked list of Tasks waiting on completion of this event
|
||||
self._awt = None # task that is going to acquire the lock. Needed to prevent race
|
||||
# condition between pushing the next waiting task and the task actually acquiring
|
||||
# the lock because during that time another newly started task could acquire the
|
||||
|
@ -21,8 +21,7 @@ class Lock:
|
|||
if self._locked or self._awt:
|
||||
# Lock set or just released but has tasks waiting on it,
|
||||
# put the calling task on the Lock's waiting queue and yield
|
||||
self.waiting.push_head(uasyncio.cur_task)
|
||||
uasyncio.cur_task.data = self
|
||||
self.save_current()
|
||||
try:
|
||||
yield
|
||||
except uasyncio.CancelledError:
|
||||
|
@ -46,10 +45,8 @@ class Lock:
|
|||
if not self._locked:
|
||||
raise RuntimeError("Lock is not acquired.")
|
||||
self._locked = False
|
||||
self._awt = self.waiting.next # Task which will get lock
|
||||
if self.waiting.next:
|
||||
# Lock becomes available, schedule next task waiting on it
|
||||
uasyncio.tqueue.push_head(self.waiting.pop_head())
|
||||
# Lock becomes available. If task(s) are waiting on it save task which will
|
||||
self.awt = self.run_next() # get lock and schedule that task
|
||||
|
||||
async def __aexit__(self, *args):
|
||||
return self.release()
|
||||
|
|
|
@ -23,12 +23,12 @@ class QueueFull(Exception):
|
|||
# interrupted between calling qsize() and doing an operation on the Queue.
|
||||
|
||||
|
||||
class Queue:
|
||||
class Queue(uasyncio.Primitive):
|
||||
|
||||
def __init__(self, maxsize=0):
|
||||
super().__init__()
|
||||
self.maxsize = maxsize
|
||||
self._queue = deque((), maxsize)
|
||||
self.waiting = uasyncio.TQueue() # Linked list of Tasks waiting on queue
|
||||
|
||||
def _get(self):
|
||||
return self._queue.popleft()
|
||||
|
@ -36,14 +36,9 @@ class Queue:
|
|||
async def get(self): # Usage: item = await queue.get()
|
||||
if not self._queue:
|
||||
# Queue is empty, put the calling Task on the waiting queue
|
||||
task = uasyncio.cur_task
|
||||
self.waiting.push_head(task)
|
||||
# Set calling task's data to double-link it
|
||||
task.data = self
|
||||
self.save_current()
|
||||
yield
|
||||
if self.waiting.next:
|
||||
# Task(s) waiting to put on queue, schedule first Task
|
||||
uasyncio.tqueue.push_head(self.waiting.pop_head())
|
||||
self.run_next() # Task(s) waiting to put on queue, schedule first Task
|
||||
return self._get()
|
||||
|
||||
def get_nowait(self): # Remove and return an item from the queue.
|
||||
|
@ -58,14 +53,9 @@ class Queue:
|
|||
async def put(self, val): # Usage: await queue.put(item)
|
||||
if self.qsize() >= self.maxsize and self.maxsize:
|
||||
# Queue full, put the calling Task on the waiting queue
|
||||
task = uasyncio.cur_task
|
||||
self.waiting.push_head(task)
|
||||
# Set calling task's data to double-link it
|
||||
uasyncio.cur_task.data = self
|
||||
self.save_current()
|
||||
yield
|
||||
if self.waiting.next:
|
||||
# Task(s) waiting to get from queue, schedule first Task
|
||||
uasyncio.tqueue.push_head(self.waiting.pop_head())
|
||||
self.run_next() # Task(s) waiting to get from queue, schedule first Task
|
||||
self._put(val)
|
||||
|
||||
def put_nowait(self, val): # Put an item into the queue without blocking.
|
||||
|
|
|
@ -3,10 +3,10 @@
|
|||
import uasyncio
|
||||
|
||||
|
||||
class Semaphore():
|
||||
class Semaphore((uasyncio.Primitive)):
|
||||
def __init__(self, value=1):
|
||||
super().__init__()
|
||||
self._count = value
|
||||
self.waiting = uasyncio.TQueue() # Linked list of Tasks waiting on completion of this event
|
||||
|
||||
async def __aenter__(self):
|
||||
await self.acquire()
|
||||
|
@ -18,17 +18,13 @@ class Semaphore():
|
|||
async def acquire(self):
|
||||
if self._count == 0:
|
||||
# Semaphore unavailable, put the calling Task on the waiting queue
|
||||
self.waiting.push_head(uasyncio.cur_task)
|
||||
# Set calling task's data to double-link it
|
||||
uasyncio.cur_task.data = self
|
||||
self.save_current()
|
||||
yield
|
||||
self._count -= 1
|
||||
|
||||
def release(self):
|
||||
self._count += 1
|
||||
if self.waiting.next:
|
||||
# Task(s) waiting on semaphore, schedule first Task
|
||||
uasyncio.tqueue.push_head(self.waiting.pop_head())
|
||||
self.run_next() # Task(s) waiting on semaphore, schedule first Task
|
||||
|
||||
class BoundedSemaphore(Semaphore):
|
||||
def __init__(self, value=1):
|
||||
|
|
Ładowanie…
Reference in New Issue