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

doc/go_mem: "hello, world" will not always be printed twice #33815

Open
ehsangifani opened this issue Aug 24, 2019 · 6 comments

Comments

@ehsangifani
Copy link

commented Aug 24, 2019

It seems like the referenced doc is misleading:

The result will be that <code>"hello, world"</code> will be printed

What did you do?

package main

import "sync"

var a string
var once sync.Once

func setup() {
	a = "hello, world"
}

func doprint() {
	once.Do(setup)
	print(a)
}

func twoprint() {
	go doprint()
	go doprint()
}

func main() {
	twoprint()
}

What did you expect to see?

The result will be that "hello, world" will be printed twice.

What did you see instead?

Sometimes it was printed twice, and sometimes it was not printed at all.

System details

go version go1.12.9 windows/amd64
GOARCH="amd64"
GOBIN=""
GOCACHE="C:\Users\*****\AppData\Local\go-build"
GOEXE=".exe"
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="windows"
GOOS="windows"
GOPATH="C:\Users\******\go"
GOPROXY=""
GORACE=""
GOROOT="c:\go"
GOTMPDIR=""
GOTOOLDIR="c:\go\pkg\tool\windows_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
GOROOT/bin/go version: go version go1.12.9 windows/amd64
GOROOT/bin/go tool compile -V: compile version go1.12.9
@DisposaBoy

This comment has been minimized.

Copy link

commented Aug 24, 2019

You need some kind of synchronization like a channel or sync.WaitGroup to wait for the goroutines to complete before returning from main().

Quoting https://golang.org/ref/spec#Program_execution

Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

@ehsangifani

This comment has been minimized.

Copy link
Author

commented Aug 24, 2019

Yes, of course. But the problem is with the documentation that states printing twice happens without any mention of a synchronization method.
Here is my proposed change to the example code that shows the functionality of Once.Do while being correct:

var a string
var once sync.Once

func setup() {
	a += "hello, world" //change: to show that it doesn't run twice
}

func doprint(done chan int) {
	once.Do(setup)
	print(a)
	done <- 1
}

func twoprint() {
	var done = make(chan int) //change: synchronization
	go doprint(done)
	go doprint(done)
	<-done
	<-done
}
@bcmills

This comment has been minimized.

Copy link
Member

commented Aug 26, 2019

You assumed a particular func main(). This one is guaranteed to always execute both prints — just not guaranteed to terminate!

@bcmills

This comment has been minimized.

Copy link
Member

commented Aug 26, 2019

On the one hand, the missing func main() does leave out an interesting detail.

On the other hand, the document is full of interesting details already — and adding that detail to one example implies that we would need to add it to the other examples, obscuring the more important point about the specific synchronization mechanism that is the focus of the example.

Is there a way to make the examples more correct without making them substantially more verbose?

@bcmills bcmills added this to the Unplanned milestone Aug 26, 2019

@bcmills

This comment has been minimized.

Copy link
Member

commented Aug 26, 2019

CC @dvyukov @ianlancetaylor

See previously #27808.

@bcmills bcmills changed the title doc: go memory model, "hello, world" will not always be printed twice. doc/go_mem: "hello, world" will not always be printed twice Aug 26, 2019

@ehsangifani

This comment has been minimized.

Copy link
Author

commented Sep 5, 2019

@bcmills
Actually, I didn't assume anything. As you can see on this line , it says calling twoprint will call setup exactly once.
So it's the issue of wording in my opinion, which could create a wrong impression to someone new to Go, that sync.Once somehow stops main from returning before its spawned go-routines return.

I think changing the sentence to something like:
calling twoprint , assuming main() won't return yet, will call setup exactly once.
can somewhat mitigate the situtation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.