C++ grpc Work around inability to unregister service

184 views
Skip to first unread message

Christopher Pisz

unread,
Jul 14, 2021, 2:57:18 PM7/14/21
to grpc.io
I've got a server class that has a `start` method and a `stop` method. The listening port is unknown until a user calls start.

As such, I build the server there, something like:

     void GrpcStreamingServerImpl::start(QUrl const& endpoint)
{
stop();

::grpc::ServerBuilder builder;
builder.AddListeningPort(endpoint.toString().toStdString(), grpc::InsecureServerCredentials());
builder.RegisterService(&m_service);
m_completionQueueCalls         = builder.AddCompletionQueue();
m_completionQueueNotifications = builder.AddCompletionQueue();
m_server                       = builder.BuildAndStart();

m_running = true;
               ....

and then I tear everything down when the server stops. Something like:

    if(!m_running)
    {
        return;
    }

    m_running = false;

    {
        std::lock_guard<std::mutex> local_lock_guard {m_mutexSessions};
        for(const auto& it : m_sessions)
        {
            it.second->finish();
        }
    }

    if(m_server)
    {
        m_server->Shutdown();
        m_completionQueueCalls->Shutdown();
        m_completionQueueNotifications->Shutdown();

        // Wait for the threads to exit before allowing the destruction of the completion queues they use
        if(m_callQueueThread && m_callQueueThread->isRunning() )
        {
            m_callQueueThread->wait();
        }

        if(m_notificationQueueThread && m_notificationQueueThread->isRunning())
        {
            m_notificationQueueThread->wait();
        }

        delete m_completionQueueNotifications;
        delete m_completionQueueCalls;
        delete m_server;
....


Problem is, if the user wants to start, stop, and start again, the RegisterService call results in abort being called and complains a service is already registered.

I tried to move the registration part to the constructor and only add a listening port when start() is called, but that results in a seg fault. Builder isn't a singleton, although I dunno how or why it retains the registration after the server object it returned was destroyed.

A little googling and I found issues were submitted regarding this, but there are no plans to implement an "unregisterService" method. So, what does one to work around this problem?

pixel...@gmail.com

unread,
Jul 21, 2021, 1:32:04 PM7/21/21
to grpc.io
What assert are you seeing exactly? We have tests explicitly that are creating the same service multiple times within the same process, so this is supposed to be a supported use case:

Ashish Kumar

unread,
Mar 23, 2022, 11:25:30 AM3/23/22
to grpc.io
Hi Guys, 
I am new here ( as well as  new to google groups).
We are trying to do something similar to what "christo" is doing.
In our case -   we  are trying to create automated C++ tests ( using gtest).
In our Setup() -- we want to create server/ register services
In our TearDown() -- we want to shutdown server/ delete every thing.
-------------
So that all the tests are exactly same precondition in terms of servers.
We are running the server and it's Wait function ( event loop)  in a separate thread.
One thread is client, the testcases   and  another thread is server -- wjhich waits on the port.
All this effort is also aimed to create a debugable server side testcases.
As Chrsto has mentiond there are tickets which are closed without resolution.
https://github.com/grpc/grpc-go/issues/928
But in case if anyone has any ideas/suggestions, it will be helpful.

pixel...@gmail.com

unread,
Mar 23, 2022, 1:11:55 PM3/23/22
to grpc.io
Well, what issue are you having that's not currently exposed in our test suite?

Ashish Kumar

unread,
Mar 24, 2022, 7:11:18 AM3/24/22
to grpc.io
There are minimum two differences which I suspect.
1)   In the testcase  the g_service   is global, but in my case it is not a global object.
       It's a class object member.  And when the class object dies, it also dies.
       
       In the testcase, multiple tests are sharing this global object.  Another scenario can be two
      tests don't share any state at all. (complete reset).

2)   In my case the   RegisterService part is being done in a   dynamically loaded library. (.so).
       In my testcase,  I create a Builder,   then  do a DL_OPEN a plugin lib.
       I have a helper abstrct class, for which the plugin provides the concrete implementation.
       Then I pass the Builder object to helper object--gets dispatched to the concrete implementor.
       There the concrete implemenator in that shraed library adds some services to the Builder.
        The concrete implementor also stores the server (unique_ptr) object.
         In the TearDown step, I ask the implmenator to ShutDown.

       It's exactly similar, but I see two differences.
      One nothing is global  and two  the different libraries. (Parent has builder,  and DL-OPENed has the service).
Reply all
Reply to author
Forward
0 new messages