Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it safe to read a function pointer concurrently without a lock?

Suppose I have this:

go func() {
    for range time.Tick(1 * time.Millisecond) {
        a, b = b, a
    }
}()

And elsewhere:

i := a // <-- Is this safe?

For this question, it's unimportant what the value of i is with respect to the original a or b. The only question is whether reading a is safe. That is, is it possible for a to be nil, partially assigned, invalid, undefined, ... anything other than a valid value?

I've tried to make it fail but so far it always succeeds (on my Mac).

I haven't been able to find anything specific beyond this quote in the The Go Memory Model doc:

Reads and writes of values larger than a single machine word behave as multiple machine-word-sized operations in an unspecified order.

Is this implying that a single machine word write is effectively atomic? And, if so, are function pointer writes in Go a single machine word operation?

Update: Here's a properly synchronized solution

like image 663
nicerobot Avatar asked Dec 31 '16 07:12

nicerobot


People also ask

Can pointer hold address of function?

A function pointer is a pointer variable, but it holds the address of a function, not the address of a data item. The only things you can do with a function pointer are read its value, assign its value, or call the function that it points toward.

Are maps in Golang thread safe?

Map is concurrent safe for read only in Golang.

Why do we need function pointers?

Function pointers can be useful when you want to create callback mechanism, and need to pass address of a function to another function. They can also be useful when you want to store an array of functions, to call dynamically for example.


2 Answers

Unsynchronized, concurrent access to any variable from multiple goroutines where at least one of them is a write is undefined behavior by The Go Memory Model.

Undefined means what it says: undefined. It may be that your program will work correctly, it may be it will work incorrectly. It may result in losing memory and type safety provided by the Go runtime (see example below). It may even crash your program. Or it may even cause the Earth to explode (probability of that is extremely small, maybe even less than 1e-40, but still...).

This undefined in your case means that yes, i may be nil, partially assigned, invalid, undefined, ... anything other than either a or b. This list is just a tiny subset of all the possible outcomes.

Stop thinking that some data races are (or may be) benign or unharmful. They can be the source of the worst things if left unattended.

Since your code writes to the variable a in one goroutine and reads it in another goroutine (which tries to assign its value to another variable i), it's a data race and as such it's not safe. It doesn't matter if in your tests it works "correctly". One could take your code as a starting point, extend / build on it and result in a catastrophe due to your initially "unharmful" data race.

As related questions, read How safe are Golang maps for concurrent Read/Write operations? and Incorrect synchronization in go lang.

Strongly recommended to read the blog post by Dmitry Vyukov: Benign data races: what could possibly go wrong?

Also a very interesting blog post which shows an example which breaks Go's memory safety with intentional data race: Golang data races to break memory safety

like image 62
icza Avatar answered Jan 22 '23 10:01

icza


In terms of Race condition, it's not safe. In short my understanding of race condition is when there're more than one asynchronous routine (coroutines, threads, process, goroutines etc.) trying to access the same resource and at least one is a writing operation, so in your example we have 2 goroutines reading and writing variables of type function, I think what's matter from a concurrent point of view is those variables have a memory space somewhere and we're trying to read or write in that portion of memory.

Short answer: just run your example using the -race flag with go run -race or go build -race and you'll see a detected data race.

like image 23
Yandry Pozo Avatar answered Jan 22 '23 08:01

Yandry Pozo