Locking golang recursive map

Issue

I have a recursive map-like structure which looks like this:

type RecurseTable struct {
    Table map[string]*RecurseTable
    // Other fields
    sync.RWMutex
}

If I am going to access this structure from multiple goroutines, how exactly do I go about locking it? Let’s say I’m reading from the top-level map and writing to the third-level, nested map. Is it accurate to say that this wouldn’t cause any problems because changing the third level (and hence indirecting through two pointers) shouldn’t affect the top level map?

Similarly, if I have a pool of goroutines all modifying information in the second-level, nested struct, would it be the case I only need to lock each second-level map because the top-level map only contains a pointer to the nested RecurseTable? Or is it the case that I have to lock both the top-level map as well as the nested struct, because the struct could somehow be reallocated in memory, causing a change to the pointer stored as a value in the top-level map?

Another situation would be adding keys to the top-level map whilst reading from the second-level struct. Is it safe to assume that any reorganising of the top-level map due to the new key wouldn’t affect the location of the second-level struct in memory, and hence it wouldn’t be necessary to lock the struct whilst reading?

My goal here is to minimise global locks of the entire recursive structure so that my goroutines can work on different parts of the structure in parallel, with minimal lock contention. I guess the core of my question is about how maps in Golang resize.

Solution

From the Go maps in action blogpost :

Maps are not safe for concurrent use: it’s not defined what happens when you read and write to them simultaneously. If you need to read from and write to a map from concurrently executing goroutines, the accesses must be mediated by some kind of synchronization mechanism. One common way to protect maps is with sync.RWMutex.

As you are feeding the map pointers to other maps, you can safely lock a map independently from the others (siblings or descendants): even if you delete a map entry while using its descendants, it won’t be a problem since their references will be kept until you release the pointers (ie the var holding them is deleted).

Answered By – Elwinar

Answer Checked By – David Goodson (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.