dictionarygoconcurrencynested-map

How to safely allow current access to nested maps in go?


I'm a bit confused about how to ensure I safe concurrent access to nested maps. Originally my setup was like this, bit I realize I'd need to be able to lock at least one of the maps.

map[string]map[string]Dish

After some thought, the structures I am envisioning look like this:

type Manager struct {
    mu sync.RWMutex
    locations map[string]Restaurant
}
type Restaurant struct {
    mu sync.RWMutex
    menu map[string]Dish
}
type Dish struct {
    name string
    price string
    vegan bool
}

My basic understanding is as follows: If I want to add a new Restaurant to locations, I'd need to lock Manager. If I wanted to add or modify a Dish to menu, I'd need to lock Restaurant, but I'm not sure if I'd need to lock the Manager as well. Similarly, if I wanted to access values from Manager, I'm not sure if I'd need to lock Restaurant as well.

I've tried (unsuccessfully) to force data races while using the -race flag, so I'm not sure if locking Manager anytime Restaurant is mutated, and locking Restaurant everytime I access Manager is necessary, or if my attempts to force a race didn't work.


Solution

  • First, you don't want to be copying locks, so you need to use pointers:

    type Manager struct {
        mu sync.RWMutex
        locations map[string]*Restaurant
    }
    type Restaurant struct {
        mu sync.RWMutex
        menu map[string]Dish
    }
    

    Once you have a *Restaurant instance, you have to lock it to write to menu, and rlock it to read from menu.

    To get a restaurant by map lookup, you need to rlock the Manager, and to add things to the Manager, you need to lock it.

    So if you want to start from the top and go to the bottom, you have to lock both the manager and then the restaurant. You also have to make sure you lock them in the same order (manager first, restaurant next) whenever you do this to avoid deadlocks.