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

cmd/compile: range chan loop retains previously received value while blocked #15281

Closed
rsc opened this issue Apr 13, 2016 · 2 comments
Closed
Milestone

Comments

@rsc
Copy link
Contributor

rsc commented Apr 13, 2016

[This would be nice to fix for Go 1.7 but is not a regression from Go 1.6 and so is not critical.]

During a range loop on a channel, it appears that the previously received value is held live while blocked waiting for the next value on the channel. The program below demonstrates: f1 retains x even after it is cleared during the loop, while f2 does not. Adjusting the program to block on the channel and then check the memory usage after a suitable timeout produces the same result (but requires sleeps and the like). This matters if the values being received are large and timely garbage collection is important.

This could be fixed by mucking with the liveness code, but it could also be fixed by zeroing the channel receive temporary in the code generated by the range loop after copying it into the real iteration variable. The latter is much less scary at this point in the cycle.

/cc @randall77 @dr2chase @RLH @aclements

package main

import "runtime"

func main() {
    {
        x := inuse()
        c := make(chan []byte, 10)
        c <- make([]byte, 10<<20)
        close(c)
        f1(c, x)
    }
    {
        x := inuse()
        c := make(chan []byte, 10)
        c <- make([]byte, 10<<20)
        close(c)
        f2(c, x)
    }
}

func f1(c chan []byte, start int64) {
    for x := range c {
        if delta := inuse() - start; delta < 9<<20 {
            println("f1: after alloc: expected delta at least 9MB, got: ", delta)
        }
        println(x)
        x = nil
        if delta := inuse() - start; delta > 1<<20 {
            println("f1: after alloc: expected delta below 1MB, got: ", delta)
        }
        println(x)
    }
}

func f2(c chan []byte, start int64) {
    for {
        x, ok := <-c
        if !ok {
            break
        }
        if delta := inuse() - start; delta < 9<<20 {
            println("f2: after alloc: expected delta at least 9MB, got: ", delta)
        }
        println(x)
        x = nil
        if delta := inuse() - start; delta > 1<<20 {
            println("f2: after alloc: expected delta below 1MB, got: ", delta)
        }
        println(x)
    }
}

func inuse() int64 {
    runtime.GC()
    var st runtime.MemStats
    runtime.ReadMemStats(&st)
    return int64(st.Alloc)
}
@rsc rsc added this to the Go1.7 milestone Apr 13, 2016
@josharian
Copy link
Contributor

Started.

@gopherbot
Copy link

CL https://golang.org/cl/22082 mentions this issue.

@golang golang locked and limited conversation to collaborators Apr 15, 2017
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

3 participants