@@ -115,41 +115,60 @@ 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
+ glog .V (logger .Debug ).Infof ("%v: remote requested disconnect: %v\n " , p , r )
149
+ requested = true
150
+ reason = r
151
+ } else {
152
+ glog .V (logger .Detail ).Infof ("%v: read error: %v\n " , p , err )
153
+ reason = DiscNetworkError
154
+ }
155
+ break loop
156
+ case err := <- p .protoErr :
157
+ reason = discReasonForError (err )
158
+ glog .V (logger .Debug ).Infof ("%v: protocol error: %v (%v)\n " , p , err , reason )
159
+ break loop
160
+ case reason = <- p .disc :
161
+ glog .V (logger .Debug ).Infof ("%v: locally requested disconnect: %v\n " , p , reason )
162
+ break loop
139
163
}
140
- case err := <- p .protoErr :
141
- reason = discReasonForError (err )
142
- case reason = <- p .disc :
143
- requested = true
144
164
}
165
+
145
166
close (p .closed )
146
167
p .rw .close (reason )
147
168
p .wg .Wait ()
148
-
149
169
if requested {
150
170
reason = DiscRequested
151
171
}
152
- glog .V (logger .Debug ).Infof ("%v: Disconnected: %v\n " , p , reason )
153
172
return reason
154
173
}
155
174
@@ -196,7 +215,6 @@ func (p *Peer) handle(msg Msg) error {
196
215
// This is the last message. We don't need to discard or
197
216
// check errors because, the connection will be closed after it.
198
217
rlp .Decode (msg .Payload , & reason )
199
- glog .V (logger .Debug ).Infof ("%v: Disconnect Requested: %v\n " , p , reason [0 ])
200
218
return reason [0 ]
201
219
case msg .Code < baseProtocolLength :
202
220
// ignore other base protocol messages
@@ -247,11 +265,13 @@ outer:
247
265
return result
248
266
}
249
267
250
- func (p * Peer ) startProtocols () {
268
+ func (p * Peer ) startProtocols (writeStart <- chan struct {}, writeErr chan <- error ) {
251
269
p .wg .Add (len (p .running ))
252
270
for _ , proto := range p .running {
253
271
proto := proto
254
272
proto .closed = p .closed
273
+ proto .wstart = writeStart
274
+ proto .werr = writeErr
255
275
glog .V (logger .Detail ).Infof ("%v: Starting protocol %s/%d\n " , p , proto .Name , proto .Version )
256
276
go func () {
257
277
err := proto .Run (p , proto )
@@ -280,18 +300,31 @@ func (p *Peer) getProto(code uint64) (*protoRW, error) {
280
300
281
301
type protoRW struct {
282
302
Protocol
283
- in chan Msg
284
- closed <- chan struct {}
303
+ in chan Msg // receices read messages
304
+ closed <- chan struct {} // receives when peer is shutting down
305
+ wstart <- chan struct {} // receives when write may start
306
+ werr chan <- error // for write results
285
307
offset uint64
286
308
w MsgWriter
287
309
}
288
310
289
- func (rw * protoRW ) WriteMsg (msg Msg ) error {
311
+ func (rw * protoRW ) WriteMsg (msg Msg ) ( err error ) {
290
312
if msg .Code >= rw .Length {
291
313
return newPeerError (errInvalidMsgCode , "not handled" )
292
314
}
293
315
msg .Code += rw .offset
294
- return rw .w .WriteMsg (msg )
316
+ select {
317
+ case <- rw .wstart :
318
+ err = rw .w .WriteMsg (msg )
319
+ // Report write status back to Peer.run. It will initiate
320
+ // shutdown if the error is non-nil and unblock the next write
321
+ // otherwise. The calling protocol code should exit for errors
322
+ // as well but we don't want to rely on that.
323
+ rw .werr <- err
324
+ case <- rw .closed :
325
+ err = fmt .Errorf ("shutting down" )
326
+ }
327
+ return err
295
328
}
296
329
297
330
func (rw * protoRW ) ReadMsg () (Msg , error ) {
0 commit comments