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 }