@@ -115,37 +115,54 @@ func newPeer(conn *conn, protocols []Protocol) *Peer {
115
115
}
116
116
117
117
func (p * Peer ) run () DiscReason {
118
- readErr := make (chan error , 1 )
118
+ var (
119
+ writeStart = make (chan struct {}, 1 )
120
+ writeErr = make (chan error , 1 )
121
+ readErr = make (chan error , 1 )
122
+ reason DiscReason
123
+ requested bool
124
+ )
119
125
p .wg .Add (2 )
120
126
go p .readLoop (readErr )
121
127
go p .pingLoop ()
122
128
123
- p .startProtocols ()
129
+ // Start all protocol handlers.
130
+ writeStart <- struct {}{}
131
+ p .startProtocols (writeStart , writeErr )
124
132
125
133
// Wait for an error or disconnect.
126
- var (
127
- reason DiscReason
128
- requested bool
129
- )
130
- select {
131
- case err := <- readErr :
132
- if r , ok := err .(DiscReason ); ok {
133
- reason = r
134
- } else {
135
- // Note: We rely on protocols to abort if there is a write
136
- // error. It might be more robust to handle them here as well.
137
- glog .V (logger .Detail ).Infof ("%v: Read error: %v\n " , p , err )
138
- reason = DiscNetworkError
134
+ loop:
135
+ for {
136
+ select {
137
+ case err := <- writeErr :
138
+ // A write finished. Allow the next write to start if
139
+ // there was no error.
140
+ if err != nil {
141
+ glog .V (logger .Detail ).Infof ("%v: Write error: %v\n " , p , err )
142
+ reason = DiscNetworkError
143
+ break loop
144
+ }
145
+ writeStart <- struct {}{}
146
+ case err := <- readErr :
147
+ if r , ok := err .(DiscReason ); ok {
148
+ reason = r
149
+ } else {
150
+ glog .V (logger .Detail ).Infof ("%v: Read error: %v\n " , p , err )
151
+ reason = DiscNetworkError
152
+ }
153
+ break loop
154
+ case err := <- p .protoErr :
155
+ reason = discReasonForError (err )
156
+ break loop
157
+ case reason = <- p .disc :
158
+ requested = true
159
+ break loop
139
160
}
140
- case err := <- p .protoErr :
141
- reason = discReasonForError (err )
142
- case reason = <- p .disc :
143
- requested = true
144
161
}
162
+
145
163
close (p .closed )
146
164
p .rw .close (reason )
147
165
p .wg .Wait ()
148
-
149
166
if requested {
150
167
reason = DiscRequested
151
168
}
@@ -247,11 +264,13 @@ outer:
247
264
return result
248
265
}
249
266
250
- func (p * Peer ) startProtocols () {
267
+ func (p * Peer ) startProtocols (writeStart <- chan struct {}, writeErr chan <- error ) {
251
268
p .wg .Add (len (p .running ))
252
269
for _ , proto := range p .running {
253
270
proto := proto
254
271
proto .closed = p .closed
272
+ proto .wstart = writeStart
273
+ proto .werr = writeErr
255
274
glog .V (logger .Detail ).Infof ("%v: Starting protocol %s/%d\n " , p , proto .Name , proto .Version )
256
275
go func () {
257
276
err := proto .Run (p , proto )
@@ -280,18 +299,31 @@ func (p *Peer) getProto(code uint64) (*protoRW, error) {
280
299
281
300
type protoRW struct {
282
301
Protocol
283
- in chan Msg
284
- closed <- chan struct {}
302
+ in chan Msg // receices read messages
303
+ closed <- chan struct {} // receives when peer is shutting down
304
+ wstart <- chan struct {} // receives when write may start
305
+ werr chan <- error // for write results
285
306
offset uint64
286
307
w MsgWriter
287
308
}
288
309
289
- func (rw * protoRW ) WriteMsg (msg Msg ) error {
310
+ func (rw * protoRW ) WriteMsg (msg Msg ) ( err error ) {
290
311
if msg .Code >= rw .Length {
291
312
return newPeerError (errInvalidMsgCode , "not handled" )
292
313
}
293
314
msg .Code += rw .offset
294
- return rw .w .WriteMsg (msg )
315
+ select {
316
+ case <- rw .wstart :
317
+ err = rw .w .WriteMsg (msg )
318
+ // Report write status back to Peer.run. It will initiate
319
+ // shutdown if the error is non-nil and unblock the next write
320
+ // otherwise. The calling protocol code should exit for errors
321
+ // as well but we don't want to rely on that.
322
+ rw .werr <- err
323
+ case <- rw .closed :
324
+ err = fmt .Errorf ("shutting down" )
325
+ }
326
+ return err
295
327
}
296
328
297
329
func (rw * protoRW ) ReadMsg () (Msg , error ) {
0 commit comments