Search code examples
pythonpython-asynciocoroutineyieldcocotb

Python3.6 cocotb coroutine: calling asynchronous function containing ”yield“ in synchronous function


I encountered some problems while trying to make a synchronous function that calls asynchronous functions.(python 3.6.9, cocotb 1.4.0)

As shown in the following example code. The read_cb function will call read function(in FakeDriver class).

After running, I get the error

yield self._fake_lock()
RuntimeError: Task got bad yield: <cocotb.decorators.RunningCoroutine object at 0x7f7fecdbfe10>

What I want is

init FakerDriver
locking... 
locking done
read...
addr: 0x01 
unlocking... 
unlocking done
read done
import cocotb
import asyncio
from cocotb.decorators import coroutine
from cocotb.triggers import Event

class FakeDriver():
    def __init__(self):
        print("init FakeDriver")
        self.busy_event = Event("driver_busy")
        self.busy = False

    @coroutine
    def read(self, addr):
        print("read...")

        yield self._fake_lock()
        print("addr: ", addr)
        self._fake_unlock()

        print("read done")

    @coroutine
    def _fake_lock(self):
        print("locking...")
        if self.busy:
            yield self.busy_event.wait()

        self.busy_event.clear()
        self.busy = True
        print("locking done")

    def _fake_unlock(self):
        print("unlocking...")
        self.busy = False
        self.busy_event.set()
        print("unlocking done")

def read_cb():
    dri = FakeDriver()
    loop = asyncio.get_event_loop()
    task = loop.create_task(dri.read("0x01"))
    ret = loop.run_until_complete(task)
    loop.close()

if __name__ == "__main__":
    read_cb()


Solution

  • Do not mix up cocotb's own implementation of coroutines with asyncio's. In your case, get rid of the asyncio import completely and, with your oldish cocotb 1.4.0, use cocotb's fork() instead of create_task() (as described in https://docs.cocotb.org/en/v1.4.0/quickstart.html?highlight=fork#parallel-and-sequential-execution).

    Before you create lots of new code using the deprecated yield etc. in cocotb, consider upgrading cocotb to 1.6.1 and use async def/await (again from cocotb, not asyncio), and instead of fork(), use start_soon() (see https://docs.cocotb.org/en/v1.6.1/coroutines.html#concurrent-execution and https://www.fossi-foundation.org/2021/10/20/cocotb-1-6-0).

    (I now see you also asked this in https://github.com/cocotb/cocotb/issues/2819)