Skip to content

Commit

Permalink
fix(qgisserver): suppress wait in fcgi response destructor
Browse files Browse the repository at this point in the history
In the previous version, the response destructor waited for the monitoring thread to end that may induce a 333ms sleep each time. We fix that by using a classic ptr to separate the thread and the response lifecycles.
  • Loading branch information
benoitdm-oslandia committed Nov 28, 2024
1 parent da86bc1 commit fc27b8d
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 15 deletions.
53 changes: 42 additions & 11 deletions src/server/qgsfcgiserverresponse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,11 @@ typedef struct QgsFCGXStreamData
} QgsFCGXStreamData;
#endif

QgsSocketMonitoringThread::QgsSocketMonitoringThread( bool *isResponseFinished, QgsFeedback *feedback )
: mIsResponseFinished( isResponseFinished )
, mFeedback( feedback )
QgsSocketMonitoringThread::QgsSocketMonitoringThread( QgsFeedback *feedback )
: mFeedback( feedback )
, mIpcFd( -1 )
{
setObjectName( "FCGI socket monitor" );
Q_ASSERT( mIsResponseFinished );
Q_ASSERT( mFeedback );

#if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
Expand All @@ -73,20 +71,29 @@ QgsSocketMonitoringThread::QgsSocketMonitoringThread( bool *isResponseFinished,
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "FCGI_stdin stream data is null! Socket monitoring disable." ),
QgsMessageLog::logMessage( QStringLiteral( "FCGI_stdin stream data is null! Socket monitoring disabled." ),
QStringLiteral( "FCGIServer" ),
Qgis::MessageLevel::Warning );
}
}
else
{
QgsMessageLog::logMessage( QStringLiteral( "FCGI_stdin is null! Socket monitoring disable." ),
QgsMessageLog::logMessage( QStringLiteral( "FCGI_stdin is null! Socket monitoring disabled." ),
QStringLiteral( "FCGIServer" ),
Qgis::MessageLevel::Warning );
}

// Suicide the thread when it ends
connect( this, &QThread::finished, this, &QThread::deleteLater );

#endif
}

void QgsSocketMonitoringThread::setResponseFinished( bool responseFinished )
{
mIsResponseFinished = responseFinished;
}

void QgsSocketMonitoringThread::run( )
{
if ( mIpcFd < 0 )
Expand All @@ -99,7 +106,7 @@ void QgsSocketMonitoringThread::run( )

#if defined(Q_OS_UNIX) && !defined(Q_OS_ANDROID)
char c;
while ( !*mIsResponseFinished )
while ( !mIsResponseFinished )
{
const ssize_t x = recv( mIpcFd, &c, 1, MSG_PEEK | MSG_DONTWAIT ); // see https://stackoverflow.com/a/12402596
if ( x < 0 )
Expand All @@ -118,7 +125,7 @@ void QgsSocketMonitoringThread::run( )
QThread::msleep( 333L );
}

if ( *mIsResponseFinished )
if ( mIsResponseFinished )
{
QgsDebugMsgLevel( QStringLiteral( "FCGIServer: socket monitoring quits normally." ), 2 );
}
Expand All @@ -141,15 +148,39 @@ QgsFcgiServerResponse::QgsFcgiServerResponse( QgsServerRequest::Method method )
mBuffer.open( QIODevice::ReadWrite );
setDefaultHeaders();

mSocketMonitoringThread = std::make_unique<QgsSocketMonitoringThread>( &mFinished, mFeedback.get() );
// This is not a unique_ptr because we want the response to not depend on the thread lifecycle.
mSocketMonitoringThread = new QgsSocketMonitoringThread( mFeedback.get() );
mSocketMonitoringThread->start();
}

QgsFcgiServerResponse::QgsFcgiServerResponse( const QgsFcgiServerResponse &copy )
: mFinished( copy.mFinished )
, mHeadersSent( copy.mHeadersSent )
, mMethod( copy.mMethod )
, mStatusCode( copy.mStatusCode )
, mSocketMonitoringThread( nullptr )
, mFeedback( nullptr )
{
}

QgsFcgiServerResponse &QgsFcgiServerResponse::operator =( const QgsFcgiServerResponse &copy )
{
mFinished = copy.mFinished;
mHeadersSent = copy.mHeadersSent;
mMethod = copy.mMethod;
mStatusCode = copy.mStatusCode;
mSocketMonitoringThread = nullptr;
mFeedback = nullptr;

return *this;
}

QgsFcgiServerResponse::~QgsFcgiServerResponse()
{
mFinished = true;
mSocketMonitoringThread->exit();
mSocketMonitoringThread->wait();
if ( mSocketMonitoringThread )
// This will allow the thread to finish sleeping and exit its while loop in the background, without us needing to wait for it to finish.
mSocketMonitoringThread->setResponseFinished( mFinished );
}

void QgsFcgiServerResponse::removeHeader( const QString &key )
Expand Down
21 changes: 17 additions & 4 deletions src/server/qgsfcgiserverresponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ class QgsSocketMonitoringThread: public QThread
* \param isResponseFinished
* \param feedback
*/
QgsSocketMonitoringThread( bool *isResponseFinished, QgsFeedback *feedback );
QgsSocketMonitoringThread( QgsFeedback *feedback );
void run( );

void setResponseFinished( bool responseFinished );

private:
bool *mIsResponseFinished = nullptr;
bool mIsResponseFinished = false;
QgsFeedback *mFeedback = nullptr;
int mIpcFd = -1;
};
Expand All @@ -68,7 +70,18 @@ class SERVER_EXPORT QgsFcgiServerResponse: public QgsServerResponse
* \param method The HTTP method (Get by default)
*/
QgsFcgiServerResponse( QgsServerRequest::Method method = QgsServerRequest::GetMethod );
virtual ~QgsFcgiServerResponse();

/**
* Dummy copy constructor. Needed because of QgsSocketMonitoringThread ptr
*/
QgsFcgiServerResponse( const QgsFcgiServerResponse &copy );

/**
* Dummy operator = . Needed because of QgsSocketMonitoringThread ptr
*/
QgsFcgiServerResponse &operator = ( const QgsFcgiServerResponse &copy );

virtual ~QgsFcgiServerResponse() override;

void setHeader( const QString &key, const QString &value ) override;

Expand Down Expand Up @@ -117,7 +130,7 @@ class SERVER_EXPORT QgsFcgiServerResponse: public QgsServerResponse
QgsServerRequest::Method mMethod;
int mStatusCode = 0;

std::unique_ptr<QgsSocketMonitoringThread> mSocketMonitoringThread;
QgsSocketMonitoringThread *mSocketMonitoringThread;
std::unique_ptr<QgsFeedback> mFeedback;
};

Expand Down

0 comments on commit fc27b8d

Please sign in to comment.