namespace Fleet.Hosting; /// /// 当前生效的定时重启:节点本地的设置优先;本地没有写时用面板下发的,面板改了之后立即重新排下一次,不需要重启进程。 /// public sealed class RestartController : IDisposable { private readonly RestartSchedule? _local; private readonly object _gate = new(); private RestartSchedule? _remote; private readonly CancellationTokenSource _now = new(); private CancellationTokenSource _changed = new(); private string? _nowReason; public RestartController(RestartSchedule? local = null) { _local = local; } /// 本地有设置时为 true:面板下发的不生效。 public bool IsLocal => _local is not null; public RestartSchedule? Current { get { lock (_gate) { return _local ?? _remote; } } } /// 当前设置被改动时会被取消的令牌;先取令牌、再取 ,就不会漏掉中间的改动。 public CancellationToken ChangeToken { get { lock (_gate) { return _changed.Token; } } } /// 应用要求现在就有序退出(比如管理员请求更新);退出的原因,没有就是 null。 public string? NowReason { get { lock (_gate) { return _nowReason; } } } /// 应用要求现在就退出的令牌。 public CancellationToken NowToken => _now.Token; /// 要求进程现在就有序退出,由服务管理器拉起;不管有没有设置定时重启。 public void RequestNow(string reason) { lock (_gate) { _nowReason ??= reason; } _now.Cancel(); } /// 面板下发的设置;为空表示面板关闭了定时重启。返回 false 表示文本不合法(节点忽略它)。 public bool SetRemote(string? text) { RestartSchedule? schedule = null; if (!string.IsNullOrWhiteSpace(text) && !RestartSchedule.TryParse(text, out schedule)) { return false; } CancellationTokenSource previous; lock (_gate) { _remote = schedule; previous = _changed; _changed = new CancellationTokenSource(); } previous.Cancel(); previous.Dispose(); return true; } public void Dispose() { lock (_gate) { _changed.Dispose(); _now.Dispose(); } } }