Skip to content

Commit eaad3ee

Browse files
committed
Make sure timers are stopped after use.
`time.After` keeps a timer running until the specified duration is completed. It also allocates a new timer on each call. This can wind up leaving lots of uneccessary timers running in the background that are not needed and consume resources. Instead of `time.After`, use `time.NewTimer` so the timer can actually be stopped. In some of these cases it's not a big deal since the duraiton is really short, but in others it is much worse. Signed-off-by: Brian Goff <[email protected]>
1 parent 77df18c commit eaad3ee

15 files changed

Lines changed: 121 additions & 40 deletions

File tree

api/server/router/system/system_routes.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ func (s *systemRouter) getEvents(ctx context.Context, w http.ResponseWriter, r *
165165

166166
if !onlyPastEvents {
167167
dur := until.Sub(now)
168-
timeout = time.After(dur)
168+
timer := time.NewTimer(dur)
169+
defer timer.Stop()
170+
timeout = timer.C
169171
}
170172
}
171173

cmd/dockerd/daemon.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,10 +378,14 @@ func shutdownDaemon(d *daemon.Daemon) {
378378
logrus.Debug("Clean shutdown succeeded")
379379
return
380380
}
381+
382+
timeout := time.NewTimer(time.Duration(shutdownTimeout) * time.Second)
383+
defer timeout.Stop()
384+
381385
select {
382386
case <-ch:
383387
logrus.Debug("Clean shutdown succeeded")
384-
case <-time.After(time.Duration(shutdownTimeout) * time.Second):
388+
case <-timeout.C:
385389
logrus.Error("Force shutdown daemon")
386390
}
387391
}

container/monitor.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@ func (container *Container) Reset(lock bool) {
3333
container.LogCopier.Wait()
3434
close(exit)
3535
}()
36+
37+
timer := time.NewTimer(loggerCloseTimeout)
38+
defer timer.Stop()
3639
select {
37-
case <-time.After(loggerCloseTimeout):
40+
case <-timer.C:
3841
logrus.Warn("Logger didn't exit in time: logs may be truncated")
3942
case <-exit:
4043
}

daemon/cluster/cluster.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,11 @@ func (c *Cluster) Start() error {
186186
}
187187
c.nr = nr
188188

189+
timer := time.NewTimer(swarmConnectTimeout)
190+
defer timer.Stop()
191+
189192
select {
190-
case <-time.After(swarmConnectTimeout):
193+
case <-timer.C:
191194
logrus.Error("swarm component could not be started before timeout was reached")
192195
case err := <-nr.Ready():
193196
if err != nil {

daemon/cluster/swarm.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,11 @@ func (c *Cluster) Join(req types.JoinRequest) error {
194194
c.nr = nr
195195
c.mu.Unlock()
196196

197+
timeout := time.NewTimer(swarmConnectTimeout)
198+
defer timeout.Stop()
199+
197200
select {
198-
case <-time.After(swarmConnectTimeout):
201+
case <-timeout.C:
199202
return errSwarmJoinTimeoutReached
200203
case err := <-nr.Ready():
201204
if err != nil {

daemon/daemon.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import (
4242
"github.com/moby/buildkit/util/resolver"
4343
"github.com/moby/buildkit/util/tracing"
4444
"github.com/sirupsen/logrus"
45+
4546
// register graph drivers
4647
_ "github.com/docker/docker/daemon/graphdriver/register"
4748
"github.com/docker/docker/daemon/stats"
@@ -479,12 +480,14 @@ func (daemon *Daemon) restore() error {
479480
// ignore errors here as this is a best effort to wait for children to be
480481
// running before we try to start the container
481482
children := daemon.children(c)
482-
timeout := time.After(5 * time.Second)
483+
timeout := time.NewTimer(5 * time.Second)
484+
defer timeout.Stop()
485+
483486
for _, child := range children {
484487
if notifier, exists := restartContainers[child]; exists {
485488
select {
486489
case <-notifier:
487-
case <-timeout:
490+
case <-timeout.C:
488491
}
489492
}
490493
}
@@ -602,6 +605,7 @@ func (daemon *Daemon) waitForNetworks(c *container.Container) {
602605
if daemon.discoveryWatcher == nil {
603606
return
604607
}
608+
605609
// Make sure if the container has a network that requires discovery that the discovery service is available before starting
606610
for netName := range c.NetworkSettings.Networks {
607611
// If we get `ErrNoSuchNetwork` here, we can assume that it is due to discovery not being ready
@@ -610,13 +614,19 @@ func (daemon *Daemon) waitForNetworks(c *container.Container) {
610614
if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok {
611615
continue
612616
}
617+
613618
// use a longish timeout here due to some slowdowns in libnetwork if the k/v store is on anything other than --net=host
614619
// FIXME: why is this slow???
620+
dur := 60 * time.Second
621+
timer := time.NewTimer(dur)
622+
615623
logrus.Debugf("Container %s waiting for network to be ready", c.Name)
616624
select {
617625
case <-daemon.discoveryWatcher.ReadyCh():
618-
case <-time.After(60 * time.Second):
626+
case <-timer.C:
619627
}
628+
timer.Stop()
629+
620630
return
621631
}
622632
}
@@ -666,10 +676,14 @@ func (daemon *Daemon) DaemonLeavesCluster() {
666676
// This is called also on graceful daemon shutdown. We need to
667677
// wait, because the ingress release has to happen before the
668678
// network controller is stopped.
679+
669680
if done, err := daemon.ReleaseIngress(); err == nil {
681+
timeout := time.NewTimer(5 * time.Second)
682+
defer timeout.Stop()
683+
670684
select {
671685
case <-done:
672-
case <-time.After(5 * time.Second):
686+
case <-timeout.C:
673687
logrus.Warn("timeout while waiting for ingress network removal")
674688
}
675689
} else {

daemon/discovery/discovery.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,14 @@ func (d *daemonDiscoveryReloader) initHeartbeat(address string) error {
148148
// Setup a short ticker until the first heartbeat has succeeded
149149
t := time.NewTicker(500 * time.Millisecond)
150150
defer t.Stop()
151+
151152
// timeout makes sure that after a period of time we stop being so aggressive trying to reach the discovery service
152-
timeout := time.After(60 * time.Second)
153+
timeout := time.NewTimer(60 * time.Second)
154+
defer timeout.Stop()
153155

154156
for {
155157
select {
156-
case <-timeout:
158+
case <-timeout.C:
157159
return errors.New("timeout waiting for initial discovery")
158160
case <-d.term:
159161
return errors.New("terminated")

daemon/exec.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
)
2323

2424
// Seconds to wait after sending TERM before trying KILL
25-
const termProcessTimeout = 10
25+
const termProcessTimeout = 10 * time.Second
2626

2727
func (d *Daemon) registerExecCommand(container *container.Container, config *exec.Config) {
2828
// Storing execs in container in order to kill them gracefully whenever the container is stopped or removed.
@@ -265,9 +265,13 @@ func (d *Daemon) ContainerExecStart(ctx context.Context, name string, stdin io.R
265265
case <-ctx.Done():
266266
logrus.Debugf("Sending TERM signal to process %v in container %v", name, c.ID)
267267
d.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["TERM"]))
268+
269+
timeout := time.NewTimer(termProcessTimeout)
270+
defer timeout.Stop()
271+
268272
select {
269-
case <-time.After(termProcessTimeout * time.Second):
270-
logrus.Infof("Container %v, process %v failed to exit within %d seconds of signal TERM - using the force", c.ID, name, termProcessTimeout)
273+
case <-timeout.C:
274+
logrus.Infof("Container %v, process %v failed to exit within %v of signal TERM - using the force", c.ID, name, termProcessTimeout)
271275
d.containerd.SignalProcess(ctx, c.ID, name, int(signal.SignalMap["KILL"]))
272276
case <-attachErr:
273277
// TERM signal worked

daemon/health.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,12 +187,18 @@ func handleProbeResult(d *Daemon, c *container.Container, result *types.Healthch
187187
func monitor(d *Daemon, c *container.Container, stop chan struct{}, probe probe) {
188188
probeTimeout := timeoutWithDefault(c.Config.Healthcheck.Timeout, defaultProbeTimeout)
189189
probeInterval := timeoutWithDefault(c.Config.Healthcheck.Interval, defaultProbeInterval)
190+
191+
intervalTimer := time.NewTimer(probeInterval)
192+
defer intervalTimer.Stop()
193+
190194
for {
195+
intervalTimer.Reset(probeInterval)
196+
191197
select {
192198
case <-stop:
193199
logrus.Debugf("Stop healthcheck monitoring for container %s (received while idle)", c.ID)
194200
return
195-
case <-time.After(probeInterval):
201+
case <-intervalTimer.C:
196202
logrus.Debugf("Running health check for container %s ...", c.ID)
197203
startTime := time.Now()
198204
ctx, cancelProbe := context.WithTimeout(context.Background(), probeTimeout)

daemon/resize.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@ func (daemon *Daemon) ContainerExecResize(name string, height, width int) error
3838
if err != nil {
3939
return err
4040
}
41+
4142
// TODO: the timeout is hardcoded here, it would be more flexible to make it
4243
// a parameter in resize request context, which would need API changes.
43-
timeout := 10 * time.Second
44+
timeout := time.NewTimer(10 * time.Second)
45+
defer timeout.Stop()
46+
4447
select {
4548
case <-ec.Started:
4649
return daemon.containerd.ResizeTerminal(context.Background(), ec.ContainerID, ec.ID, width, height)
47-
case <-time.After(timeout):
50+
case <-timeout.C:
4851
return fmt.Errorf("timeout waiting for exec session ready")
4952
}
5053
}

0 commit comments

Comments
 (0)