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  }