mirror of
https://github.com/wwiinnddyy/LanMountainDesktop.git
synced 2026-06-21 16:14:28 +08:00
Add launcher coordinator IPC and startup reservation
Introduce a launcher coordinator to reserve startup ownership and prevent duplicate host launches. Adds a NamedPipe-based IPC server/client (LauncherCoordinatorIpcServer/Client), coordinator messages/models, and PublicShellStatus/activation types for richer shell reporting. Enhances StartupAttemptRecord and StartupAttemptRegistry to track coordinator pid/pipe, heartbeat, reserved-before-host-start, and public IPC status, plus new reservation/heartbeat APIs and takeover logic. Wire coordinator into App and LauncherFlowCoordinator to attach secondary launchers, publish coordinator status, probe existing hosts, and include more detailed launch result details. Also adds unit tests and docs describing coordinator and startup visuals behavior.
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using LanMountainDesktop.Shared.Contracts.Launcher;
|
||||
using LanMountainDesktop.Shared.IPC.Abstractions.Services;
|
||||
|
||||
namespace LanMountainDesktop.Launcher.Models;
|
||||
|
||||
internal static class LauncherCoordinatorCommands
|
||||
{
|
||||
public const string Attach = "attach";
|
||||
public const string ActivateDesktop = "activate-desktop";
|
||||
public const string GetStatus = "get-status";
|
||||
}
|
||||
|
||||
internal sealed class LauncherCoordinatorRequest
|
||||
{
|
||||
[JsonPropertyName("requestId")]
|
||||
public string RequestId { get; init; } = Guid.NewGuid().ToString("N");
|
||||
|
||||
[JsonPropertyName("command")]
|
||||
public string Command { get; init; } = LauncherCoordinatorCommands.Attach;
|
||||
|
||||
[JsonPropertyName("launcherPid")]
|
||||
public int LauncherPid { get; init; } = Environment.ProcessId;
|
||||
|
||||
[JsonPropertyName("launchSource")]
|
||||
public string LaunchSource { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("successPolicy")]
|
||||
public string SuccessPolicy { get; init; } = string.Empty;
|
||||
}
|
||||
|
||||
internal sealed class LauncherCoordinatorResponse
|
||||
{
|
||||
[JsonPropertyName("accepted")]
|
||||
public bool Accepted { get; init; }
|
||||
|
||||
[JsonPropertyName("code")]
|
||||
public string Code { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("message")]
|
||||
public string Message { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("status")]
|
||||
public LauncherCoordinatorStatus? Status { get; init; }
|
||||
|
||||
[JsonPropertyName("activationResult")]
|
||||
public PublicShellActivationResult? ActivationResult { get; init; }
|
||||
}
|
||||
|
||||
internal sealed class LauncherCoordinatorStatus
|
||||
{
|
||||
[JsonPropertyName("attemptId")]
|
||||
public string AttemptId { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("coordinatorPid")]
|
||||
public int CoordinatorPid { get; init; } = Environment.ProcessId;
|
||||
|
||||
[JsonPropertyName("hostPid")]
|
||||
public int HostPid { get; init; }
|
||||
|
||||
[JsonPropertyName("hostProcessAlive")]
|
||||
public bool HostProcessAlive { get; init; }
|
||||
|
||||
[JsonPropertyName("launchSource")]
|
||||
public string LaunchSource { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("successPolicy")]
|
||||
public string SuccessPolicy { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("lastObservedStage")]
|
||||
public StartupStage LastObservedStage { get; init; } = StartupStage.Initializing;
|
||||
|
||||
[JsonPropertyName("lastObservedMessage")]
|
||||
public string LastObservedMessage { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("publicIpcConnected")]
|
||||
public bool PublicIpcConnected { get; init; }
|
||||
|
||||
[JsonPropertyName("state")]
|
||||
public string State { get; init; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("softTimeoutShown")]
|
||||
public bool SoftTimeoutShown { get; init; }
|
||||
|
||||
[JsonPropertyName("completed")]
|
||||
public bool Completed { get; init; }
|
||||
|
||||
[JsonPropertyName("succeeded")]
|
||||
public bool Succeeded { get; init; }
|
||||
|
||||
[JsonPropertyName("shellStatus")]
|
||||
public PublicShellStatus? ShellStatus { get; init; }
|
||||
|
||||
[JsonPropertyName("updatedAtUtc")]
|
||||
public DateTimeOffset UpdatedAtUtc { get; init; } = DateTimeOffset.UtcNow;
|
||||
}
|
||||
@@ -20,12 +20,21 @@ internal sealed class StartupAttemptRecord
|
||||
[JsonPropertyName("hostPid")]
|
||||
public int HostPid { get; set; }
|
||||
|
||||
[JsonPropertyName("coordinatorPid")]
|
||||
public int CoordinatorPid { get; set; }
|
||||
|
||||
[JsonPropertyName("coordinatorPipeName")]
|
||||
public string CoordinatorPipeName { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("startedAtUtc")]
|
||||
public DateTimeOffset StartedAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
||||
|
||||
[JsonPropertyName("updatedAtUtc")]
|
||||
public DateTimeOffset UpdatedAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
||||
|
||||
[JsonPropertyName("heartbeatAtUtc")]
|
||||
public DateTimeOffset HeartbeatAtUtc { get; set; } = DateTimeOffset.UtcNow;
|
||||
|
||||
[JsonPropertyName("launchSource")]
|
||||
public string LaunchSource { get; set; } = string.Empty;
|
||||
|
||||
@@ -41,6 +50,15 @@ internal sealed class StartupAttemptRecord
|
||||
[JsonPropertyName("ipcConnected")]
|
||||
public bool IpcConnected { get; set; }
|
||||
|
||||
[JsonPropertyName("publicIpcConnected")]
|
||||
public bool PublicIpcConnected { get; set; }
|
||||
|
||||
[JsonPropertyName("shellStatus")]
|
||||
public string ShellStatus { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("reservedBeforeHostStart")]
|
||||
public bool ReservedBeforeHostStart { get; set; }
|
||||
|
||||
[JsonPropertyName("state")]
|
||||
public StartupAttemptState State { get; set; } = StartupAttemptState.Pending;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user