-
Notifications
You must be signed in to change notification settings - Fork 4.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stream: fix calloption.After() race in finish #3672
Conversation
test/end2end_test.go
Outdated
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | ||
var p peer.Peer | ||
stream, err := ss.client.FullDuplexCall(ctx, grpc.Peer(&p)) | ||
if err != nil { | ||
t.Fatalf("_.FullDuplexCall(_) = _, %v", err) | ||
} | ||
if err := stream.Send(&testpb.StreamingOutputCallRequest{}); err != nil { | ||
t.Fatalf("_ has error %v while sending", err) | ||
} | ||
if _, err := stream.Recv(); err != nil { | ||
t.Fatalf("%v.Recv() = %v", stream, err) | ||
} | ||
cancel() | ||
if _, err := stream.Recv(); status.Code(err) != codes.Canceled { | ||
t.Fatalf("%v compleled with error %v, want %s", stream, err, codes.Canceled) | ||
} | ||
// If recv returns before call options are executed, peer.Addr is not set, | ||
// fail the test. | ||
if p.Addr == nil { | ||
t.Fatalf("peer.Addr is nil, want non-nil") | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to do this in a loop to have a better chance of stimulating the race?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
rpc_util.go
Outdated
if c.stream != nil { | ||
*o.HeaderAddr, _ = c.stream.Header() | ||
func (o HeaderCallOption) after(c *callInfo, attempt *csAttempt) { | ||
if attempt != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we now assume this is never called with a nil
attempt
or attempt.s
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If rpc is canceled before we make the attempt (e.g. blocking on pick)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But there's only one call site (right?) and it's guarded by if attempt != nil && attempt.s != nil
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, right. Removed
Race:
After cancel, two calls to stream.Finish()
grpc-go/stream.go
Line 790 in 479df5e
grpc-go/stream.go
Line 1089 in 479df5e
grpc-go/stream.go
Line 745 in 479df5e
Call 1 happens first, sets
finished=true
, before callingafter()
Call 2 happens now, return early because
finished==true
. So RecvMsg() also returnsUser sees error from RecvMsg(), but because 1 still hasn't call
after()
, peer is not set.This fix:
after() should work on attempt instead, because after() is only called in finish,
and the stream should have been committed.