Files
DDSRem 2de976f6b3 Merge pull request #7050 from DDSRem-Dev/codex-comment-migration
docs: 统一全项目中文注释和结构化契约说明
2026-10-17 19:26:29 +08:00

141 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
插件宿主可变事务的停机准入。
"""
from __future__ import annotations
import threading
from collections.abc import Iterator
from contextlib import contextmanager
from contextvars import ContextVar
from dataclasses import dataclass
from app.schemas.exception import PluginMutationRejectedError
@dataclass(slots=True)
class _MutationContext:
"""
记录一个可跨协程和受控线程传播的事务上下文。
:param admission: 负责本次变更事务准入的控制器
:param holders: 持有该上下文的调用数,初始为 0
:param open: 上下文是否仍接受后续持有者,初始为 True
"""
admission: "PluginMutationAdmission"
holders: int = 0
open: bool = True
class PluginMutationAdmission:
"""
在停机封口与插件可变事务之间维护真实 owner 计数。
"""
def __init__(self) -> None:
"""
初始化开放准入、活动 owner 计数和事务上下文。
"""
self._condition = threading.Condition()
self._accepting = True
self._active_count = 0
self._current_context: ContextVar[_MutationContext | None] = ContextVar(
"plugin_mutation_context",
default=None,
)
@property
def active_count(self) -> int:
"""
返回尚未退出的 lease 数量,用于停机诊断和严格卸载判断。
"""
with self._condition:
return self._active_count
@property
def accepting(self) -> bool:
"""
返回当前生命周期是否仍接纳新的根事务。
"""
with self._condition:
return self._accepting
def is_held(self) -> bool:
"""
判断当前执行上下文是否持有仍有效的本 admission lease。
"""
context = self._current_context.get()
with self._condition:
return bool(
context
and context.admission is self
and context.open
and context.holders > 0
)
@contextmanager
def hold(self, operation: str) -> Iterator[None]:
"""
取得可变事务 lease;封口后仅允许已获准事务的嵌套调用。
"""
context = self._current_context.get()
context_token = None
acquired = False
try:
with self._condition:
nested = bool(
context
and context.admission is self
and context.open
and context.holders > 0
)
if not self._accepting and not nested:
raise PluginMutationRejectedError(operation)
if not nested:
context = _MutationContext(admission=self)
context_token = self._current_context.set(context)
assert context is not None
self._active_count += 1
context.holders += 1
acquired = True
yield
finally:
if acquired:
# acquired 只会在上下文已验证且 owner 计数增加后置为 True。
assert context is not None
with self._condition:
self._active_count -= 1
context.holders -= 1
if context.holders == 0:
context.open = False
self._condition.notify_all()
if context_token is not None:
self._current_context.reset(context_token)
def seal(self) -> int:
"""
原子停止接纳根事务,并返回封口瞬间的活动 lease 数量。
"""
with self._condition:
self._accepting = False
return self._active_count
def wait_until_idle(self) -> None:
"""
自然等待全部已获准 lease 退出,不取消或遗失其 owner。
"""
with self._condition:
while self._active_count:
self._condition.wait()
def reopen(self) -> bool:
"""
仅在没有遗留 lease 时为新的应用生命周期重新开放准入。
"""
with self._condition:
if self._active_count:
return False
self._accepting = True
return True