Skip to content

Commit 86863ae

Browse files
zeripathtechknowlogicklunny
authored
Prevent timer leaks in Workerpool and others (#11333) (#11340)
There is a potential memory leak in `Workerpool` due to the intricacies of `time.Timer` stopping. Whenever a `time.Timer` is `Stop`ped its channel must be cleared using a `select` if the result of the `Stop()` is `false`. Unfortunately in `Workerpool` these were checked the wrong way round. However, there were a few other places that were not being checked. Signed-off-by: Andrew Thornton <[email protected]> Co-authored-by: techknowlogick <[email protected]> Co-authored-by: Lunny Xiao <[email protected]> Co-authored-by: techknowlogick <[email protected]> Co-authored-by: Lunny Xiao <[email protected]>
1 parent f3a9005 commit 86863ae

File tree

5 files changed

+32
-29
lines changed

5 files changed

+32
-29
lines changed

cmd/hook.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"code.gitea.io/gitea/modules/git"
2020
"code.gitea.io/gitea/modules/private"
2121
"code.gitea.io/gitea/modules/setting"
22+
"code.gitea.io/gitea/modules/util"
2223

2324
"github.com/urfave/cli"
2425
)
@@ -113,15 +114,8 @@ func (d *delayWriter) Close() error {
113114
if d == nil {
114115
return nil
115116
}
116-
stopped := d.timer.Stop()
117-
if stopped {
118-
return nil
119-
}
120-
select {
121-
case <-d.timer.C:
122-
default:
123-
}
124-
if d.buf == nil {
117+
stopped := util.StopTimer(d.timer)
118+
if stopped || d.buf == nil {
125119
return nil
126120
}
127121
_, err := d.internal.Write(d.buf.Bytes())

modules/migrations/github.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"code.gitea.io/gitea/modules/log"
1717
"code.gitea.io/gitea/modules/migrations/base"
1818
"code.gitea.io/gitea/modules/structs"
19+
"code.gitea.io/gitea/modules/util"
1920

2021
"github.com/google/go-github/v24/github"
2122
"golang.org/x/oauth2"
@@ -121,7 +122,7 @@ func (g *GithubDownloaderV3) sleep() {
121122
timer := time.NewTimer(time.Until(g.rate.Reset.Time))
122123
select {
123124
case <-g.ctx.Done():
124-
timer.Stop()
125+
util.StopTimer(timer)
125126
return
126127
case <-timer.C:
127128
}

modules/queue/queue_wrapped.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"time"
1313

1414
"code.gitea.io/gitea/modules/log"
15+
"code.gitea.io/gitea/modules/util"
1516
)
1617

1718
// WrappedQueueType is the type for a wrapped delayed starting queue
@@ -77,7 +78,7 @@ func (q *delayedStarter) setInternal(atShutdown func(context.Context, func()), h
7778
t := time.NewTimer(sleepTime)
7879
select {
7980
case <-ctx.Done():
80-
t.Stop()
81+
util.StopTimer(t)
8182
case <-t.C:
8283
}
8384
}

modules/queue/workerpool.go

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"code.gitea.io/gitea/modules/log"
13+
"code.gitea.io/gitea/modules/util"
1314
)
1415

1516
// WorkerPool takes
@@ -56,12 +57,7 @@ func (p *WorkerPool) pushBoost(data Data) {
5657
p.lock.Unlock()
5758
select {
5859
case p.dataChan <- data:
59-
if timer.Stop() {
60-
select {
61-
case <-timer.C:
62-
default:
63-
}
64-
}
60+
util.StopTimer(timer)
6561
case <-timer.C:
6662
p.lock.Lock()
6763
if p.blockTimeout > ourTimeout || (p.numberOfWorkers > p.maxNumberOfWorkers && p.maxNumberOfWorkers >= 0) {
@@ -277,25 +273,15 @@ func (p *WorkerPool) doWork(ctx context.Context) {
277273
timer := time.NewTimer(delay)
278274
select {
279275
case <-ctx.Done():
280-
if timer.Stop() {
281-
select {
282-
case <-timer.C:
283-
default:
284-
}
285-
}
276+
util.StopTimer(timer)
286277
if len(data) > 0 {
287278
log.Trace("Handling: %d data, %v", len(data), data)
288279
p.handle(data...)
289280
}
290281
log.Trace("Worker shutting down")
291282
return
292283
case datum, ok := <-p.dataChan:
293-
if timer.Stop() {
294-
select {
295-
case <-timer.C:
296-
default:
297-
}
298-
}
284+
util.StopTimer(timer)
299285
if !ok {
300286
// the dataChan has been closed - we should finish up:
301287
if len(data) > 0 {

modules/util/timer.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright 2020 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package util
6+
7+
import (
8+
"time"
9+
)
10+
11+
// StopTimer is a utility function to safely stop a time.Timer and clean its channel
12+
func StopTimer(t *time.Timer) bool {
13+
stopped := t.Stop()
14+
if !stopped {
15+
select {
16+
case <-t.C:
17+
default:
18+
}
19+
}
20+
return stopped
21+
}

0 commit comments

Comments
 (0)