startReloadIfNeeded registers the reload with the singleflight group using a fixed key so concurrent triggers share one body. The body always runs under m.ctx. The returned channel yields the body's result exactly once. All concurrent callers share one in-flight reload keyed by "reload". If a concu
(paths []string)
| 268 | // consulted. The next SnapshotChanged check after this reload completes |
| 269 | // will detect the mismatch and trigger a fresh reload. |
| 270 | func (m *Manager) startReloadIfNeeded(paths []string) (<-chan reloadResult, bool, error) { |
| 271 | m.mu.RLock() |
| 272 | closed := m.closed |
| 273 | firstSyncSettled := m.firstSyncSettled |
| 274 | m.mu.RUnlock() |
| 275 | if closed { |
| 276 | return nil, false, ErrManagerClosed |
| 277 | } |
| 278 | if err := m.ctx.Err(); err != nil { |
| 279 | if closeErr := m.closeErr(); closeErr != nil { |
| 280 | return nil, false, closeErr |
| 281 | } |
| 282 | return nil, false, err |
| 283 | } |
| 284 | // Arm the fsnotify watcher before deciding whether to short |
| 285 | // circuit. The first call lazily creates it; subsequent calls |
| 286 | // re-sync the watched path set if it changed. Arming before |
| 287 | // the SnapshotChanged check ensures any Create event that |
| 288 | // races with parseAndDedup is still delivered: the watcher |
| 289 | // is running when parseAndDedup returns the empty snapshot. |
| 290 | m.armWatcher(paths) |
| 291 | |
| 292 | if firstSyncSettled && !m.SnapshotChanged(paths) { |
| 293 | return nil, false, nil |
| 294 | } |
| 295 | |
| 296 | ch := m.sf.DoChan("reload", func() (struct{}, error) { |
| 297 | defer m.markFirstSyncSettled() |
| 298 | err := m.doReload(m.ctx, paths) |
| 299 | return struct{}{}, err |
| 300 | }) |
| 301 | return ch, true, nil |
| 302 | } |
| 303 | |
| 304 | // armWatcher lazily initializes the fsnotify-backed configWatcher |
| 305 | // and syncs it to the latest config paths. Lazy initialization |
no test coverage detected