@ -115,37 +115,54 @@ func newPeer(conn *conn, protocols []Protocol) *Peer {
}
func ( p * Peer ) run ( ) DiscReason {
readErr := make ( chan error , 1 )
var (
writeStart = make ( chan struct { } , 1 )
writeErr = make ( chan error , 1 )
readErr = make ( chan error , 1 )
reason DiscReason
requested bool
)
p . wg . Add ( 2 )
go p . readLoop ( readErr )
go p . pingLoop ( )
p . startProtocols ( )
// Start all protocol handlers.
writeStart <- struct { } { }
p . startProtocols ( writeStart , writeErr )
// Wait for an error or disconnect.
var (
reason DiscReason
requested bool
)
select {
case err := <- readErr :
if r , ok := err . ( DiscReason ) ; ok {
reason = r
} else {
// Note: We rely on protocols to abort if there is a write
// error. It might be more robust to handle them here as well.
glog . V ( logger . Detail ) . Infof ( "%v: Read error: %v\n" , p , err )
reason = DiscNetworkError
loop :
for {
select {
case err := <- writeErr :
// A write finished. Allow the next write to start if
// there was no error.
if err != nil {
glog . V ( logger . Detail ) . Infof ( "%v: Write error: %v\n" , p , err )
reason = DiscNetworkError
break loop
}
writeStart <- struct { } { }
case err := <- readErr :
if r , ok := err . ( DiscReason ) ; ok {
reason = r
} else {
glog . V ( logger . Detail ) . Infof ( "%v: Read error: %v\n" , p , err )
reason = DiscNetworkError
}
break loop
case err := <- p . protoErr :
reason = discReasonForError ( err )
break loop
case reason = <- p . disc :
requested = true
break loop
}
case err := <- p . protoErr :
reason = discReasonForError ( err )
case reason = <- p . disc :
requested = true
}
close ( p . closed )
p . rw . close ( reason )
p . wg . Wait ( )
if requested {
reason = DiscRequested
}
@ -247,11 +264,13 @@ outer:
return result
}
func ( p * Peer ) startProtocols ( ) {
func ( p * Peer ) startProtocols ( writeStart <- chan struct { } , writeErr chan <- error ) {
p . wg . Add ( len ( p . running ) )
for _ , proto := range p . running {
proto := proto
proto . closed = p . closed
proto . wstart = writeStart
proto . werr = writeErr
glog . V ( logger . Detail ) . Infof ( "%v: Starting protocol %s/%d\n" , p , proto . Name , proto . Version )
go func ( ) {
err := proto . Run ( p , proto )
@ -280,18 +299,31 @@ func (p *Peer) getProto(code uint64) (*protoRW, error) {
type protoRW struct {
Protocol
in chan Msg
closed <- chan struct { }
in chan Msg // receices read messages
closed <- chan struct { } // receives when peer is shutting down
wstart <- chan struct { } // receives when write may start
werr chan <- error // for write results
offset uint64
w MsgWriter
}
func ( rw * protoRW ) WriteMsg ( msg Msg ) error {
func ( rw * protoRW ) WriteMsg ( msg Msg ) ( err error ) {
if msg . Code >= rw . Length {
return newPeerError ( errInvalidMsgCode , "not handled" )
}
msg . Code += rw . offset
return rw . w . WriteMsg ( msg )
select {
case <- rw . wstart :
err = rw . w . WriteMsg ( msg )
// Report write status back to Peer.run. It will initiate
// shutdown if the error is non-nil and unblock the next write
// otherwise. The calling protocol code should exit for errors
// as well but we don't want to rely on that.
rw . werr <- err
case <- rw . closed :
err = fmt . Errorf ( "shutting down" )
}
return err
}
func ( rw * protoRW ) ReadMsg ( ) ( Msg , error ) {