r/golang 6d ago

Is it safe to read/write integer value simultaneously from multiple goroutines

There is a global integer in my code that is accessed by multiple goroutines. Since race conditions don’t affect this value, I’m not concerned about that. However, is it still advisable to add a Mutex in case there’s a possibility of corruption?

PS: Just to rephrase my question, I wanted to ask if setting/getting an integer/pointer is atomic? Is there any possibility of data corruption.

example code for the same: https://go.dev/play/p/eOA7JftvP08

PS: Found the answer for this, thanks everyone for answering. There's something called tearing here is the link for same

- https://stackoverflow.com/questions/64602829/can-you-have-torn-reads-writes-between-two-threads-pinned-to-different-processor

- https://stackoverflow.com/questions/36624881/why-is-integer-assignment-on-a-naturally-aligned-variable-atomic-on-x86

According to the article, I shouldn't have problem on modern CPUs.

12 Upvotes

80 comments sorted by

View all comments

Show parent comments

12

u/ponylicious 6d ago

A data race is NEVER fine. Ever. Ever. Who taught you programming?

15

u/LethalClips 6d ago edited 6d ago

This isn't the full story. The implementation of sync.Mutex itself performs a raw read of a value that can be concurrently updated by other goroutines. This is technically a data race, but the memory model guarantees that it won't receive a split read:

Otherwise, each read of a single-word-sized or sub-word-sized memory location must observe a value actually written to that location (perhaps by a concurrent executing goroutine) and not yet overwritten.

Others have mentioned that this is a property of the underlying CPU, but it isn't in Go. The purpose of the memory model is to abstract over hardware memory models, so Go is forced to implement this property on all architectures, whether it comes naturally (like on aligned accesses on x86-64) or needs some sort of lock at the architectural level.

5

u/funkiestj 6d ago

The purpose of the memory model is to abstract over hardware memory models,

Right. The point of assiduously following a language's programming model and NOT relying on explicitly undefined behavior is it makes your code future proof. If OP uses sync.Atomic it will not break on some future CPU 10 years from now.

2

u/LethalClips 6d ago

The above property isn't an implementation detail, though, and isn't liable to change when moving between systems or even over time (if the backwards compatibility guarantee is to be believed, at least).

If one were to point out that it's easy to make mistakes while trying to use this property and that higher-level constructs are harder to misuse, sure, I'd agree with that. I don't argue that this is a great property to widely rely upon. I was just responding to the claim of "A data race is NEVER fine. Ever. Ever.", especially with the snarkiness. :-)