github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/uniter/relation/hooksender.go (about)

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