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

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package relation
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"launchpad.net/tomb"
     9  
    10  	"github.com/juju/juju/state/watcher"
    11  	"github.com/juju/juju/worker/uniter/hook"
    12  )
    13  
    14  // Peeker maintains a HookSource, and allows an external client to inspect
    15  // and consume, or reject, the head of the queue.
    16  type Peeker interface {
    17  	// Peeks returns a channel on which Peeks are delivered. The receiver of a
    18  	// Peek must Consume or Reject the Peek before further peeks will be sent.
    19  	Peeks() <-chan Peek
    20  	// Stop stops the Peeker's HookSource, and prevents any further Peeks from
    21  	// being delivered.
    22  	Stop() error
    23  }
    24  
    25  // Peek exists to support Peeker and has no independent meaning or existence.
    26  // Receiving a Peek from a Peeker's Peek channel implies acceptance of the
    27  // responsibility to either Consume or Reject the Peek.
    28  type Peek interface {
    29  	// HookInfo returns information about the hook at the head of the queue.
    30  	HookInfo() hook.Info
    31  	// Consume pops the hook from the head of the queue and makes new Peeks
    32  	// available.
    33  	Consume()
    34  	// Reject makes new Peeks available.
    35  	Reject()
    36  }
    37  
    38  // NewPeeker returns a new Peeker providing a view of the supplied source
    39  // (of which it takes ownership).
    40  func NewPeeker(source HookSource) Peeker {
    41  	p := &peeker{
    42  		peeks: make(chan Peek),
    43  	}
    44  	go func() {
    45  		defer p.tomb.Done()
    46  		defer close(p.peeks)
    47  		defer watcher.Stop(source, &p.tomb)
    48  		p.tomb.Kill(p.loop(source))
    49  	}()
    50  	return p
    51  }
    52  
    53  // peeker implements Peeker.
    54  type peeker struct {
    55  	tomb  tomb.Tomb
    56  	peeks chan Peek
    57  }
    58  
    59  // Peeks is part of the Peeker interface.
    60  func (p *peeker) Peeks() <-chan Peek {
    61  	return p.peeks
    62  }
    63  
    64  // Stop is part of the Peeker interface.
    65  func (p *peeker) Stop() error {
    66  	p.tomb.Kill(nil)
    67  	return p.tomb.Wait()
    68  }
    69  
    70  // loop delivers events from the source's Changes channel to its Update method,
    71  // continually, unless a Peek is active.
    72  func (p *peeker) loop(source HookSource) error {
    73  	for {
    74  		var next *peek
    75  		var peeks chan Peek
    76  		if !source.Empty() {
    77  			peeks = p.peeks
    78  			next = &peek{
    79  				source: source,
    80  				done:   make(chan struct{}),
    81  			}
    82  		}
    83  		select {
    84  		case <-p.tomb.Dying():
    85  			return tomb.ErrDying
    86  		case peeks <- next:
    87  			select {
    88  			case <-p.tomb.Dying():
    89  			case <-next.done:
    90  			}
    91  		case change, ok := <-source.Changes():
    92  			if !ok {
    93  				return errors.New("hook source stopped providing updates")
    94  			}
    95  			if err := source.Update(change); err != nil {
    96  				return errors.Trace(err)
    97  			}
    98  		}
    99  	}
   100  }
   101  
   102  // peek implements Peek.
   103  type peek struct {
   104  	source HookSource
   105  	done   chan struct{}
   106  }
   107  
   108  // HookInfo is part of the Peek interface.
   109  func (p *peek) HookInfo() hook.Info {
   110  	return p.source.Next()
   111  }
   112  
   113  // Consume is part of the Peek interface.
   114  func (p *peek) Consume() {
   115  	p.source.Pop()
   116  	close(p.done)
   117  }
   118  
   119  // Reject is part of the Peek interface.
   120  func (p *peek) Reject() {
   121  	close(p.done)
   122  }