Merge lp:~pedronis/ubuntu-push/automatic-bug-fixes-into-krillin-rtm into lp:ubuntu-push/krillin-rtm

Proposed by Samuele Pedroni
Status: Merged
Approved by: Roberto Alsina
Approved revision: 143
Merged at revision: 143
Proposed branch: lp:~pedronis/ubuntu-push/automatic-bug-fixes-into-krillin-rtm
Merge into: lp:ubuntu-push/krillin-rtm
Diff against target: 954 lines (+624/-59)
9 files modified
bus/networkmanager/networkmanager.go (+65/-12)
bus/networkmanager/networkmanager_test.go (+109/-1)
bus/urfkill/urfkill.go (+94/-0)
bus/urfkill/urfkill.go_test.go (+121/-0)
client/client.go (+2/-1)
client/client_test.go (+1/-0)
debian/config.json (+2/-1)
poller/poller.go (+159/-33)
poller/poller_test.go (+71/-11)
To merge this branch: bzr merge lp:~pedronis/ubuntu-push/automatic-bug-fixes-into-krillin-rtm
Reviewer Review Type Date Requested Status
Roberto Alsina (community) Approve
Review via email: mp+254714@code.launchpad.net

Commit message

[Bret Barker]
* add a hacky busy sleep loop to workaround go's sleep not
  accounting for suspended time (lp:1435109).

[Samuele Pedroni]
* stop waking up for polling if in flight-mode and wireless not
  enabled (lp:1437135).

To post a comment you must log in.
Revision history for this message
Roberto Alsina (ralsina) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bus/networkmanager/networkmanager.go'
2--- bus/networkmanager/networkmanager.go 2015-03-26 15:57:45 +0000
3+++ bus/networkmanager/networkmanager.go 2015-03-31 09:33:32 +0000
4@@ -1,5 +1,5 @@
5 /*
6- Copyright 2013-2014 Canonical Ltd.
7+ Copyright 2013-2015 Canonical Ltd.
8
9 This program is free software: you can redistribute it and/or modify it
10 under the terms of the GNU General Public License version 3, as published
11@@ -14,9 +14,10 @@
12 with this program. If not, see <http://www.gnu.org/licenses/>.
13 */
14
15-// Package networkmanager wraps a couple of NetworkManager's DBus API points:
16-// the org.freedesktop.NetworkManager.state call, and listening for the
17-// StateChange signal.
18+// Package networkmanager wraps a couple of NetworkManager's DBus API
19+// points: the org.freedesktop.NetworkManager.state call, and
20+// listening for the StateChange signal, similarly for the primary
21+// connection and wireless enabled state.
22 package networkmanager
23
24 import (
25@@ -47,8 +48,14 @@
26 // primary connection.
27 GetPrimaryConnection() string
28 // WatchPrimaryConnection listens for changes of NetworkManager's
29- // Primary Connection, and sends it out over the channel returned.
30+ // Primary Connection, and sends them out over the channel returned.
31 WatchPrimaryConnection() (<-chan string, bus.Cancellable, error)
32+ // GetWirelessEnabled fetches and returns NetworkManager's
33+ // wireless state.
34+ GetWirelessEnabled() bool
35+ // WatchWirelessEnabled listens for changes of NetworkManager's
36+ // wireless state, and sends them out over the channel returned.
37+ WatchWirelessEnabled() (<-chan bool, bus.Cancellable, error)
38 }
39
40 type networkManager struct {
41@@ -71,7 +78,7 @@
42 func (nm *networkManager) GetState() State {
43 s, err := nm.bus.GetProperty("state")
44 if err != nil {
45- nm.log.Errorf("failed gettting current state: %s", err)
46+ nm.log.Errorf("failed getting current state: %s", err)
47 nm.log.Debugf("defaulting state to Unknown")
48 return Unknown
49 }
50@@ -108,16 +115,16 @@
51 }
52
53 func (nm *networkManager) GetPrimaryConnection() string {
54- s, err := nm.bus.GetProperty("PrimaryConnection")
55+ got, err := nm.bus.GetProperty("PrimaryConnection")
56 if err != nil {
57- nm.log.Errorf("failed gettting current primary connection: %s", err)
58- nm.log.Debugf("defaulting primary connection to empty")
59+ nm.log.Errorf("failed getting current PrimaryConnection: %s", err)
60+ nm.log.Debugf("defaulting PrimaryConnection to empty")
61 return ""
62 }
63
64- v, ok := s.(dbus.ObjectPath)
65+ v, ok := got.(dbus.ObjectPath)
66 if !ok {
67- nm.log.Errorf("got weird PrimaryConnection: %#v", s)
68+ nm.log.Errorf("got weird PrimaryConnection: %#v", got)
69 return ""
70 }
71
72@@ -142,7 +149,7 @@
73 nm.log.Errorf("got weird PrimaryConnection via PropertiesChanged: %#v", v)
74 return
75 }
76- nm.log.Debugf("got primary connection: %s", con)
77+ nm.log.Debugf("got PrimaryConnection change: %s", con)
78 ch <- string(con)
79 }, func() { close(ch) })
80 if err != nil {
81@@ -152,3 +159,49 @@
82
83 return ch, w, nil
84 }
85+
86+func (nm *networkManager) GetWirelessEnabled() bool {
87+ got, err := nm.bus.GetProperty("WirelessEnabled")
88+ if err != nil {
89+ nm.log.Errorf("failed getting WirelessEnabled: %s", err)
90+ nm.log.Debugf("defaulting WirelessEnabled to true")
91+ return true
92+ }
93+
94+ v, ok := got.(bool)
95+ if !ok {
96+ nm.log.Errorf("got weird WirelessEnabled: %#v", got)
97+ return true
98+ }
99+
100+ return v
101+}
102+
103+func (nm *networkManager) WatchWirelessEnabled() (<-chan bool, bus.Cancellable, error) {
104+ ch := make(chan bool)
105+ w, err := nm.bus.WatchSignal("PropertiesChanged",
106+ func(ppsi ...interface{}) {
107+ pps, ok := ppsi[0].(map[string]dbus.Variant)
108+ if !ok {
109+ nm.log.Errorf("got weird PropertiesChanged: %#v", ppsi[0])
110+ return
111+ }
112+ v, ok := pps["WirelessEnabled"]
113+ if !ok {
114+ return
115+ }
116+ en, ok := v.Value.(bool)
117+ if !ok {
118+ nm.log.Errorf("got weird WirelessEnabled via PropertiesChanged: %#v", v)
119+ return
120+ }
121+ nm.log.Debugf("got WirelessEnabled change: %v", en)
122+ ch <- en
123+ }, func() { close(ch) })
124+ if err != nil {
125+ nm.log.Debugf("failed to set up the watch: %s", err)
126+ return nil, nil, err
127+ }
128+
129+ return ch, w, nil
130+}
131
132=== modified file 'bus/networkmanager/networkmanager_test.go'
133--- bus/networkmanager/networkmanager_test.go 2015-02-26 19:36:57 +0000
134+++ bus/networkmanager/networkmanager_test.go 2015-03-31 09:33:32 +0000
135@@ -1,5 +1,5 @@
136 /*
137- Copyright 2013-2014 Canonical Ltd.
138+ Copyright 2013-2015 Canonical Ltd.
139
140 This program is free software: you can redistribute it and/or modify it
141 under the terms of the GNU General Public License version 3, as published
142@@ -232,3 +232,111 @@
143 c.Check(ok, Equals, true)
144 c.Check(v, Equals, "42")
145 }
146+
147+// GetWirelessEnabled returns the right state when everything works
148+func (s *NMSuite) TestGetWirelessEnabled(c *C) {
149+ nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), false), s.log)
150+ en := nm.GetWirelessEnabled()
151+ c.Check(en, Equals, false)
152+}
153+
154+// GetWirelessEnabled returns the right state when dbus fails
155+func (s *NMSuite) TestGetWirelessEnabledFail(c *C) {
156+ nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log)
157+ en := nm.GetWirelessEnabled()
158+ c.Check(en, Equals, true)
159+}
160+
161+// GetWirelessEnabled returns the right state when dbus works but delivers rubbish values
162+func (s *NMSuite) TestGetWirelessEnabledRubbishValues(c *C) {
163+ nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), "broken"), s.log)
164+ en := nm.GetWirelessEnabled()
165+ c.Check(en, Equals, true)
166+}
167+
168+// GetWirelessEnabled returns the right state when dbus works but delivers a rubbish structure
169+func (s *NMSuite) TestGetWirelessEnabledRubbishStructure(c *C) {
170+ nm := New(testingbus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}), s.log)
171+ en := nm.GetWirelessEnabled()
172+ c.Check(en, Equals, true)
173+}
174+
175+func mkWirelessEnMap(en bool) map[string]dbus.Variant {
176+ m := make(map[string]dbus.Variant)
177+ m["WirelessEnabled"] = dbus.Variant{en}
178+ return m
179+}
180+
181+// WatchWirelessEnabled sends a stream of wireless enabled states over the channel
182+func (s *NMSuite) TestWatchWirelessEnabled(c *C) {
183+ tc := testingbus.NewTestingEndpoint(nil, condition.Work(true),
184+ mkWirelessEnMap(true),
185+ mkWirelessEnMap(false),
186+ mkWirelessEnMap(true),
187+ )
188+ nm := New(tc, s.log)
189+ ch, w, err := nm.WatchWirelessEnabled()
190+ c.Assert(err, IsNil)
191+ defer w.Cancel()
192+ l := []bool{<-ch, <-ch, <-ch}
193+ c.Check(l, DeepEquals, []bool{true, false, true})
194+}
195+
196+// WatchWirelessEnabled returns on error if the dbus call fails
197+func (s *NMSuite) TestWatchWirelessEnabledFails(c *C) {
198+ nm := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log)
199+ _, _, err := nm.WatchWirelessEnabled()
200+ c.Check(err, NotNil)
201+}
202+
203+// WatchWirelessEnabled calls close on its channel when the watch bails
204+func (s *NMSuite) TestWatchWirelessEnabledClosesOnWatchBail(c *C) {
205+ tc := testingbus.NewTestingEndpoint(nil, condition.Work(true))
206+ nm := New(tc, s.log)
207+ ch, w, err := nm.WatchWirelessEnabled()
208+ c.Assert(err, IsNil)
209+ defer w.Cancel()
210+ _, ok := <-ch
211+ c.Check(ok, Equals, false)
212+}
213+
214+// WatchWirelessEnabled survives rubbish values
215+func (s *NMSuite) TestWatchWirelessEnabledSurvivesRubbishValues(c *C) {
216+ tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "gorp")
217+ nm := New(tc, s.log)
218+ ch, w, err := nm.WatchWirelessEnabled()
219+ c.Assert(err, IsNil)
220+ defer w.Cancel()
221+ _, ok := <-ch
222+ c.Check(ok, Equals, false)
223+}
224+
225+// WatchWirelessEnabled ignores non-WirelessEnabled PropertyChanged
226+func (s *NMSuite) TestWatchWirelessEnabledIgnoresIrrelephant(c *C) {
227+ tc := testingbus.NewTestingEndpoint(nil, condition.Work(true),
228+ map[string]dbus.Variant{"foo": dbus.Variant{}},
229+ map[string]dbus.Variant{"WirelessEnabled": dbus.Variant{true}},
230+ )
231+ nm := New(tc, s.log)
232+ ch, w, err := nm.WatchWirelessEnabled()
233+ c.Assert(err, IsNil)
234+ defer w.Cancel()
235+ v, ok := <-ch
236+ c.Check(ok, Equals, true)
237+ c.Check(v, Equals, true)
238+}
239+
240+// WatchWirelessEnabled ignores rubbish WirelessEnabled
241+func (s *NMSuite) TestWatchWirelessEnabledIgnoresRubbishValues(c *C) {
242+ tc := testingbus.NewTestingEndpoint(nil, condition.Work(true),
243+ map[string]dbus.Variant{"WirelessEnabled": dbus.Variant{-12}},
244+ map[string]dbus.Variant{"WirelessEnabled": dbus.Variant{false}},
245+ )
246+ nm := New(tc, s.log)
247+ ch, w, err := nm.WatchWirelessEnabled()
248+ c.Assert(err, IsNil)
249+ defer w.Cancel()
250+ v, ok := <-ch
251+ c.Check(ok, Equals, true)
252+ c.Check(v, Equals, false)
253+}
254
255=== added directory 'bus/urfkill'
256=== added file 'bus/urfkill/urfkill.go'
257--- bus/urfkill/urfkill.go 1970-01-01 00:00:00 +0000
258+++ bus/urfkill/urfkill.go 2015-03-31 09:33:32 +0000
259@@ -0,0 +1,94 @@
260+/*
261+ Copyright 2015 Canonical Ltd.
262+
263+ This program is free software: you can redistribute it and/or modify it
264+ under the terms of the GNU General Public License version 3, as published
265+ by the Free Software Foundation.
266+
267+ This program is distributed in the hope that it will be useful, but
268+ WITHOUT ANY WARRANTY; without even the implied warranties of
269+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
270+ PURPOSE. See the GNU General Public License for more details.
271+
272+ You should have received a copy of the GNU General Public License along
273+ with this program. If not, see <http://www.gnu.org/licenses/>.
274+*/
275+
276+// Package urfkill wraps a couple of URfkill's DBus API points to
277+// watch for flight mode state changes.
278+package urfkill
279+
280+import (
281+ //"launchpad.net/go-dbus/v1"
282+
283+ "launchpad.net/ubuntu-push/bus"
284+ "launchpad.net/ubuntu-push/logger"
285+)
286+
287+// URfkill lives on a well-knwon bus.Address
288+var BusAddress bus.Address = bus.Address{
289+ Interface: "org.freedesktop.URfkill",
290+ Path: "/org/freedesktop/URfkill",
291+ Name: "org.freedesktop.URfkill",
292+}
293+
294+/*****************************************************************
295+ * URfkill (and its implementation)
296+ */
297+
298+type URfkill interface {
299+ // IsFlightMode returns flight mode state.
300+ IsFlightMode() bool
301+ // WatchFlightMode listens for changes to URfkill's flight
302+ // mode state, and sends them out over the channel returned.
303+ WatchFlightMode() (<-chan bool, bus.Cancellable, error)
304+}
305+
306+type uRfkill struct {
307+ bus bus.Endpoint
308+ log logger.Logger
309+}
310+
311+// New returns a new URfkill that'll use the provided bus.Endpoint
312+func New(endp bus.Endpoint, log logger.Logger) URfkill {
313+ return &uRfkill{endp, log}
314+}
315+
316+// ensure uRfkill implements URfkill
317+var _ URfkill = &uRfkill{}
318+
319+/*
320+ public methods
321+*/
322+
323+func (ur *uRfkill) IsFlightMode() bool {
324+ var res bool
325+ err := ur.bus.Call("IsFlightMode", bus.Args(), &res)
326+ if err != nil {
327+ ur.log.Errorf("failed getting flight-mode state: %s", err)
328+ ur.log.Debugf("defaulting flight-mode state to false")
329+ return false
330+ }
331+ return res
332+}
333+
334+func (ur *uRfkill) WatchFlightMode() (<-chan bool, bus.Cancellable, error) {
335+ ch := make(chan bool)
336+ w, err := ur.bus.WatchSignal("FlightModeChanged",
337+ func(ns ...interface{}) {
338+ stbool, ok := ns[0].(bool)
339+ if !ok {
340+ ur.log.Errorf("got weird flight-mode state: %#v", ns[0])
341+ return
342+ }
343+ ur.log.Debugf("got flight-mode change: %v", stbool)
344+ ch <- stbool
345+ },
346+ func() { close(ch) })
347+ if err != nil {
348+ ur.log.Debugf("Failed to set up the watch: %s", err)
349+ return nil, nil, err
350+ }
351+
352+ return ch, w, nil
353+}
354
355=== added file 'bus/urfkill/urfkill.go_test.go'
356--- bus/urfkill/urfkill.go_test.go 1970-01-01 00:00:00 +0000
357+++ bus/urfkill/urfkill.go_test.go 2015-03-31 09:33:32 +0000
358@@ -0,0 +1,121 @@
359+/*
360+ Copyright 2015 Canonical Ltd.
361+
362+ This program is free software: you can redistribute it and/or modify it
363+ under the terms of the GNU General Public License version 3, as published
364+ by the Free Software Foundation.
365+
366+ This program is distributed in the hope that it will be useful, but
367+ WITHOUT ANY WARRANTY; without even the implied warranties of
368+ MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
369+ PURPOSE. See the GNU General Public License for more details.
370+
371+ You should have received a copy of the GNU General Public License along
372+ with this program. If not, see <http://www.gnu.org/licenses/>.
373+*/
374+
375+package urfkill
376+
377+import (
378+ "testing"
379+
380+ //"launchpad.net/go-dbus/v1"
381+ . "launchpad.net/gocheck"
382+
383+ testingbus "launchpad.net/ubuntu-push/bus/testing"
384+ "launchpad.net/ubuntu-push/logger"
385+ helpers "launchpad.net/ubuntu-push/testing"
386+ "launchpad.net/ubuntu-push/testing/condition"
387+)
388+
389+// hook up gocheck
390+func Test(t *testing.T) { TestingT(t) }
391+
392+type URSuite struct {
393+ log logger.Logger
394+}
395+
396+var _ = Suite(&URSuite{})
397+
398+func (s *URSuite) SetUpTest(c *C) {
399+ s.log = helpers.NewTestLogger(c, "debug")
400+}
401+
402+func (s *URSuite) TestNew(c *C) {
403+ ur := New(testingbus.NewTestingEndpoint(nil, condition.Work(true)), s.log)
404+ c.Check(ur, NotNil)
405+}
406+
407+// IsFlightMode returns the right state when everything works
408+func (s *URSuite) TestIsFlightMode(c *C) {
409+ endp := testingbus.NewTestingEndpoint(nil, condition.Work(true), true)
410+ ur := New(endp, s.log)
411+ state := ur.IsFlightMode()
412+ c.Check(state, Equals, true)
413+ callArgs := testingbus.GetCallArgs(endp)
414+ c.Assert(callArgs, HasLen, 1)
415+ c.Assert(callArgs[0].Member, Equals, "IsFlightMode")
416+ c.Assert(callArgs[0].Args, HasLen, 0)
417+}
418+
419+// IsFlightMode returns the right state when dbus fails
420+func (s *URSuite) TestIsFlightModeFail(c *C) {
421+ ur := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log)
422+ state := ur.IsFlightMode()
423+ c.Check(state, Equals, false)
424+}
425+
426+// IsFlightMode returns the right state when dbus works but delivers
427+// rubbish values
428+func (s *URSuite) TestIsFlightModeRubbishValues(c *C) {
429+ ur := New(testingbus.NewTestingEndpoint(nil, condition.Work(true), "broken"), s.log)
430+ state := ur.IsFlightMode()
431+ c.Check(state, Equals, false)
432+}
433+
434+// IsFlightMode returns the right state when dbus works but delivers a rubbish structure
435+func (s *URSuite) TestIsFlightModeRubbishStructure(c *C) {
436+ ur := New(testingbus.NewMultiValuedTestingEndpoint(nil, condition.Work(true), []interface{}{}), s.log)
437+ state := ur.IsFlightMode()
438+ c.Check(state, Equals, false)
439+}
440+
441+// WatchFightMode sends a stream of states over the channel
442+func (s *URSuite) TestWatchFlightMode(c *C) {
443+ tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), false, true, false)
444+ ur := New(tc, s.log)
445+ ch, w, err := ur.WatchFlightMode()
446+ c.Assert(err, IsNil)
447+ defer w.Cancel()
448+ l := []bool{<-ch, <-ch, <-ch}
449+ c.Check(l, DeepEquals, []bool{false, true, false})
450+}
451+
452+// WatchFlightMode returns on error if the dbus call fails
453+func (s *URSuite) TestWatchFlightModeFails(c *C) {
454+ ur := New(testingbus.NewTestingEndpoint(nil, condition.Work(false)), s.log)
455+ _, _, err := ur.WatchFlightMode()
456+ c.Check(err, NotNil)
457+}
458+
459+// WatchFlightMode calls close on its channel when the watch bails
460+func (s *URSuite) TestWatchFlightModeClosesOnWatchBail(c *C) {
461+ tc := testingbus.NewTestingEndpoint(nil, condition.Work(true))
462+ ur := New(tc, s.log)
463+ ch, w, err := ur.WatchFlightMode()
464+ c.Assert(err, IsNil)
465+ defer w.Cancel()
466+ _, ok := <-ch
467+ c.Check(ok, Equals, false)
468+}
469+
470+// WatchFlightMode survives rubbish values
471+func (s *URSuite) TestWatchFlightModeSurvivesRubbishValues(c *C) {
472+ tc := testingbus.NewTestingEndpoint(nil, condition.Work(true), "gorp")
473+ ur := New(tc, s.log)
474+ ch, w, err := ur.WatchFlightMode()
475+ c.Assert(err, IsNil)
476+ defer w.Cancel()
477+ _, ok := <-ch
478+ c.Check(ok, Equals, false)
479+}
480
481=== modified file 'client/client.go'
482--- client/client.go 2015-03-26 15:57:45 +0000
483+++ client/client.go 2015-03-31 09:33:32 +0000
484@@ -79,6 +79,7 @@
485 PollNetworkWait config.ConfigTimeDuration `json:"poll_net_wait"`
486 PollPolldWait config.ConfigTimeDuration `json:"poll_polld_wait"`
487 PollDoneWait config.ConfigTimeDuration `json:"poll_done_wait"`
488+ PollBusyWait config.ConfigTimeDuration `json:"poll_busy_wait"`
489 }
490
491 // PushService is the interface we use of service.PushService.
492@@ -246,6 +247,7 @@
493 NetworkWait: client.config.PollNetworkWait.TimeDuration(),
494 PolldWait: client.config.PollPolldWait.TimeDuration(),
495 DoneWait: client.config.PollDoneWait.TimeDuration(),
496+ BusyWait: client.config.PollBusyWait.TimeDuration(),
497 },
498 Log: client.log,
499 SessionStateGetter: client.session,
500@@ -285,7 +287,6 @@
501
502 // takeTheBus starts the connection(s) to D-Bus and sets up associated event channels
503 func (client *PushClient) takeTheBus() error {
504- fmt.Println("FOO")
505 cs := connectivity.New(client.connectivityEndp,
506 client.config.ConnectivityConfig, client.log)
507 go cs.Track(client.connCh)
508
509=== modified file 'client/client_test.go'
510--- client/client_test.go 2015-03-26 15:57:45 +0000
511+++ client/client_test.go 2015-03-31 09:33:32 +0000
512@@ -185,6 +185,7 @@
513 "poll_net_wait": "1m",
514 "poll_polld_wait": "3m",
515 "poll_done_wait": "5s",
516+ "poll_busy_wait": "0s",
517 }
518 for k, v := range overrides {
519 cfgMap[k] = v
520
521=== modified file 'debian/config.json'
522--- debian/config.json 2015-03-26 15:57:45 +0000
523+++ debian/config.json 2015-03-31 09:33:32 +0000
524@@ -19,5 +19,6 @@
525 "poll_settle": "20ms",
526 "poll_net_wait": "1m",
527 "poll_polld_wait": "3m",
528- "poll_done_wait": "5s"
529+ "poll_done_wait": "5s",
530+ "poll_busy_wait": "1s"
531 }
532
533=== modified file 'poller/poller.go'
534--- poller/poller.go 2015-03-26 15:57:45 +0000
535+++ poller/poller.go 2015-03-31 09:33:32 +0000
536@@ -1,5 +1,5 @@
537 /*
538- Copyright 2014 Canonical Ltd.
539+ Copyright 2014-2015 Canonical Ltd.
540
541 This program is free software: you can redistribute it and/or modify it
542 under the terms of the GNU General Public License version 3, as published
543@@ -25,8 +25,10 @@
544 "time"
545
546 "launchpad.net/ubuntu-push/bus"
547+ "launchpad.net/ubuntu-push/bus/networkmanager"
548 "launchpad.net/ubuntu-push/bus/polld"
549 "launchpad.net/ubuntu-push/bus/powerd"
550+ "launchpad.net/ubuntu-push/bus/urfkill"
551 "launchpad.net/ubuntu-push/client/session"
552 "launchpad.net/ubuntu-push/logger"
553 "launchpad.net/ubuntu-push/util"
554@@ -48,6 +50,7 @@
555 NetworkWait time.Duration
556 PolldWait time.Duration
557 DoneWait time.Duration
558+ BusyWait time.Duration
559 }
560
561 type Poller interface {
562@@ -63,21 +66,29 @@
563 }
564
565 type poller struct {
566- times Times
567- log logger.Logger
568- powerd powerd.Powerd
569- polld polld.Polld
570- cookie string
571- sessionState stater
572+ times Times
573+ log logger.Logger
574+ nm networkmanager.NetworkManager
575+ powerd powerd.Powerd
576+ polld polld.Polld
577+ urfkill urfkill.URfkill
578+ cookie string
579+ sessionState stater
580+ requestWakeupCh chan struct{}
581+ requestedWakeupErrCh chan error
582+ holdsWakeLockCh chan bool
583 }
584
585 func New(setup *PollerSetup) Poller {
586 return &poller{
587- times: setup.Times,
588- log: setup.Log,
589- powerd: nil,
590- polld: nil,
591- sessionState: setup.SessionStateGetter,
592+ times: setup.Times,
593+ log: setup.Log,
594+ powerd: nil,
595+ polld: nil,
596+ sessionState: setup.SessionStateGetter,
597+ requestWakeupCh: make(chan struct{}),
598+ requestedWakeupErrCh: make(chan error),
599+ holdsWakeLockCh: make(chan bool),
600 }
601 }
602
603@@ -92,10 +103,17 @@
604 if p.powerd != nil || p.polld != nil {
605 return ErrAlreadyStarted
606 }
607+ nmEndp := bus.SystemBus.Endpoint(networkmanager.BusAddress, p.log)
608 powerdEndp := bus.SystemBus.Endpoint(powerd.BusAddress, p.log)
609 polldEndp := bus.SessionBus.Endpoint(polld.BusAddress, p.log)
610+ urEndp := bus.SystemBus.Endpoint(urfkill.BusAddress, p.log)
611 var wg sync.WaitGroup
612- wg.Add(2)
613+ wg.Add(4)
614+ go func() {
615+ n := util.NewAutoRedialer(nmEndp).Redial()
616+ p.log.Debugf("NetworkManager dialed on try %d", n)
617+ wg.Done()
618+ }()
619 go func() {
620 n := util.NewAutoRedialer(powerdEndp).Redial()
621 p.log.Debugf("powerd dialed on try %d", n)
622@@ -106,10 +124,31 @@
623 p.log.Debugf("polld dialed in on try %d", n)
624 wg.Done()
625 }()
626+ go func() {
627+ n := util.NewAutoRedialer(urEndp).Redial()
628+ p.log.Debugf("URfkill dialed on try %d", n)
629+ wg.Done()
630+ }()
631 wg.Wait()
632
633+ p.nm = networkmanager.New(nmEndp, p.log)
634 p.powerd = powerd.New(powerdEndp, p.log)
635 p.polld = polld.New(polldEndp, p.log)
636+ p.urfkill = urfkill.New(urEndp, p.log)
637+
638+ // busy sleep loop to workaround go's timer/sleep
639+ // not accounting for time when the system is suspended
640+ // see https://bugs.launchpad.net/ubuntu/+source/ubuntu-push/+bug/1435109
641+ if p.times.BusyWait > 0 {
642+ p.log.Debugf("starting busy loop with %s interval", p.times.BusyWait)
643+ go func() {
644+ for {
645+ time.Sleep(p.times.BusyWait)
646+ }
647+ }()
648+ } else {
649+ p.log.Debugf("skipping busy loop")
650+ }
651
652 return nil
653 }
654@@ -118,7 +157,7 @@
655 if p.log == nil {
656 return ErrUnconfigured
657 }
658- if p.powerd == nil || p.polld == nil {
659+ if p.nm == nil || p.powerd == nil || p.polld == nil || p.urfkill == nil {
660 return ErrNotStarted
661 }
662 wakeupCh, err := p.powerd.WatchWakeups()
663@@ -129,10 +168,110 @@
664 if err != nil {
665 return err
666 }
667- go p.run(wakeupCh, doneCh)
668+ flightMode := p.urfkill.IsFlightMode()
669+ wirelessEnabled := p.nm.GetWirelessEnabled()
670+ flightModeCh, _, err := p.urfkill.WatchFlightMode()
671+ if err != nil {
672+ return err
673+ }
674+ wirelessEnabledCh, _, err := p.nm.WatchWirelessEnabled()
675+ if err != nil {
676+ return err
677+ }
678+
679+ filteredWakeUpCh := make(chan bool)
680+ go p.control(wakeupCh, filteredWakeUpCh, flightMode, flightModeCh, wirelessEnabled, wirelessEnabledCh)
681+ go p.run(filteredWakeUpCh, doneCh)
682 return nil
683 }
684
685+func (p *poller) doRequestWakeup(delta time.Duration) (time.Time, string, error) {
686+ t := time.Now().Add(delta).Truncate(time.Second)
687+ cookie, err := p.powerd.RequestWakeup("ubuntu push client", t)
688+ if err == nil {
689+ p.log.Debugf("requested wakeup at %s", t)
690+ } else {
691+ p.log.Errorf("RequestWakeup got %v", err)
692+ t = time.Time{}
693+ cookie = ""
694+ }
695+ return t, cookie, err
696+}
697+
698+func (p *poller) control(wakeupCh <-chan bool, filteredWakeUpCh chan<- bool, flightMode bool, flightModeCh <-chan bool, wirelessEnabled bool, wirelessEnabledCh <-chan bool) {
699+ dontPoll := flightMode && !wirelessEnabled
700+ var t time.Time
701+ cookie := ""
702+ holdsWakeLock := false
703+ for {
704+ select {
705+ case holdsWakeLock = <-p.holdsWakeLockCh:
706+ case <-p.requestWakeupCh:
707+ if !t.IsZero() || dontPoll {
708+ // earlier wakeup or we shouldn't be polling
709+ // => don't request wakeup
710+ if dontPoll {
711+ p.log.Debugf("skip requesting wakeup")
712+ }
713+ p.requestedWakeupErrCh <- nil
714+ break
715+ }
716+ var err error
717+ t, cookie, err = p.doRequestWakeup(p.times.AlarmInterval)
718+ p.requestedWakeupErrCh <- err
719+ case b := <-wakeupCh:
720+ // seems we get here also on clear wakeup, oh well
721+ if !b {
722+ panic("WatchWakeups channel produced a false value (??)")
723+ }
724+ // the channel will produce a true for every
725+ // wakeup, not only the one we asked for
726+ now := time.Now()
727+ if t.IsZero() {
728+ p.log.Debugf("got woken up; time is %s", now)
729+ } else {
730+ p.log.Debugf("got woken up; time is %s (𝛥: %s)", now, now.Sub(t))
731+ if !now.Before(t) {
732+ t = time.Time{}
733+ filteredWakeUpCh <- true
734+ }
735+ }
736+ case flightMode = <-flightModeCh:
737+ case wirelessEnabled = <-wirelessEnabledCh:
738+ }
739+ newDontPoll := flightMode && !wirelessEnabled
740+ p.log.Debugf("control: flightMode:%v wirelessEnabled:%v prevDontPoll:%v dontPoll:%v wakeupReq:%v holdsWakeLock:%v", flightMode, wirelessEnabled, dontPoll, newDontPoll, !t.IsZero(), holdsWakeLock)
741+ if newDontPoll != dontPoll {
742+ if dontPoll = newDontPoll; dontPoll {
743+ if !t.IsZero() {
744+ err := p.powerd.ClearWakeup(cookie)
745+ if err == nil {
746+ // cleared
747+ t = time.Time{}
748+ p.log.Debugf("cleared wakeup")
749+ } else {
750+ p.log.Errorf("ClearWakeup got %v", err)
751+ }
752+ }
753+ } else {
754+ if t.IsZero() && !holdsWakeLock {
755+ // reschedule soon
756+ t, cookie, _ = p.doRequestWakeup(p.times.NetworkWait / 20)
757+ }
758+ }
759+ }
760+ }
761+}
762+
763+func (p *poller) requestWakeup() error {
764+ p.requestWakeupCh <- struct{}{}
765+ return <-p.requestedWakeupErrCh
766+}
767+
768+func (p *poller) holdsWakeLock(has bool) {
769+ p.holdsWakeLockCh <- has
770+}
771+
772 func (p *poller) run(wakeupCh <-chan bool, doneCh <-chan bool) {
773 var lockCookie string
774
775@@ -143,15 +282,13 @@
776
777 func (p *poller) step(wakeupCh <-chan bool, doneCh <-chan bool, lockCookie string) string {
778
779- t := time.Now().Add(p.times.AlarmInterval).Truncate(time.Second)
780- _, err := p.powerd.RequestWakeup("ubuntu push client", t)
781+ err := p.requestWakeup()
782 if err != nil {
783- p.log.Errorf("RequestWakeup got %v", err)
784 // Don't do this too quickly. Pretend we are just skipping one wakeup
785 time.Sleep(p.times.AlarmInterval)
786 return lockCookie
787 }
788- p.log.Debugf("requested wakeup at %s", t)
789+ p.holdsWakeLock(false)
790 if lockCookie != "" {
791 if err := p.powerd.ClearWakelock(lockCookie); err != nil {
792 p.log.Errorf("ClearWakelock(%#v) got %v", lockCookie, err)
793@@ -160,26 +297,15 @@
794 }
795 lockCookie = ""
796 }
797- for b := range wakeupCh {
798- if !b {
799- panic("WatchWakeups channel produced a false value (??)")
800- }
801- // the channel will produce a true for every
802- // wakeup, not only the one we asked for
803- now := time.Now()
804- p.log.Debugf("got woken up; time is %s (𝛥: %s)", now, now.Sub(t))
805- if !now.Before(t) {
806- break
807- }
808- }
809+ <-wakeupCh
810 lockCookie, err = p.powerd.RequestWakelock("ubuntu push client")
811 if err != nil {
812 p.log.Errorf("RequestWakelock got %v", err)
813 return lockCookie
814 }
815+ p.holdsWakeLock(true)
816 p.log.Debugf("got wakelock cookie of %s, checking conn state", lockCookie)
817- // XXX killed as part of bug #1435109 troubleshooting, remove cfg if remains unused
818- // time.Sleep(p.times.SessionStateSettle)
819+ time.Sleep(p.times.SessionStateSettle)
820 for i := 0; i < 20; i++ {
821 if p.IsConnected() {
822 p.log.Debugf("iter %02d: connected", i)
823
824=== modified file 'poller/poller_test.go'
825--- poller/poller_test.go 2014-08-21 10:56:12 +0000
826+++ poller/poller_test.go 2015-03-31 09:33:32 +0000
827@@ -1,5 +1,5 @@
828 /*
829- Copyright 2014 Canonical Ltd.
830+ Copyright 2014-2015 Canonical Ltd.
831
832 This program is free software: you can redistribute it and/or modify it
833 under the terms of the GNU General Public License version 3, as published
834@@ -42,7 +42,7 @@
835 reqWakeCookie string
836 reqWakeErr error
837 // WatchWakeups
838- watchWakeCh <-chan bool
839+ watchWakeCh chan bool
840 watchWakeErr error
841 // RequestWakelock
842 reqLockName string
843@@ -63,6 +63,9 @@
844 func (m *myD) RequestWakeup(name string, wakeupTime time.Time) (string, error) {
845 m.reqWakeName = name
846 m.reqWakeTime = wakeupTime
847+ time.AfterFunc(100*time.Millisecond, func() {
848+ m.watchWakeCh <- true
849+ })
850 return m.reqWakeCookie, m.reqWakeErr
851 }
852 func (m *myD) RequestWakelock(name string) (string, error) {
853@@ -73,7 +76,10 @@
854 m.clearLockCookie = cookie
855 return m.clearLockErr
856 }
857-func (m *myD) ClearWakeup(cookie string) error { panic("clearwakeup called??") }
858+func (m *myD) ClearWakeup(cookie string) error {
859+ m.watchWakeCh <- false
860+ return nil
861+}
862 func (m *myD) WatchWakeups() (<-chan bool, error) { return m.watchWakeCh, m.watchWakeErr }
863 func (m *myD) Poll() error { return m.pollErr }
864 func (m *myD) WatchDones() (<-chan bool, error) { return m.watchDonesCh, m.watchDonesErr }
865@@ -86,23 +92,27 @@
866
867 func (s *PrSuite) TestStep(c *C) {
868 p := &poller{
869- times: Times{},
870- log: s.log,
871- powerd: s.myd,
872- polld: s.myd,
873- sessionState: s.myd,
874+ times: Times{},
875+ log: s.log,
876+ powerd: s.myd,
877+ polld: s.myd,
878+ sessionState: s.myd,
879+ requestWakeupCh: make(chan struct{}),
880+ requestedWakeupErrCh: make(chan error),
881+ holdsWakeLockCh: make(chan bool),
882 }
883 s.myd.reqLockCookie = "wakelock cookie"
884 s.myd.stateState = session.Running
885- // we'll get the wakeup right away
886 wakeupCh := make(chan bool, 1)
887- wakeupCh <- true
888+ s.myd.watchWakeCh = wakeupCh
889 // we won't get the "done" signal in time ;)
890 doneCh := make(chan bool)
891 // and a channel to get the return value from a goroutine
892 ch := make(chan string)
893 // now, run
894- go func() { ch <- p.step(wakeupCh, doneCh, "old cookie") }()
895+ filteredWakeUpCh := make(chan bool)
896+ go p.control(wakeupCh, filteredWakeUpCh, false, nil, true, nil)
897+ go func() { ch <- p.step(filteredWakeUpCh, doneCh, "old cookie") }()
898 select {
899 case s := <-ch:
900 c.Check(s, Equals, "wakelock cookie")
901@@ -112,3 +122,53 @@
902 // check we cleared the old cookie
903 c.Check(s.myd.clearLockCookie, Equals, "old cookie")
904 }
905+
906+func (s *PrSuite) TestControl(c *C) {
907+ p := &poller{
908+ times: Times{},
909+ log: s.log,
910+ powerd: s.myd,
911+ polld: s.myd,
912+ sessionState: s.myd,
913+ requestWakeupCh: make(chan struct{}),
914+ requestedWakeupErrCh: make(chan error),
915+ holdsWakeLockCh: make(chan bool),
916+ }
917+ wakeUpCh := make(chan bool)
918+ filteredWakeUpCh := make(chan bool)
919+ s.myd.watchWakeCh = make(chan bool, 1)
920+ flightModeCh := make(chan bool)
921+ wirelessModeCh := make(chan bool)
922+ go p.control(wakeUpCh, filteredWakeUpCh, false, flightModeCh, true, wirelessModeCh)
923+
924+ // works
925+ err := p.requestWakeup()
926+ c.Assert(err, IsNil)
927+ c.Check(<-s.myd.watchWakeCh, Equals, true)
928+
929+ // there's a wakeup already
930+ err = p.requestWakeup()
931+ c.Assert(err, IsNil)
932+ c.Check(s.myd.watchWakeCh, HasLen, 0)
933+
934+ // wakeup happens
935+ wakeUpCh <- true
936+ <-filteredWakeUpCh
937+
938+ // flight mode
939+ flightModeCh <- true
940+ wirelessModeCh <- false
941+ err = p.requestWakeup()
942+ c.Assert(err, IsNil)
943+ c.Check(s.myd.watchWakeCh, HasLen, 0)
944+
945+ // wireless on
946+ wirelessModeCh <- true
947+ c.Check(<-s.myd.watchWakeCh, Equals, true)
948+
949+ // wireless off
950+ wirelessModeCh <- false
951+ // pending wakeup was cleared
952+ c.Check(<-s.myd.watchWakeCh, Equals, false)
953+
954+}

Subscribers

People subscribed via source and target branches