summaryrefslogtreecommitdiff
path: root/vendor/github.com/hashicorp/terraform/helper/shadow/compared_value.go
blob: 4223e9255e25af11fd1eae95ffbd99020513d7b5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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 }
	}
}