Subtle observation--it is not fundamentally the memory model theorized in Go or C++ specifications that causes this--rather it is the *least strong guarantee of the hardware *on which these languages promise that *standard programs will work as specified, *and following that, the decision of language designers how efficiently they want to use the fastest of that hardware.
If one is designing a language that is more demanding than the execution hardware promises, that's fine. It just means that the generated/evaluated code must virtualize what appears to be simple things. For example, if we want multiple threads to be able to do "i++" against the same variable, we can generate an implicit mutex around every access to every "i" that could ever be visible to a concurrent thread. There is nothing wrong with this, but the resulting code will not generally be as fast as the code of other languages where the offer is less general, say, requiring developers to put locks around critical sections or use atomic increments when they know memory could be accessed concurrently. As it happens, the "auto-magic" mode can be just as fast in some cases. On IBM POWER9+ there are special memory ranges where the synchronization is hardware facilitated. On the CPUs most people have, though, the code would be much slower and this would likely be seen as a flaw. On Sun, Mar 18, 2018 at 2:30 AM, Devon H. O'Dell <devon.od...@gmail.com> wrote: > 2018-03-18 1:42 GMT-07:00 David Anderson <d...@natulte.net>: > > There's a difference though. The program that uses sync/atomic will > behave > > non-deterministically, but within a small set of possible outcomes. The > one > > While a fair point, I'd also note the set of possible outcomes is not > small and my point is more that the term "race" doesn't just mean > "access that violates a particular language's memory model." > > > without could do anything. In addition to producing an incorrect numeric > > result, it could deadlock, segfault, jump execution to some random place > in > > the program code, ... > > "Incorrect" is subjective WRT the counters themselves; there is no > concurrent access to those. I agree that, per the Go specification, > the program is not guaranteed to halt without the use of sync/atomic; > I should've worded that better. But the non-determinism in the > behavior of the program is precisely because of racing. (The term > itself comes from EE where effectively the length of wire can cause > races in signals.) > > > I highly recommend > > https://software.intel.com/en-us/blogs/2013/01/06/benign- > data-races-what-could-possibly-go-wrong > > . It's an excellent article (by the author of the go race detector) that > > explains why safe, benign data races are never safe or benign. The > examples > > are in C++, but the same applies to Go - the compiler makes assumptions > > about code execution in the absence of synchronization points, and even > > simple optimizations can have hilarious consequences on code that > violates > > those assumptions. > > It's a good article; I've read it several times before and should've > qualified things better. Some points related to the article: > > * Prior to C11, C had no concurrency model and as such, any > multithreaded program exhibits undefined behavior when compared to the > specification. > * POSIX avoids a formal specification of a memory model entirely and > instead encourages what can effectively be summed up as "best > practices" for concurrent access, so it's no help either. > * Both C and C++ have adopted memory models that are provably > inconsistent (though I seem to have lost links to the papers). > > So while it's easy for this sort of thing to be "mis"-compiled, it's > still very much true that we're already relying on compiler behavior > (as opposed to some formal specification) in many languages. > > While the languages themselves may leave this behavior undefined, it's > easy enough to link against things written in other languages in most > of them. So we can take my invalid program, link it with some > racy_stop.s that does (for example) MOVB $1, (DI), relying on whatever > ordering and visibility semantics the machine provides, and implement > a similar interface for reading that memory. At that point which point > language specification is moot, and my crappy version is guaranteed to > halt again. > > Anyway, I had hoped to not get into the weeds here. Ignoring the > version that is not guaranteed to halt when specified in pure Go, > non-determinism in both other examples occurs due to racing on data. > "Any race is a bug" is not true, and that was really the only point I > was trying to make. > > > - Dave > > --dho > > > On Sun, Mar 18, 2018 at 1:19 AM, Devon H. O'Dell <devon.od...@gmail.com> > > wrote: > >> > >> Perhaps better illustrated: > >> > >> package main > >> > >> import ( > >> "fmt" > >> "time" > >> ) > >> > >> type info struct { > >> count int > >> id int > >> } > >> > >> var stop int32 > >> > >> func runner(id int, c chan info) { > >> info := info{id: id} > >> for stop == 0 { > >> info.count++ > >> time.Sleep(1 * time.Microsecond) > >> } > >> c <- info > >> } > >> > >> func main() { > >> c := make(chan info) > >> for i := 0; i < 10; i++ { > >> go runner(i, c) > >> } > >> > >> time.Sleep(10 * time.Millisecond) > >> stop = 1 > >> > >> for i := 0; i < 10; i++ { > >> fmt.Printf("Returned %+v\n", <-c) > >> } > >> } > >> > >> This program is guaranteed to halt, regardless of any GOMAXPROCS > >> setting. If you use the race detector on this program, it will > >> complain. You can "fix" "the race" by changing the loop condition to > >> "atomic.LoadInt32(&stop) == 0" and use atomic.StoreInt32(&stop, 1) as > >> your signal. Is the program still racy? Yep. Is either version of this > >> program buggy? That just depends. It's not too hard to think of cases > >> where this behavior might be desirable; even lossy counters are a > >> thing. > >> > >> --dho > >> > >> 2018-03-18 0:43 GMT-07:00 Devon H. O'Dell <devon.od...@gmail.com>: > >> > 2018-03-16 2:16 GMT-07:00 Jérôme Champion <champ...@gmail.com>: > >> >> Any race is a bug. When there is a race, the compiler is free to do > >> >> whatever > >> >> it wants. > >> > > >> > This is a false statement; whether or not a race constitutes a bug > >> > depends on the encapsulating protocol. Consider the following: > >> > > >> > package main > >> > > >> > import ( > >> > "fmt" > >> > "sync" > >> > "sync/atomic" > >> > ) > >> > > >> > var wg sync.WaitGroup > >> > > >> > type Spinlock struct { > >> > state uint32 > >> > } > >> > > >> > func (s *Spinlock) Lock() { > >> > for atomic.SwapUint32(&s.state, 1) == 1 { > >> > } > >> > } > >> > > >> > func (s *Spinlock) Unlock() { > >> > atomic.StoreUint32(&s.state, 0) > >> > } > >> > > >> > func locker(s *Spinlock, i int) { > >> > defer wg.Done() > >> > s.Lock() > >> > fmt.Printf("Locker %d acquired lock\n", i) > >> > s.Unlock() > >> > } > >> > > >> > func main() { > >> > s := &Spinlock{} > >> > for i := 0; i < 10; i++ { > >> > wg.Add(1) > >> > go locker(s, i) > >> > } > >> > wg.Wait() > >> > } > >> > > >> > This program does not have deterministic output because each goroutine > >> > races to acquire the lock, and which gets it first relies on > >> > scheduling, which is effectively stoachastic. (Playground caches > >> > output, so no use illustrating it there.) Despite this, the program > >> > compiles just fine and behaves correctly for any reasonable definition > >> > of correct. > >> > > >> > I recognize this is not the issue mentioned in OP, but hopefully it's > >> > clear that this is not just a semantics argument (race doesn't just > >> > mean "only things the race detector can find") and that the statement > >> > "any race is a bug" is false. This race only constitutes a bug when > >> > the requirements of this program require deterministic output. > >> > > >> > --dho > >> > > >> >> What do you want to do? Do you want to println before or after > >> >> cancelContext? In your example, println could even be executed during > >> >> cancelContext! > >> >> Your example is not enough, such program : > >> >> https://play.golang.org/p/KJsYwu-ivjb would fix your problem, but I > >> >> don't > >> >> think that's what you asked for :) > >> >> > >> >> > >> >> Le vendredi 16 mars 2018 03:11:49 UTC+1, Anmol Sethi a écrit : > >> >>> > >> >>> https://play.golang.org/p/82Um1jSntBo > >> >>> > >> >>> Please run with the race detector to see the race. > >> >>> > >> >>> This race condition arises because private variables are read via > >> >>> reflection by the fmt package. Is there any elegant way to avoid > such > >> >>> a > >> >>> race? > >> >>> > >> >>> -- > >> >>> Best, > >> >>> Anmol > >> >>> > >> >> -- > >> >> 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. > >> > >> -- > >> 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. > > > > > > -- > 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. > -- Michael T. Jones michael.jo...@gmail.com -- 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.