uasyncio Lock

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

uasyncio Lock

Post by kevinkk525 » Thu Jun 21, 2018 9:07 pm

I was trying to use an async Lock in my programs which works great, except for the following case:

Code: Select all

import uasyncio as asyncio
l = Lock()

async def test():
    async with l:
        print("hi")
        return False

>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(test())
hi
>>> print(l.locked())
True
The lock should not be locked after the function is complete.

This is my implementation (based on the Lock class in mqtt_as by @pythoncoder:

Code: Select all

class Lock():
    def __init__(self):
        self._locked = False

    async def __aenter__(self):
        while True:
            if self._locked:
                await asyncio.sleep_ms(_DEFAULT_MS)
            else:
                self._locked = True
                break

    async def __aexit__(self, *args):
        self._locked = False
        await asyncio.sleep_ms(_DEFAULT_MS)

    def locked(self):
        return self._locked
Can someone please point out what I'm missing? Why is __aexit__ not called if I use a "return" inside the "async with lock"?
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: uasyncio Lock

Post by kevinkk525 » Fri Jun 22, 2018 7:58 am

I guess the problem runs deeper than just the lock implementation as I took the cpython lock implementation and just stripped the non-micropython code and it still does not work:

Code: Select all

class _ContextManagerMixin:
    def __enter__(self):
        raise RuntimeError(
            '"yield from" should be used as context manager expression')

    def __exit__(self, *args):
        # This must exist because __enter__ exists, even though that
        # always raises; that's how the with-statement works.
        pass

    async def __acquire_ctx(self):
        await self.acquire()
        return _ContextManager(self)

    def __await__(self):
        warnings.warn("'with await lock' is deprecated "
                      "use 'async with lock' instead",
                      DeprecationWarning, stacklevel=2)
        # To make "with await lock" work.
        return self.__acquire_ctx().__await__()

    async def __aenter__(self):
        await self.acquire()
        # We have no use for the "as ..."  clause in the with
        # statement for locks.
        return None

    async def __aexit__(self, exc_type, exc, tb):
        self.release()


class Lock(_ContextManagerMixin):
    def __init__(self):
        self._locked = False

    def __repr__(self):
        extra = 'locked' if self._locked else 'unlocked'
        return extra

    def locked(self):
        """Return True if lock is acquired."""
        return self._locked

    async def acquire(self):
        while True:
            if self._locked:
                await asyncio.sleep_ms(_DEFAULT_MS)
            else:
                self._locked = True
                break
        return True

    def release(self):
        if self._locked:
            self._locked = False
        else:
            raise RuntimeError('Lock is not acquired.')
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uasyncio Lock

Post by pythoncoder » Fri Jun 22, 2018 8:55 am

Regarding your first code sample I suggest you read section 4.3 of my tutorial re the bug in MicroPython. See also this issue where I raised the problem.

This example works:

Code: Select all

import uasyncio as asyncio
from asyn import Lock

async def test():
    l = Lock()
    async with l:
        print("hi", l.locked())
    print(l.locked())

loop = asyncio.get_event_loop()
loop.run_until_complete(test())
Peter Hinch
Index to my micropython libraries.

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: uasyncio Lock

Post by kevinkk525 » Fri Jun 22, 2018 9:53 am

oh thank you. Looks like I did not search carefully enough.
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: uasyncio Lock

Post by pythoncoder » Sat Jun 23, 2018 5:17 am

It looks as if @Damien plans to fix this bug :)
Peter Hinch
Index to my micropython libraries.

kevinkk525
Posts: 969
Joined: Sat Feb 03, 2018 7:02 pm

Re: uasyncio Lock

Post by kevinkk525 » Sat Jun 23, 2018 7:39 am

That would be great. I'm following the issue.

The workaround placing lock.release() before any return is a bit ugly :D
Kevin Köck
Micropython Smarthome Firmware (with Home-Assistant integration): https://github.com/kevinkk525/pysmartnode

Post Reply