github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/gate/manifold.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package gate
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/errors"
    10  	"launchpad.net/tomb"
    11  
    12  	"github.com/juju/juju/worker"
    13  	"github.com/juju/juju/worker/dependency"
    14  )
    15  
    16  // Manifold returns a dependency.Manifold that wraps a single channel, shared
    17  // across all workers returned by the start func; it can be used to synchronize
    18  // operations acrosss manifolds that lack direct dependency relationships.
    19  //
    20  // The output func accepts an out pointer to either an Unlocker or a Waiter.
    21  func Manifold() dependency.Manifold {
    22  
    23  	// mu and ch are shared across all workers started by the returned manifold.
    24  	// In normal operation, there will only be one such worker at a time; but if
    25  	// multiple workers somehow run in parallel, mu should prevent panic and/or
    26  	// confusion.
    27  	mu := new(sync.Mutex)
    28  	ch := make(chan struct{})
    29  
    30  	return dependency.Manifold{
    31  		Start: func(_ dependency.GetResourceFunc) (worker.Worker, error) {
    32  			w := &gate{
    33  				mu: mu,
    34  				ch: ch,
    35  			}
    36  			go func() {
    37  				defer w.tomb.Done()
    38  				<-w.tomb.Dying()
    39  			}()
    40  			return w, nil
    41  		},
    42  		Output: func(in worker.Worker, out interface{}) error {
    43  			inWorker, _ := in.(*gate)
    44  			if inWorker == nil {
    45  				return errors.Errorf("in should be a *gate; is %#v", in)
    46  			}
    47  			switch outPointer := out.(type) {
    48  			case *Unlocker:
    49  				*outPointer = inWorker
    50  			case *Waiter:
    51  				*outPointer = inWorker
    52  			default:
    53  				return errors.Errorf("out should be a pointer to an Unlocker or a Waiter; is %#v", out)
    54  			}
    55  			return nil
    56  		},
    57  	}
    58  }
    59  
    60  // gate implements Waiter, Unlocker, and worker.Worker.
    61  type gate struct {
    62  	tomb tomb.Tomb
    63  	mu   *sync.Mutex
    64  	ch   chan struct{}
    65  }
    66  
    67  // Kill is part of the worker.Worker interface.
    68  func (w *gate) Kill() {
    69  	w.tomb.Kill(nil)
    70  }
    71  
    72  // Wait is part of the worker.Worker interface.
    73  func (w *gate) Wait() error {
    74  	return w.tomb.Wait()
    75  }
    76  
    77  // Unlocked is part of the Waiter interface.
    78  func (w *gate) Unlocked() <-chan struct{} {
    79  	return w.ch
    80  }
    81  
    82  // Unlock is part of the Unlocker interface.
    83  func (w *gate) Unlock() {
    84  	w.mu.Lock()
    85  	defer w.mu.Unlock()
    86  	select {
    87  	case <-w.ch:
    88  	default:
    89  		close(w.ch)
    90  	}
    91  }