-
Notifications
You must be signed in to change notification settings - Fork 18k
runtime: deleted timer was not cleaned up in time, causing the context chain saved in timerCtx to not be GCed in time #60144
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
package main
import (
"context"
"flag"
"fmt"
"runtime"
"time"
)
type A [100]int
func run(i int) func() {
c := context.TODO()
a := &A{}
runtime.SetFinalizer(a, func(a *A) {
fmt.Println("Free ", i)
})
c = context.WithValue(c, "a", a)
c, cancel := context.WithTimeout(c, time.Second*time.Duration(i))
return cancel
}
func run2(i int) *time.Timer {
a := &A{}
runtime.SetFinalizer(a, func(a *A) {
fmt.Println("Free ", i)
})
return time.AfterFunc(time.Duration(i)*time.Second, func() {
fmt.Println(i, a[3])
})
}
func main() {
count := flag.Int("c", 1, "Count")
flag.Parse()
fmt.Println("Count: ", *count)
for i := 0; i < *count; i++ {
// run(6)
run2(6)
}
// run(3)()
run2(3).Stop()
begin := time.Now()
for i := 0; i < 10; i++ {
runtime.GC()
time.Sleep(time.Second)
fmt.Printf("After %.2f seconds\n", time.Since(begin).Seconds())
}
}
|
There are absolutely no guarantees as to when finalizers are run. They may never be run. Quoting https://pkg.go.dev/runtime#SetFinalizer
|
As @ianlancetaylor noted, the finalizer doesn't require to run at a specific time. If this causes issues for real production programs, please add more detail. Closing for now. Thanks. |
The focus of this issue is not on finalizers, they are only used to demonstrate when "a" should be garbage collected. By setting GODEBUG=allocfreetrace=1, we can also see that "a" is not being garbage collected in time. |
It's kind of the same problem, though. Garbage collection is not guaranteed to happen on any particular time scale. I'm not sure we can fix this. The timer code is tied into the scheduler. One aspect of this is that timers are not released from the queue when they are stopped. Instead they are simply marked as deleted, and a later scheduling pass will remove them. So the timer will remain pointing to the function in the queue. We can't simply clear the function in the Actually, though, I think I do see a way, though I'm not sure whether it is a good idea. The context code knows that it won't call the |
Change https://go.dev/cl/495080 mentions this issue: |
OK, that change to the context package doesn't work. We could perhaps add a Timer.Clear method. That would require a proposal. See https://go.dev/s/proposal. |
Thanks @ianlancetaylor . I think Timer.Clear should just clear Timer.r.f and Timer.r.arg. I have tested it and it works fine. |
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
Yes.
It can also reproduce in 1.19.
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
test4.go:
go1.20.4 run test4.go -c 2
go1.20.4 run test4.go -c 4
What did you expect to see?
What did you see instead?
Expect to see
Free 3
in the first round of GC. But when-c 4
, it appeared after 3 seconds.The text was updated successfully, but these errors were encountered: