Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang, race condition with local map

I don't seem to totally understand maps in Go.

I have this code:

fetch := map[string]int{some data}

for condition {
    fetchlocal := map[string]int{}

    for key, value := range fetch {
        if condition {
            fetchlocal[key] = value
        }
    }

    go threadfunc (fetchlocal)
}

Now wherever the threadfunc function uses the fetchlocal variable Go (go -race) says warning: data race. I also got a few panics already. But why? The fetchlocal variable isn't used by any other goroutine.

Can someone enlighten me, please?

like image 787
sh sh sh Avatar asked Apr 10 '14 12:04

sh sh sh


People also ask

How can we avoid data race?

The only way to avoid data races is to synchronize access to all mutable data that is shared between threads. There are several ways to achieve this. In Go, you would normally use a channel or a lock. (Lower-lever mechanisms are available in the sync and sync/atomic packages.)

How do you avoid race conditions in Golang?

We will use a Mutex to prevent the race condition. We know the data race occurs due to multiple goroutines accessing a shared variable. We can avoid this by locking access to our counter variable when one of the goroutines reads it, and then unlocking it when it is done writing the incremented value.

How do I copy a map in Golang?

๐Ÿ’• Copy a map in Go Maps in Go are reference types, so to deep copy the contents of a map, you cannot assign one instance to another. You can do this by creating a new, empty map and then iterating over the old map in a for range loop to assign the appropriate key-value pairs to the new map.

How Go race detector works?

Go has a built-in race detector that can be used to instrument the code at compile time and detect the races during their execution. Internally, the Go race detector uses the ThreadSanitizer runtime library which uses a combination of lock-set and happens-before based algorithms to report races.


1 Answers

I'm assuming your fetch := map[string]int{some data} was actually supposed to be: fetch := map[string][]int{..some data..}.

For this to be a race threadfunc must be changing a value within fetchlocal or something else must be changing the value within fetch.

This is to say a slice is a actually:

type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
}

When you are copying the elements from one map to another you are not doing a deep copy of the slices (you are just creating a new struct with the same Data,Len,Cap), that is so say fetch["foo"].Data == fetchlocal["foo"].Data.

Therefore you can say fetch[someExistingKey] = someNewValue and this will not race with threadfunc, but if you say fetch[someExistingKey][x] == foobar or fetchlocal[someExistingKey][x] == foobar the race will be on.

If fetchlocal needs to mutated by threadfunc you could change your inner loop to look like:

for key, value := range fetch {
    if condition {
        newVal := make([]int, len(value))
        copy(newVal, val)
        fetchlocal[key] = newVal
    }
}

Or alternatively, do the copy inside threadfunc as needed before mutating.

P.S. If you shared your actual threadfunc or code that is modifying fetch while these two loops are running, we will be able to help more.

like image 98
voidlogic Avatar answered Sep 19 '22 23:09

voidlogic