From 0d74cd9aeafe210295e22b2285f08c4025484d69 Mon Sep 17 00:00:00 2001 From: ale <ale@incal.net> Date: Sat, 18 Dec 2021 12:03:31 +0000 Subject: [PATCH] Improve robustness of update canceling Also prevents update goroutines from piling up if they are delayed by adding a semaphore in startUpdate(). --- manager.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/manager.go b/manager.go index cb8195c5..06214358 100644 --- a/manager.go +++ b/manager.go @@ -249,12 +249,23 @@ func (m *Manager) loop(ctx context.Context) { // Updates are long-term jobs, so they should be // interruptible. We run updates in a separate goroutine, and // cancel them when the configuration is reloaded or on exit. + // A simple channel is used as a semaphore, so that only one + // update goroutine can be running at any given time (without + // other ones piling up). var upCancel context.CancelFunc var wg sync.WaitGroup + sem := make(chan struct{}, 1) startUpdate := func(certs []*certInfo) context.CancelFunc { - // Ensure the previous update has finished. - wg.Wait() + // Acquire the semaphore, return if we fail to. + select { + case sem <- struct{}{}: + default: + return nil + } + defer func() { + <-sem + }() upCtx, cancel := context.WithCancel(ctx) wg.Add(1) @@ -270,6 +281,7 @@ func (m *Manager) loop(ctx context.Context) { cancelUpdate := func() { if upCancel != nil { upCancel() + upCancel = nil } wg.Wait() } @@ -278,17 +290,21 @@ func (m *Manager) loop(ctx context.Context) { tick := time.NewTicker(5 * time.Minute) defer tick.Stop() for { + var c func() select { case <-tick.C: - upCancel = startUpdate(m.certs) + c = startUpdate(m.certs) case <-testUpdateCh: - upCancel = startUpdate(m.certs) + c = startUpdate(m.certs) case certDomains := <-m.configCh: cancelUpdate() m.certs = m.loadConfig(certDomains) case <-ctx.Done(): return } + if c != nil { + upCancel = nil + } } } -- GitLab