Repository navigation
Expand file tree
/
Copy pathcompared_value.go
More file actions
128 lines (108 loc) · 2.87 KB
/
Copy pathcompared_value.go
File metadata and controls
128 lines (108 loc) · 2.87 KB
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 }
}
}