0

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.

Break
  • 105
  • 1
  • 9
  • 1
    It would help if you described what concrete issues you are having with the code as written. Also, why call `sys.exit()` in one place and `os._exit()` in another? Finally, if you're having issues with running trio in a non-main thread, have you tried switching them around, and run trio in the main thread and asyncio in a background thread? You don't need to (and shouldn't) use `run_in_executor` to run something in a background thread, you can just spawn a `threading.Thread()` and start it with the `start()` method. – user4815162342 Dec 03 '20 at 12:03
  • I guess my problem is that prior to including trio, I used to be able to end the program with `b.loop.close` but that with trio running, it could never execute and raised a RuntimeError which traps 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. I actually haven't tried reversing them and running asyncio in the thread instead but I will try that, along with threading.Thread(). Thanks – Break Dec 03 '20 at 12:18
  • Could you please explain more about the downsides of using `run_in_executor` in this way? – Break Dec 03 '20 at 12:19
  • 1
    Please edit the question to include the additional information from your comment. I don't know why you even care about `loop.close()`, as the program is exiting anyway. The point of `loop.close()` is releasing resources internally acquired by the event loop, but that only makes sense for a program that runs a temporary event loop and wants to dispose of it and keep running. In a program where the event loop runs throughout the program, you shouldn't invest effort into `loop.close()`, especially when the program is interrupted with Ctrl-C or equvalent. – user4815162342 Dec 03 '20 at 12:32
  • 1
    *Could you please explain more about the downsides of using run_in_executor in this way?* - Since the job you've given it to run will never exit (it's another event loop), it just ties a slot in the executor. The point of `run_in_executor` is not to start a thread, it is to off-load a synchronous function to a thread pool and return an awaitable handle. When `run_in_executor` is used properly, you're supposed to _await_ the returned handle, and use the result returned (or exception raised) by the function. Since you do none of those things, you're better off just starting a thread yourself. – user4815162342 Dec 03 '20 at 12:35
  • Oh I see. I guess I don't have a reason for caring about `loop.close()` other than I had always been using it before this lol. So I guess this seems like a non-issue and the program is probably fine by closing with `sys.exit()`/`os._exit()`? The only reason I had os._exit() in that spot was that I just wanted to dodge any exception and just end the program and it seemed like `os._exit()` was the "safest" way to ensure my program would actually exit. But yeah thanks again, this was good info. – Break Dec 03 '20 at 12:43
  • 2
    It's entirely clear to me what the issue really, and more importantly trying to achieve. Is having two loops essential to your software, or are you simply trying to make trio and asyncio code interoperate? because in that case there is https://github.com/python-trio/trio-asyncio which allows you to use asyncio code (with some caveats) within a trio loop – Nico Dec 03 '20 at 12:44
  • The latter, basically just for fun/experimentation. Thanks, I will look into that – Break Dec 03 '20 at 12:46

0 Answers0