github.com/m3db/m3@v1.5.0/src/x/watch/watch.go (about) 1 // Copyright (c) 2016 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 // Package watch provides utilities for watching resources for changes. 22 package watch 23 24 import ( 25 "errors" 26 "sync" 27 28 xresource "github.com/m3db/m3/src/x/resource" 29 ) 30 31 var errClosed = errors.New("closed") 32 33 type closer func() 34 35 // Updatable can be updated. 36 type Updatable interface { 37 xresource.SimpleCloser 38 39 // C returns the notification channel for updates. 40 C() <-chan struct{} 41 } 42 43 // Watch watches a Watchable instance, can get notification when the Watchable updates. 44 type Watch interface { 45 Updatable 46 47 // Get returns the latest value of the Watchable instance. 48 Get() interface{} 49 } 50 51 // Watchable can be watched 52 type Watchable interface { 53 xresource.SimpleCloser 54 55 // IsClosed returns true if the Watchable is closed 56 IsClosed() bool 57 // Get returns the latest value 58 Get() interface{} 59 // Watch returns the value and a Watch that will be notified on updates 60 Watch() (interface{}, Watch, error) 61 // NumWatches returns the number of watches on the Watchable 62 NumWatches() int 63 // Update sets the value and notify Watches 64 Update(interface{}) error 65 } 66 67 // NewWatchable returns a Watchable 68 func NewWatchable() Watchable { 69 return &watchable{} 70 } 71 72 type watchable struct { 73 sync.RWMutex 74 75 value interface{} 76 active []chan struct{} 77 closed bool 78 } 79 80 func (w *watchable) Get() interface{} { 81 w.RLock() 82 v := w.value 83 w.RUnlock() 84 return v 85 } 86 87 func (w *watchable) Watch() (interface{}, Watch, error) { 88 w.Lock() 89 90 if w.closed { 91 w.Unlock() 92 return nil, nil, errClosed 93 } 94 95 c := make(chan struct{}, 1) 96 notify := w.value != nil 97 w.active = append(w.active, c) 98 w.Unlock() 99 100 if notify { 101 select { 102 case c <- struct{}{}: 103 default: 104 } 105 } 106 107 closeFn := w.closeFunc(c) 108 watch := &watch{o: w, c: c, closeFn: closeFn} 109 return w.Get(), watch, nil 110 } 111 112 func (w *watchable) Update(v interface{}) error { 113 w.Lock() 114 defer w.Unlock() 115 116 if w.closed { 117 return errClosed 118 } 119 120 w.value = v 121 122 for _, s := range w.active { 123 select { 124 case s <- struct{}{}: 125 default: 126 } 127 } 128 129 return nil 130 } 131 132 func (w *watchable) NumWatches() int { 133 w.RLock() 134 l := len(w.active) 135 w.RUnlock() 136 137 return l 138 } 139 140 func (w *watchable) IsClosed() bool { 141 w.RLock() 142 c := w.closed 143 w.RUnlock() 144 145 return c 146 } 147 148 func (w *watchable) Close() { 149 w.Lock() 150 defer w.Unlock() 151 152 if w.closed { 153 return 154 } 155 156 w.closed = true 157 158 for _, ch := range w.active { 159 close(ch) 160 } 161 w.active = nil 162 } 163 164 func (w *watchable) closeFunc(c chan struct{}) closer { 165 return func() { 166 w.Lock() 167 defer w.Unlock() 168 169 if w.closed { 170 return 171 } 172 173 close(c) 174 175 for i := 0; i < len(w.active); i++ { 176 if w.active[i] == c { 177 w.active = append(w.active[:i], w.active[i+1:]...) 178 break 179 } 180 } 181 } 182 } 183 184 type watch struct { 185 sync.Mutex 186 187 o Watchable 188 c <-chan struct{} 189 closed bool 190 closeFn closer 191 } 192 193 func (w *watch) C() <-chan struct{} { 194 return w.c 195 } 196 197 func (w *watch) Get() interface{} { 198 return w.o.Get() 199 } 200 201 func (w *watch) Close() { 202 w.Lock() 203 defer w.Unlock() 204 205 if w.closed { 206 return 207 } 208 209 w.closed = true 210 211 if w.closeFn != nil { 212 w.closeFn() 213 } 214 }