Commit 95b40f6c authored by Kai Backman's avatar Kai Backman Committed by Rob Pike

Fix a deadlock bug in the rpc client. The panic will trigger

regularly when client connections are flaky (probably another
issue).

(credits to jussi@tinkercad.com for finding the issue)

R=rsc, r
CC=golang-dev, jussi
https://golang.org/cl/2831042
parent 59315fbf
...@@ -69,12 +69,12 @@ func (client *Client) send(c *Call) { ...@@ -69,12 +69,12 @@ func (client *Client) send(c *Call) {
// Encode and send the request. // Encode and send the request.
request := new(Request) request := new(Request)
client.sending.Lock() client.sending.Lock()
defer client.sending.Unlock()
request.Seq = c.seq request.Seq = c.seq
request.ServiceMethod = c.ServiceMethod request.ServiceMethod = c.ServiceMethod
if err := client.codec.WriteRequest(request, c.Args); err != nil { if err := client.codec.WriteRequest(request, c.Args); err != nil {
panic("rpc: client encode error: " + err.String()) panic("rpc: client encode error: " + err.String())
} }
client.sending.Unlock()
} }
func (client *Client) input() { func (client *Client) input() {
......
...@@ -13,6 +13,7 @@ import ( ...@@ -13,6 +13,7 @@ import (
"strings" "strings"
"sync" "sync"
"testing" "testing"
"time"
) )
var ( var (
...@@ -332,3 +333,52 @@ func TestRegistrationError(t *testing.T) { ...@@ -332,3 +333,52 @@ func TestRegistrationError(t *testing.T) {
t.Errorf("expected error registering ReplyNotPublic") t.Errorf("expected error registering ReplyNotPublic")
} }
} }
type WriteFailCodec int
func (WriteFailCodec) WriteRequest(*Request, interface{}) os.Error {
// the panic caused by this error used to not unlock a lock.
return os.NewError("fail")
}
func (WriteFailCodec) ReadResponseHeader(*Response) os.Error {
time.Sleep(60e9)
panic("unreachable")
}
func (WriteFailCodec) ReadResponseBody(interface{}) os.Error {
time.Sleep(60e9)
panic("unreachable")
}
func (WriteFailCodec) Close() os.Error {
return nil
}
func TestSendDeadlock(t *testing.T) {
client := NewClientWithCodec(WriteFailCodec(0))
done := make(chan bool)
go func() {
testSendDeadlock(client)
testSendDeadlock(client)
done <- true
}()
for i := 0; i < 50; i++ {
time.Sleep(100 * 1e6)
_, ok := <-done
if ok {
return
}
}
t.Fatal("deadlock")
}
func testSendDeadlock(client *Client) {
defer func() {
recover()
}()
args := &Args{7, 8}
reply := new(Reply)
client.Call("Arith.Add", args, reply)
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment