102 lines
3.9 KiB
GDScript
102 lines
3.9 KiB
GDScript
@tool
|
|
class_name McpClientRefreshState
|
|
extends RefCounted
|
|
|
|
## State machine for the plugin-lifetime client's status refresh sweep. Single
|
|
## source of truth — supersedes the seven booleans + deadline previously
|
|
## scattered across `mcp_dock.gd` (`_client_status_refresh_in_flight`,
|
|
## `_client_status_refresh_pending`, `_client_status_refresh_pending_force`,
|
|
## `_client_status_refresh_timed_out`, `_client_status_refresh_started_msec`,
|
|
## `_client_status_refresh_deferred_until_filesystem_ready`,
|
|
## `_client_status_refresh_deferred_force`,
|
|
## `_client_status_refresh_deferred_initial`,
|
|
## `_client_status_refresh_shutdown_requested`).
|
|
##
|
|
## The ints are stable for tests; reordering is a breaking change.
|
|
|
|
## No worker running, no pending request. Default state.
|
|
const IDLE := 0
|
|
## A refresh request landed but the editor filesystem is busy
|
|
## (`EditorInterface.get_resource_filesystem().is_scanning()` is true);
|
|
## the job owner parks the request and retries on the next `_process` after
|
|
## the scan settles. A separate force bit preserves an explicit retry request.
|
|
const DEFERRED_FOR_FILESYSTEM := 1
|
|
## Worker thread is alive and probing client status off-main. The
|
|
## Dock snapshots paint "(checking...)" in the clients summary and the owner accepts
|
|
## additional requests as `pending`.
|
|
const RUNNING := 2
|
|
## Worker has been alive past CLIENT_STATUS_REFRESH_TIMEOUT_MSEC. The
|
|
## dock paints "(client probe still running)"; further requests coalesce into
|
|
## one retry after this sole worker exits.
|
|
const RUNNING_TIMED_OUT := 3
|
|
## Plugin teardown / update activation is draining workers. New refresh
|
|
## requests are rejected outright until an explicitly failed handoff resumes
|
|
## the same owner.
|
|
const SHUTTING_DOWN := 4
|
|
|
|
const _NAMES := {
|
|
IDLE: "idle",
|
|
DEFERRED_FOR_FILESYSTEM: "deferred_for_filesystem",
|
|
RUNNING: "running",
|
|
RUNNING_TIMED_OUT: "running_timed_out",
|
|
SHUTTING_DOWN: "shutting_down",
|
|
}
|
|
|
|
|
|
static func name_of(state: int) -> String:
|
|
return _NAMES.get(state, "unknown(%d)" % state)
|
|
|
|
|
|
## True when a worker thread should be alive in this state. Combined
|
|
## state — RUNNING or RUNNING_TIMED_OUT both have the sole worker running.
|
|
static func has_worker_alive(state: int) -> bool:
|
|
return state == RUNNING or state == RUNNING_TIMED_OUT
|
|
|
|
|
|
## True while the status worker is still within its healthy budget. Once a
|
|
## refresh has timed out, the dock keeps the warning badge but must let users
|
|
## retry Configure / Configure all instead of stranding the controls behind an
|
|
## orphaned, uninterruptible worker.
|
|
static func should_disable_client_actions(state: int) -> bool:
|
|
return state == RUNNING
|
|
|
|
|
|
## True when the dock should reject new refresh spawns. Used by the
|
|
## job owner's deferred entrypoint and status-refresh scheduler.
|
|
static func is_blocked_for_spawn(state: int) -> bool:
|
|
return state == SHUTTING_DOWN
|
|
|
|
|
|
## True when the summary label should show the in-flight badge.
|
|
static func should_show_checking_badge(state: int) -> bool:
|
|
return state == RUNNING or state == RUNNING_TIMED_OUT
|
|
|
|
|
|
## Transition table. Same shape as McpServerState — illegal transitions
|
|
## return false; callers `push_warning` and no-op.
|
|
static func can_transition(from: int, to: int) -> bool:
|
|
if from == to:
|
|
return true
|
|
## Shutdown is sticky.
|
|
if from == SHUTTING_DOWN:
|
|
return false
|
|
## Anything → SHUTTING_DOWN is legal (drain on _exit_tree / install).
|
|
if to == SHUTTING_DOWN:
|
|
return true
|
|
match from:
|
|
IDLE:
|
|
return to == RUNNING or to == DEFERRED_FOR_FILESYSTEM
|
|
DEFERRED_FOR_FILESYSTEM:
|
|
## When the filesystem scan settles we either spawn a worker
|
|
## (RUNNING) or roll back to IDLE if no rows need probing.
|
|
return to == RUNNING or to == IDLE
|
|
RUNNING:
|
|
## Worker finishes -> IDLE. Worker outlives budget ->
|
|
## RUNNING_TIMED_OUT.
|
|
return to == IDLE or to == RUNNING_TIMED_OUT
|
|
RUNNING_TIMED_OUT:
|
|
## The sole late-arriving worker drops back to IDLE. A coalesced
|
|
## retry then starts through the ordinary IDLE -> RUNNING edge.
|
|
return to == IDLE
|
|
return false
|