~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/apiserver/pinger.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package apiserver
 
5
 
 
6
import (
 
7
        "errors"
 
8
        "time"
 
9
 
 
10
        "launchpad.net/tomb"
 
11
 
 
12
        "github.com/juju/juju/apiserver/common"
 
13
        "github.com/juju/juju/apiserver/facade"
 
14
        "github.com/juju/juju/state"
 
15
        "github.com/juju/utils/clock"
 
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 }