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