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 }