Merge lp:~chipaca/ubuntu-push/flapping-fixes into lp:ubuntu-push/automatic
- flapping-fixes
- Merge into automatic
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | John Lenton | ||||
Approved revision: | 360 | ||||
Merged at revision: | 356 | ||||
Proposed branch: | lp:~chipaca/ubuntu-push/flapping-fixes | ||||
Merge into: | lp:ubuntu-push/automatic | ||||
Prerequisite: | lp:~chipaca/ubuntu-push/lots-of-small-logging-changes | ||||
Diff against target: |
674 lines (+139/-87) 23 files modified
bus/connectivity/connectivity.go (+11/-7) bus/connectivity/connectivity_test.go (+10/-11) bus/endpoint.go (+6/-6) bus/networkmanager/networkmanager.go (+6/-6) bus/notifications/raw.go (+1/-1) client/client.go (+2/-3) client/client_test.go (+1/-1) client/service/service.go (+3/-3) client/session/session.go (+34/-2) identifier/identifier.go (+1/-1) identifier/identifier_test.go (+1/-1) launch_helper/helper_finder/helper_finder.go (+1/-1) launch_helper/helper_finder/helper_finder_test.go (+1/-1) launch_helper/legacy/legacy.go (+1/-1) launch_helper/legacy/legacy_test.go (+1/-1) messaging/messaging.go (+1/-1) testing/helpers.go (+1/-1) urldispatcher/curldispatcher/curldispatcher.go (+1/-1) urldispatcher/urldispatcher.go (+2/-2) urldispatcher/urldispatcher_test.go (+2/-2) util/redialer.go (+36/-27) util/redialer_states.gv (+9/-0) util/redialer_test.go (+7/-7) |
||||
To merge this branch: | bzr merge lp:~chipaca/ubuntu-push/flapping-fixes | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Bret Barker (community) | Approve | ||
Review via email: mp+247193@code.launchpad.net |
This proposal supersedes a proposal from 2015-01-21.
Commit message
Partially work around the issue in a minimally intrusive way (real fix will have to wait).
Description of the change
Partially work around the issue in a minimally intrusive way (real fix will have to wait).
Ubuntu One Auto Pilot (otto-pilot) wrote : | # |
The attempt to merge lp:~chipaca/ubuntu-push/flapping-fixes into lp:ubuntu-push/automatic failed. Below is the output from the failed tests.
scripts/deps.sh ubuntu-
scripts/deps.sh server/
scripts/deps.sh server/
/mnt/tarmac/
rm -f -r /mnt/tarmac/
mkdir -p /mnt/tarmac/
mkdir -p /mnt/tarmac/
go get -u launchpad.
go get -d -u launchpad.
/mnt/tarmac/
code.google.
- 360. By John Lenton
-
fix a massive data race in session
Preview Diff
1 | === modified file 'bus/connectivity/connectivity.go' |
2 | --- bus/connectivity/connectivity.go 2015-01-22 10:38:04 +0000 |
3 | +++ bus/connectivity/connectivity.go 2015-01-22 10:38:04 +0000 |
4 | @@ -132,13 +132,17 @@ |
5 | return false, errors.New("got not-OK from StateChanged watch") |
6 | } |
7 | cs.webgetCh = nil |
8 | - cs.currentState = v |
9 | - cs.timer.Reset(stabilizingTimeout) |
10 | - log.Debugf("State changed to %s. Assuming disconnect.", v) |
11 | - if cs.lastSent == true { |
12 | - log.Infof("Sending 'disconnected'.") |
13 | - cs.lastSent = false |
14 | - break Loop |
15 | + if v != networkmanager.Connecting && cs.currentState != v { |
16 | + cs.currentState = v |
17 | + cs.timer.Reset(stabilizingTimeout) |
18 | + log.Debugf("state changed to %s. Assuming disconnect.", v) |
19 | + if cs.lastSent == true { |
20 | + log.Debugf("sending 'disconnected'.") |
21 | + cs.lastSent = false |
22 | + break Loop |
23 | + } |
24 | + } else { |
25 | + log.Debugf("got State of %s, current is %s, ignoring.", v, cs.currentState) |
26 | } |
27 | |
28 | case <-cs.timer.C: |
29 | |
30 | === modified file 'bus/connectivity/connectivity_test.go' |
31 | --- bus/connectivity/connectivity_test.go 2014-07-04 23:00:42 +0000 |
32 | +++ bus/connectivity/connectivity_test.go 2015-01-22 10:38:04 +0000 |
33 | @@ -217,20 +217,22 @@ |
34 | f, e := cs.connectedStateStep() |
35 | c.Check(e, IsNil) |
36 | c.Check(f, Equals, true) |
37 | - ch <- networkmanager.ConnectedGlobal // a ConnectedGlobal when connected signals trouble |
38 | - f, e = cs.connectedStateStep() |
39 | - c.Check(e, IsNil) |
40 | - c.Check(f, Equals, false) // so we assume a disconnect happened |
41 | - f, e = cs.connectedStateStep() |
42 | - c.Check(e, IsNil) |
43 | - c.Check(f, Equals, true) // and if the web check works, go back to connected |
44 | + ch <- networkmanager.Disconnected |
45 | + ch <- networkmanager.ConnectedGlobal |
46 | + f, e = cs.connectedStateStep() |
47 | + c.Check(e, IsNil) |
48 | + c.Check(f, Equals, false) |
49 | + f, e = cs.connectedStateStep() |
50 | + c.Check(e, IsNil) |
51 | + c.Check(f, Equals, true) |
52 | |
53 | // same scenario, but with failing web check |
54 | webget_p = condition.Fail2Work(1) |
55 | + ch <- networkmanager.Disconnected |
56 | ch <- networkmanager.ConnectedGlobal |
57 | f, e = cs.connectedStateStep() |
58 | c.Check(e, IsNil) |
59 | - c.Check(f, Equals, false) // first false is from assuming a Connected signals trouble |
60 | + c.Check(f, Equals, false) // first false is from the Disconnected |
61 | |
62 | // the next call to Step will time out |
63 | _ch := make(chan bool, 1) |
64 | @@ -284,7 +286,6 @@ |
65 | |
66 | endp := testingbus.NewTestingEndpoint(condition.Work(true), condition.Work(true), |
67 | uint32(networkmanager.ConnectedGlobal), |
68 | - uint32(networkmanager.ConnectedGlobal), |
69 | uint32(networkmanager.Disconnected), |
70 | ) |
71 | |
72 | @@ -303,8 +304,6 @@ |
73 | }{ |
74 | {false, "first state is always false", 0}, |
75 | {true, "then it should be true as per ConnectedGlobal above", 0}, |
76 | - {false, "then, false (upon receiving the next ConnectedGlobal)", 2}, |
77 | - {true, "then it should be true (webcheck passed)", 0}, |
78 | {false, "then it should be false (Disconnected)", 2}, |
79 | {false, "then it should be false again because it's restarted", 2}, |
80 | } |
81 | |
82 | === modified file 'bus/endpoint.go' |
83 | --- bus/endpoint.go 2015-01-21 17:21:42 +0000 |
84 | +++ bus/endpoint.go 2015-01-22 10:38:04 +0000 |
85 | @@ -84,7 +84,7 @@ |
86 | name := endp.addr.Name |
87 | hasOwner, err := d.NameHasOwner(name) |
88 | if err != nil { |
89 | - endp.log.Debugf("Unable to determine ownership of %#v: %v", name, err) |
90 | + endp.log.Debugf("unable to determine ownership of %#v: %v", name, err) |
91 | bus.Close() |
92 | return err |
93 | } |
94 | @@ -126,7 +126,7 @@ |
95 | func (endp *endpoint) WatchSignal(member string, f func(...interface{}), d func()) error { |
96 | watch, err := endp.proxy.WatchSignal(endp.addr.Interface, member) |
97 | if err != nil { |
98 | - endp.log.Debugf("Failed to set up the watch: %s", err) |
99 | + endp.log.Debugf("failed to set up the watch: %s", err) |
100 | return err |
101 | } |
102 | |
103 | @@ -167,15 +167,15 @@ |
104 | variantvs := endp.unpackOneMsg(msg, property) |
105 | switch len(variantvs) { |
106 | default: |
107 | - return nil, fmt.Errorf("Too many values in Properties.Get response: %d", len(variantvs)) |
108 | + return nil, fmt.Errorf("too many values in Properties.Get response: %d", len(variantvs)) |
109 | case 0: |
110 | - return nil, fmt.Errorf("Not enough values in Properties.Get response: %d", len(variantvs)) |
111 | + return nil, fmt.Errorf("not enough values in Properties.Get response: %d", len(variantvs)) |
112 | case 1: |
113 | // carry on |
114 | } |
115 | variant, ok := variantvs[0].(*dbus.Variant) |
116 | if !ok { |
117 | - return nil, fmt.Errorf("Response from Properties.Get wasn't a *dbus.Variant") |
118 | + return nil, fmt.Errorf("response from Properties.Get wasn't a *dbus.Variant") |
119 | } |
120 | return variant.Value, nil |
121 | } |
122 | @@ -324,6 +324,6 @@ |
123 | } |
124 | f(endp.unpackOneMsg(msg, member)...) |
125 | } |
126 | - endp.log.Errorf("Got not-OK from %s watch", member) |
127 | + endp.log.Errorf("got not-OK from %s watch", member) |
128 | d() |
129 | } |
130 | |
131 | === modified file 'bus/networkmanager/networkmanager.go' |
132 | --- bus/networkmanager/networkmanager.go 2014-04-04 12:01:42 +0000 |
133 | +++ bus/networkmanager/networkmanager.go 2015-01-22 10:38:04 +0000 |
134 | @@ -71,14 +71,14 @@ |
135 | func (nm *networkManager) GetState() State { |
136 | s, err := nm.bus.GetProperty("state") |
137 | if err != nil { |
138 | - nm.log.Errorf("Failed gettting current state: %s", err) |
139 | - nm.log.Debugf("Defaulting state to Unknown") |
140 | + nm.log.Errorf("failed gettting current state: %s", err) |
141 | + nm.log.Debugf("defaulting state to Unknown") |
142 | return Unknown |
143 | } |
144 | |
145 | v, ok := s.(uint32) |
146 | if !ok { |
147 | - nm.log.Errorf("Got weird state: %#v", s) |
148 | + nm.log.Errorf("got weird state: %#v", s) |
149 | return Unknown |
150 | } |
151 | |
152 | @@ -110,8 +110,8 @@ |
153 | func (nm *networkManager) GetPrimaryConnection() string { |
154 | s, err := nm.bus.GetProperty("PrimaryConnection") |
155 | if err != nil { |
156 | - nm.log.Errorf("Failed gettting current primary connection: %s", err) |
157 | - nm.log.Debugf("Defaulting primary connection to empty") |
158 | + nm.log.Errorf("failed gettting current primary connection: %s", err) |
159 | + nm.log.Debugf("defaulting primary connection to empty") |
160 | return "" |
161 | } |
162 | |
163 | @@ -146,7 +146,7 @@ |
164 | ch <- string(con) |
165 | }, func() { close(ch) }) |
166 | if err != nil { |
167 | - nm.log.Debugf("Failed to set up the watch: %s", err) |
168 | + nm.log.Debugf("failed to set up the watch: %s", err) |
169 | return nil, err |
170 | } |
171 | |
172 | |
173 | === modified file 'bus/notifications/raw.go' |
174 | --- bus/notifications/raw.go 2014-08-15 10:33:04 +0000 |
175 | +++ bus/notifications/raw.go 2015-01-22 10:38:04 +0000 |
176 | @@ -119,7 +119,7 @@ |
177 | ch <- action |
178 | }, func() { close(ch) }) |
179 | if err != nil { |
180 | - raw.log.Debugf("Failed to set up the watch: %s", err) |
181 | + raw.log.Debugf("failed to set up the watch: %s", err) |
182 | return nil, err |
183 | } |
184 | return ch, nil |
185 | |
186 | === modified file 'client/client.go' |
187 | --- client/client.go 2014-11-03 13:36:00 +0000 |
188 | +++ client/client.go 2015-01-22 10:38:04 +0000 |
189 | @@ -381,10 +381,9 @@ |
190 | return |
191 | } |
192 | client.hasConnectivity = hasConnectivity |
193 | + client.session.Close() |
194 | if hasConnectivity { |
195 | client.session.AutoRedial(client.sessionConnectedCh) |
196 | - } else { |
197 | - client.session.Close() |
198 | } |
199 | } |
200 | |
201 | @@ -490,7 +489,7 @@ |
202 | case err := <-client.session.ErrCh: |
203 | errhandler(err) |
204 | case count := <-client.sessionConnectedCh: |
205 | - client.log.Debugf("Session connected after %d attempts", count) |
206 | + client.log.Debugf("session connected after %d attempts", count) |
207 | case app := <-client.unregisterCh: |
208 | unregisterhandler(app) |
209 | } |
210 | |
211 | === modified file 'client/client_test.go' |
212 | --- client/client_test.go 2014-09-05 10:47:29 +0000 |
213 | +++ client/client_test.go 2015-01-22 10:38:04 +0000 |
214 | @@ -1135,7 +1135,7 @@ |
215 | // sessionConnectedCh to nothing in particular, but it'll help sync this test |
216 | cli.sessionConnectedCh <- 42 |
217 | tick() |
218 | - c.Check(cs.log.Captured(), Matches, "(?ms).*Session connected after 42 attempts$") |
219 | + c.Check(cs.log.Captured(), Matches, "(?msi).*Session connected after 42 attempts$") |
220 | |
221 | // loop() should have connected: |
222 | // * connCh to the connectivity checker |
223 | |
224 | === modified file 'client/service/service.go' |
225 | --- client/service/service.go 2014-11-19 13:58:12 +0000 |
226 | +++ client/service/service.go 2015-01-22 10:38:04 +0000 |
227 | @@ -149,14 +149,14 @@ |
228 | // errors below here Can't Happen (tm). |
229 | body, err := ioutil.ReadAll(resp.Body) |
230 | if err != nil { |
231 | - svc.Log.Errorf("Reading response body: %v", err) |
232 | + svc.Log.Errorf("during ReadAll() of response body: %v", err) |
233 | return nil, err |
234 | } |
235 | |
236 | var reply registrationReply |
237 | err = json.Unmarshal(body, &reply) |
238 | if err != nil { |
239 | - svc.Log.Errorf("Unmarshalling response body: %v", err) |
240 | + svc.Log.Errorf("during Unmarshal of response body: %v", err) |
241 | return nil, fmt.Errorf("unable to unmarshal register response: %v", err) |
242 | } |
243 | |
244 | @@ -181,7 +181,7 @@ |
245 | } |
246 | |
247 | if !reply.Ok || reply.Token == "" { |
248 | - svc.Log.Errorf("Unexpected response: %#v", reply) |
249 | + svc.Log.Errorf("unexpected response: %#v", reply) |
250 | return nil, ErrBadToken |
251 | } |
252 | |
253 | |
254 | === modified file 'client/session/session.go' |
255 | --- client/session/session.go 2015-01-21 17:21:42 +0000 |
256 | +++ client/session/session.go 2015-01-22 10:38:04 +0000 |
257 | @@ -74,8 +74,22 @@ |
258 | Connected |
259 | Started |
260 | Running |
261 | + Unknown |
262 | ) |
263 | |
264 | +func (s ClientSessionState) String() string { |
265 | + if s >= Unknown { |
266 | + return fmt.Sprintf("??? (%d)", s) |
267 | + } |
268 | + return [Unknown]string{ |
269 | + "Error", |
270 | + "Disconnected", |
271 | + "Connected", |
272 | + "Started", |
273 | + "Running", |
274 | + }[s] |
275 | +} |
276 | + |
277 | type hostGetter interface { |
278 | Get() (*gethosts.Host, error) |
279 | } |
280 | @@ -131,6 +145,7 @@ |
281 | proto protocol.Protocol |
282 | pingInterval time.Duration |
283 | retrier util.AutoRedialer |
284 | + retrierLock sync.Mutex |
285 | cookie string |
286 | // status |
287 | stateP *uint32 |
288 | @@ -348,6 +363,8 @@ |
289 | } |
290 | |
291 | func (sess *ClientSession) stopRedial() { |
292 | + sess.retrierLock.Lock() |
293 | + defer sess.retrierLock.Unlock() |
294 | if sess.retrier != nil { |
295 | sess.retrier.Stop() |
296 | sess.retrier = nil |
297 | @@ -360,15 +377,30 @@ |
298 | sess.setShouldDelay() |
299 | } |
300 | time.Sleep(sess.redialDelay(sess)) |
301 | + sess.retrierLock.Lock() |
302 | + defer sess.retrierLock.Unlock() |
303 | + if sess.retrier != nil { |
304 | + panic("session AutoRedial: unexpected non-nil retrier.") |
305 | + } |
306 | sess.retrier = util.NewAutoRedialer(sess) |
307 | sess.lastAutoRedial = time.Now() |
308 | - go func() { doneCh <- sess.retrier.Redial() }() |
309 | + go func() { |
310 | + sess.retrierLock.Lock() |
311 | + retrier := sess.retrier |
312 | + sess.retrierLock.Unlock() |
313 | + if retrier == nil { |
314 | + sess.Log.Debugf("session autoredialer skipping retry: retrier has been set to nil.") |
315 | + return |
316 | + } |
317 | + doneCh <- retrier.Redial() |
318 | + }() |
319 | } |
320 | |
321 | func (sess *ClientSession) Close() { |
322 | sess.stopRedial() |
323 | sess.doClose() |
324 | } |
325 | + |
326 | func (sess *ClientSession) doClose() { |
327 | sess.connLock.Lock() |
328 | defer sess.connLock.Unlock() |
329 | @@ -593,7 +625,7 @@ |
330 | } |
331 | sess.proto = proto |
332 | sess.pingInterval = pingInterval |
333 | - sess.Log.Debugf("Connected %v.", conn.RemoteAddr()) |
334 | + sess.Log.Debugf("connected %v.", conn.RemoteAddr()) |
335 | sess.started() // deals with choosing which host to retry with as well |
336 | return nil |
337 | } |
338 | |
339 | === modified file 'identifier/identifier.go' |
340 | --- identifier/identifier.go 2014-08-04 20:40:50 +0000 |
341 | +++ identifier/identifier.go 2015-01-22 10:38:04 +0000 |
342 | @@ -48,7 +48,7 @@ |
343 | func New() (Id, error) { |
344 | value, err := readMachineId() |
345 | if err != nil { |
346 | - return &Identifier{value: ""}, fmt.Errorf("Failed to read the machine id: %s", err) |
347 | + return &Identifier{value: ""}, fmt.Errorf("failed to read the machine id: %s", err) |
348 | } |
349 | return &Identifier{value: value}, nil |
350 | } |
351 | |
352 | === modified file 'identifier/identifier_test.go' |
353 | --- identifier/identifier_test.go 2014-08-12 00:32:32 +0000 |
354 | +++ identifier/identifier_test.go 2015-01-22 10:38:04 +0000 |
355 | @@ -48,7 +48,7 @@ |
356 | machineIdPath = "/var/lib/dbus/no-such-file" |
357 | id, err := New() |
358 | c.Check(err, NotNil) |
359 | - c.Check(err.Error(), Equals, "Failed to read the machine id: open /var/lib/dbus/no-such-file: no such file or directory") |
360 | + c.Check(err.Error(), Equals, "failed to read the machine id: open /var/lib/dbus/no-such-file: no such file or directory") |
361 | c.Check(id.String(), HasLen, 0) |
362 | } |
363 | |
364 | |
365 | === modified file 'launch_helper/helper_finder/helper_finder.go' |
366 | --- launch_helper/helper_finder/helper_finder.go 2014-07-29 15:36:00 +0000 |
367 | +++ launch_helper/helper_finder/helper_finder.go 2015-01-22 10:38:04 +0000 |
368 | @@ -72,7 +72,7 @@ |
369 | fInfo, err := os.Stat(helpersDataPath) |
370 | if err != nil { |
371 | // cache file is missing, go via the slow route |
372 | - log.Infof("Cache file not found, falling back to .json file lookup") |
373 | + log.Infof("cache file not found, falling back to .json file lookup") |
374 | return helperFromHookFile(app) |
375 | } |
376 | // get the lock as the map can be changed while we read |
377 | |
378 | === modified file 'launch_helper/helper_finder/helper_finder_test.go' |
379 | --- launch_helper/helper_finder/helper_finder_test.go 2014-07-29 15:36:00 +0000 |
380 | +++ launch_helper/helper_finder/helper_finder_test.go 2015-01-22 10:38:04 +0000 |
381 | @@ -139,7 +139,7 @@ |
382 | hid, hex := Helper(app, s.log) |
383 | c.Check(hid, Equals, "com.example.test_test-helper_1") |
384 | c.Check(hex, Equals, filepath.Join(s.symlinkPath, "tsthlpr")) |
385 | - c.Check(s.log.Captured(), Matches, ".*Cache file not found, falling back to .json file lookup\n") |
386 | + c.Check(s.log.Captured(), Matches, ".*(?i)Cache file not found, falling back to .json file lookup\n") |
387 | } |
388 | |
389 | func (s *helperSuite) TestHelperFromHookBasic(c *C) { |
390 | |
391 | === modified file 'launch_helper/legacy/legacy.go' |
392 | --- launch_helper/legacy/legacy.go 2014-11-05 12:07:53 +0000 |
393 | +++ launch_helper/legacy/legacy.go 2015-01-22 10:38:04 +0000 |
394 | @@ -78,7 +78,7 @@ |
395 | p_err := cmd.Wait() |
396 | if p_err != nil { |
397 | // Helper failed or got killed, log output/errors |
398 | - lhl.log.Errorf("Legacy helper failed: appId: %v, helper: %v, pid: %v, error: %v, stdout: %#v, stderr: %#v.", |
399 | + lhl.log.Errorf("legacy helper failed: appId: %v, helper: %v, pid: %v, error: %v, stdout: %#v, stderr: %#v.", |
400 | appId, progname, id, p_err, stdout.String(), stderr.String()) |
401 | } |
402 | lhl.done(id) |
403 | |
404 | === modified file 'launch_helper/legacy/legacy_test.go' |
405 | --- launch_helper/legacy/legacy_test.go 2014-08-21 18:03:49 +0000 |
406 | +++ launch_helper/legacy/legacy_test.go 2015-01-22 10:38:04 +0000 |
407 | @@ -104,7 +104,7 @@ |
408 | c.Assert(err, IsNil) |
409 | |
410 | takeNext(ch, c) |
411 | - c.Check(ls.log.Captured(), Matches, "(?s).*Legacy helper failed.*") |
412 | + c.Check(ls.log.Captured(), Matches, "(?si).*Legacy helper failed.*") |
413 | } |
414 | |
415 | func (ls *legacySuite) TestHelperFailsLog(c *C) { |
416 | |
417 | === modified file 'messaging/messaging.go' |
418 | --- messaging/messaging.go 2014-07-27 02:54:40 +0000 |
419 | +++ messaging/messaging.go 2015-01-22 10:38:04 +0000 |
420 | @@ -181,7 +181,7 @@ |
421 | Action: action, |
422 | }) |
423 | if err != nil { |
424 | - mmu.Log.Errorf("Failed to build action: %s", action) |
425 | + mmu.Log.Errorf("failed to build action: %s", action) |
426 | return false |
427 | } |
428 | actions[2*i] = string(act) |
429 | |
430 | === modified file 'testing/helpers.go' |
431 | --- testing/helpers.go 2014-07-11 19:42:57 +0000 |
432 | +++ testing/helpers.go 2015-01-22 10:38:04 +0000 |
433 | @@ -118,7 +118,7 @@ |
434 | |
435 | idx := strings.LastIndex(dir, sep) |
436 | if idx == -1 { |
437 | - panic(fmt.Errorf("Unable to find %s in %#v", sep, dir)) |
438 | + panic(fmt.Errorf("unable to find %s in %#v", sep, dir)) |
439 | } |
440 | idx += len(sep) |
441 | |
442 | |
443 | === modified file 'urldispatcher/curldispatcher/curldispatcher.go' |
444 | --- urldispatcher/curldispatcher/curldispatcher.go 2014-09-01 13:27:17 +0000 |
445 | +++ urldispatcher/curldispatcher/curldispatcher.go 2015-01-22 10:38:04 +0000 |
446 | @@ -79,7 +79,7 @@ |
447 | C.dispatch_url(c_url, (C.gpointer)(&payload)) |
448 | success := <-doneCh |
449 | if !success { |
450 | - return fmt.Errorf("Failed to DispatchURL: %s for %s", url, appPackage) |
451 | + return fmt.Errorf("failed to DispatchURL: %s for %s", url, appPackage) |
452 | } |
453 | return nil |
454 | } |
455 | |
456 | === modified file 'urldispatcher/urldispatcher.go' |
457 | --- urldispatcher/urldispatcher.go 2014-09-01 13:27:17 +0000 |
458 | +++ urldispatcher/urldispatcher.go 2015-01-22 10:38:04 +0000 |
459 | @@ -44,7 +44,7 @@ |
460 | var cTestURL = curldispatcher.TestURL |
461 | |
462 | func (ud *urlDispatcher) DispatchURL(url string, app *click.AppId) error { |
463 | - ud.log.Debugf("Dispatching %s", url) |
464 | + ud.log.Debugf("dispatching %s", url) |
465 | err := cDispatchURL(url, app.DispatchPackage()) |
466 | if err != nil { |
467 | ud.log.Errorf("DispatchURL failed: %s", err) |
468 | @@ -62,7 +62,7 @@ |
469 | } |
470 | for _, appId := range appIds { |
471 | if appId != app.Versioned() { |
472 | - ud.log.Debugf("Notification skipped because of different appid for actions: %v - %s != %s", urls, appId, app.Versioned()) |
473 | + ud.log.Debugf("notification skipped because of different appid for actions: %v - %s != %s", urls, appId, app.Versioned()) |
474 | return false |
475 | } |
476 | } |
477 | |
478 | === modified file 'urldispatcher/urldispatcher_test.go' |
479 | --- urldispatcher/urldispatcher_test.go 2014-08-21 17:45:01 +0000 |
480 | +++ urldispatcher/urldispatcher_test.go 2015-01-22 10:38:04 +0000 |
481 | @@ -106,7 +106,7 @@ |
482 | appId := clickhelp.MustParseAppId("com.example.test_app_0.99") |
483 | urls := []string{"potato://test-app"} |
484 | c.Check(ud.TestURL(appId, urls), Equals, false) |
485 | - c.Check(s.log.Captured(), Matches, `(?sm).*Notification skipped because of different appid for actions: \[potato://test-app\] - com.example.test_test-app_0.1 != com.example.test_app_0.99`) |
486 | + c.Check(s.log.Captured(), Matches, `(?smi).*notification skipped because of different appid for actions: \[potato://test-app\] - com.example.test_test-app_0.1 != com.example.test_app_0.99`) |
487 | } |
488 | |
489 | func (s *UDSuite) TestTestURLOneWrongApp(c *C) { |
490 | @@ -117,7 +117,7 @@ |
491 | appId := clickhelp.MustParseAppId("com.example.test_test-app_0") |
492 | urls := []string{"potato://test-app", "potato_a://foo"} |
493 | c.Check(ud.TestURL(appId, urls), Equals, false) |
494 | - c.Check(s.log.Captured(), Matches, `(?sm).*Notification skipped because of different appid for actions: \[potato://test-app potato_a://foo\] - com.example.test_test-app1 != com.example.test_test-app.*`) |
495 | + c.Check(s.log.Captured(), Matches, `(?smi).*notification skipped because of different appid for actions: \[potato://test-app potato_a://foo\] - com.example.test_test-app1 != com.example.test_test-app.*`) |
496 | } |
497 | |
498 | func (s *UDSuite) TestTestURLInvalidURL(c *C) { |
499 | |
500 | === modified file 'util/redialer.go' |
501 | --- util/redialer.go 2014-03-20 12:15:47 +0000 |
502 | +++ util/redialer.go 2015-01-22 10:38:04 +0000 |
503 | @@ -19,6 +19,7 @@ |
504 | |
505 | import ( |
506 | "sync" |
507 | + "sync/atomic" |
508 | "time" |
509 | ) |
510 | |
511 | @@ -65,30 +66,42 @@ |
512 | Stop() // Stop shuts down the given AutoRedialer, if it is still retrying. |
513 | } |
514 | |
515 | +type redialerState uint32 |
516 | + |
517 | +const ( |
518 | + Unconfigured redialerState = iota |
519 | + Redialing |
520 | + Stopped |
521 | +) |
522 | + |
523 | +func (s *redialerState) String() string { |
524 | + return [3]string{"Unconfigured", "Redialing", "Stopped"}[uint32(*s)] |
525 | +} |
526 | + |
527 | type autoRedialer struct { |
528 | - stop chan bool |
529 | - lock sync.RWMutex |
530 | + stateP *redialerState |
531 | dial func() error |
532 | jitter func(time.Duration) time.Duration |
533 | } |
534 | |
535 | +func (ar *autoRedialer) state() redialerState { |
536 | + return redialerState(atomic.LoadUint32((*uint32)(ar.stateP))) |
537 | +} |
538 | + |
539 | +func (ar *autoRedialer) setState(s redialerState) { |
540 | + atomic.StoreUint32((*uint32)(ar.stateP), uint32(s)) |
541 | +} |
542 | + |
543 | +func (ar *autoRedialer) setStateIfEqual(oldState, newState redialerState) bool { |
544 | + return atomic.CompareAndSwapUint32((*uint32)(ar.stateP), uint32(oldState), uint32(newState)) |
545 | +} |
546 | + |
547 | func (ar *autoRedialer) Stop() { |
548 | if ar != nil { |
549 | - ar.lock.RLock() |
550 | - defer ar.lock.RUnlock() |
551 | - if ar.stop != nil { |
552 | - ar.stop <- true |
553 | - } |
554 | + ar.setState(Stopped) |
555 | } |
556 | } |
557 | |
558 | -func (ar *autoRedialer) shutdown() { |
559 | - ar.lock.Lock() |
560 | - defer ar.lock.Unlock() |
561 | - close(ar.stop) |
562 | - ar.stop = nil |
563 | -} |
564 | - |
565 | // Redial keeps on calling Dial until it stops returning an error. It does |
566 | // exponential backoff, adding back the output of Jitter at each step. |
567 | func (ar *autoRedialer) Redial() uint32 { |
568 | @@ -96,20 +109,19 @@ |
569 | // at least it's better than a segfault... |
570 | panic("you can't Redial a nil AutoRedialer") |
571 | } |
572 | - if ar.stop == nil { |
573 | - panic("this AutoRedialer has already been shut down") |
574 | + if !ar.setStateIfEqual(Unconfigured, Redialing) { |
575 | + // XXX log this |
576 | + return 0 |
577 | } |
578 | - defer ar.shutdown() |
579 | - |
580 | - ar.lock.RLock() |
581 | - stop := ar.stop |
582 | - ar.lock.RUnlock() |
583 | |
584 | var timeout time.Duration |
585 | var dialAttempts uint32 = 0 // unsigned so it can wrap safely ... |
586 | timeouts := Timeouts() |
587 | var numTimeouts uint32 = uint32(len(timeouts)) |
588 | for { |
589 | + if ar.state() != Redialing { |
590 | + return dialAttempts |
591 | + } |
592 | if ar.dial() == nil { |
593 | return dialAttempts + 1 |
594 | } |
595 | @@ -122,18 +134,15 @@ |
596 | timeout += ar.jitter(timeout) |
597 | } |
598 | dialAttempts++ |
599 | - select { |
600 | - case <-stop: |
601 | - return dialAttempts |
602 | - case <-time.After(timeout): |
603 | - } |
604 | + time.Sleep(timeout) |
605 | } |
606 | } |
607 | |
608 | // Returns a stoppable AutoRedialer using the provided Dialer. If the Dialer |
609 | // is also a Jitterer, the backoff will be jittered. |
610 | func NewAutoRedialer(dialer Dialer) AutoRedialer { |
611 | - ar := &autoRedialer{stop: make(chan bool), dial: dialer.Dial} |
612 | + state := Unconfigured |
613 | + ar := &autoRedialer{stateP: &state, dial: dialer.Dial} |
614 | jitterer, ok := dialer.(Jitterer) |
615 | if ok { |
616 | ar.jitter = jitterer.Jitter |
617 | |
618 | === added file 'util/redialer_states.gv' |
619 | --- util/redialer_states.gv 1970-01-01 00:00:00 +0000 |
620 | +++ util/redialer_states.gv 2015-01-22 10:38:04 +0000 |
621 | @@ -0,0 +1,9 @@ |
622 | +digraph "redialer" { |
623 | + "Unconfigured" -> "Redialing" [ label="Redial" ] |
624 | + "Unconfigured" -> "Stopped" [ label="Stop" ] |
625 | + |
626 | + "Redialing" -> "Redialing" [ label="Redial" ] |
627 | + "Redialing" -> "Stopped" [ label="Stop" ] |
628 | + |
629 | + "Stopped" -> "Stopped" [ label="*" ] |
630 | +} |
631 | |
632 | === modified file 'util/redialer_test.go' |
633 | --- util/redialer_test.go 2014-02-05 13:02:47 +0000 |
634 | +++ util/redialer_test.go 2015-01-22 10:38:04 +0000 |
635 | @@ -48,10 +48,10 @@ |
636 | func (s *RedialerSuite) TestWorks(c *C) { |
637 | endp := testibus.NewTestingEndpoint(condition.Fail2Work(3), nil) |
638 | ar := NewAutoRedialer(endp) |
639 | - c.Check(ar.(*autoRedialer).stop, NotNil) |
640 | + // c.Check(ar.(*autoRedialer).stop, NotNil) |
641 | c.Check(ar.Redial(), Equals, uint32(4)) |
642 | // and on success, the stopper goes away |
643 | - c.Check(ar.(*autoRedialer).stop, IsNil) |
644 | + // c.Check(ar.(*autoRedialer).stop, IsNil) |
645 | } |
646 | |
647 | func (s *RedialerSuite) TestRetryNil(c *C) { |
648 | @@ -63,7 +63,7 @@ |
649 | endp := testibus.NewTestingEndpoint(condition.Work(true), nil) |
650 | ar := NewAutoRedialer(endp) |
651 | c.Check(ar.Redial(), Equals, uint32(1)) |
652 | - c.Check(ar.Redial, PanicMatches, ".*shut.?down.*") |
653 | + c.Check(ar.Redial(), Equals, uint32(0)) |
654 | } |
655 | |
656 | type JitteringEndpoint struct { |
657 | @@ -103,13 +103,13 @@ |
658 | go func() { countCh <- ar.Redial() }() |
659 | ar.Stop() |
660 | select { |
661 | - case n := <-countCh: |
662 | - c.Check(n, Equals, uint32(1)) |
663 | + case <-countCh: |
664 | + // pass |
665 | case <-time.After(20 * time.Millisecond): |
666 | c.Fatal("timed out waiting for redial") |
667 | } |
668 | - // on Stop(), the stopper goes away too |
669 | - c.Check(ar.(*autoRedialer).stop, IsNil) |
670 | + // on Stop(), the redialer is Stopped |
671 | + c.Check(ar.(*autoRedialer).state(), Equals, Stopped) |
672 | // and the next Stop() doesn't panic nor block |
673 | ar.Stop() |
674 | } |
LGTM