]> Cypherpunks.ru repositories - gostls13.git/commitdiff
runtime: fix memory leaks due to defers
authorDmitriy Vyukov <dvyukov@google.com>
Mon, 1 Jul 2013 21:36:08 +0000 (17:36 -0400)
committerRuss Cox <rsc@golang.org>
Mon, 1 Jul 2013 21:36:08 +0000 (17:36 -0400)
fn can clearly hold a closure in memory.
argp/pc point into stack and so can hold
in memory a block that was previously
a large stack serment.

R=golang-dev, dave, rsc
CC=golang-dev
https://golang.org/cl/10784043

src/pkg/runtime/panic.c
test/deferfin.go [new file with mode: 0644]

index 963063a8e85fcf9ac3c29342f595334a11d3c6d7..8ec8078d22d414a93998f7efc95d1e92df55c94e 100644 (file)
@@ -104,11 +104,15 @@ popdefer(void)
 static void
 freedefer(Defer *d)
 {
+       int32 total;
+
        if(d->special) {
                if(d->free)
                        runtime·free(d);
        } else {
-               runtime·memclr((byte*)d->args, d->siz);
+               // Wipe out any possible pointers in argp/pc/fn/args.
+               total = sizeof(*d) + ROUND(d->siz, sizeof(uintptr)) - sizeof(d->args);
+               runtime·memclr((byte*)d, total);
        }
 }
 
diff --git a/test/deferfin.go b/test/deferfin.go
new file mode 100644 (file)
index 0000000..f9a74eb
--- /dev/null
@@ -0,0 +1,59 @@
+// run
+
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Test that defers do not prevent garbage collection.
+
+package main
+
+import (
+       "runtime"
+       "sync"
+       "sync/atomic"
+       "time"
+)
+
+var sink func()
+
+func main() {
+       // Does not work on 32-bits due to partially conservative GC.
+       // Try to enable when we have fully precise GC.
+       if runtime.GOARCH != "amd64" {
+               return
+       }
+       N := 10
+       count := int32(N)
+       var wg sync.WaitGroup
+       wg.Add(N)
+       for i := 0; i < N; i++ {
+               go func() {
+                       defer wg.Done()
+                       v := new(int)
+                       f := func() {
+                               if *v != 0 {
+                                       panic("oops")
+                               }
+                       }
+                       if *v != 0 {
+                               // let the compiler think f escapes
+                               sink = f
+                       }
+                       runtime.SetFinalizer(v, func(p *int) {
+                               atomic.AddInt32(&count, -1)
+                       })
+                       defer f()
+               }()
+       }
+       wg.Wait()
+       for i := 0; i < 3; i++ {
+               time.Sleep(10 * time.Millisecond)
+               runtime.GC()
+       }
+       if count != 0 {
+               println(count, "out of", N, "finalizer are not called")
+               panic("not all finalizers are called")
+       }
+}
+