github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/hook/sender.go (about)

     1  // Copyright 2012-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package hook
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/tomb.v1"
     9  
    10  	"github.com/juju/juju/state/watcher"
    11  )
    12  
    13  // Sender maintains a Source and delivers its hooks via a channel.
    14  type Sender interface {
    15  	Stop() error
    16  }
    17  
    18  // NewSender starts sending hooks from source onto the out channel, and will
    19  // continue to do so until Stop()ped (or the source is exhausted). NewSender
    20  // takes ownership of the supplied source, and responsibility for cleaning it up;
    21  // but it will not close the out channel.
    22  func NewSender(out chan<- Info, source Source) Sender {
    23  	sender := &hookSender{
    24  		out: out,
    25  	}
    26  	go func() {
    27  		defer sender.tomb.Done()
    28  		defer watcher.Stop(source, &sender.tomb)
    29  		sender.tomb.Kill(sender.loop(source))
    30  	}()
    31  	return sender
    32  }
    33  
    34  // hookSender implements Sender.
    35  type hookSender struct {
    36  	tomb tomb.Tomb
    37  	out  chan<- Info
    38  }
    39  
    40  // Stop stops the Sender and returns any errors encountered during
    41  // operation or while shutting down.
    42  func (sender *hookSender) Stop() error {
    43  	sender.tomb.Kill(nil)
    44  	return sender.tomb.Wait()
    45  }
    46  
    47  // loop synchronously delivers the source's change events to its update method,
    48  // and, whenever the source is nonempty, repeatedly sends its first scheduled
    49  // event on the out chan (and pops it from the source).
    50  func (sender *hookSender) loop(source Source) error {
    51  	var next Info
    52  	var out chan<- Info
    53  	for {
    54  		if source.Empty() {
    55  			out = nil
    56  		} else {
    57  			out = sender.out
    58  			next = source.Next()
    59  		}
    60  		select {
    61  		case <-sender.tomb.Dying():
    62  			return tomb.ErrDying
    63  		case out <- next:
    64  			source.Pop()
    65  		case change, ok := <-source.Changes():
    66  			if !ok {
    67  				return errors.New("hook source stopped providing updates")
    68  			}
    69  			if err := change.Apply(); err != nil {
    70  				return errors.Trace(err)
    71  			}
    72  		}
    73  	}
    74  }