zapret-kvn/xray_fluent/engines/singbox/__init__.py
loop-uh 18096c1319 Переходы подключения целиком шагами через координатор, без зависаний GUI
connect/disconnect/reconnect/смена прокси/полная замена runtime стали
генераторами (connect_selected_steps, disconnect_current_steps,
reconnect_steps, _replace_runtime_steps, _start_singbox_runtime_plan_steps,
_apply_proxy_runtime_change_steps) и выполняются TransitionRunner'ом.
Устаревание поколения генераторы проверяют сами после каждого шага и сами
прибирают недостартовавшее; раннер их не закрывает.

Координатор:
- клик во время перехода только фиксирует желаемое состояние и поколение;
  подготовка выбранного сервера (DNS/Zapret) повторяется после завершения
  идущего перехода (_kick_transition), клики сливаются в последнее состояние;
- синхронные disconnect_current() из обработчиков убраны: остановка перед
  DNS/Zapret — шаг координатора (_run_prestop), отказ профиля Zapret и потеря
  winws2 — тихий disconnect через очередь (_request_stop), lock() — через
  очередь;
- устаревший переход не блокирует сигнатуру и не сбрасывает desired;
  процессы прерванного перехода (включая запущенные, но не готовые —
  process_alive) гасит отдельный шаг cleanup;
- горячий свитч во время полного перехода уходит в очередь.

Системный прокси: все записи WinAPI — в отдельном FIFO-потоке
(PROXY_EXECUTOR): шагами в переходах, без ожидания из обработчиков сигналов,
с дренажом очереди перед финальным восстановлением при выходе.

Аварийное отключение из обработчиков (unexpected disconnect, отказ Hysteria)
закрывает допуск kill'ом без ожидания; внутри перехода — шагами.

Повторный PUT выбора сервера после connect удалён: тот же тег
(plan.selected_outbound_tag == selector_tags[node.id]) уже закреплён при
старте runtime; инварианты пула/тегов не затронуты.

Сигнатуры переходов больше не строят полный план 7–10 раз за клик: поля
плана (теги селектора, провайдер, used_selected_node) кэшируются по
документу, режиму, ноде и составу пула; сброс по nodes_changed.

Приёмка: tests/test_app_transition_gui_stalls.py — настоящий контроллер и
QProcess с заглушкой ядра, блокирующие примитивы с реалистичными задержками;
connect, горячий и полный свитч, смена прокси, TUN туда-обратно и disconnect
дают max_stall < 50 мс, ноль processEvents, клик во время перехода не
вкладывается.
2026-09-26 13:27:04 +03:00

60 lines
1.5 KiB
Python

"""sing-box engine helpers."""
from .config_builder import build_singbox_outbound
from .manager import SingBoxManager, get_singbox_version
from .operations import (
restart_proxy_runtime,
restart_proxy_runtime_steps,
restart_runtime,
restart_runtime_steps,
start_proxy,
start_proxy_steps,
start_tun,
start_tun_steps,
)
from .runtime_planner import (
ParsedSingboxDocument,
SingboxDocumentState,
SingboxHysteriaSidecarPlan,
SingboxRuntimePlan,
SingboxXraySidecarPlan,
classify_node_for_singbox,
inspect_singbox_document_text,
parse_singbox_document,
plan_singbox_proxy_runtime,
plan_singbox_runtime,
)
from .selector_api import (
build_selector_url,
select_outbound,
select_outbound_when_ready,
select_outbound_when_ready_steps,
)
__all__ = [
"build_singbox_outbound",
"SingBoxManager",
"get_singbox_version",
"restart_runtime",
"restart_runtime_steps",
"restart_proxy_runtime",
"restart_proxy_runtime_steps",
"start_proxy",
"start_proxy_steps",
"start_tun",
"start_tun_steps",
"ParsedSingboxDocument",
"SingboxDocumentState",
"SingboxHysteriaSidecarPlan",
"SingboxRuntimePlan",
"SingboxXraySidecarPlan",
"classify_node_for_singbox",
"inspect_singbox_document_text",
"parse_singbox_document",
"plan_singbox_proxy_runtime",
"plan_singbox_runtime",
"build_selector_url",
"select_outbound",
"select_outbound_when_ready",
"select_outbound_when_ready_steps",
]