]> Cypherpunks repositories - gostls13.git/commitdiff
net/rpc: fix race condition when request write partially fails
authorAlexey Borzenkov <snaury@gmail.com>
Thu, 24 May 2012 23:07:08 +0000 (16:07 -0700)
committerRob Pike <r@golang.org>
Thu, 24 May 2012 23:07:08 +0000 (16:07 -0700)
When client fails to write a request is sends caller that error,
however server might have failed to read that request in the mean
time and replied with that error. When client then reads the
response the call would no longer be pending, so call will be nil

Handle this gracefully by discarding such server responses

R=golang-dev, r
CC=golang-dev, rsc
https://golang.org/cl/5956051

src/pkg/net/rpc/client.go

index db2da8e4418d32ba2c6200d1e263eebe0206068a..e19bd484bd8be198ecda1e55cfc82cd1d83ac4af 100644 (file)
@@ -88,10 +88,13 @@ func (client *Client) send(call *Call) {
        err := client.codec.WriteRequest(&client.request, call.Args)
        if err != nil {
                client.mutex.Lock()
+               call = client.pending[seq]
                delete(client.pending, seq)
                client.mutex.Unlock()
-               call.Error = err
-               call.done()
+               if call != nil {
+                       call.Error = err
+                       call.done()
+               }
        }
 }
 
@@ -113,22 +116,26 @@ func (client *Client) input() {
                delete(client.pending, seq)
                client.mutex.Unlock()
 
-               if response.Error == "" {
-                       err = client.codec.ReadResponseBody(call.Reply)
-                       if err != nil {
-                               call.Error = errors.New("reading body " + err.Error())
-                       }
-               } else {
+               if call == nil || response.Error != "" {
                        // We've got an error response. Give this to the request;
                        // any subsequent requests will get the ReadResponseBody
                        // error if there is one.
-                       call.Error = ServerError(response.Error)
+                       if call != nil {
+                               call.Error = ServerError(response.Error)
+                       }
                        err = client.codec.ReadResponseBody(nil)
                        if err != nil {
                                err = errors.New("reading error body: " + err.Error())
                        }
+               } else if response.Error == "" {
+                       err = client.codec.ReadResponseBody(call.Reply)
+                       if err != nil {
+                               call.Error = errors.New("reading body " + err.Error())
+                       }
+               }
+               if call != nil {
+                       call.done()
                }
-               call.done()
        }
        // Terminate pending calls.
        client.sending.Lock()