1# MicroPython uasyncio module
2# MIT license; Copyright (c) 2019-2020 Damien P. George
3
4from . import core
5
6# Event class for primitive events that can be waited on, set, and cleared
7class Event:
8    def __init__(self):
9        self.state = False  # False=unset; True=set
10        self.waiting = core.TaskQueue()  # Queue of Tasks waiting on completion of this event
11
12    def is_set(self):
13        return self.state
14
15    def set(self):
16        # Event becomes set, schedule any tasks waiting on it
17        # Note: This must not be called from anything except the thread running
18        # the asyncio loop (i.e. neither hard or soft IRQ, or a different thread).
19        while self.waiting.peek():
20            core._task_queue.push_head(self.waiting.pop_head())
21        self.state = True
22
23    def clear(self):
24        self.state = False
25
26    async def wait(self):
27        if not self.state:
28            # Event not set, put the calling task on the event's waiting queue
29            self.waiting.push_head(core.cur_task)
30            # Set calling task's data to the event's queue so it can be removed if needed
31            core.cur_task.data = self.waiting
32            yield
33        return True
34
35
36# MicroPython-extension: This can be set from outside the asyncio event loop,
37# such as other threads, IRQs or scheduler context. Implementation is a stream
38# that asyncio will poll until a flag is set.
39# Note: Unlike Event, this is self-clearing.
40try:
41    import uio
42
43    class ThreadSafeFlag(uio.IOBase):
44        def __init__(self):
45            self._flag = 0
46
47        def ioctl(self, req, flags):
48            if req == 3:  # MP_STREAM_POLL
49                return self._flag * flags
50            return None
51
52        def set(self):
53            self._flag = 1
54
55        async def wait(self):
56            if not self._flag:
57                yield core._io_queue.queue_read(self)
58            self._flag = 0
59
60
61except ImportError:
62    pass
63