Live data from Hacker News

A million ways to die from a data race in Go

gaultier.github.io

41–50 of 146 posts

Re: A million ways to die from a data race in Go

#41

[flagged]

To me it looks like simple, clear examples of potential issues. It's unfortunate to frame that as "crapping on Go", how are new Go programmers going to learn about the pitfalls if all discussion of them are seen as hostility? Like, rightly or wrongly, Go chose pervasive mutability and shared memory, it inevitably comes with drawbacks. Pretending they don't exist doesn't make them go away.

Concurrent programming is hard and has many pitfalls; people are warned about this from the very, very start. If you then go about it without studying proper usage/common pitfalls and do not use (very) defensive coding practices (violated by all examples) then the main issue is just naivity. No programming language can really defend against that.

Re: A million ways to die from a data race in Go

#42
post #37

Earlier quoted context omitted.

Go famously summed up their preferred approach to shared state: > Don't communicate by sharing memory; share memory by communicating.

Which they then failed to follow, especially since goroutines share memory with each other.

Threads share the same memory by definition, though. When you isolate these threads from a memory PoV, they become processes.

Moreover, threads are arguably useless without shared memory anyway. A thread is invoked to work on the same data structure with multiple "affectors". Coordination of these affectors is up to you. Atomics, locks, queues... The tools are many.

In fact, processes are just threads which are isolated from each other, and this isolation is enforced by the processor.

Re: A million ways to die from a data race in Go

#43
post #36
post #28

All code is inherently not concurrency-safe unless it says so. The http.Client docs mention concurrent usage is safe, but not modification. The closure compiler flag trick looks interesting though, will give this a spin on some projects.

> The http.Client docs mention concurrent usage is safe, but not modification. Subtle linguistic distinctions are not what I want to see in my docs, especially if the context is concurrency.

> Subtle linguistic distinctions are not what I want to see in my docs, especially if the context is concurrency.

Which PL do you use then ? Because even Rust makes "Subtle linguistic distinctions" in a lot of places and also in concurrency.

Re: A million ways to die from a data race in Go

#44
post #36
post #28

All code is inherently not concurrency-safe unless it says so. The http.Client docs mention concurrent usage is safe, but not modification. The closure compiler flag trick looks interesting though, will give this a spin on some projects.

> The http.Client docs mention concurrent usage is safe, but not modification. Subtle linguistic distinctions are not what I want to see in my docs, especially if the context is concurrency.

On the other hand, it should be very obvious for anyone that has experience with concurrency, that changing a field on an object like the author showed can never be safe in a concurrency setting. In any language.

Re: A million ways to die from a data race in Go

#45

I dislike some of this article, my impression is similar to some of the complaints of others here. However, are Go programs not supposed to typically avoid sharing mutable data across goroutines in the first place? If only immutable messages are shared between goroutines, it should be way easier to avoid many of these issues. That is of course not always viable, for instance due to performance concerns, but in theory…

There is a LOT of demand for explicit capture clauses. This is one thing that C++ got right and Rust got wrong with all its implicit and magic behaviour.

https://www.reddit.com/r/rust/comments/1odrf9s/explicit_capt...

Re: A million ways to die from a data race in Go

#46
post #32

Every language has an arsenal of footguns. Go is no different. I would say that overall it is not too bad, comparatively. From all the listed cases, only the first one is easy to get caught by, even as an experienced developer. There, the IDE and syntax highlighting is of tremendous help and for general prevention. The rest is just understanding the language and having some practice.

The first one should be caught by the Go race detector AFAIK. It will warn about the conflicting write accesses to err when both goroutines run.

Re: A million ways to die from a data race in Go

#47

Earlier quoted context omitted.

Rust concurrency also has issues, there are many complaints about async [0], and some Rust developers point to Go as having green threads. The original author of Rust originally wanted green threads as I understand it, but Rust evolved in a different direction. As for Java, there are fibers/virtual threads now, but I know too little of them to comment on them. Go's green thread story is presumably still good, also re…

Async and concurrency are orthogonal concepts.

While I agree, in practice they can actually be parallel. Case in point - the Java Vert.x toolkit. It uses event-loop and futures, but they have also adopted virtual threads in the toolkit. So you still got your async concepts in the toolkit but the VTs are your concurrency carriers.

Re: A million ways to die from a data race in Go

#48
post #37

Earlier quoted context omitted.

Go famously summed up their preferred approach to shared state: > Don't communicate by sharing memory; share memory by communicating.

Which they then failed to follow, especially since goroutines share memory with each other.

Who is "they"? This isn't Rust. It's still up to the developer to follow the advice.

Anyway, I would stop short of saying "Go chose shared memory". They've always been clear that that's plan B.

Re: A million ways to die from a data race in Go

#49
post #36

Earlier quoted context omitted.

> The http.Client docs mention concurrent usage is safe, but not modification. Subtle linguistic distinctions are not what I want to see in my docs, especially if the context is concurrency.

On the other hand, it should be very obvious for anyone that has experience with concurrency, that changing a field on an object like the author showed can never be safe in a concurrency setting. In any language.

This is not true in the general case. E.g. setting a field to true from potentially multiple threads can be a completely meaningful operation e.g. if you only care about if ANY of the threads have finished execution.

It depends on the platform though (e.g. in Java it is guaranteed that there is no tearing [1]).

[1] In OpenJDK. The JVM spec itself only guarantees it for 32-bit primitives and references, but given that 64-bit CPUs can cheaply/freely write a 64-bit value atomically, that's how it's implemented.

Re: A million ways to die from a data race in Go

#50

Earlier quoted context omitted.

To me it looks like simple, clear examples of potential issues. It's unfortunate to frame that as "crapping on Go", how are new Go programmers going to learn about the pitfalls if all discussion of them are seen as hostility? Like, rightly or wrongly, Go chose pervasive mutability and shared memory, it inevitably comes with drawbacks. Pretending they don't exist doesn't make them go away.

> Pretending they don't exist doesn't make them go away. It's generally assumed that people who defend their favorite programming language are oblivious to the problems the language has or choose to ignore these problems to cope with the language. There's another possibility: Knowing the footguns and how to avoid them well. This is generally prevalent in (Go/C/C++) vs. Rust discussions. I for one know the footguns, I…

While I agree with you in principle, there is a small but important caveat about large codebases with hundreds of contributors or more. It only takes 1 bad apple to ruin the bunch.

I'll always love a greenfield C project, though!

Post reply on HN