Skip to content

uasyncio: make uasyncio.Event() safe to call from an interrupt v2 (RFC, WIP) #6106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conversation

dpgeorge
Copy link
Member

@dpgeorge dpgeorge commented Jun 2, 2020

This is a slight variation on #6056, making uasyncio.Event().set() safe to call from a soft-scheduled callback (with micropython.schedule()).

This version does not introduce socket.socketpair() for the signalling, but rather hides this in the poll object as a more general "notifier" object. So you can now do this (independent to asyncio):

import uselect
poll = uselect.poll()

# pass None to register a special notifier entity; returns the notifier
# on unix the notifier is a socketpair, but it could be anything on other ports
notifier = poll.register(None, uselect.POLLIN)

def callback():
    # set the notifier, will be irq/signal/thread safe
    # this will wake any poll.poll() sleeps
    notifier.set()

# sleep until an event
for ev, s in poll.poll():
    if s is notifier:
        # got woken by the notifier, clear it
        s.clear()

This abstracts out a lot of the detail of poll and wake-up behaviour and should allow a bare-metal port more flexibility in implementing the notifier (eg with a semaphore).

Apart from the addition of micropython.scheduler_lock()/micropython.scheduler_unlock(), the only public facing API behaviour that is added here is the ability to call Event.set() from a scheduled callback. This would hopefully allow room for the underlying poll implementation to be completely changed (eg made more efficient) without the user noticing.

@dpgeorge dpgeorge added the extmod Relates to extmod/ directory in source label Jun 2, 2020
@tve
Copy link
Contributor

tve commented Jun 4, 2020

This looks nicer than V1!

Can you go further? In the stm32 port there's all that notifier stream object code when all that's really needed is a boolean flag... Why can't poll_poll_internal just check the flag in https://github.com/micropython/micropython/blob/master/extmod/moduselect.c#L250-L256 and return empty sets or EINTR if the flag is set?

For the unix port, instead of the socketpair, have you looked at ppoll (or pselect), it seems to be designed exactly to solve the race condition you had with simply setting a flag in the signal handler. Caveat: I have not looked at the detail of how one has to use signal masks nor at how this works in a multithreaded context.

I continue to find it awful that the WFI/sleep is buried in that MICROPY_EVENT_POLL_HOOK macro called in the bowels of poll, maybe that's unavoidable at the moment.

@dpgeorge
Copy link
Member Author

dpgeorge commented Jun 4, 2020

Can you go further? In the stm32 port there's all that notifier stream object code when all that's really needed is a boolean flag...

Yeah, I realised that after I posted this PR. Would be even simpler to not even have a notifier object at all and instead have poll.notify() which sets the flag (and it gets cleared automatically after the poll.poll() call). It's just that this select.poll() class starts to diverge more and more from CPython...

For the unix port, instead of the socketpair, have you looked at ppoll (or pselect), it seems to be designed exactly to solve the race condition you had with simply setting a flag in the signal handler.

Yes, good idea, that may work! Again though, this makes poll diverge even more from CPython.

I'm actually working on a more drastic change that makes everything event/callback based and O(1) on bare-metal (eg lwip sockets call back into the polling loop to indicate they are readable/writable). And as part of this I'm making a separate uevent module which is a MicroPython-specific version of select. So the above changes can go in it.

@dpgeorge
Copy link
Member Author

ThreadSafeFlag was added in 5e96e89 as a (temporary) way to connect interrupts with uasyncio. And ThreadSafeFlag will eventually be optimised/improved with a more sophisticated way of waiting on events, see #6125 for ideas on that front.

This PR is made obsolete by that.

@dpgeorge dpgeorge closed this Nov 17, 2021
@dpgeorge dpgeorge deleted the extmod-uasyncio-threadsafe-event-v2 branch November 17, 2021 03:24
tannewt pushed a commit to tannewt/circuitpython that referenced this pull request Mar 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
extmod Relates to extmod/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants