blob: f19f75c080b0bfaf7fd27dec787ae3137fd74782 [file] [log] [blame]
Colin Cross9272ade2016-08-17 15:24:12 -07001// Copyright 2016 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
Colin Cross99c6dfa2017-11-07 13:34:26 -080018 "fmt"
Colin Cross9272ade2016-08-17 15:24:12 -070019 "sync"
Colin Cross9272ade2016-08-17 15:24:12 -070020)
21
22type OncePer struct {
Colin Cross99c6dfa2017-11-07 13:34:26 -080023 values sync.Map
Colin Cross9272ade2016-08-17 15:24:12 -070024 valuesLock sync.Mutex
25}
26
27type valueMap map[interface{}]interface{}
28
29// Once computes a value the first time it is called with a given key per OncePer, and returns the
30// value without recomputing when called with the same key. key must be hashable.
31func (once *OncePer) Once(key interface{}, value func() interface{}) interface{} {
Colin Cross99c6dfa2017-11-07 13:34:26 -080032 // Fast path: check if the key is already in the map
33 if v, ok := once.values.Load(key); ok {
Colin Cross9272ade2016-08-17 15:24:12 -070034 return v
35 }
36
Colin Cross99c6dfa2017-11-07 13:34:26 -080037 // Slow path: lock so that we don't call the value function twice concurrently
Colin Cross9272ade2016-08-17 15:24:12 -070038 once.valuesLock.Lock()
39 defer once.valuesLock.Unlock()
40
41 // Check again with the lock held
Colin Cross99c6dfa2017-11-07 13:34:26 -080042 if v, ok := once.values.Load(key); ok {
Colin Cross9272ade2016-08-17 15:24:12 -070043 return v
44 }
45
Colin Cross99c6dfa2017-11-07 13:34:26 -080046 // Still not in the map, call the value function and store it
Colin Cross9272ade2016-08-17 15:24:12 -070047 v := value()
Colin Cross99c6dfa2017-11-07 13:34:26 -080048 once.values.Store(key, v)
Colin Cross9272ade2016-08-17 15:24:12 -070049
Colin Cross99c6dfa2017-11-07 13:34:26 -080050 return v
51}
52
53func (once *OncePer) Get(key interface{}) interface{} {
54 v, ok := once.values.Load(key)
55 if !ok {
56 panic(fmt.Errorf("Get() called before Once()"))
57 }
Colin Cross9272ade2016-08-17 15:24:12 -070058
59 return v
60}
61
62func (once *OncePer) OnceStringSlice(key interface{}, value func() []string) []string {
63 return once.Once(key, func() interface{} { return value() }).([]string)
64}
65
66func (once *OncePer) Once2StringSlice(key interface{}, value func() ([]string, []string)) ([]string, []string) {
67 type twoStringSlice [2][]string
68 s := once.Once(key, func() interface{} {
69 var s twoStringSlice
70 s[0], s[1] = value()
71 return s
72 }).(twoStringSlice)
73 return s[0], s[1]
74}