aboutsummaryrefslogtreecommitdiff
path: root/test/tinyfin.go
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2015-11-03 12:19:15 +0100
committerDmitry Vyukov <dvyukov@google.com>2015-11-03 18:57:18 +0000
commitbf606094ee0671cdd776aea43aabb842b6c1e3af (patch)
treef32c9c73fe91426a3f89f162e2a32614cf164f28 /test/tinyfin.go
parent95333aea53e1476587e29a55e3e4f34ccf61ce6a (diff)
downloadgo-bf606094ee0671cdd776aea43aabb842b6c1e3af.tar.gz
go-bf606094ee0671cdd776aea43aabb842b6c1e3af.zip
runtime: fix finalization and profiling of tiny allocations
Handling of special records for tiny allocations has two problems: 1. Once we queue a finalizer we mark the object. As the result any subsequent finalizers for the same object will not be queued during this GC cycle. If we have 16 finalizers setup (the worst case), finalization will take 16 GC cycles. This is what caused misbehave of tinyfin.go. The actual flakiness was caused by the fact that fing is asynchronous and don't always run before the check. 2. If a tiny block has both finalizer and profile specials, it is possible that we both queue finalizer, preserve the object live and free the profile record. As the result heap profile can be skewed. Fix both issues by analyzing all special records for a single object at once. Also, make tinyfin test stricter and remove reliance on real time. Also, add a test for the problem 2. Currently heap profile missed about a half of live memory. Fixes #13100 Change-Id: I9ae4dc1c44893724138a4565ca5cae29f2e97544 Reviewed-on: https://go-review.googlesource.com/16591 Reviewed-by: Austin Clements <austin@google.com> Reviewed-by: Keith Randall <khr@golang.org> Run-TryBot: Dmitry Vyukov <dvyukov@google.com>
Diffstat (limited to 'test/tinyfin.go')
-rw-r--r--test/tinyfin.go56
1 files changed, 31 insertions, 25 deletions
diff --git a/test/tinyfin.go b/test/tinyfin.go
index d9ffa7cab2..5171dfc72e 100644
--- a/test/tinyfin.go
+++ b/test/tinyfin.go
@@ -10,7 +10,6 @@ package main
import (
"runtime"
- "sync/atomic"
"time"
)
@@ -20,39 +19,46 @@ func main() {
if runtime.Compiler == "gccgo" {
return
}
- N := int32(100)
- count := N
- done := make([]bool, N)
- for i := int32(0); i < N; i++ {
+ const N = 100
+ finalized := make(chan int32, N)
+ for i := 0; i < N; i++ {
x := new(int32) // subject to tiny alloc
- *x = i
+ *x = int32(i)
// the closure must be big enough to be combined
runtime.SetFinalizer(x, func(p *int32) {
+ finalized <- *p
+ })
+ }
+ runtime.GC()
+ count := 0
+ done := make([]bool, N)
+ timeout := time.After(5*time.Second)
+ for {
+ select {
+ case <-timeout:
+ println("timeout,", count, "finalized so far")
+ panic("not all finalizers are called")
+ case x := <-finalized:
// Check that p points to the correct subobject of the tiny allocation.
// It's a bit tricky, because we can't capture another variable
// with the expected value (it would be combined as well).
- if *p < 0 || *p >= N {
- println("got", *p)
+ if x < 0 || x >= N {
+ println("got", x)
panic("corrupted")
}
- if done[*p] {
- println("got", *p)
+ if done[x] {
+ println("got", x)
panic("already finalized")
}
- done[*p] = true
- atomic.AddInt32(&count, -1)
- })
- }
- for i := 0; i < 4; i++ {
- runtime.GC()
- time.Sleep(10 * time.Millisecond)
- }
- // Some of the finalizers may not be executed,
- // if the outermost allocations are combined with something persistent.
- // Currently 4 int32's are combined into a 16-byte block,
- // ensure that most of them are finalized.
- if atomic.LoadInt32(&count) >= N/4 {
- println(count, "out of", N, "finalizer are not called")
- panic("not all finalizers are called")
+ done[x] = true
+ count++
+ if count > N/10*9 {
+ // Some of the finalizers may not be executed,
+ // if the outermost allocations are combined with something persistent.
+ // Currently 4 int32's are combined into a 16-byte block,
+ // ensure that most of them are finalized.
+ return
+ }
+ }
}
}