blob: 5fefb710cd23723461a714681877aab52c2e0ecd [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
Colin Cross9272ade2016-08-17 15:24:12 -070027// Once computes a value the first time it is called with a given key per OncePer, and returns the
28// value without recomputing when called with the same key. key must be hashable.
Colin Crosse48ff5b2019-02-04 13:03:13 -080029func (once *OncePer) Once(key OnceKey, value func() interface{}) interface{} {
Colin Cross99c6dfa2017-11-07 13:34:26 -080030 // Fast path: check if the key is already in the map
31 if v, ok := once.values.Load(key); ok {
Colin Cross9272ade2016-08-17 15:24:12 -070032 return v
33 }
34
Colin Cross99c6dfa2017-11-07 13:34:26 -080035 // Slow path: lock so that we don't call the value function twice concurrently
Colin Cross9272ade2016-08-17 15:24:12 -070036 once.valuesLock.Lock()
37 defer once.valuesLock.Unlock()
38
39 // Check again with the lock held
Colin Cross99c6dfa2017-11-07 13:34:26 -080040 if v, ok := once.values.Load(key); ok {
Colin Cross9272ade2016-08-17 15:24:12 -070041 return v
42 }
43
Colin Cross99c6dfa2017-11-07 13:34:26 -080044 // Still not in the map, call the value function and store it
Colin Cross9272ade2016-08-17 15:24:12 -070045 v := value()
Colin Cross99c6dfa2017-11-07 13:34:26 -080046 once.values.Store(key, v)
Colin Cross9272ade2016-08-17 15:24:12 -070047
Colin Cross99c6dfa2017-11-07 13:34:26 -080048 return v
49}
50
Colin Cross571cccf2019-02-04 11:22:08 -080051// Get returns the value previously computed with Once for a given key. If Once has not been called for the given
52// key Get will panic.
Colin Crosse48ff5b2019-02-04 13:03:13 -080053func (once *OncePer) Get(key OnceKey) interface{} {
Colin Cross99c6dfa2017-11-07 13:34:26 -080054 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
Colin Cross571cccf2019-02-04 11:22:08 -080062// OnceStringSlice is the same as Once, but returns the value cast to a []string
Colin Crosse48ff5b2019-02-04 13:03:13 -080063func (once *OncePer) OnceStringSlice(key OnceKey, value func() []string) []string {
Colin Cross9272ade2016-08-17 15:24:12 -070064 return once.Once(key, func() interface{} { return value() }).([]string)
65}
66
Colin Cross571cccf2019-02-04 11:22:08 -080067// OnceStringSlice is the same as Once, but returns two values cast to []string
Colin Crosse48ff5b2019-02-04 13:03:13 -080068func (once *OncePer) Once2StringSlice(key OnceKey, value func() ([]string, []string)) ([]string, []string) {
Colin Cross9272ade2016-08-17 15:24:12 -070069 type twoStringSlice [2][]string
70 s := once.Once(key, func() interface{} {
71 var s twoStringSlice
72 s[0], s[1] = value()
73 return s
74 }).(twoStringSlice)
75 return s[0], s[1]
76}
Colin Cross571cccf2019-02-04 11:22:08 -080077
78// OnceKey is an opaque type to be used as the key in calls to Once.
79type OnceKey struct {
80 key interface{}
81}
82
83// NewOnceKey returns an opaque OnceKey object for the provided key. Two calls to NewOnceKey with the same key string
84// DO NOT produce the same OnceKey object.
85func NewOnceKey(key string) OnceKey {
86 return OnceKey{&key}
87}
88
89// NewCustomOnceKey returns an opaque OnceKey object for the provided key. The key can be any type that is valid as the
90// key in a map, i.e. comparable. Two calls to NewCustomOnceKey with key values that compare equal will return OnceKey
91// objects that access the same value stored with Once.
92func NewCustomOnceKey(key interface{}) OnceKey {
93 return OnceKey{key}
94}