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