[C++/async/stream] getting "pure virtual method called" in grpc_completion_queue_next ()

1,973 views
Skip to first unread message

rkr...@gmail.com

unread,
Nov 15, 2016, 11:56:28 AM11/15/16
to grpc.io

Hi all,

I've created a tiny code sample that is based off the hello world async greeter example. I keep getting a "pure virtual method called" in the grpc_completion_queue_next() function, and I'm having a hard time understanding why. What am I doing wrong?

I've included the proto file, C++ code and gdb output... (the program is both a client or a server, if you give it a command line value of "c" it acts as a client).

Thanks in advance!

PROTO FILE

syntax = "proto3";

package helloworld;

service Greeter
{
    rpc SayHello (HelloRequest) returns (stream HelloReply) {}
}

message HelloRequest
{
    string  message = 1;
}

message HelloReply
{
    string  reply = 1;
}



C++ CODE

#include <iostream>
#include <string>
#include <grpc++/grpc++.h>
#include "minfail.grpc.pb.h"

using namespace std;
using namespace helloworld;

class CallData {
public:
    CallData(Greeter::AsyncService* service, grpc::ServerCompletionQueue* cq) : service_(service), cq_(cq), responder_(&ctx_), status_(CREATE) {
        Proceed();
    }

    void Proceed() {
        if (status_ == CREATE) {
            status_ = PROCESS;
            service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_, this);
        } else if (status_ == PROCESS) {
            new CallData(service_, cq_);
            reply_.set_reply("hello " + request_.message());
            responder_.Write (reply_, this);
            responder_.Finish(grpc::Status::OK, this);
            status_ = FINISH;
        } else {
            GPR_ASSERT(status_ == FINISH);
            delete this;
        }
    }

private:
    HelloRequest request_;
    HelloReply reply_;
    grpc::ServerAsyncWriter<HelloReply> responder_;

    enum CallStatus { CREATE, PROCESS, FINISH };
    CallStatus status_;
    grpc::ServerContext ctx_;
    grpc::ServerCompletionQueue* cq_;
    Greeter::AsyncService* service_;
};

class GreeterClient
{
public:
    GreeterClient (shared_ptr <grpc::Channel> channel)
    : stub_ (Greeter::NewStub (channel)) {}
    string
    SayHello (const string &user) {
        HelloRequest request;
        request.set_message (user);
        grpc::ClientContext context;
        unique_ptr <grpc::ClientReader <HelloReply>> reader (stub_->SayHello (&context, request));
        HelloReply reply;
        while (reader -> Read (&reply)) {
            cout << "SayHello returns " << reply.reply() << "\n";
        }
        grpc::Status status = reader -> Finish();
        if (status.ok()) {
            return (reply.reply());
        }
        return ("<RPC failed>");
    }

private:
    unique_ptr<Greeter::Stub> stub_;
};

int
main (int argc, char **argv)
{
    string server_address ("0.0.0.0:50051");

    if (argc == 2 && argv [1][0] == 'c') {
        GreeterClient greeter(grpc::CreateChannel (server_address, grpc::InsecureChannelCredentials()));
        cout << "Sending world...\n";
        cout << "Response is " << greeter.SayHello ("world") << "\n";
    } else {
        unique_ptr<grpc::ServerCompletionQueue> cq;
        Greeter::AsyncService service;
        unique_ptr<grpc::Server> server;

        grpc::ServerBuilder builder;

        builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
        builder.RegisterService(&service);
        cq = builder.AddCompletionQueue();
        server = builder.BuildAndStart();

        new CallData(&service, cq.get());
        CallData* tag;  // uniquely identifies a request.
        bool ok;
        while (true) {
            GPR_ASSERT(cq->Next((void **)&tag, &ok));
            GPR_ASSERT(ok);
            tag->Proceed();
        }

        server->Shutdown();
        cq->Shutdown();
    }
}

and run / GDB output:

$ ./minfail
D1115 11:45:47.919769500   24264 env_linux.c:77]             Warning: insecure environment read function 'getenv' used
pure virtual method called
terminate called without an active exception
Aborted (core dumped)


$ gdb ./minfail
... elided ...
Program received signal SIG36, Real-time event 36.
0x00000035ce0e8cec in epoll_pwait () from /lib64/libc.so.6
(gdb) bt
#0  0x00000035ce0e8cec in epoll_pwait () from /lib64/libc.so.6
#1  0x00007ffff76f8d4a in ?? () from /.../lib/libgrpc++.so.1
#2  0x00007ffff76f91a6 in ?? () from /.../lib/libgrpc++.so.1
#3  0x00007ffff770d61b in grpc_completion_queue_next () from /.../lib/libgrpc++.so.1
#4  0x00007ffff76e3f20 in grpc::CompletionQueue::AsyncNextInternal(void**, bool*, gpr_timespec) () from /.../lib/libgrpc++.so.1
#5  0x000000000040919f in grpc::CompletionQueue::Next [_ZN4grpc15Completion...] (this=0x628fa0, tag=0x7fffffffe1e8, ok=0x7fffffffe1e7) at .../include/grpc++/impl/codegen/completion_queue.h:148
#6  0x00000000004085ca in main [main] (argc=1, argv=0x7fffffffe3a8) at minfail.cc:95
(gdb)

Any help greatly appreciated!

Cheers,
-RK

Craig Tiller

unread,
Nov 15, 2016, 1:55:48 PM11/15/16
to rkr...@gmail.com, grpc.io
You probably want to first do 'handle SIG36 noprint' to let gdb pass the (totally legitimate) signal to the application.

--
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 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/34f059a0-f825-4aa3-a725-84fb61e46b1f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

rkr...@gmail.com

unread,
Nov 15, 2016, 2:01:50 PM11/15/16
to grpc.io, rkr...@gmail.com
Ok:

(gdb) handle SIG36 noprint
Signal        Stop      Print   Pass to program Description
SIG36         No        No      Yes             Real-time event 36
(gdb) run
Starting program: /.../minfail
[Thread debugging using libthread_db enabled]
D1115 13:59:40.187940393   34391 env_linux.c:77]             Warning: insecure environment read function 'getenv' used
[New Thread 0x7ffff6fcc700 (LWP 34394)]
[New Thread 0x7ffff65cb700 (LWP 34395)]
[New Thread 0x7ffff5bca700 (LWP 34396)]
[New Thread 0x7ffff51c9700 (LWP 34397)]
[New Thread 0x7ffff47c8700 (LWP 34398)]
[New Thread 0x7ffff3dc7700 (LWP 34399)]
[New Thread 0x7ffff33c6700 (LWP 34400)]
[New Thread 0x7ffff29c5700 (LWP 34401)]
[New Thread 0x7ffff1fc4700 (LWP 34402)]
[New Thread 0x7ffff15c3700 (LWP 34403)]
[New Thread 0x7ffff0bc2700 (LWP 34404)]
[New Thread 0x7ffff01c1700 (LWP 34405)]
[New Thread 0x7fffef7c0700 (LWP 34406)]
[New Thread 0x7fffeedbf700 (LWP 34407)]
[New Thread 0x7fffee3be700 (LWP 34408)]
[New Thread 0x7fffed9bd700 (LWP 34409)]

pure virtual method called
terminate called without an active exception

Program received signal SIGABRT, Aborted.
0x00000035ce0325e5 in raise () from /lib64/libc.so.6
(gdb) bt
#0  0x00000035ce0325e5 in raise () from /lib64/libc.so.6
#1  0x00000035ce033dc5 in abort () from /lib64/libc.so.6
#2  0x00000035d4cbea7d in __gnu_cxx::__verbose_terminate_handler() () from /usr/lib64/libstdc++.so.6
#3  0x00000035d4cbcbd6 in ?? () from /usr/lib64/libstdc++.so.6
#4  0x00000035d4cbcc03 in std::terminate() () from /usr/lib64/libstdc++.so.6
#5  0x00000035d4cbd55f in __cxa_pure_virtual () from /usr/lib64/libstdc++.so.6
#6  0x00007ffff76e3f7a in grpc::CompletionQueue::AsyncNextInternal(void**, bool*, gpr_timespec) () from /.../lib/libgrpc++.so.1
#7  0x000000000040919f in grpc::CompletionQueue::Next [_ZN4grpc15Completion...] (this=0x628fa0, tag=0x7fffffffe1e8, ok=0x7fffffffe1e7) at .../include/grpc++/impl/codegen/completion_queue.h:148
#8  0x00000000004085ca in main [main] (argc=1, argv=0x7fffffffe3a8) at minfail.cc:95
(gdb)

rkr...@gmail.com

unread,
Nov 16, 2016, 9:12:43 AM11/16/16
to grpc.io, rkr...@gmail.com
Sadly, I'm still stuck. I don't know enough C++ to be able to diagnose this further... :-(

rkr...@gmail.com

unread,
Nov 17, 2016, 9:23:25 AM11/17/16
to grpc.io, rkr...@gmail.com

OK, tracked it down to grpc-1.0.0/include/grpc++/impl/codegen/completion_queue_tag.h -- the pure virtual function "bool FinalizeResult()" -- what do I need to do to initialize it that I'm not doing in the program above? (As a hack, I just had it print a message to stdout and return "false", now the program doesn't give the "pure virtual method" error, but obviously isn't working completely as intended)

Sree Kuchibhotla

unread,
Nov 17, 2016, 2:58:59 PM11/17/16
to grpc.io, rkr...@gmail.com

Looks like you are doing two async operations together in your server: (in calldata when status == PROCESS)

            responder_.Write (reply_, this);
            responder_.Finish(grpc::Status::OK, this);

Instead try one operation at a time..For example, add a new state (for example: DESTROY) and you can modify the CallData's Process() function the following way:

   enum CallStatus { CREATE, PROCESS, FINISH, DESTROY };

   void Proceed() {
        if (status_ == CREATE) {
            status_ = PROCESS;
            service_->RequestSayHello(&ctx_, &request_, &responder_, cq_, cq_, this);
        } else if (status_ == PROCESS) {
            new CallData(service_, cq_);
            reply_.set_reply("hello " + request_.message());
            responder_.Write (reply_, this); // Do only Write here. Not Finish
            status_ = FINISH;
        } else if (status_ == FINISH) {
            responder_.Finish(grpc::Status::OK, this);  // Do Finish here.. Not 'delete this'
            status_ = DESTROY
        } else {
            GPR_ASSERT(status_ == DESTROY); 
            delete this;
        }
    }

Let me know if that works.

thanks,
Sree

rkr...@gmail.com

unread,
Nov 17, 2016, 3:02:29 PM11/17/16
to grpc.io, rkr...@gmail.com
Woo hoo! That fixed it! Thanks Sree.

Cheers,
-RK
Reply all
Reply to author
Forward
0 new messages