Live data from Hacker News

A million ways to die from a data race in Go

gaultier.github.io

71–80 of 146 posts

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

#71
post #58
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.

I'm still relatively new to Go, but I've never seen closures used that often thus far in production code. Is it really a common practice?

That first example is an unintended closure, since the err at the top level actually has nothing to do with the errs in the goroutines. I have seen that sometimes, although the use of = rather than := normally makes it obvious that something dodgy is going on.

As to whether it's a common pattern, I see closures on WaitGroups or ErrGroups quite often:

  workerCount := 5
  var wg sync.WaitGroup
  wg.Add(workerCount)

  for range workerCount {
    go func() {
      // Do work
      wg.Done()
    }()
  }

  wg.Wait()
You can avoid the closure by making the worker func take a *sync.WaitGroup and passing in &wg, but it doesn't really have any benefit over just using the closure for convenience.

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

#72
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.

Go is a bit more of a low level language compared to actor languages where the language enforces that programming model. I think the point of the slogan is that you want to make the shared memory access an implementation detail of your larger system.

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

#73

OT: This page uses the term "Learnings" a lot. As a Murrcan in tech comms in Europe, I always corrected this to something else. But, well, is it some sort of Britishism ? Or is it some weird internet usage that is creeping into general usage ? Likewise for "Trainings". Looks weird to Murrcan eyes but maybe it's a Britishism.

The author is obviously an overcompensating French speaker naively going for the more English-sounding word, i.e. "learnings" instead of "lesson", in this instance an overly literal translation of French "enseignements" as in "tirer des enseignements" meaning "learn a lesson", but since you can also say "tirer des leçons" in French with the same meaning and root, it's just a case of choosing the wrong side haphazardly on the Anglo-Saxon/Latin-Norman-French divide of the English vocabulary, sheep/mutton ox/beef pairs and the like.

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

#74

OT: This page uses the term "Learnings" a lot. As a Murrcan in tech comms in Europe, I always corrected this to something else. But, well, is it some sort of Britishism ? Or is it some weird internet usage that is creeping into general usage ? Likewise for "Trainings". Looks weird to Murrcan eyes but maybe it's a Britishism.

The author is obviously an overcompensating French speaker naively going for the more English-sounding word, i.e. "learnings" instead of "lesson", in this instance an overly literal translation of French "enseignements" as in "tirer des enseignements" meaning "learn a lesson", but since you can also say "tirer des leçons" in French with the same meaning and root, it's just a case of choosing the wrong side haphazardl…

Interesting theory! And "Trainings"?

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

#75
post #58
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.

I'm still relatively new to Go, but I've never seen closures used that often thus far in production code. Is it really a common practice?

Yes, kind of.

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

#76
post #69
post #54

Earlier quoted context omitted.

Java consumes memory because collecting garbage is extra work and under most circumstances it makes no sense to rush it. Meanwhile Go will rather take time away from your code to collect garbage, decreasing throughput. If there is ample memory available, why waste energy on that? Nonetheless, it's absolutely trivial to set a single parameter to limit memory usage and Java's GCs being absolute beasts, they will have n…

refcounting gc is very fast and works fine for most of the references. Java not using a combination of both methods is a flaw.

Refcounting is significantly slower under most circumstances. You are literally putting a bunch of atomic increments/decrements into your code (if you can't prove that the given object is only used from a single thread) which are crazy expensive operations on modern CPUs, evicting caches.

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

#77
post #53
post #33

Earlier quoted context omitted.

But even so, the JVM has well-defined data races that may cause logical problems, but can never cause memory issues. That's not the case with Go, so these are significantly worse than both Rust and Java/C#, etc.

What is your definition of memory issues? Of course you can have memory corruption in Java. The easiest way is to spawn 2 threads that write to the same ByteBuffer without write locks.

And you would get garbled up bytes in application logic. But it has absolutely no way to mess up the runtime's state, so any future code can still execute correctly.

Meanwhile a memory issue in C/Rust and even Go will immediately drop every assumption out the window, the whole runtime is corrupted from that point on. If we are lucky, it soon ends in a segfault, if we are less lucky it can silently cause much bigger problems.

So there are objective distinctions to have here, e.g. Rust guarantees that the source of such a corruption can only be an incorrect `unsafe` block, and Java flat out has no platform-native unsafe operations, even under data races. Go can segfault with data races on fat pointers.

Of course every language capable of FFI calls can corrupt its runtime, Java is no exception.

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

#78

On a phone and the formatting of the snippets is unreadable with the 8 space tabs… That said, i think about all languages have their own quirks and footguns. I think people sometimes forget that tools are just that, tools. Go is remarkably easy to be productive in which is what the label on the tin can claims. It isnt “fearless concurrency” but get shit done before 5 pm because traffics a bitch on Wednesdays

Author here, thanks for the feedback on legibility, I have now just learned about the CSS `tab-size` property to control how much space tabs get rendered with. I have reduced it, should be better now.

Thanks, much nicer now

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

#79

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.

But Rust's async is one of the primary ways to handle concurrency in Rust, right? Like, async is a core part of how Tokio handles concurrency.
Post reply on HN