github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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 across 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  	return ManifoldEx(NewLock())
    23  }
    24  
    25  // ManifoldEx does the same thing as Manifold but takes the
    26  // Lock which used to wait on or unlock the gate. This
    27  // allows code running outside of a dependency engine managed worker
    28  // to monitor or unlock the gate.
    29  //
    30  // TODO(mjs) - this can likely go away once all machine agent workers
    31  // are running inside the dependency engine.
    32  func ManifoldEx(lock Lock) dependency.Manifold {
    33  	return dependency.Manifold{
    34  		Start: func(_ dependency.Context) (worker.Worker, error) {
    35  			w := &gate{lock: lock}
    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.lock
    50  			case *Waiter:
    51  				*outPointer = inWorker.lock
    52  			case *Lock:
    53  				*outPointer = inWorker.lock
    54  			default:
    55  				return errors.Errorf("out should be a *Unlocker, *Waiter, *Lock; is %#v", out)
    56  			}
    57  			return nil
    58  		},
    59  	}
    60  }
    61  
    62  // NewLock returns a new Lock for the gate manifold, suitable for
    63  // passing to ManifoldEx. It can be safely unlocked and monitored by
    64  // code running inside or outside of the dependency engine.
    65  func NewLock() Lock {
    66  	return &lock{
    67  		// mu and ch are shared across all workers started by the returned manifold.
    68  		// In normal operation, there will only be one such worker at a time; but if
    69  		// multiple workers somehow run in parallel, mu should prevent panic and/or
    70  		// confusion.
    71  		mu: new(sync.Mutex),
    72  		ch: make(chan struct{}),
    73  	}
    74  }
    75  
    76  // Lock implements of Unlocker and Waiter
    77  type lock struct {
    78  	mu *sync.Mutex
    79  	ch chan struct{}
    80  }
    81  
    82  // Unlock implements Unlocker.
    83  func (l *lock) Unlock() {
    84  	l.mu.Lock()
    85  	defer l.mu.Unlock()
    86  	select {
    87  	case <-l.ch:
    88  	default:
    89  		close(l.ch)
    90  	}
    91  }
    92  
    93  // Unlocked implements Waiter.
    94  func (l *lock) Unlocked() <-chan struct{} {
    95  	return l.ch
    96  }
    97  
    98  // IsUnlocked implements Waiter.
    99  func (l *lock) IsUnlocked() bool {
   100  	select {
   101  	case <-l.ch:
   102  		return true
   103  	default:
   104  		return false
   105  	}
   106  }
   107  
   108  // gate implements a degenerate worker that holds a Lock.
   109  type gate struct {
   110  	tomb tomb.Tomb
   111  	lock Lock
   112  }
   113  
   114  // Kill is part of the worker.Worker interface.
   115  func (w *gate) Kill() {
   116  	w.tomb.Kill(nil)
   117  }
   118  
   119  // Wait is part of the worker.Worker interface.
   120  func (w *gate) Wait() error {
   121  	return w.tomb.Wait()
   122  }