github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/apicaller/worker.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apicaller 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "gopkg.in/tomb.v1" 10 11 "github.com/juju/juju/api" 12 "github.com/juju/juju/worker" 13 ) 14 15 var logger = loggo.GetLogger("juju.worker.apicaller") 16 17 // newAPIConnWorker returns a worker that exists for as long as the associated 18 // connection, and provides access to a base.APICaller via its manifold's Output 19 // func. If the worker is killed, the connection will be closed; and if the 20 // connection is broken, the worker will be killed. 21 // 22 // The lack of error return is considered and intentional; it signals the 23 // transfer of responsibility for the connection from the caller to the 24 // worker. 25 func newAPIConnWorker(conn api.Connection) worker.Worker { 26 w := &apiConnWorker{conn: conn} 27 go func() { 28 defer w.tomb.Done() 29 w.tomb.Kill(w.loop()) 30 }() 31 return w 32 } 33 34 type apiConnWorker struct { 35 tomb tomb.Tomb 36 conn api.Connection 37 } 38 39 // Kill is part of the worker.Worker interface. 40 func (w *apiConnWorker) Kill() { 41 w.tomb.Kill(nil) 42 } 43 44 // Wait is part of the worker.Worker interface. 45 func (w *apiConnWorker) Wait() error { 46 return w.tomb.Wait() 47 } 48 49 // loop is somewhat out of the ordinary, because an api.Connection 50 // *does* maintain an internal workeresque heartbeat goroutine, but it 51 // doesn't implement Worker. 52 func (w *apiConnWorker) loop() (err error) { 53 // TODO(fwereade): we should make this rational at some point. 54 55 defer func() { 56 // Since we can't tell for sure what error killed the connection, any 57 // error out of Close is more important and relevant than any error we 58 // might return in the loop. 59 if closeErr := w.conn.Close(); closeErr != nil { 60 err = closeErr 61 } 62 }() 63 64 // Note that we should never return a nil error from this loop. If we're 65 // shut down deliberately we should return ErrDying, to be overwritten by 66 // any non-nil Close error and otherwise not perturb the tomb; and if the 67 // connection closes on its own there's surely *something* wrong even if 68 // there's no error reported from Close. (sample problem: *someone else* 69 // closed the conn that we're meant to be in control of). 70 for { 71 select { 72 case <-w.tomb.Dying(): 73 return tomb.ErrDying 74 case <-w.conn.Broken(): 75 return errors.New("api connection broken unexpectedly") 76 } 77 } 78 }