mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-07 08:26:53 +08:00
fix(runtime): enforce main event loop ownership (#6424)
This commit is contained in:
+2
-2
@@ -143,8 +143,8 @@ def _pooled_loop() -> Optional[Any]:
|
||||
|
||||
只认常驻主循环:它承载了绝大多数异步 DB 流量,且生命周期与进程一致,
|
||||
池中连接不会因循环销毁而失效。
|
||||
直接读 CURRENT_EVENT_LOOP 而不用 global_vars.loop——后者在未设置时会
|
||||
新建一个事件循环,仅为判断就产生副作用是不可接受的。
|
||||
直接读 CURRENT_EVENT_LOOP 而不用 global_vars.loop,避免主循环尚未就绪时
|
||||
把正常的回退引擎选择转换成生命周期异常。
|
||||
"""
|
||||
if not _async_pool_enabled():
|
||||
return None
|
||||
|
||||
+14
-23
@@ -1,4 +1,3 @@
|
||||
import asyncio
|
||||
import copy
|
||||
import json
|
||||
import os
|
||||
@@ -1359,18 +1358,8 @@ class GlobalVar(object):
|
||||
EMERGENCY_STOP_WORKFLOWS: List[int] = []
|
||||
# 需应急停止文件整理
|
||||
EMERGENCY_STOP_TRANSFER: List[str] = []
|
||||
# 当前事件循环
|
||||
CURRENT_EVENT_LOOP: AbstractEventLoop = None
|
||||
|
||||
@classmethod
|
||||
def _get_event_loop(cls) -> AbstractEventLoop:
|
||||
"""返回当前线程事件循环,缺失时创建并绑定新循环。"""
|
||||
try:
|
||||
return asyncio.get_event_loop()
|
||||
except RuntimeError:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
return loop
|
||||
# 生命周期登记的主事件循环
|
||||
CURRENT_EVENT_LOOP: Optional[AbstractEventLoop] = None
|
||||
|
||||
def stop_system(self):
|
||||
"""
|
||||
@@ -1461,19 +1450,21 @@ class GlobalVar(object):
|
||||
|
||||
@property
|
||||
def loop(self) -> AbstractEventLoop:
|
||||
"""
|
||||
当前循环
|
||||
"""
|
||||
if self.CURRENT_EVENT_LOOP is None:
|
||||
self.CURRENT_EVENT_LOOP = self._get_event_loop()
|
||||
return self.CURRENT_EVENT_LOOP
|
||||
"""返回由应用生命周期登记的主事件循环。"""
|
||||
loop = self.CURRENT_EVENT_LOOP
|
||||
if loop is None or not loop.is_running() or loop.is_closed():
|
||||
raise RuntimeError("主事件循环尚未启动或已经停止")
|
||||
return loop
|
||||
|
||||
def set_loop(self, loop: AbstractEventLoop):
|
||||
"""
|
||||
设置循环
|
||||
"""
|
||||
def set_loop(self, loop: AbstractEventLoop) -> None:
|
||||
"""登记承载主程序异步任务的事件循环。"""
|
||||
self.CURRENT_EVENT_LOOP = loop
|
||||
|
||||
def clear_loop(self, loop: AbstractEventLoop) -> None:
|
||||
"""仅在登记值仍为目标循环时清除主事件循环。"""
|
||||
if self.CURRENT_EVENT_LOOP is loop:
|
||||
self.CURRENT_EVENT_LOOP = None
|
||||
|
||||
|
||||
# 全局标识
|
||||
global_vars = GlobalVar()
|
||||
|
||||
@@ -368,14 +368,14 @@ async def lifespan(app: FastAPI):
|
||||
"""
|
||||
health = get_application_health(app)
|
||||
health.begin_startup()
|
||||
main_loop = asyncio.get_running_loop()
|
||||
try:
|
||||
validate_process_topology(
|
||||
workers=settings.API_WORKERS,
|
||||
safe_mode=settings.MOVIEPILOT_SAFE_MODE,
|
||||
)
|
||||
print("Starting up...")
|
||||
# 存储当前循环
|
||||
global_vars.set_loop(asyncio.get_event_loop())
|
||||
global_vars.set_loop(main_loop)
|
||||
components = build_lifecycle_components(app)
|
||||
enabled_components = tuple(
|
||||
component
|
||||
@@ -411,6 +411,8 @@ async def lifespan(app: FastAPI):
|
||||
await stop_task_registry(app)
|
||||
except Exception as cleanup_error:
|
||||
logger.error(f"启动失败后的后台任务清理失败:{cleanup_error}")
|
||||
finally:
|
||||
global_vars.clear_loop(main_loop)
|
||||
raise
|
||||
try:
|
||||
# 在此处 yield,表示应用已经启动,控制权交回 FastAPI 主事件循环
|
||||
@@ -435,5 +437,8 @@ async def lifespan(app: FastAPI):
|
||||
component.stop_timeout_seconds,
|
||||
)
|
||||
finally:
|
||||
# 日志最后关闭,确保其他组件的收尾信息已写入文件
|
||||
LoggerManager.shutdown()
|
||||
try:
|
||||
# 日志最后关闭,确保其他组件的收尾信息已写入文件
|
||||
LoggerManager.shutdown()
|
||||
finally:
|
||||
global_vars.clear_loop(main_loop)
|
||||
|
||||
Reference in New Issue
Block a user