Merge lp:~chipaca/ubuntu-push/lets-go-to-the-races into lp:ubuntu-push/automatic

Proposed by John Lenton
Status: Merged
Approved by: John Lenton
Approved revision: 138
Merged at revision: 135
Proposed branch: lp:~chipaca/ubuntu-push/lets-go-to-the-races
Merge into: lp:ubuntu-push/automatic
Diff against target: 164 lines (+89/-13)
3 files modified
README (+4/-3)
bus/connectivity/connectivity.go (+8/-7)
bus/connectivity/connectivity_test.go (+77/-3)
To merge this branch: bzr merge lp:~chipaca/ubuntu-push/lets-go-to-the-races
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+217376@code.launchpad.net

Commit message

Fix race in setting up nm state watch.

Description of the change

Fix race in setting up nm state watch.

To post a comment you must log in.
136. By John Lenton

make format

137. By John Lenton

fixed the README a bit.

138. By John Lenton

moved the lock/unlock pair to where it makes sense

Revision history for this message
Samuele Pedroni (pedronis) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'README'
2--- README 2014-04-15 14:20:09 +0000
3+++ README 2014-04-28 08:26:01 +0000
4@@ -6,8 +6,9 @@
5 The code expects to be checked out as launchpad.net/ubuntu-push in a Go
6 workspace, see "go help gopath".
7
8-To setup Go dependencies, install the following libraries:
9+To setup Go dependencies, install the following dependencies:
10
11+ build-essential
12 libsqlite3-dev
13 qtbase5-private-dev
14 qtdeclarative5-dev
15@@ -18,8 +19,8 @@
16
17 make bootstrap
18
19-To run tests, install libgcrypt11-dev, libwhoopsie-dev, and
20-libubuntuoneauth-2.0-dev and run:
21+To run tests, install libglib2.0-dev, libgcrypt11-dev, libwhoopsie-dev,
22+and run:
23
24 make check
25
26
27=== modified file 'bus/connectivity/connectivity.go'
28--- bus/connectivity/connectivity.go 2014-04-04 11:08:28 +0000
29+++ bus/connectivity/connectivity.go 2014-04-28 08:26:01 +0000
30@@ -72,19 +72,20 @@
31 cs.connAttempts += ar.Redial()
32 nm := networkmanager.New(cs.endp, cs.log)
33
34+ // set up the watch
35+ stateCh, err = nm.WatchState()
36+ if err != nil {
37+ cs.log.Debugf("failed to set up the state watch: %s", err)
38+ goto Continue
39+ }
40+
41 // Get the current state.
42 initial = nm.GetState()
43 if initial == networkmanager.Unknown {
44 cs.log.Debugf("Failed to get state.")
45 goto Continue
46 }
47-
48- // set up the watch
49- stateCh, err = nm.WatchState()
50- if err != nil {
51- cs.log.Debugf("failed to set up the state watch: %s", err)
52- goto Continue
53- }
54+ cs.log.Debugf("got initial state of %s", initial)
55
56 primary = nm.GetPrimaryConnection()
57 cs.log.Debugf("primary connection starts as %#v", primary)
58
59=== modified file 'bus/connectivity/connectivity_test.go'
60--- bus/connectivity/connectivity_test.go 2014-04-04 12:01:42 +0000
61+++ bus/connectivity/connectivity_test.go 2014-04-28 08:26:01 +0000
62@@ -17,8 +17,15 @@
63 package connectivity
64
65 import (
66+ "net/http/httptest"
67+ "sync"
68+ "testing"
69+ "time"
70+
71 "launchpad.net/go-dbus/v1"
72 . "launchpad.net/gocheck"
73+
74+ "launchpad.net/ubuntu-push/bus"
75 "launchpad.net/ubuntu-push/bus/networkmanager"
76 testingbus "launchpad.net/ubuntu-push/bus/testing"
77 "launchpad.net/ubuntu-push/config"
78@@ -26,9 +33,6 @@
79 helpers "launchpad.net/ubuntu-push/testing"
80 "launchpad.net/ubuntu-push/testing/condition"
81 "launchpad.net/ubuntu-push/util"
82- "net/http/httptest"
83- "testing"
84- "time"
85 )
86
87 // hook up gocheck
88@@ -115,6 +119,76 @@
89 c.Check(<-cs.networkStateCh, Equals, networkmanager.ConnectedGlobal)
90 }
91
92+// a racyEndpoint is an endpoint that behaves differently depending on
93+// how much time passes between getting the state and setting up the
94+// watch
95+type racyEndpoint struct {
96+ stateGot bool
97+ maxTime time.Time
98+ delta time.Duration
99+ lock sync.RWMutex
100+}
101+
102+func (rep *racyEndpoint) GetProperty(prop string) (interface{}, error) {
103+ switch prop {
104+ case "state":
105+ rep.lock.Lock()
106+ defer rep.lock.Unlock()
107+ rep.stateGot = true
108+ rep.maxTime = time.Now().Add(rep.delta)
109+ return uint32(networkmanager.Connecting), nil
110+ case "PrimaryConnection":
111+ return dbus.ObjectPath("/something"), nil
112+ default:
113+ return nil, nil
114+ }
115+}
116+
117+func (rep *racyEndpoint) WatchSignal(member string, f func(...interface{}), d func()) error {
118+ if member == "StateChanged" {
119+ // we count never having gotten the state as happening "after" now.
120+ rep.lock.RLock()
121+ defer rep.lock.RUnlock()
122+ ok := !rep.stateGot || time.Now().Before(rep.maxTime)
123+ go func() {
124+ if ok {
125+ f(uint32(networkmanager.ConnectedGlobal))
126+ }
127+ d()
128+ }()
129+ }
130+ return nil
131+}
132+
133+func (*racyEndpoint) Close() {}
134+func (*racyEndpoint) Dial() error { return nil }
135+func (*racyEndpoint) String() string { return "racyEndpoint" }
136+func (*racyEndpoint) Call(string, []interface{}, ...interface{}) error { return nil }
137+
138+var _ bus.Endpoint = (*racyEndpoint)(nil)
139+
140+// takeNext takes a value from given channel with a 1s timeout
141+func takeNext(ch <-chan networkmanager.State) networkmanager.State {
142+ select {
143+ case <-time.After(time.Second):
144+ panic("channel stuck: too long waiting")
145+ case v := <-ch:
146+ return v
147+ }
148+}
149+
150+// test that if the nm state goes from connecting to connected very
151+// shortly after calling GetState, we don't lose the event.
152+func (s *ConnSuite) TestStartAvoidsRace(c *C) {
153+ for delta := time.Second; delta > 1; delta /= 2 {
154+ rep := &racyEndpoint{delta: delta}
155+ cs := connectedState{config: ConnectivityConfig{}, log: s.log, endp: rep}
156+ f := Commentf("when delta=%s", delta)
157+ c.Assert(cs.start(), Equals, networkmanager.Connecting, f)
158+ c.Assert(takeNext(cs.networkStateCh), Equals, networkmanager.ConnectedGlobal, f)
159+ }
160+}
161+
162 /*
163 tests for connectedStateStep()
164 */

Subscribers

People subscribed via source and target branches