github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/pinger.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package apiserver 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/utils/clock" 11 "gopkg.in/tomb.v1" 12 13 "github.com/juju/juju/apiserver/common" 14 "github.com/juju/juju/apiserver/facade" 15 "github.com/juju/juju/state" 16 ) 17 18 func init() { 19 common.RegisterStandardFacade("Pinger", 1, NewPinger) 20 } 21 22 // NewPinger returns an object that can be pinged by calling its Ping method. 23 // If this method is not called frequently enough, the connection will be 24 // dropped. 25 func NewPinger(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (Pinger, error) { 26 pingTimeout, ok := resources.Get("pingTimeout").(*pingTimeout) 27 if !ok { 28 return nullPinger{}, nil 29 } 30 return pingTimeout, nil 31 } 32 33 // pinger describes a resource that can be pinged and stopped. 34 type Pinger interface { 35 Ping() 36 Stop() error 37 } 38 39 // pingTimeout listens for pings and will call the 40 // passed action in case of a timeout. This way broken 41 // or inactive connections can be closed. 42 type pingTimeout struct { 43 tomb tomb.Tomb 44 action func() 45 clock clock.Clock 46 timeout time.Duration 47 reset chan struct{} 48 } 49 50 // newPingTimeout returns a new pingTimeout instance 51 // that invokes the given action asynchronously if there 52 // is more than the given timeout interval between calls 53 // to its Ping method. 54 func newPingTimeout(action func(), clock clock.Clock, timeout time.Duration) Pinger { 55 pt := &pingTimeout{ 56 action: action, 57 clock: clock, 58 timeout: timeout, 59 reset: make(chan struct{}), 60 } 61 go func() { 62 defer pt.tomb.Done() 63 pt.tomb.Kill(pt.loop()) 64 }() 65 return pt 66 } 67 68 // Ping is used by the client heartbeat monitor and resets 69 // the killer. 70 func (pt *pingTimeout) Ping() { 71 select { 72 case <-pt.tomb.Dying(): 73 case pt.reset <- struct{}{}: 74 } 75 } 76 77 // Stop terminates the ping timeout. 78 func (pt *pingTimeout) Stop() error { 79 pt.tomb.Kill(nil) 80 return pt.tomb.Wait() 81 } 82 83 // loop waits for a reset signal, otherwise it performs 84 // the initially passed action. 85 func (pt *pingTimeout) loop() error { 86 for { 87 select { 88 case <-pt.tomb.Dying(): 89 return tomb.ErrDying 90 case <-pt.reset: 91 case <-pt.clock.After(pt.timeout): 92 go pt.action() 93 return errors.New("ping timeout") 94 } 95 } 96 } 97 98 // nullPinger implements the pinger interface but just does nothing 99 type nullPinger struct{} 100 101 func (nullPinger) Ping() {} 102 func (nullPinger) Stop() error { return nil }