I have modified example(examples/cpp/helloworld/greeter_async_server.cc) given under grpc source code path, in order to understand how Server and CQ shutdown happens. And to know whether CQ is properly drained once shutdown is triggered.
Additional Changes Done
++++++++++++++++++
Completion Queue should be fully drained with out any failure, and then Process must exit smoothly.
Workaround that i made to see expected result is, I have added 2 secs delay between Server Shutdown and CQ shutdown, where i can see CQ is completely drained and Process has been exited normally.
So what i am doing wrong here? so what is the correct steps for graceful shutdown ?
Getting Assert failure, i.e. Receiving SIGABRT,
./greeter_async_server
Server listening on 0.0.0.0:50051
shutdown server
E0617 19:35:23.920990861 30581 completion_queue.cc:247] assertion failed: queue.num_items() == 0
Aborted (core dumped)
root@nvme-rdc:~/sathya-207-mdev/nz-svc/third-party/grpc_src/examples/cpp/helloworld#
Provided backtrace as well
++++++++++++++++++
Thread 2 "greeter_async_s" received signal SIGABRT, Aborted.
[Switching to Thread 0x7ffff6d34700 (LWP 30387)]
0x00007ffff6d6a428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007ffff6d6a428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007ffff6d6c02a in __GI_abort () at abort.c:89
#2 0x000000000059fc13 in cq_destroy_next(void*) ()
#3 0x00000000005a008f in grpc_cq_internal_unref(grpc_completion_queue*) ()
#4 0x0000000000579131 in grpc_core::ExecCtx::Flush() ()
#5 0x00000000005a10d0 in cq_next(grpc_completion_queue*, gpr_timespec, void*) ()
#6 0x00000000005a15eb in grpc_completion_queue_next ()
#7 0x0000000000508cf8 in grpc_impl::CompletionQueue::AsyncNextInternal(void**, bool*, gpr_timespec) ()
#8 0x000000000042490d in grpc_impl::CompletionQueue::Next(void**, bool*) ()
#9 0x0000000000425730 in ServerImpl::HandleRpcs() ()
#10 0x0000000000425261 in ServerImpl::Run() ()
#11 0x0000000000429e11 in void std::_Mem_fn_base<void (ServerImpl::)(), true>::operator()<, void>(ServerImpl) const ()
#12 0x0000000000429d85 in void std::_Bind_simple<std::_Mem_fn<void (ServerImpl::)()> (ServerImpl)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) ()
#13 0x0000000000429a5e in std::_Bind_simple<std::_Mem_fn<void (ServerImpl::)()> (ServerImpl)>::operator()() ()
#14 0x0000000000429316 in std:🧵:_Impl<std::_Bind_simple<std::_Mem_fn<void (ServerImpl::)()> (ServerImpl)> >::_M_run() ()
#15 0x00007ffff76d6c80 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#16 0x00007ffff7bc16ba in start_thread (arg=0x7ffff6d34700) at pthread_create.c:333
#17 0x00007ffff6e3c41d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
(gdb) info threads
Id Target Id Frame
1 Thread 0x7ffff7fdf7c0 (LWP 30383) "greeter_async_s" 0x00007ffff7bc298d in pthread_join (threadid=140737334429440, thread_return=0x0)
at pthread_join.c:90
am copying my entire code here for your information which is nothing but greeter_async_server.cc with few modifications
#include
#include
#include
#include
#include <unistd.h>
#include <grpcpp/grpcpp.h>
#include <grpc/support/log.h>
#ifdef BAZEL_BUILD
#include "examples/protos/helloworld.grpc.pb.h"
#else
#include "helloworld.grpc.pb.h"
#endif
using grpc::Server;
using grpc::ServerAsyncResponseWriter;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::ServerCompletionQueue;
using grpc::Status;
using helloworld::HelloRequest;
using helloworld::HelloReply;
using helloworld::Greeter;
class ServerImpl final {
public:
~ServerImpl() {
std::cout << "shutdown server" << std::endl;
// Always shutdown the completion queue after the server.
server_->Shutdown();
cq_->Shutdown();
}
// There is no shutdown handling in this code.
void Run() {
std::string server_address("0.0.0.0:50051");
ServerBuilder builder;
// Listen on the given address without any authentication mechanism.
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
// Register "service_" as the instance through which we'll communicate with
// clients. In this case it corresponds to an *asynchronous* service.
builder.RegisterService(&service_);
// Get hold of the completion queue used for the asynchronous communication
// with the gRPC runtime.
cq_ = builder.AddCompletionQueue();
// Finally assemble the server.
server_ = builder.BuildAndStart();
std::cout << "Server listening on " << server_address << std::endl;
// Proceed to the server's main loop.
HandleRpcs();
}
private:
// Class encompasing the state and logic needed to serve a request.
class CallData {
public:
// Take in the "service" instance (in this case representing an asynchronous
// server) and the completion queue "cq" used for asynchronous communication
// with the gRPC runtime.
CallData(Greeter::AsyncService* service, ServerCompletionQueue* cq)
: service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) {
// Invoke the serving logic right away.
Proceed();
}
void Proceed() {
if (status_ == CREATE) {
// Make this instance progress to the PROCESS state.
status_ = PROCESS;
// As part of the initial CREATE state, we *request* that the system
// start processing SayHello requests. In this request, "this" acts are
// the tag uniquely identifying the request (so that different CallData
// instances can serve different requests concurrently), in this case
// the memory address of this CallData instance.
service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_,
this);
} else if (status_ == PROCESS) {
// Spawn a new CallData instance to serve new clients while we process
// the one for this CallData. The instance will deallocate itself as
// part of its FINISH state.
new CallData(service_, cq_);
// The actual processing.
std::string prefix("Hello ");
reply_.set_message(prefix + request_.name());
// And we are done! Let the gRPC runtime know we've finished, using the
// memory address of this instance as the uniquely identifying tag for
// the event.
status_ = FINISH;
responder_.Finish(reply_, Status::OK, this);
} else {
GPR_ASSERT(status_ == FINISH);
// Once in the FINISH state, deallocate ourselves (CallData).
delete this;
}
}
private:
// The means of communication with the gRPC runtime for an asynchronous
// server.
Greeter::AsyncService* service_;
// The producer-consumer queue where for asynchronous server notifications.
ServerCompletionQueue* cq_;
// Context for the rpc, allowing to tweak aspects of it such as the use
// of compression, authentication, as well as to send metadata back to the
// client.
ServerContext ctx_;
// What we get from the client.
HelloRequest request_;
// What we send back to the client.
HelloReply reply_;
// The means to get back to the client.
ServerAsyncResponseWriter<HelloReply> responder_;
// Let's implement a tiny state machine with the following states.
enum CallStatus { CREATE, PROCESS, FINISH };
CallStatus status_; // The current serving state.
};
// This can be run in multiple threads if needed.
void HandleRpcs() {
// Spawn a new CallData instance to serve new clients.
new CallData(&service_, cq_.get());
void* tag; // uniquely identifies a request.
bool ok;
while (true) {
// Block waiting to read the next event from the completion queue. The
// event is uniquely identified by its tag, which in this case is the
// memory address of a CallData instance.
// The return value of Next should always be checked. This return value
// tells us whether there is any kind of event or cq_ is shutting down.
if(cq_->Next(&tag, &ok)) {
if (ok == false) {
std::cout << "Next returned ok as false" << std::endl;
delete ((CallData*)tag);
} else {
std::cout << "Next returned ok as true" << std::endl;
static_cast<CallData*>(tag)->Proceed();
}
continue;
}
std::cout << "Queue is drained" << std::endl;
break;
}
}
std::unique_ptr cq_;
Greeter::AsyncService service_;
std::unique_ptr server_;
};
int main(int argc, char** argv) {
ServerImpl *server = new ServerImpl;
std::thread thread_ = std::thread(&ServerImpl::Run, server);
/* Added wait so that server can serve async client meanwhile and
go for shutdown aftee mentioned delay*/
sleep(30);
delete server;
thread_.join();
return 0;
}
--
You received this message because you are subscribed to the Google Groups "grpc.io" group.
To unsubscribe from this group and stop receiving emails from it, send an email to grpc-io+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/grpc-io/2a6041a9-4a03-4c40-a147-bc7d1f4d52aco%40googlegroups.com.
