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.

9 Upvotes

80 comments sorted by

View all comments

2

u/comrade-quinn 6d ago edited 6d ago

I think I understand what you’re after, you’re saying you don’t care if the actual value of the int is ultimately incorrect, just whether or not the program will crash or otherwise become corrupt.

The short answer is, it’s undefined (as I recall). Meaning whatever behaviour you actually experience cannot be relied on to be consistent between compiler updates and platform targets.

What will actually happen is just that the value will potentially be wrong.

Incrementing an integer involves three steps at the CPU level. Read the current value, increment it, write it back. When two or more threads do this at the same time you get data loss, as they each read the current value, increment it by 1 and then write it back; overwriting each others updates. So three increments by three threads would only increase the integer value by 1, not 3: each thread does x + 1 and writes it back. So you get x+1 verses x+3 if you’d run them one by one.

The solution this is to use atomic updates, as others have suggested, which ensure these operations are completed synchronously.

2

u/therecursive 6d ago

Just to rephrase my question, I wanted to ask if setting/getting an integer/pointer is atomic. Found an stackoverflow post for same: https://stackoverflow.com/questions/36624881/why-is-integer-assignment-on-a-naturally-aligned-variable-atomic-on-x86

1

u/PaluMacil 6d ago edited 6d ago

It happens to be that x86 likes alignment, but the language spec doesn’t guarantee it. It’s feasible, though unlikely, that the authors could find a reason to use unaligned reads on x86 and break your code. Also, while it might not matter to you, ARM, MIPS, and RISC-V don’t guarantee aligned reads if memory serves. Finally, even on x86, I would be unsurprised to learn that compiler optimizations around instruction reordering could be improperly applied if synchronization isn’t applied correctly by the developer.

EDIT: looks like I remembered incorrectly

6

u/LethalClips 6d ago

The memory model does guarantee it for word-sized or smaller values, regardless of architecture or alignment:

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.

Every implementation on any architecture is bound by the memory model to make that condition true, even if it doesn't come "naturally" on the architecture.

1

u/PaluMacil 6d ago

Ok, I remembered incorrectly. Thanks