Emit cancelled finished() in channel implementation

Currently, the QGrpcOperation was finishing itself so that the channel
implementation should only cancel the corresponding RPC. This is the
only place such self-finishing is used.

Let the channel implementation rather take care of the cancellation
logic and emit the finished signal. The actual handler implementation is
responsible for itself, i.e. finished meaning it's done and no
communication will happen through this stream.

Simplify the logic for timeouts and requested cancellations.

Update the deadline testcase to not check for specific messages but
rather for non-empty messages.

Changes will only be relevant for custom channel implementation.

[ChangeLog][QGrpcOperationContext][Important Behavior Changes]
Cancellation logic should also emit finished now. Custom
QAbstractGrpcChannel implementations should adapt their logic.

Pick-to: 6.10 6.9 6.8
Change-Id: Ic4e70b50afe46b5a883f099a6cf245ea9a0e66c1
Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
This commit is contained in:
Dennis Oberst 2025-08-01 12:02:46 +02:00
parent 3fea4be230
commit 814c0b6ac2
4 changed files with 19 additions and 25 deletions

View File

@ -286,6 +286,7 @@ public:
void finish(const QGrpcStatus &status);
void asyncFinish(const QGrpcStatus &status);
void cancelWithStatus(const QGrpcStatus &status);
[[nodiscard]] bool expired() const { return !m_operation; }
@ -299,10 +300,13 @@ public:
}
// context slot handlers:
void cancel();
void cancel() { cancelWithStatus({ StatusCode::Cancelled, tr("Cancelled by client") }); }
void writesDone();
void writeMessage(QByteArrayView data);
void deadlineTimeout();
void deadlineTimeout()
{
cancelWithStatus({ StatusCode::DeadlineExceeded, tr("Deadline exceeded") });
}
void handleHeaders(const HPack::HttpHeader &headers, HeaderPhase phase);
@ -437,10 +441,8 @@ Http2Handler::Http2Handler(QGrpcOperationContext *operation, QGrpcHttp2ChannelPr
// QHttp2Stream will handle any outstanding cancellations appropriately.
QObject::connect(operation, &QGrpcOperationContext::destroyed, this,
&Http2Handler::deleteLater);
QObject::connect(operation, &QGrpcOperationContext::cancelRequested, this, [this] {
cancel();
deleteLater();
});
QObject::connect(operation, &QGrpcOperationContext::cancelRequested, this,
&Http2Handler::cancel);
QObject::connect(operation, &QGrpcOperationContext::writesDoneRequested, this,
&Http2Handler::writesDone);
if (!m_endStreamAtFirstData) {
@ -704,7 +706,7 @@ void Http2Handler::asyncFinish(const QGrpcStatus &status)
QTimer::singleShot(0, m_operation.get(), [this, status]() { finish(status); });
}
void Http2Handler::cancel()
void Http2Handler::cancelWithStatus(const QGrpcStatus &status)
{
if (m_state >= State::Cancelled)
return;
@ -718,6 +720,8 @@ void Http2Handler::cancel()
qGrpcDebug("Failed cancellation on stream: %p, Handler::state: %s", m_stream.get(),
QDebug::toBytes(m_state).data());
}
finish(status);
}
void Http2Handler::writesDone()
@ -734,14 +738,6 @@ void Http2Handler::writesDone()
processQueue();
}
void Http2Handler::deadlineTimeout()
{
Q_ASSERT_X(m_stream, "onDeadlineTimeout", "stream is not available");
cancel();
finish({ StatusCode::DeadlineExceeded, "Deadline Exceeded" });
}
void Http2Handler::handleHeaders(const HPack::HttpHeader &headers, HeaderPhase phase)
{
// ABNF syntax: Rule, [Optional-Rule], *Variable-Repetition

View File

@ -149,13 +149,10 @@ bool QGrpcOperation::read(QProtobufMessage *message) const
*/
void QGrpcOperation::cancel()
{
if (!isFinished()) {
if (isFinished())
return;
Q_D(QGrpcOperation);
d->isFinished.storeRelaxed(true);
emit d->operationContext->cancelRequested();
Q_EMIT finished(QGrpcStatus{ QtGrpc::StatusCode::Cancelled,
tr("Operation is cancelled by client") });
}
}
/*!

View File

@ -103,8 +103,9 @@ QT_BEGIN_NAMESPACE
This signal is emitted by QGrpcOperation when requesting cancellation of the
communication.
The channel is expected to connect its cancellation logic to this signal and
attempt to cancel the RPC and return immediately. Successful cancellation
The channel is expected to connect its cancellation logic to this signal
and attempt to cancel the RPC and finish it with a
\l{QtGrpc::StatusCode::}{Cancelled} status code. Successful cancellation
cannot be guaranteed. Further processing of the data received from a
channel is not required and should be avoided.

View File

@ -49,7 +49,7 @@ void QtGrpcClientDeadlineTest::channelDeadlineCallExceeds()
const auto code = qvariant_cast<QGrpcStatus>(finSpy.at(0).first());
QCOMPARE_EQ(code.code(), QtGrpc::StatusCode::DeadlineExceeded);
QCOMPARE_EQ(code.message(), QString("Deadline Exceeded"));
QVERIFY(!code.message().isEmpty());
}
void QtGrpcClientDeadlineTest::channelDeadlineCallFinishes()