python windows service only runs when started as -debug

114 views Asked by At

I've got a Windows Service that runs hypercorn. Everything worked fine in python 3.7. Now we want to move our service to python 3.11, but when starting the service I get the error:

Traceback (most recent call last):
  File "...\service.py", line 108, in main
    run_hypercorn(app, self.host, self.port, self.shutdown_trigger)
  File "...\run.py", line 36, in run_hypercorn
    asyncio.run(serve(dispatcher, hypercorn_cfg, shutdown_trigger=shutdown_trigger.wait))
  File "asyncio\runners.py", line 189, in run
  File "asyncio\runners.py", line 59, in __enter__
  File "asyncio\runners.py", line 137, in _lazy_init
  File "asyncio\events.py", line 806, in new_event_loop
  File "asyncio\events.py", line 695, in new_event_loop
  File "asyncio\windows_events.py", line 315, in __init__
  File "asyncio\proactor_events.py", line 642, in __init__
ValueError: set_wakeup_fd only works in main thread of the main interpreter

However, this only happens when starting the service via sc start service_name, it does not happen, when I start it via pytonservice.exe -debug service_name. In the latter case, the service runs without issues.

The failing code looks like this:

import asyncio
from hypercorn.config import Config as HypercornConfig
from hypercorn.asyncio import serve
from hypercorn.middleware import DispatcherMiddleware

def run_hypercorn(app, host, port, shutdown_trigger=None):
    dav = dav_app(...)

    dispatcher = DispatcherMiddleware(
        {
            '/dav': dav,
            '': app,
        }
    )

    hypercorn_cfg = HypercornConfig()
    hypercorn_cfg.bind = f'{host}:{port}'
    if shutdown_trigger is None:
        asyncio.run(serve(dispatcher, hypercorn_cfg))
    else:
        loop = asyncio.get_event_loop()  # fails here
        loop.run_until_complete(serve(dispatcher, hypercorn_cfg, shutdown_trigger=shutdown_trigger.wait))

I've read that there have been some changes to asyncio, including get_event_loop(), that caused some bugs, but allegedly those bugs have been fixed in 3.9.

However. I also tried running it with

asyncio.run(serve(dispatcher, hypercorn_cfg, shutdown_trigger=shutdown_trigger.wait))

but, I get the same error. Same issue when I omit the shutdown_trigger entirely.

Service Class is nothing special:

class AppServerSvc(win32serviceutil.ServiceFramework):
    _svc_name_ = ""
    _svc_display_name_ = ""

    def __init__(self, args):
        self.shutdown_trigger = asyncio.Event()
        self.app: FastAPI = None
        self.config: Config = None
        self.port: int = None
        self.host: str = None

    def SvcDoRun(self, *args, **kwargs):
        self.ReportServiceStatus(win32service.SERVICE_START_PENDING)
        self.main()

    def main():
        from ___.app import app
        from ___.run import run_hypercorn, run_uvicorn

        self.config = Config.new('config.ini', installation_folder=self.path)
        self.app = app

        self.port = self.config.webserver.get('port', None)
        self.host = self.config.webserver.get('host', None)

        self.app.state.config = self.config

        self.ReportServiceStatus(win32service.SERVICE_RUNNING)
    
        run_hypercorn(app, self.host, self.port, self.shutdown_trigger)

if __name__ == '__main__':
    win32serviceutil.HandleCommandLine(AppServerSvc)

I don't understand how running the service regularly puts the code in a different thread and I don't know what to do about that (or how to start hypercorn differently in that case).

Edit:

I also checked for the thread I am in: In both cases there are 5 threads, and the current thread is MainThread.

Could this point to a bug still existing in python?

1

There are 1 answers

0
Tekay37 On

Nevermind. I found the solution. For some reason, the default WindowsProactorEventLoopPolicy doesn't like to be run as a service.

Adding

asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

solves the problem. If someone can explain to me, why that is the case, it'll be super nice.

Have a great day, dear StackOverflowers.