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.
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.