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

i+=1 as a only code line in a for loop didn't work normally #36012

Closed
trainyao opened this issue Dec 6, 2019 · 6 comments
Closed

i+=1 as a only code line in a for loop didn't work normally #36012

trainyao opened this issue Dec 6, 2019 · 6 comments

Comments

@trainyao
Copy link

trainyao commented Dec 6, 2019

What version of Go are you using (go version)?

$ go version
go version go1.13.5 linux/amd64

Does this issue reproduce with the latest release?

yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN="/home/me/go/bin"
GOCACHE="/home/me/.cache/go-build"
GOENV="/home/me/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/me/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build161703294=/tmp/go-build -gno-record-gcc-switches"

What did you do?

1. origin question

run following go script and get different output with and without the comment line

package main

import (
        "fmt"
        "time"
)

func main() {
        var i = 0

        go func() {
                for {
                        i += 1
                //      time.Sleep(1*time.Nanosecond)     <- this comment line
                }
        }()

        for {
                fmt.Println("i is", i)
                time.Sleep(time.Second)
        }
}

with the comment line I get

$ go run main.go
i is 0
i is 3673005
i is 7360961
i is 11182533
i is 14892563
i is 18696280
i is 21821713
....

but strangely, without that comment line I get:

$ go run main.go 
i is 0
i is 0
i is 0
i is 0
i is 0
i is 0
....

2. debug by checking assemble code

with and without the comment line I get different assemble code:
assemble code shown below include the anonymous function run by go routine only.

without that comment line:

....

TEXT main.main.func1(SB) /home/me/go/src/github.com/trainyao/test/main.go
		for {
  0x48d0c0		eb00			JMP 0x48d0c2		
			i += 1
  0x48d0c2		eb00			JMP 0x48d0c4		
  0x48d0c4		eb00			JMP 0x48d0c6		
  0x48d0c6		ebfa			JMP 0x48d0c2	

.....

with that comment line:

...

TEXT main.main.func1(SB) /home/me/go/src/github.com/trainyao/test/main.go
	go func() {
  0x48d0c0		64488b0c25f8ffffff	MOVQ FS:0xfffffff8, CX	
  0x48d0c9		483b6110		CMPQ 0x10(CX), SP	
  0x48d0cd		7636			JBE 0x48d105		
  0x48d0cf		4883ec10		SUBQ $0x10, SP		
  0x48d0d3		48896c2408		MOVQ BP, 0x8(SP)	
  0x48d0d8		488d6c2408		LEAQ 0x8(SP), BP	
		for {
  0x48d0dd		eb00			JMP 0x48d0df		
			i += 1
  0x48d0df		eb00			JMP 0x48d0e1		
  0x48d0e1		488b442418		MOVQ 0x18(SP), AX	
  0x48d0e6		488b00			MOVQ 0(AX), AX		
  0x48d0e9		488b4c2418		MOVQ 0x18(SP), CX	
  0x48d0ee		48ffc0			INCQ AX			
  0x48d0f1		488901			MOVQ AX, 0(CX)		
			time.Sleep(1*time.Nanosecond)
  0x48d0f4		48c7042401000000	MOVQ $0x1, 0(SP)	
  0x48d0fc		e8df71fbff		CALL time.Sleep(SB)	
  0x48d101		eb00			JMP 0x48d103		
			i += 1
  0x48d103		ebda			JMP 0x48d0df		
	go func() {
  0x48d105		e8f646fcff		CALL runtime.morestack_noctxt(SB)	
  0x48d10a		ebb4			JMP main.main.func1(SB)		

...

it seems like go complier ignore that i+=1 when complie into assemble code, and make that i+=1 do nothing.

command I use to generate assemble code:

$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build --gcflags "-N -l" -o main main.go
$ go tool objdump -S main > main.S  

What did you expect to see?

output of main.go is the same, with and without that comment line

What did you see instead?

different output

@nanmu42
Copy link

nanmu42 commented Dec 6, 2019

This problem also happens on go version go1.13.4 linux/amd64.

package main

import (
	"fmt"
	"time"
)

func main() {
	var i, j int

	noop := func() {}

	onlyAdd := func() {
		fmt.Println("onlyAdd runs")
		for {
			i += 1
		}
	}

	notOnlyAdd := func() {
		fmt.Println("notOnlyAdd runs")
		for {
			j += 1
			noop()
		}
	}

	go onlyAdd()
	go notOnlyAdd()

	for {
		fmt.Println("i is", i, " and j is", j)
		time.Sleep(time.Second)
	}
}

output:

i is 0  and j is 0
onlyAdd runs
notOnlyAdd runs
i is 0  and j is 715125111
i is 0  and j is 1433046851
i is 0  and j is 2154902962
i is 0  and j is 2877082636
i is 0  and j is 3599230418
i is 0  and j is 4319943058
i is 0  and j is 5041545338
i is 0  and j is 5758950518
^C

@darkLord19
Copy link
Contributor

Same issue on macos on go1.13.4 darwin/amd64.

i is 0  and j is 0
onlyAdd runs
notOnlyAdd runs
i is 0  and j is 800290823
i is 0  and j is 1604651414
i is 0  and j is 2395110243
i is 0  and j is 3187198741
i is 0  and j is 3971968750
i is 0  and j is 4752123974
...

@mvdan
Copy link
Member

mvdan commented Dec 6, 2019

You have a data race. You can see it if you do go run -race main.go, or if you read https://golang.org/doc/articles/race_detector.html.

You should use https://golang.org/wiki/Questions for general questions about why your code doesn't work.

@mvdan mvdan closed this as completed Dec 6, 2019
@lyeka
Copy link

lyeka commented Dec 6, 2019

if use go run -race main.go,
it output

i is 53406210
i is 106633590
i is 160145249
i is 213722371
i is 266922550

it looks like the race tool affected the code?

@trainyao
Copy link
Author

trainyao commented Dec 6, 2019

You have a data race. You can see it if you do go run -race main.go, or if you read https://golang.org/doc/articles/race_detector.html.

You should use https://golang.org/wiki/Questions for general questions about why your code doesn't work.

I know there is a data race in the code, but a data race would not make the for loop not adding i variable right? and how to explain the assemble code generated....will data race effect code compile? @mvdan

@randall77
Copy link
Contributor

for {
i += 1
}

This is an infinite loop with no synchronization. No other thread is guaranteed to ever see the increment (because that would be a data race, and behavior under data races is undefined), so the compiler optimizes it away.

@golang golang locked and limited conversation to collaborators Dec 5, 2020
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

7 participants