How to to close BiDi streaming gracefully from C++ server thread (pthr)when c++ client gets aborted.

2,838 views
Skip to first unread message

rajeev...@rediffmail.com

unread,
May 2, 2017, 1:45:37 AM5/2/17
to grpc.io

Hello,

Is there a way to close down BiDi streaming gracefully from C++ server thread (pthr) when c++ client gets aborted? When I simply return from Bidi streaming rpc based on IsCancelled() I observe memory leaks.

Here is the sample code


Status BiDiServiceImpl::BiDiStreamingRpc(::grpc::ServerContext* context,  ::grpc::ServerReaderWriter< BiDiResponse, BiDiRequest>* stream)
{
    BiDiRequest     req;
    BiDiResponse   resp;
               
    stream->Read(&req);
    while(context->IsCancelled() == false)
    {
        getResp(resp);
        stream->Write(resp);
    }
    return(Status(Status::GRPC_OK, ""));
}


Thanks
Rajeev

Sree Kuchibhotla

unread,
Jun 27, 2017, 7:18:43 PM6/27/17
to grpc.io, rajeev...@rediffmail.com
Sorry for the late response. 

There is no special method to 'close' the BiDi streams. On the server, just returning a status would mean that you are done with the stream.

However, in the example you have given, you seem to be calling just 1 read. Since you mentioned you are noticing a memory leak, I was wondering...are you sure that you are reading all the messages that client is sending ? 

You don't have to really check 'context->IsCancelled()' here. stream->Read() has a return value. It returns 'true' as long as there is a message to read from the client. stream->Read() returns a 'false' either when the client gracefully finished the writes (by calling stream->WritesDone() and stream->Finish() on the client side) or if the client cancelled the RPC or there was some other error..

So I recommend you rewrite your loop as

Status BidiServiceImpl::BiDiStreamingRpc(...)
{
  ..
  while (stream->Read(&req)) {
    getRest(resp);
    stream->Write(resp);
  }
  
  // You can check context->IsCancelled() here if you
  // are interested in whether the RPC was cancelled
 
 return Status(..)
}

thanks,
Sree

yihao yang

unread,
Jul 2, 2017, 5:57:47 PM7/2/17
to grpc.io, rajeev...@rediffmail.com
Hi, Sree:

Do you know what if the stream is an async bidi stream? If I want to close from the server side by TryCancel() and followed by a Finish(), will this cause any problem? After what I called stream->Finish(status), will this event return in the CQ if the client is closed already?

Thanks,
Yihao

在 2017年6月27日星期二 UTC-7下午4:18:43,Sree Kuchibhotla写道:

Sree Kuchibhotla

unread,
Jul 11, 2017, 8:05:17 PM7/11/17
to grpc.io, rajeev...@rediffmail.com
On async streams on servers, you simple call  stream->Finish(const Status& status, void* tag) to send the final status (and do a Cq->next) and close the stream (from server side).  On the client side, the client would do a  cli_stream->Read() Which would fail  (since the stream is closed by server). The client would then do a cli_stream->Finish() to get the status sent by the server. That is the normal sequence. You really do not need to do TryCancel().   Just FYI, the following is the typical sequence of events on a Bidi Stream on client and server side:

Sequence of events on the client side:
1) Create a client bidi stream "cli_stream"
2) Do one or more cli_stream->Read() and cli_stream->Write() (need to match 
3) "half-close" i.e close the stream (for writes) from client side by doing: cli_stream->WritesDone(); 
4) Note: At this point, cli_stream->Read() will still work (since the server has not closed the stream from its side)
5) Once the server closes the stream from it's side (cli_stream->Read() would return false)
6) Do cli_stream->Finish() to get the status from the server

Sequence of events on the server side:
1) Create a server bidi stream "server_stream"
2) Do one more server_stream->Read() and server_stream->Write()  (need to match with step #2 in client side)
3) Once client does a close from its side,  server_stream->Read() would return "false"
4) Note: At this point server_stream->Write() will still work.
5) Now close the writes stream from server side and send a status by calling server_stream->Finish()  
---

Now to specifically answer your question:
>>> If I want to close from the server side by TryCancel() and followed by a Finish() will this cause any problem? 
If for whatever reason you decided to do TryCancel() first on server followed by server_stream->Finish(), it is a race-condition (The TryCancel() API is best-effort API and doesn't guarantee the stream will be cancelled right-away. So it is possible that operation may fail and the stream wasn't cancelled by the time you called Finish(). In this case, Finish() would succeed and you get a success event in CQ. If not, i.e if stream cancel happened by the time you called Finish(), then you get a failure event in CQ (with status CANCELLED - I think..i am not sure of exact status code).

>>>After what I called stream->Finish(status), will this event return in the CQ if the client is closed already?
Well, from the sequence of events I posted above, if the client did a "half-close" by doing a cli_stream->WritesDone(), then its perfectly safe for server to do stream->Finish(status).
However, if the client closed the stream by calling "TryCancel", then again its a race condition. If the cancellation reached server, then stream->Finish(status) will fail (i.e a failed event with return on CQ) else, it would succeed if the cancellation did not happen yet.

Hope this helps,
Sree

rajeev...@rediffmail.com

unread,
Jul 12, 2017, 12:55:59 AM7/12/17
to grpc.io, rajeev...@rediffmail.com
Sorry there was some observational error. Anyways; thanks all of you for your help.

-Rajeev

yihao yang

unread,
Jul 12, 2017, 3:59:16 PM7/12/17
to Sree Kuchibhotla, grpc.io, rajeev...@rediffmail.com
Hi, Sree,

Thank you for the reply.  It's very helpful.
I still want to know what is the best practice for the TryCancel in both server and client side. I use it because I can easily stop both the stream read and write through this interface. If the client side does not care about the final status of server, I think TryCancel is more convenient, right? Is there any side-effects of TryCancel?

Thanks,
Yihao

--
You received this message because you are subscribed to a topic in the Google Groups "grpc.io" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/grpc-io/AlwaSuDTcoM/unsubscribe.
To unsubscribe from this group and all its topics, send an email to grpc-io+unsubscribe@googlegroups.com.
To post to this group, send email to grp...@googlegroups.com.
Visit this group at https://groups.google.com/group/grpc-io.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/dc1a9aeb-5f8c-4291-a755-9b3db1d063ca%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Sree Kuchibhotla

unread,
Jul 12, 2017, 4:09:34 PM7/12/17
to yihao yang, rajeev...@rediffmail.com, grpc.io
I'm not sure if TryCancel is the easy way to stop a stream. Calling WritesDone() on client and Finish on server is equally simple.
 
In any case, the recommended way is to do TryCancel() only when you no longer see a point in continuing the call. Also TryCancel is a best-effort call..so Once you do TryCancel, you must still do a CQ next (and see if it returns a failure) to make sure the call is actually cancelled.

Sree

yihao yang

unread,
Jul 14, 2017, 9:06:51 PM7/14/17
to grpc.io, rajeev...@rediffmail.com
Hi Sree,

I want to ask another question about the exit order of a bidirectional async stream. When I stop the grpc::Server, I am confused about the right order.
Can I do it like this:
  1. Shutdown grpc  (servergrpc::Server::Shutdown)
  2. Shutdown stream related CQ (cq->Shutdown())
  3. Destruct ServerContext and its related ServerAsyncReaderWriter. (Is there any exit order need to be maintained on this two? Do I need to destruct ServerContext first?)
  4. Destruct specific Service
Thanks,
Yihao

在 2017年7月11日星期二 UTC-7下午5:05:17,Sree Kuchibhotla写道:
Reply all
Reply to author
Forward
0 new messages