[client] Drop DownAsync RPC in favor of bounded sync Down on quit

Remove the DownAsync RPC and call the plain Down RPC from the tray Quit
handler under the existing 5s client-side deadline.

Why DownAsync did not pay for itself:

- Its premise ("quitting never blocks on the engine shutdown") did not
  hold: engine.Stop() ran synchronously inside beginDown, so the GUI
  blocked on it either way. Only the retry-goroutine wait (typically
  near-zero, at most 5s when wedged) moved off the caller.

- The daemon's Down handler never consults the caller's context, so the
  client-side deadline in handleQuit already bounds how long Quit waits
  while the daemon completes the full teardown regardless of the caller
  timing out. Same disconnect guarantee, no extra API surface.

- A DownAsync that truly returned before the engine shutdown would have
  to return right after actCancel and stop the engine on a background
  goroutine, opening an Up-after-Down race (new engine starting while
  the old one is still tearing down) that would need an Up-side guard.
  That is a connect-lifecycle refactor (see the TODO in
  cleanupConnection about run-loop shutdown ownership), not a quit-UX
  fix.

Down keeps the error handling from the previous commit: engine.Stop()
failures are logged and cleanup continues so the daemon always returns
to Idle, and ErrServiceNotUp returns without an error log. The generated
proto files are restored to their previous content rather than
regenerated.
This commit is contained in:
Zoltan Papp
2026-07-20 19:04:27 +02:00
parent 79dd5c5fc1
commit 01ad77895e
7 changed files with 118 additions and 339 deletions

View File

@@ -177,17 +177,6 @@ func (s *Connection) Down(ctx context.Context) error {
return nil
}
func (s *Connection) DownAsync(ctx context.Context) error {
cli, err := s.conn.Client()
if err != nil {
return err
}
if _, err = cli.DownAsync(ctx, &proto.DownAsyncRequest{}); err != nil {
return s.classifyDaemonError(err)
}
return nil
}
// OpenURL opens url in an external browser; the embedded webview blocks
// window.open, so the SSO verification page can't pop inline. Honors $BROWSER
// before the platform default.

View File

@@ -464,7 +464,7 @@ func (t *Tray) handleQuit() {
ctx, cancel := context.WithTimeout(context.Background(), quitDownTimeout)
defer cancel()
if err := t.svc.Connection.DownAsync(ctx); err != nil {
if err := t.svc.Connection.Down(ctx); err != nil {
log.Errorf("disconnect on quit: %v", err)
}
t.app.Quit()