-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
util+plugins: Fix memory leak with explicit timer cancellation.
This commit adds a utility for explicitly creating cancelable timers, to avoid a possible memory leak caused by timer.After. This issue is fixed in Go 1.23, but since we're still on Go 1.21, this will resolve the possibility of leaks in the mean time. Signed-off-by: Philip Conrad <[email protected]>
- Loading branch information
1 parent
69cd388
commit 910f252
Showing
7 changed files
with
75 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
package util | ||
|
||
import "time" | ||
|
||
// TimerWithCancel exists because of memory leaks when using | ||
// time.After in select statements. Instead, we now manually create timers, | ||
// wait on them, and manually free them. | ||
// | ||
// See this for more details: | ||
// https://www.arangodb.com/2020/09/a-story-of-a-memory-leak-in-go-how-to-properly-use-time-after/ | ||
// | ||
// Note: This issue is fixed in Go 1.23, but this fix helps us until then. | ||
// | ||
// Warning: the cancel cannot be done concurrent to reading, everything should | ||
// work in the same goroutine. | ||
// | ||
// Example: | ||
// | ||
// for retries := 0; true; retries++ { | ||
// | ||
// ...main logic... | ||
// | ||
// timer, cancel := utils.TimerWithCancel(utils.Backoff(retries)) | ||
// select { | ||
// case <-ctx.Done(): | ||
// cancel() | ||
// return ctx.Err() | ||
// case <-timer.C: | ||
// continue | ||
// } | ||
// } | ||
func TimerWithCancel(delay time.Duration) (*time.Timer, func()) { | ||
timer := time.NewTimer(delay) | ||
|
||
return timer, func() { | ||
// Note: The Stop function returns: | ||
// - true: if the timer is active. (no draining required) | ||
// - false: if the timer was already stopped or fired/expired. | ||
// In this case the channel should be drained to prevent memory | ||
// leaks only if it is not empty. | ||
// This operation is safe only if the cancel function is | ||
// used in same goroutine. Concurrent reading or canceling may | ||
// cause deadlock. | ||
if !timer.Stop() && len(timer.C) > 0 { | ||
<-timer.C | ||
} | ||
} | ||
} |