Trio with Asyncio and starting/closing loops properly

648 views Asked by At

So I am starting my program like this:

    try: b.loop.call_soon_threadsafe(b.loop.run_in_executor, None, lambda: trio.run(wallpaper))
    except BaseException as e:
        sys.exit(e)
    try:
        b.loop.run_until_complete(background_task())
    except (KeyboardInterrupt,SystemExit,RuntimeError):
        try: b.loop.close()
        except BaseException: os._exit(0) # I have to do this or else the program will never close as asyncio will raise a RuntimeError indicating the loop is already closed. The `try` block might as well not exist cause it never works anyway.

The trio coroutine function starts like this:

sp = Sleeper() 

async def wallpaper() -> typing.NoReturn:

    with trio.CancelScope() as scope:
        async with trio.open_nursery() as nursery:
            nursery.start_soon(sp.sleep_handler)
            await nursery.start(sp.watchdog)
            await sp.nursery_closed()
            scope.cancel()

and here is that class:

class Sleeper:

    _end = trio.Event()

    def __init__(this, *args, **kwds):
        # some variables

    @classmethod
    def set_end(cls):
        cls._end.set()

    @classmethod
    async def nursery_closed(cls):

        await cls._end.wait()

    @classmethod
    def scope_canceled(cls):

        return cls._end.is_set()

    async def watchdog(this, task_status=trio.TASK_STATUS_IGNORED):
        task_status.started()
        while True:
            await trio.sleep(5)
            if b.is_closed(): # `b` is a class instance running in asyncio.
                this.set_end()
                break
        """# I can't use this outside of the main thread unfortunately, so I am trying the above instead.
        with trio.open_signal_receiver(signal.SIGINT) as watcher:
            task_status.started()
            async for signum in watcher:
                assert signum == signal.SIGINT
                this.set_end()
        """
    async def sleep_handler(this) -> typing.NoReturn:
        if not this.scope_ready():
            await this.nursery_spawned() # I didn't include it here but this is the same as below, but to indicate when it's ready rather than closed.
        while not this.scope_canceled():
            pass # stuff that is always running and never returning

I used to be able to end the program with b.loop.close but with trio running, it could never execute and raised a RuntimeError which trapped my program into some infinite loop, not even stopping when I close the powershell window, requiring me to end the process from the task manager. That is solved with os_exit() but it feels like I'm dodging the issue instead of correcting it. The asyncio portion of the code mostly can't be changed unless I greatly modify the library I'm using that runs with asyncio. Is there any big downside to just exiting with os._exit(0) and is there a better way to run Trio alongside Asyncio? I want to keep using Trio.

0

There are 0 answers