diff --git a/agent/agentcontainers/api.go b/agent/agentcontainers/api.go index 2c6c985ef4..4c00e28892 100644 --- a/agent/agentcontainers/api.go +++ b/agent/agentcontainers/api.go @@ -87,7 +87,8 @@ type API struct { agentDirectory string mu sync.RWMutex // Protects the following fields. - initDone chan struct{} // Closed by Init. + initDone bool // Whether Init has been called. + initialUpdateDone chan struct{} // Closed after first updateContainers call in updaterLoop. updateChans []chan struct{} closed bool containers codersdk.WorkspaceAgentListContainersResponse // Output from the last list operation. @@ -325,7 +326,7 @@ func NewAPI(logger slog.Logger, options ...Option) *API { api := &API{ ctx: ctx, cancel: cancel, - initDone: make(chan struct{}), + initialUpdateDone: make(chan struct{}), updateTrigger: make(chan chan error), updateInterval: defaultUpdateInterval, logger: logger, @@ -379,20 +380,15 @@ func NewAPI(logger slog.Logger, options ...Option) *API { return api } -// Init applies a final set of options to the API and then -// closes initDone. This method can only be called once. +// Init applies a final set of options to the API and marks +// initialization as done. This method can only be called once. func (api *API) Init(opts ...Option) { api.mu.Lock() defer api.mu.Unlock() - if api.closed { + if api.closed || api.initDone { return } - select { - case <-api.initDone: - return - default: - } - defer close(api.initDone) + api.initDone = true for _, opt := range opts { opt(api) @@ -651,6 +647,7 @@ func (api *API) updaterLoop() { } else { api.logger.Debug(api.ctx, "initial containers update complete") } + close(api.initialUpdateDone) // We utilize a TickerFunc here instead of a regular Ticker so that // we can guarantee execution of the updateContainers method after @@ -715,7 +712,7 @@ func (api *API) UpdateSubAgentClient(client SubAgentClient) { func (api *API) Routes() http.Handler { r := chi.NewRouter() - ensureInitDoneMW := func(next http.Handler) http.Handler { + ensureInitialUpdateDoneMW := func(next http.Handler) http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { select { case <-api.ctx.Done(): @@ -726,8 +723,8 @@ func (api *API) Routes() http.Handler { return case <-r.Context().Done(): return - case <-api.initDone: - // API init is done, we can start processing requests. + case <-api.initialUpdateDone: + // Initial update is done, we can start processing requests. } next.ServeHTTP(rw, r) }) @@ -736,7 +733,7 @@ func (api *API) Routes() http.Handler { // For now, all endpoints require the initial update to be done. // If we want to allow some endpoints to be available before // the initial update, we can enable this per-route. - r.Use(ensureInitDoneMW) + r.Use(ensureInitialUpdateDoneMW) r.Get("/", api.handleList) r.Get("/watch", api.watchContainers)