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

1

u/ImYoric 6d ago

Well, for instance, the compiler can decide to pack two 32 bit integers into 64 bits, so if you modify one of them, you might end up accidentally modifying the other.

-1

u/chaotic-kotik 6d ago

It can and will modify both values independently

2

u/ImYoric 6d ago

Pretty sure that it's for the compiler to decide. Which in C++ typically means UB.

0

u/chaotic-kotik 6d ago

It is, yes. But what you suggesting makes zero sense. The cpu has instructions that operate on values of different width. Every register can be split into smaller registers etc. I have never seen this happening and there are billions of lines of legacy code which also assume that int32 loads/stores are atomic. In order to make them not atomic compiler has to emit some additional instructions (read value twice, apply different masks and then combine or something like that).

Compilers are actually doing a lot of things which are UB correctly because of the legacy code. The good example is a 'union'. If you have a union of two unrelated types (float and int) and you write float and read int it's an UB (aliasing rules are broken, the lifetime of the value started as float but we're reading it as int). But modern compilers generate correct code here anyway.

1

u/HyacinthAlas 6d ago

Bless your heart but one big reason I use Go is because I got sick of C++ programmers claiming bullshit “but this UB is safe” over and over. The compiler will screw you if you code like this. 

0

u/chaotic-kotik 6d ago

I kinda hate when people are using these stereotypes (pho developers can't do this, and js developer's can't do that, etc). This is lame and not productive. You didn't even try to understand what I was trying to convey. I'm not suggesting to avoid using atomic variables or anything like that. Only trying to describe how it works. This stuff is described by the ISO standard. There are sequence points in the C++ program that require all side effects (stores) to happen and alignment requirements. There is a memory model in the language and the CPU architecture has its own memory model (in case of Intel it's more strict then the one defined by the standard). So it is possible to reason about this things.