// Copyright 2016 Google LLC // // 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 atomiccache provides a map-based cache that supports very fast // reads. package atomiccache import ( "sync" "sync/atomic" ) type mapType map[interface{}]interface{} // Cache is a map-based cache that supports fast reads via use of atomics. // Writes are slow, requiring a copy of the entire cache. // The zero Cache is an empty cache, ready for use. type Cache struct { val atomic.Value // mapType mu sync.Mutex // used only by writers } // Get returns the value of the cache at key. If there is no value, // getter is called to provide one, and the cache is updated. // The getter function may be called concurrently. It should be pure, // returning the same value for every call. func (c *Cache) Get(key interface{}, getter func() interface{}) interface{} { mp, _ := c.val.Load().(mapType) if v, ok := mp[key]; ok { return v } // Compute value without lock. // Might duplicate effort but won't hold other computations back. newV := getter() c.mu.Lock() mp, _ = c.val.Load().(mapType) newM := make(mapType, len(mp)+1) for k, v := range mp { newM[k] = v } newM[key] = newV c.val.Store(newM) c.mu.Unlock() return newV }