]>
Commit | Line | Data |
---|---|---|
bae9f6d2 JC |
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 | } |