Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Copyright 2012, 2013 Canonical Ltd. | 1 // Copyright 2012, 2013 Canonical Ltd. |
2 // Licensed under the AGPLv3, see LICENCE file for details. | 2 // Licensed under the AGPLv3, see LICENCE file for details. |
3 | 3 |
4 package worker | 4 package worker |
5 | 5 |
6 import ( | 6 import ( |
7 "launchpad.net/tomb" | 7 "launchpad.net/tomb" |
8 | 8 |
9 "launchpad.net/juju-core/state/api" | 9 "launchpad.net/juju-core/state/api" |
10 "launchpad.net/juju-core/state/watcher" | 10 "launchpad.net/juju-core/state/watcher" |
11 ) | 11 ) |
12 | 12 |
13 // notifyWorker is the internal implementation of the NotifyWorker interface | 13 // notifyWorker is the internal implementation of the NotifyWorker |
14 // interface | |
14 type notifyWorker struct { | 15 type notifyWorker struct { |
15 | 16 |
16 // Internal structure | 17 // Internal structure |
17 tomb tomb.Tomb | 18 tomb tomb.Tomb |
18 | 19 |
19 // handler is what will handle when events are triggered | 20 // handler is what will handle when events are triggered |
20 » handler WatchHandler | 21 » handler NotifyWatchHandler |
21 | 22 |
22 » // closedHandler is set to watcher.MustErr, but that panic()s, so we | 23 » // closedHandler is set to watcher.MustErr, but that panic()s, so |
23 » // let the test suite override it. | 24 » // we let the test suite override it. |
24 closedHandler func(watcher.Errer) error | 25 closedHandler func(watcher.Errer) error |
rog
2013/08/01 11:18:47
can we just call this mustErr, as that represents
dimitern
2013/08/01 12:53:54
Done.
| |
25 } | 26 } |
26 | 27 |
27 // NotifyWorker encapsulates the logic for a worker which is based on a | 28 // NotifyWorker encapsulates the logic for a worker which is based on |
28 // NotifyWatcher. We do a bit of setup, and then spin waiting for the watcher | 29 // a NotifyWatcher. We do a bit of setup, and then spin waiting for |
29 // to trigger or for us to be killed, and then teardown cleanly. | 30 // the watcher to trigger or for us to be killed, and then teardown |
30 type NotifyWorker interface { | 31 // cleanly. |
31 » // Wait for the NotifyWorker to finish what it is doing an exit | 32 type NotifyWorker CommonWorker |
rog
2013/08/01 11:18:47
I don't think we need this type.
Let's just make N
dimitern
2013/08/01 12:53:54
Done.
| |
32 » Wait() error | |
33 | 33 |
34 » // Kill the running worker, indicating that it should stop what it is | 34 // NotifyWatchHandler implements the business logic that is triggered |
35 » // doing and exit. Killing a running worker should return error = nil | 35 // as part of watching a NotifyWatcher. |
36 » // from Wait. | 36 type NotifyWatchHandler interface { |
37 » Kill() | |
38 | 37 |
39 » // Stop will call both Kill and then Wait for the worker to exit. | 38 » // SetUp starts the handler, this should create the watcher we |
40 » Stop() error | 39 » // will be waiting on for more events. SetUp can return a Watcher |
41 } | 40 » // even if there is an error, and NotifyWorker will make sure to |
42 | 41 » // stop the Watcher. |
43 // WatchHandler implements the business logic that is triggered as part of | |
44 // watching a NotifyWatcher. | |
45 type WatchHandler interface { | |
46 | |
47 » // SetUp starts the handler, this should create the watcher we will be | |
48 » // waiting on for more events. SetUp can return a Watcher even if there | |
49 » // is an error, and NotifyWorker will make sure to stop the Watcher. | |
50 SetUp() (api.NotifyWatcher, error) | 42 SetUp() (api.NotifyWatcher, error) |
51 | 43 |
52 // TearDown should cleanup any resources that are left around | 44 // TearDown should cleanup any resources that are left around |
53 TearDown() error | 45 TearDown() error |
54 | 46 |
55 » // Handle is called when the Watcher has indicated there are changes, | 47 » // Handle is called when the Watcher has indicated there are |
56 » // do whatever work is necessary to process it | 48 » // changes, do whatever work is necessary to process it |
57 Handle() error | 49 Handle() error |
58 | 50 |
59 » // String is used when reporting. It is required because NotifyWatcher | 51 » // String is used when reporting. It is required because |
60 » // is wrapping the WatchHandler, but the WatchHandler is the | 52 » // NotifyWatcher is wrapping the NotifyWatchHandler, but the |
61 » // interesting (identifying) logic. | 53 » // NotifyWatchHandler is the interesting (identifying) logic. |
62 String() string | 54 String() string |
rog
2013/08/01 11:18:47
I don't think we need a String method at all. It w
dimitern
2013/08/01 12:53:54
Done.
| |
63 } | 55 } |
64 | 56 |
65 // NewNotifyWorker starts a new worker running the business logic from the | 57 // NewNotifyWorker starts a new worker running the business logic from the |
66 // handler. The worker loop is started in another goroutine as a side effect of | 58 // handler. The worker loop is started in another goroutine as a side effect of |
67 // calling this. | 59 // calling this. |
68 func NewNotifyWorker(handler WatchHandler) NotifyWorker { | 60 func NewNotifyWorker(handler NotifyWatchHandler) NotifyWorker { |
rog
2013/08/01 11:18:47
I think this should return a Worker.
dimitern
2013/08/01 12:53:54
Done.
| |
69 nw := ¬ifyWorker{ | 61 nw := ¬ifyWorker{ |
70 handler: handler, | 62 handler: handler, |
71 closedHandler: watcher.MustErr, | 63 closedHandler: watcher.MustErr, |
72 } | 64 } |
73 go func() { | 65 go func() { |
74 defer nw.tomb.Done() | 66 defer nw.tomb.Done() |
75 nw.tomb.Kill(nw.loop()) | 67 nw.tomb.Kill(nw.loop()) |
76 }() | 68 }() |
77 return nw | 69 return nw |
78 } | 70 } |
79 | 71 |
80 // Kill the loop with no-error | 72 // Kill the loop with no-error |
81 func (nw *notifyWorker) Kill() { | 73 func (nw *notifyWorker) Kill() { |
82 nw.tomb.Kill(nil) | 74 nw.tomb.Kill(nil) |
83 } | 75 } |
84 | 76 |
85 // Stop kils the worker and waits for it to exit | 77 // Stop kils the worker and waits for it to exit |
86 func (nw *notifyWorker) Stop() error { | 78 func (nw *notifyWorker) Stop() error { |
rog
2013/08/01 11:18:47
I don't think we need to bother implementing this
dimitern
2013/08/01 12:53:54
Done. As discussed, worker.Stop(x) can be used ins
| |
87 nw.tomb.Kill(nil) | 79 nw.tomb.Kill(nil) |
88 return nw.tomb.Wait() | 80 return nw.tomb.Wait() |
89 } | 81 } |
90 | 82 |
91 // Wait for the looping to finish | 83 // Wait for the looping to finish |
92 func (nw *notifyWorker) Wait() error { | 84 func (nw *notifyWorker) Wait() error { |
93 return nw.tomb.Wait() | 85 return nw.tomb.Wait() |
94 } | 86 } |
95 | 87 |
96 // String returns a nice description of this worker, taken from the underlying W atchHandler | 88 // String returns a nice description of this worker, taken from the underlying W atchHandler |
97 func (nw *notifyWorker) String() string { | 89 func (nw *notifyWorker) String() string { |
98 return nw.handler.String() | 90 return nw.handler.String() |
99 } | 91 } |
100 | 92 |
101 // TearDown the handler, but ensure any error is propagated | 93 func notifyHandlerTearDown(handler NotifyWatchHandler, t *tomb.Tomb) { |
rog
2013/08/01 11:18:47
how about this?
func propagateTearDown(handler in
dimitern
2013/08/01 12:53:54
Done.
| |
102 func handlerTearDown(handler WatchHandler, t *tomb.Tomb) { | 94 » // Tear down the handler, but ensure any error is propagated |
mue
2013/08/01 10:35:24
Why the comment in the method?
dimitern
2013/08/01 12:53:54
Because it was describing what's being done, rathe
| |
103 if err := handler.TearDown(); err != nil { | 95 if err := handler.TearDown(); err != nil { |
104 t.Kill(err) | 96 t.Kill(err) |
105 } | 97 } |
106 } | 98 } |
107 | 99 |
108 func (nw *notifyWorker) loop() error { | 100 func (nw *notifyWorker) loop() error { |
109 var w api.NotifyWatcher | 101 var w api.NotifyWatcher |
110 var err error | 102 var err error |
111 » defer handlerTearDown(nw.handler, &nw.tomb) | 103 » defer notifyHandlerTearDown(nw.handler, &nw.tomb) |
rog
2013/08/01 11:18:47
I'm not convinced that it's right to call TearDown
dimitern
2013/08/01 12:53:54
As I understand it, you propose to call this after
| |
112 if w, err = nw.handler.SetUp(); err != nil { | 104 if w, err = nw.handler.SetUp(); err != nil { |
113 if w != nil { | 105 if w != nil { |
114 // We don't bother to propogate an error, because we | 106 // We don't bother to propogate an error, because we |
rog
2013/08/01 11:18:47
s/propo/propa/
dimitern
2013/08/01 12:53:54
Done.
| |
115 // already have an error | 107 // already have an error |
116 w.Stop() | 108 w.Stop() |
117 } | 109 } |
118 return err | 110 return err |
119 } | 111 } |
120 defer watcher.Stop(w, &nw.tomb) | 112 defer watcher.Stop(w, &nw.tomb) |
121 for { | 113 for { |
122 select { | 114 select { |
123 case <-nw.tomb.Dying(): | 115 case <-nw.tomb.Dying(): |
124 return tomb.ErrDying | 116 return tomb.ErrDying |
125 case _, ok := <-w.Changes(): | 117 case _, ok := <-w.Changes(): |
126 if !ok { | 118 if !ok { |
127 return nw.closedHandler(w) | 119 return nw.closedHandler(w) |
128 } | 120 } |
129 if err := nw.handler.Handle(); err != nil { | 121 if err := nw.handler.Handle(); err != nil { |
130 return err | 122 return err |
131 } | 123 } |
132 } | 124 } |
133 } | 125 } |
134 panic("unreachable") | |
rog
2013/08/01 11:18:47
yay!
| |
135 } | 126 } |
OLD | NEW |