Woody1193
Woody1193

Reputation: 7980

Concurrency-safe map of slices

I have a type that contains a sync.Map where the key in the map is a string and the value is a slice. My code for inserting items into the map is as follows:

newList := []*Item{item}
if result, ok := map.LoadOrStore(key, newList); ok {
    resultList := result.([]*Item)
    resultList = append(resultList, item)
    map.Store(key, resultList)
}

This is not concurrency-safe because the the slice can be loaded and modified by multiple calls concurrently. This code is very fragile so I've attempted to modify it to be:

newList := []*Item{item}
if result, ok := map.LoadOrStore(key, &newList); ok {
    resultList := result.(*[]*Item)
    *resultList = append(*resultList, item)
}

All this does is make the issues occur deterministically. So, I'm trying to find a way to have a map-of-slices that can be added to concurrently. My instinct is to use sync.Mutex to lock the list while I'm adding to it but in order to maintain the concurrent access to the sync.Map I would need to create a map of sync.Mutex objects as well, like this:

newLock := sync.Mutex{}
raw, _ := lockMap.LoadOrStore(key, &newLock)
lock := raw.(*sync.Mutex)

newList := []*Item{item}
if result, ok := map.LoadOrStore(key, &newList); ok {
    lock.Lock()
    resultList := result.(*[]*Item)
    *resultList = append(*resultList, item)
    lock.Unlock()
}

Is there an easier way to go about this?

Upvotes: 1

Views: 795

Answers (1)

phonaputer
phonaputer

Reputation: 1530

It isn't very different from your current plan, but you could save yourself the trouble of handling two maps by using a struct with an embedded mutex for the values of the map.

The struct would look something like this:

type SafeItems struct {
    sync.Mutex
    Items []*Item
}

And it could be used like this:

newMapEntry := SafeItems{Items: itemPtrList}
if result, ok := map.LoadOrStore(key, &newMapEntry); ok {
    mapEntry := result.(*SafeItems)
    mapEntry.Lock()
    mapEntry.Items = append(mapEntry.Items, item)
    mapEntry.Unlock()
}

It's not a huge change but it does provide some syntactic sugar.

Upvotes: 2

Related Questions