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.