summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go')
-rw-r--r--vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go128
1 files changed, 128 insertions, 0 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go b/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go
new file mode 100644
index 00000000..4223e925
--- /dev/null
+++ b/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go
@@ -0,0 +1,128 @@
+package shadow
+
+import (
+ "sync"
+)
+
+// ComparedValue is a struct that finds a value by comparing some key
+// to the list of stored values. This is useful when there is no easy
+// uniquely identifying key that works in a map (for that, use KeyedValue).
+//
+// ComparedValue is very expensive, relative to other Value types. Try to
+// limit the number of values stored in a ComparedValue by potentially
+// nesting it within a KeyedValue (a keyed value points to a compared value,
+// for example).
+type ComparedValue struct {
+ // Func is a function that is given the lookup key and a single
+ // stored value. If it matches, it returns true.
+ Func func(k, v interface{}) bool
+
+ lock sync.Mutex
+ once sync.Once
+ closed bool
+ values []interface{}
+ waiters map[interface{}]*Value
+}
+
+// Close closes the value. This can never fail. For a definition of
+// "close" see the ErrClosed docs.
+func (w *ComparedValue) Close() error {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+
+ // Set closed to true always
+ w.closed = true
+
+ // For all waiters, complete with ErrClosed
+ for k, val := range w.waiters {
+ val.SetValue(ErrClosed)
+ delete(w.waiters, k)
+ }
+
+ return nil
+}
+
+// Value returns the value that was set for the given key, or blocks
+// until one is available.
+func (w *ComparedValue) Value(k interface{}) interface{} {
+ v, val := w.valueWaiter(k)
+ if val == nil {
+ return v
+ }
+
+ return val.Value()
+}
+
+// ValueOk gets the value for the given key, returning immediately if the
+// value doesn't exist. The second return argument is true if the value exists.
+func (w *ComparedValue) ValueOk(k interface{}) (interface{}, bool) {
+ v, val := w.valueWaiter(k)
+ return v, val == nil
+}
+
+func (w *ComparedValue) SetValue(v interface{}) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+ w.once.Do(w.init)
+
+ // Check if we already have this exact value (by simply comparing
+ // with == directly). If we do, then we don't insert it again.
+ found := false
+ for _, v2 := range w.values {
+ if v == v2 {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ // Set the value, always
+ w.values = append(w.values, v)
+ }
+
+ // Go through the waiters
+ for k, val := range w.waiters {
+ if w.Func(k, v) {
+ val.SetValue(v)
+ delete(w.waiters, k)
+ }
+ }
+}
+
+func (w *ComparedValue) valueWaiter(k interface{}) (interface{}, *Value) {
+ w.lock.Lock()
+ w.once.Do(w.init)
+
+ // Look for a pre-existing value
+ for _, v := range w.values {
+ if w.Func(k, v) {
+ w.lock.Unlock()
+ return v, nil
+ }
+ }
+
+ // If we're closed, return that
+ if w.closed {
+ w.lock.Unlock()
+ return ErrClosed, nil
+ }
+
+ // Pre-existing value doesn't exist, create a waiter
+ val := w.waiters[k]
+ if val == nil {
+ val = new(Value)
+ w.waiters[k] = val
+ }
+ w.lock.Unlock()
+
+ // Return the waiter
+ return nil, val
+}
+
+// Must be called with w.lock held.
+func (w *ComparedValue) init() {
+ w.waiters = make(map[interface{}]*Value)
+ if w.Func == nil {
+ w.Func = func(k, v interface{}) bool { return k == v }
+ }
+}