yes, It is a data race. But the problem mentioned by op is not caused by data race.
On Tuesday, September 12, 2017 at 3:59:32 AM UTC-4, Konstantin Khomoutov wrote: > > On Wed, Sep 06, 2017 at 04:26:09AM -0700, T L wrote: > > > > > It is just weird that the evaluation timing of *p is different to > other > > > expressions, such as, *p+0, *p*1, func()int{return *p}m etc. > > > > > > The value depends on a data race so it's entirely undefined in all > cases. > > > That the actual outcome then depends on trivial differences that may > affect > > > CPU loads/stores, instruction ordering, register usage, etc isn't > > > surprising. > > > > I don't think it is so complex. It is simply that gc adopts a different > > route for evaluate pointer dereference. > > It's not complex, it's a simple data race. > > In your code > > var num = 10 > var p = &num > > c := make(chan int) > > go func() { > c <- func()int{return *p}() > }() > > time.Sleep(time.Second) > num++ > fmt.Println(<-c) > > two goroutines -- the main one and the one you spawned are accessing the > variable "num" concurrently without any synchronization. > > Some assorted points to may be make this more clear: > > * Sleeping for "just enough" time is not a form of synchronization. > > * You might be driven away by using a function literal which is > immediately executed but that's pretty much equivalent to just doing > > c <- *p > > which supposedly makes the data race over the variable "num" > even more obvious. > > * You might keep false assumption that function literals producing > function values close over values -- that is, they work sort of as > code templates. This is wrong: they close over variables, so in your > example the function value you're constructing before running it on > a separate goroutine closes over the variable "p", not the value > obtained by dereferencing it at the time of producing that function > value. > > > To explain what happens, step by step, the statement > > go func() { > c <- func()int{return *p}() > }() > > in your example: > > 1) Creates a function value which is a closure consisting of the > anonymous function > > func() { > c <- func()int{return *p}() > } > > and a reference to the variable "p" in the scope of the function > main(). > > 2) Spawns a goroutine and tells it to execute the function value > created on the previous step. > > 3) When run, the function executes the function value produced by > the expression on the right side of the channel send statement. > > That function value is itself a closure, closing over the variable > "p" in the scope of its lexical parent function -- the closure > created on step 1. > > The pointer gets dereferenced and this action accesses the memory > location "backing" the variable "num". > The same memory is accessed concurrently from the main goroutine. > > Since there is no synchronization done for _this_ access, you have > a data race. The fact you have a channel send/receive has nothing > to do with regard to accessing the memory of the "num" variable > since both goroutines do it before attempting its respective channel > send/receive operations. During that time, they may have being run on > different hardware CPUs or one of the goroutines might have no chance to > run at all. Running on different CPUs involves a whole lot of > interesting problems, of which the most glaring are reordering of memory > accesses and coherence problems of CPU caches. > > Please read [1, 2] and [3] (which is written by one of the Go core devs). > > 1. http://preshing.com/20120515/memory-reordering-caught-in-the-act/ > 2. > http://preshing.com/20120710/memory-barriers-are-like-source-control-operations/ > > 3. > https://software.intel.com/en-us/blogs/2013/01/06/benign-data-races-what-could-possibly-go-wrong > > > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.