diff --git a/protoc-gen-go/grpc/grpc.go b/protoc-gen-go/grpc/grpc.go index 1723680a8b..e168e2d0ae 100644 --- a/protoc-gen-go/grpc/grpc.go +++ b/protoc-gen-go/grpc/grpc.go @@ -303,6 +303,19 @@ func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar strin if method.GetOptions().GetDeprecated() { g.P(deprecationComment) } + streamType := unexport(servName) + methName + "Client" + g.P(fmt.Sprintf(`// The ctx passed in will be used for the lifetime of this object. +// +// To ensure resources are not leaked due to the stream returned, one of the following +// actions must be performed when processing on this object is completed: +// +// 1. call CloseSend on %s, +// 2. cancel the context provided, +// 3. call RecvMsg until a non-nil error is returned, or +// 4. receive a non-nil, non-io.EOF error from Header or SendMsg. +// +// If none of the above happen, a goroutine and a context will be leaked, and grpc +// will not call the optionally-configured stats handler with a stats.End message.`, streamType)) g.P("func (c *", unexport(servName), "Client) ", g.generateClientSignature(servName, method), "{") if !method.GetServerStreaming() && !method.GetClientStreaming() { g.P("out := new(", outType, ")") @@ -314,7 +327,6 @@ func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar strin g.P() return } - streamType := unexport(servName) + methName + "Client" g.P("stream, err := ", grpcPkg, ".NewClientStream(ctx, ", descExpr, `, c.cc, "`, sname, `", opts...)`) g.P("if err != nil { return nil, err }") g.P("x := &", streamType, "{stream}")