Start performs the initial connect. On Connector error the supervisor stays in StateStopped and the caller is expected to retry. On success the watcher goroutine is launched (if not already alive) and state moves to Ready. Concurrent Start calls serialize. When policy.StartupTimeout is non-zero the
(ctx context.Context)
| 230 | // When policy.StartupTimeout is non-zero the connect is bounded by it; on |
| 231 | // expiry Start returns ErrInitTimeout and stays in StateStopped. |
| 232 | func (s *Supervisor) Start(ctx context.Context) error { |
| 233 | s.startMu.Lock() |
| 234 | defer s.startMu.Unlock() |
| 235 | |
| 236 | s.mu.Lock() |
| 237 | if s.session != nil { |
| 238 | s.mu.Unlock() |
| 239 | return nil |
| 240 | } |
| 241 | if s.stopping { |
| 242 | s.mu.Unlock() |
| 243 | return ErrNotStarted |
| 244 | } |
| 245 | s.mu.Unlock() |
| 246 | |
| 247 | s.tracker.Set(StateStarting) |
| 248 | |
| 249 | sess, err := s.connect(ctx) |
| 250 | if err != nil { |
| 251 | s.tracker.Fail(StateStopped, err) |
| 252 | return err |
| 253 | } |
| 254 | |
| 255 | s.mu.Lock() |
| 256 | if s.stopping { |
| 257 | s.mu.Unlock() |
| 258 | _ = sess.Close(context.WithoutCancel(ctx)) |
| 259 | s.tracker.Set(StateStopped) |
| 260 | return ErrNotStarted |
| 261 | } |
| 262 | s.session = sess |
| 263 | spawnWatcher := !s.watcherAlive |
| 264 | if spawnWatcher { |
| 265 | s.watchDone = make(chan struct{}) |
| 266 | } |
| 267 | s.watcherAlive = true |
| 268 | // Recovering from a terminal state (Failed → Start, or a watcher |
| 269 | // that previously exited): refresh `done` so RestartAndWait callers |
| 270 | // don't see a stale close, and clear forceRestart so a leftover flag |
| 271 | // from a prior session doesn't force-restart this fresh one. |
| 272 | select { |
| 273 | case <-s.done: |
| 274 | s.done = make(chan struct{}) |
| 275 | default: |
| 276 | } |
| 277 | s.forceRestart = false |
| 278 | s.mu.Unlock() |
| 279 | |
| 280 | s.tracker.Set(StateReady) |
| 281 | s.tracker.ResetRestarts() |
| 282 | |
| 283 | if spawnWatcher { |
| 284 | // The watcher must outlive ctx; the only way to stop it is Stop. |
| 285 | go s.watch(context.WithoutCancel(ctx)) |
| 286 | } |
| 287 | |
| 288 | s.policy.logger().Debug("supervisor: ready", "name", s.name) |
| 289 | return nil |