mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-09-06 07:56:52 +08:00
refactor: close plugin shutdown mutation races
This commit is contained in:
@@ -30,6 +30,16 @@ PluginMutationAdmission = Callable[[str], ContextManager[None]]
|
||||
PluginPackageWriteGuard = Callable[[str], ContextManager[None]]
|
||||
|
||||
|
||||
async def _await_task_to_terminal(task: asyncio.Task[Any]) -> Any:
|
||||
"""忽略调用方的重复取消,直到受保护子任务进入真实终态。"""
|
||||
while not task.done():
|
||||
try:
|
||||
return await asyncio.shield(task)
|
||||
except asyncio.CancelledError:
|
||||
continue
|
||||
return task.result()
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class PluginInstallRollback:
|
||||
"""描述失败安装中各类可补偿副作用的恢复结果。"""
|
||||
@@ -183,7 +193,7 @@ class PluginInstallCommand:
|
||||
state.checkpoint = checkpoint
|
||||
except asyncio.CancelledError:
|
||||
try:
|
||||
state.checkpoint = await asyncio.shield(checkpoint_task)
|
||||
state.checkpoint = await _await_task_to_terminal(checkpoint_task)
|
||||
except BaseException:
|
||||
pass
|
||||
raise
|
||||
@@ -359,14 +369,8 @@ class PluginInstallCommand:
|
||||
)
|
||||
)
|
||||
try:
|
||||
result = await asyncio.shield(rollback_task)
|
||||
except asyncio.CancelledError:
|
||||
try:
|
||||
result = await asyncio.shield(rollback_task)
|
||||
except BaseException as err:
|
||||
logger.error(f"插件 {plugin_id} 取消后的补偿未完成:{err}")
|
||||
return
|
||||
except Exception as err:
|
||||
result = await _await_task_to_terminal(rollback_task)
|
||||
except BaseException as err:
|
||||
logger.error(f"插件 {plugin_id} 取消后的补偿失败:{err}")
|
||||
return
|
||||
if result.rollback.errors:
|
||||
|
||||
@@ -3,8 +3,21 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from contextlib import nullcontext
|
||||
from typing import Any, ContextManager, Protocol
|
||||
|
||||
from app.schemas.types import SystemConfigKey
|
||||
|
||||
|
||||
PLUGIN_MUTATION_SYSTEM_CONFIG_KEYS = frozenset(
|
||||
{
|
||||
SystemConfigKey.UserInstalledPlugins,
|
||||
SystemConfigKey.PluginInstances,
|
||||
SystemConfigKey.PluginFolders,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class PluginRuntime(Protocol):
|
||||
"""声明入口层消费的插件宿主能力。"""
|
||||
|
||||
@@ -36,3 +49,18 @@ def configure_plugin_runtime(provider: PluginRuntimeProvider) -> None:
|
||||
def get_plugin_manager() -> PluginRuntime:
|
||||
"""返回当前组合根提供的插件运行时能力。"""
|
||||
return _runtime_provider()
|
||||
|
||||
|
||||
def plugin_system_config_mutation(
|
||||
key: str | SystemConfigKey | None,
|
||||
) -> ContextManager[None]:
|
||||
"""仅为会改变插件运行态所有权的系统配置取得 mutation lease。"""
|
||||
if key is None:
|
||||
return nullcontext()
|
||||
try:
|
||||
normalized_key = key if isinstance(key, SystemConfigKey) else SystemConfigKey(key)
|
||||
except ValueError:
|
||||
return nullcontext()
|
||||
if normalized_key not in PLUGIN_MUTATION_SYSTEM_CONFIG_KEYS:
|
||||
return nullcontext()
|
||||
return get_plugin_manager().mutation(f"更新插件系统配置 {normalized_key.value}")
|
||||
|
||||
Reference in New Issue
Block a user