Files
ResInsight/GrpcInterface/RiaGrpcAppService.cpp
T
Kristian Bendiksen 35614d160e #14683 Serve liveness requests on the gRPC server thread
Requests are queued by the completion queue thread and processed from the main
thread. A long running request, or an open progress dialog in the GUI, therefore
left every request unanswered for as long as that operation lasted, including
the liveness checks a client uses to decide whether ResInsight is still there.

Let a callback declare that it touches no application state, and serve those
directly on the completion queue thread instead of queueing them. GetVersion is
the only such callback, since it reads compile-time constants only. Everything
that touches the project or the data model is still processed on the main
thread, so no request can observe or modify the data model while another one is
running.

The allocated callback list is now used from both threads and is guarded by a
mutex. Shutdown takes a lock before flagging that it is in progress, so that it
waits for any dispatch already running on the completion queue thread and no new
request handler can be registered on a queue that is about to be shut down.

During a blocking call of 3.25 s, 65 pings were answered with a maximum latency
of 6.2 ms. Before this change none of them were answered until the call
completed.
2026-09-07 18:58:05 +02:00

108 lines
5.0 KiB
C++

/////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2019- Equinor ASA
//
// ResInsight is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
//////////////////////////////////////////////////////////////////////////////////
#include "RiaGrpcAppService.h"
#include "RiaApplication.h"
#include "RiaGrpcCallbacks.h"
#include "RiaGrpcServer.h"
#include "RiaVersionInfo.h"
#include "ProjectDataModelCommands/CommandRouter/RimCommandRouter.h"
#include <QApplication>
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
grpc::Status RiaGrpcAppService::GetVersion( grpc::ServerContext* context, const rips::Empty* request, rips::Version* reply )
{
reply->set_major_version( QString( RESINSIGHT_MAJOR_VERSION ).toInt() );
reply->set_minor_version( QString( RESINSIGHT_MINOR_VERSION ).toInt() );
reply->set_patch_version( QString( RESINSIGHT_PATCH_VERSION ).toInt() );
return grpc::Status::OK;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
grpc::Status RiaGrpcAppService::Exit( grpc::ServerContext* context, const rips::Empty* request, rips::Empty* reply )
{
RiaGrpcServer::setReceivedExitRequest();
return grpc::Status::OK;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
grpc::Status
RiaGrpcAppService::GetRuntimeInfo( grpc::ServerContext* context, const rips::Empty* request, rips::RuntimeInfo* reply )
{
rips::ApplicationTypeEnum appType = rips::CONSOLE_APPLICATION;
if ( dynamic_cast<QApplication*>( RiaApplication::instance() ) )
{
appType = rips::GUI_APPLICATION;
}
reply->set_app_type( appType );
return grpc::Status::OK;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::vector<RiaGrpcCallbackInterface*> RiaGrpcAppService::createCallbacks()
{
using Self = RiaGrpcAppService;
// GetVersion reads compile-time constants only. It is flagged to run directly on the gRPC
// completion queue thread so that clients can check liveness even while the main thread is
// busy with a long-running request (see issue #14683). All other methods touch application
// state and must be processed on the main thread.
const bool runsOnServerThread = true;
return { new RiaGrpcUnaryCallback<Self, rips::Empty, rips::Version>( this,
&Self::GetVersion,
&Self::RequestGetVersion,
runsOnServerThread ),
new RiaGrpcUnaryCallback<Self, rips::Empty, rips::Empty>( this, &Self::Exit, &Self::RequestExit ),
new RiaGrpcUnaryCallback<Self, rips::Empty, rips::RuntimeInfo>( this,
&Self::GetRuntimeInfo,
&Self::RequestGetRuntimeInfo ),
new RiaGrpcUnaryCallback<Self, rips::Empty, rips::PdmObject>( this,
&Self::GetPdmObject,
&Self::RequestGetPdmObject ) };
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
grpc::Status
RiaGrpcAppService::GetPdmObject( grpc::ServerContext* context, const rips::Empty* request, rips::PdmObject* reply )
{
auto* commandRouter = RiaApplication::instance()->commandRouter();
if ( commandRouter )
{
copyPdmObjectFromCafToRips( commandRouter, reply );
return grpc::Status::OK;
}
return grpc::Status( grpc::INTERNAL, "Command router is not available" );
}
static bool RiaGrpcAppInfoService_init =
RiaGrpcServiceFactory::instance()->registerCreator<RiaGrpcAppService>( typeid( RiaGrpcAppService ).hash_code() );