github.com/sylr/terraform@v0.11.12-beta1/helper/shadow/compared_value.go (about) 1 package shadow 2 3 import ( 4 "sync" 5 ) 6 7 // ComparedValue is a struct that finds a value by comparing some key 8 // to the list of stored values. This is useful when there is no easy 9 // uniquely identifying key that works in a map (for that, use KeyedValue). 10 // 11 // ComparedValue is very expensive, relative to other Value types. Try to 12 // limit the number of values stored in a ComparedValue by potentially 13 // nesting it within a KeyedValue (a keyed value points to a compared value, 14 // for example). 15 type ComparedValue struct { 16 // Func is a function that is given the lookup key and a single 17 // stored value. If it matches, it returns true. 18 Func func(k, v interface{}) bool 19 20 lock sync.Mutex 21 once sync.Once 22 closed bool 23 values []interface{} 24 waiters map[interface{}]*Value 25 } 26 27 // Close closes the value. This can never fail. For a definition of 28 // "close" see the ErrClosed docs. 29 func (w *ComparedValue) Close() error { 30 w.lock.Lock() 31 defer w.lock.Unlock() 32 33 // Set closed to true always 34 w.closed = true 35 36 // For all waiters, complete with ErrClosed 37 for k, val := range w.waiters { 38 val.SetValue(ErrClosed) 39 delete(w.waiters, k) 40 } 41 42 return nil 43 } 44 45 // Value returns the value that was set for the given key, or blocks 46 // until one is available. 47 func (w *ComparedValue) Value(k interface{}) interface{} { 48 v, val := w.valueWaiter(k) 49 if val == nil { 50 return v 51 } 52 53 return val.Value() 54 } 55 56 // ValueOk gets the value for the given key, returning immediately if the 57 // value doesn't exist. The second return argument is true if the value exists. 58 func (w *ComparedValue) ValueOk(k interface{}) (interface{}, bool) { 59 v, val := w.valueWaiter(k) 60 return v, val == nil 61 } 62 63 func (w *ComparedValue) SetValue(v interface{}) { 64 w.lock.Lock() 65 defer w.lock.Unlock() 66 w.once.Do(w.init) 67 68 // Check if we already have this exact value (by simply comparing 69 // with == directly). If we do, then we don't insert it again. 70 found := false 71 for _, v2 := range w.values { 72 if v == v2 { 73 found = true 74 break 75 } 76 } 77 78 if !found { 79 // Set the value, always 80 w.values = append(w.values, v) 81 } 82 83 // Go through the waiters 84 for k, val := range w.waiters { 85 if w.Func(k, v) { 86 val.SetValue(v) 87 delete(w.waiters, k) 88 } 89 } 90 } 91 92 func (w *ComparedValue) valueWaiter(k interface{}) (interface{}, *Value) { 93 w.lock.Lock() 94 w.once.Do(w.init) 95 96 // Look for a pre-existing value 97 for _, v := range w.values { 98 if w.Func(k, v) { 99 w.lock.Unlock() 100 return v, nil 101 } 102 } 103 104 // If we're closed, return that 105 if w.closed { 106 w.lock.Unlock() 107 return ErrClosed, nil 108 } 109 110 // Pre-existing value doesn't exist, create a waiter 111 val := w.waiters[k] 112 if val == nil { 113 val = new(Value) 114 w.waiters[k] = val 115 } 116 w.lock.Unlock() 117 118 // Return the waiter 119 return nil, val 120 } 121 122 // Must be called with w.lock held. 123 func (w *ComparedValue) init() { 124 w.waiters = make(map[interface{}]*Value) 125 if w.Func == nil { 126 w.Func = func(k, v interface{}) bool { return k == v } 127 } 128 }