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?
Nevermind. I found the solution. For some reason, the default
WindowsProactorEventLoopPolicy
doesn't like to be run as a service.Adding
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.