utils/lockermap/map.go (47 lines of code) (raw):
// Copyright (c) 2016-2019 Uber Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lockermap
import (
"sync"
"golang.org/x/sync/syncmap"
)
// Map is a two-level locking map which synchronizes access to the map
// in addition to synchronizing access to the values within the map. Useful
// for mutating values in-place.
//
// The zero Map is valid and empty.
type Map struct {
m syncmap.Map
}
// Load looks up the value of key k and executes f under the protection of
// said value's lock. While f executes, it is guaranteed that k will not
// be deleted from the map. Returns false if k was not found.
func (m *Map) Load(k interface{}, f func(sync.Locker)) bool {
v, ok := m.m.Load(k)
if !ok {
return false
}
l := v.(sync.Locker)
l.Lock()
defer l.Unlock()
// Now that we have the value lock, make sure k was not deleted.
if _, ok = m.m.Load(k); !ok {
return false
}
f(l)
return true
}
// TryStore stores the given key / value pair within the Map. Returns
// false if the key is already present.
func (m *Map) TryStore(k interface{}, v sync.Locker) bool {
_, loaded := m.m.LoadOrStore(k, v)
return !loaded
}
// Delete deletes the given key from the Map.
func (m *Map) Delete(k interface{}) {
v, ok := m.m.Load(k)
if !ok {
return
}
l := v.(sync.Locker)
l.Lock()
defer l.Unlock()
m.m.Delete(k)
}
// Range interates over the Map and execs f until f returns false.
func (m *Map) Range(f func(k interface{}, v sync.Locker) bool) {
m.m.Range(func(k, v interface{}) bool {
l := v.(sync.Locker)
l.Lock()
defer l.Unlock()
if _, ok := m.m.Load(k); !ok {
// Skip this entry because it has been deleted.
return true
}
return f(k, l)
})
}