I came across this piece of code and was wondering if this needs to have a R/W Mutex.
method(){
var (
wg sync.WaitGroup
rwm sync.RWMutex
vcnRegionMap map[string][]core.Vcn
)
vcnRegionMap = make(map[string][]core.Vcn)
// This loops helps us in filtering unused regions
// for composition of region/vcnid ds
for _, regionName := range regions {
wg.Add(1)
go func(ctx context.Context, region string, vcnRegionMap map[string][]core.Vcn, wg *sync.WaitGroup, rwm *sync.RWMutex) {
// for locking maps
defer wg.Done()
// TODO: make this conditional if a region is specified
c.network.SetRegion(region)
vcnResponse, err := c.network.ListVcns(ctx, core.ListVcnsRequest{
CompartmentId: &c.cID,
})
if err != nil {
logger.Debug(err.Error())
}
if len(vcnResponse.Items) == 0 {
logger.Info("status 404: No Vcns found under the given OCID and region: %s", region)
return
}
logger.Info("status 200: Vcns found under the given OCID and region: %s", region)
for _, item := range vcnResponse.Items {
logger.Debug("Vcn object: %s", *item.DisplayName)
// maps are not concurrency safe
rwm.Lock()
defer rwm.Unlock()
vcnRegionMap[region] = append(vcnRegionMap[region], item)
}
}(ctx, regionName, vcnRegionMap, &wg, &rwm)
}
wg.Wait()
}
As each go routine gets it's own copy of map, does the Mutex help in anyway and can we avoid it to reduce latency?
You need to protect the map from being concurrently accessed. The code is wrong because you are read-locking the mutex, but writing to the map.
for _, item := range vcnResponse.Items {
logger.Debug("Vcn object: %s", *item.DisplayName)
// maps are not concurrency safe
rwm.Lock()
vcnRegionMap[region] = append(vcnRegionMap[region], item)
rwm.Unlock()
}
Note that this version does not use defer
. Deferred operations run when the function returns, not when the block ends. You read-lock the mutex n
times, once for each iteration, and then release all of them when the function returns.