Skip to content
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

runtime: cpu-bound goroutine blocks finalization #1671

Closed
rogpeppe opened this issue Apr 7, 2011 · 19 comments
Closed

runtime: cpu-bound goroutine blocks finalization #1671

rogpeppe opened this issue Apr 7, 2011 · 19 comments

Comments

@rogpeppe
Copy link
Contributor

rogpeppe commented Apr 7, 2011

When I run the following program, I get this output:

finalizing 251; total 600
finalizing 250; total 602
finalizing 249; total 3997
finalizing 248; total 9501
finalizing 247; total 20908
finalizing 246; total 53407
finalizing 245; total 100954
finalizing 244; total 225684
finalizing 243; total 526626
finalizing 242; total 984099
finalizing 241; total 2116933
finalizing 240; total 4875429

Hardly any finalizers are called, even though there is evidently
some garbage collection going on.

6g macos f618e5e0991d+ tip

package main

import (
    "runtime"
    "fmt"
)

type Foo bool

func main() {
    for {
        newFoo()
    }
}

var count int

func newFoo() *Foo {
    f := new(Foo)
    this := count
    runtime.SetFinalizer(f, func(_ *Foo) {
        fmt.Printf("finalizing %d; total %d\n", this, count)
    })
    count++
    return f
}
@rsc
Copy link
Contributor

rsc commented Apr 8, 2011

Comment 1:

Revenge of issue #631.

Owner changed to @rsc.

Status changed to Accepted.

@rsc
Copy link
Contributor

rsc commented Jul 28, 2011

Comment 2:

I cannot reproduce this on Linux using 6g.
Are you still seeing it on OS X?

@gopherbot
Copy link

Comment 3 by webmaster@webmaster.ms:

reproducible on Windows and Linux

@rsc
Copy link
Contributor

rsc commented Oct 6, 2011

Comment 4:

Variant showing progress:
package main
import (
    "fmt"
    "runtime"
)
type Foo bool
func main() {
    for {
        newFoo()
        runtime.Gosched()
        runtime.Gosched()
        runtime.Gosched()
        runtime.Gosched()
        runtime.Gosched()
    }
}
var count, ndone int
func newFoo() *Foo {
    f := new(Foo)
    this := count
    runtime.SetFinalizer(f, func(_ *Foo) {
        ndone++
        fmt.Printf("finalizing #%d; total %d/%d\n", this, ndone, count)
    })
    count++
    return f
}
Dmitriy, you were looking at finalizers recently.  Any ideas?

@rsc
Copy link
Contributor

rsc commented Oct 6, 2011

Comment 5:

Status changed to HelpWanted.

@dvyukov
Copy link
Member

dvyukov commented Oct 7, 2011

Comment 6:

The problem is with fmt.Printf() which does a blocking syscall (os.Write), which in turn
causes goroutine rescheduling. So exactly 1 finalizer is run per GC. The problem does
not show up if either:
1. fmt.Printf() is replaced with println()
or 2. main goroutine outputs stats
or 3. GOMAXPROCS=2
or 4. main goroutine episodically executes sleep
So the issue is just an unfortunate interaction of several factor. I think we are
closing it.

@rsc
Copy link
Contributor

rsc commented Oct 7, 2011

Comment 7:

Thanks for identifying why it happens.
Please leave this open.  We need to fix this
one way or another.
Russ

Status changed to LongTerm.

@rsc
Copy link
Contributor

rsc commented Dec 9, 2011

Comment 8:

Labels changed: added priority-later.

@rsc
Copy link
Contributor

rsc commented Dec 12, 2011

Comment 9:

Labels changed: added priority-go1.

@robpike
Copy link
Contributor

robpike commented Jan 13, 2012

Comment 10:

Owner changed to builder@golang.org.

@rsc
Copy link
Contributor

rsc commented Feb 20, 2012

Comment 11:

package main
    
    import (
        "runtime"
        "fmt"
        "time"
    )
    
    type Foo bool
    
    func main() {
        for i := 0;; i++ {
            newFoo()
            if i%100 == 0 {
                time.Sleep(100*time.Millisecond)
            }
        }
    }
    
    var count int
    var nfin int
    
    func newFoo() *Foo {
        f := new(Foo)
        this := count
        runtime.SetFinalizer(f, func(_ *Foo) {
            nfin++
            fmt.Printf("finalized #%d %d/%d\n", this, nfin, count)
        })
        count++
        return f
    }
Sleeping every once in a while makes it work fine, so this only affects cpu-bound
programs.  In that sense it is not much different from any other cpu-bound goroutine
starving the others.  This won't be fixed for Go 1, but at least we understand it.

Labels changed: added priority-later, removed priority-go1.

@rsc
Copy link
Contributor

rsc commented Sep 12, 2012

Comment 12:

Labels changed: added go1.1maybe.

@robpike
Copy link
Contributor

robpike commented Mar 7, 2013

Comment 13:

Labels changed: removed go1.1maybe.

@rsc
Copy link
Contributor

rsc commented Nov 27, 2013

Comment 14:

Labels changed: added go1.3maybe.

@rsc
Copy link
Contributor

rsc commented Dec 4, 2013

Comment 15:

Labels changed: added release-none, removed go1.3maybe.

@rsc
Copy link
Contributor

rsc commented Dec 4, 2013

Comment 16:

Labels changed: added repo-main.

@bradfitz
Copy link
Contributor

bradfitz commented Feb 2, 2017

@aclements, I'll let you dup or close this one as appropriate.

@aclements
Copy link
Member

While it's still theoretically possible for us to fall behind on the finalizers queue since there's no backpressure on it, it looks like we keep up just fine now in practice, even without any sleeps or explicit Goscheds. I guess there have been some improvements to the scheduler in the past five years. :)

@ianlancetaylor
Copy link
Contributor

I would guess that the relevant change was invoking the finalizer queue from the sysmon thread, in https://golang.org/cl/75960043.

@golang golang locked and limited conversation to collaborators Feb 2, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

8 participants