qtgrpc/src/grpcquick/qqmlgrpcfunctionalhandlers.cpp

74 lines
3.1 KiB
C++
Raw Normal View History

// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtGrpcQuick/qqmlgrpcfunctionalhandlers.h>
QT_BEGIN_NAMESPACE
namespace QtGrpcQuickFunctional {
void handleDeserializationError(QJSEngine *jsEngine, const QJSValue &errorCallback)
{
if (!errorCallback.isCallable())
return;
using StatusCode = QtGrpc::StatusCode;
const auto status = QGrpcStatus{ StatusCode::InvalidArgument,
"Unable to deserialize return value" };
errorCallback.call(QJSValueList{ jsEngine->toScriptValue(status) });
}
bool checkReceivedStatus(QJSEngine *jsEngine, const QGrpcStatus &status,
const QJSValue &errorCallback)
{
Q_ASSERT(jsEngine != nullptr);
if (status.isOk())
return true;
if (errorCallback.isCallable())
errorCallback.call(QJSValueList{ jsEngine->toScriptValue(status) });
return false;
}
void connectMultipleReceiveOperationFinished(QJSEngine *jsEngine,
Migrate to std::unique_ptr return value for all RPCs The use of shared pointers has potential risk of storing the QGrpcOperation children forever and leaking in user code. The problem is clearly in the lambda connections that we encourage to use in the docs and examples: auto stream = testStream(...); QObject::connect(stream.get(), &QGrpcOperation::finished, ctx, [ctx, stream]{...}); The above code will hold the 'stream' forever, unless user will make the explicit disconnect in the lambda. By using std::unique_ptr we partially solve this, or at least convince user to solve this. When user creates lambda he knows the 'stream' lifetime and most probably should consider that after the move, lambda is owning the QGrpcOperation, so the need of disconnect is more clear in this case: auto stream = testStream(...); auto *streamPtr = stream.get(); QObject::connect(streamPtr, &QGrpcOperation::finished, ctx, [ctx, stream = std::move(stream)]{...}); The code becomes a bit more complicated, but it points explicitly to the potential risk. Also it disallows to make this trick to multiple lambdas at the same time. Of course using the lambda context to control the QGrpcOperation lifetime in this case is not necessary. But even if users will decide to manage the QGrpcOperation lifetime differently, the use of std::unique_ptr will clearly point to the ownership. [ChangeLog][Grpc] All generated RPC methods now return std::unique_ptr instead of std::shared_ptr. This change explicitly defines that caller takes the ownership of the returned pointers. Pick-to: 6.8 Change-Id: I271b91454f0c1b12b77127a7e025fa493367e279 Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
2024-08-28 15:15:33 +00:00
std::unique_ptr<QGrpcOperation> &&operation,
const QJSValue &successCallback,
const QJSValue &errorCallback)
{
Migrate to std::unique_ptr return value for all RPCs The use of shared pointers has potential risk of storing the QGrpcOperation children forever and leaking in user code. The problem is clearly in the lambda connections that we encourage to use in the docs and examples: auto stream = testStream(...); QObject::connect(stream.get(), &QGrpcOperation::finished, ctx, [ctx, stream]{...}); The above code will hold the 'stream' forever, unless user will make the explicit disconnect in the lambda. By using std::unique_ptr we partially solve this, or at least convince user to solve this. When user creates lambda he knows the 'stream' lifetime and most probably should consider that after the move, lambda is owning the QGrpcOperation, so the need of disconnect is more clear in this case: auto stream = testStream(...); auto *streamPtr = stream.get(); QObject::connect(streamPtr, &QGrpcOperation::finished, ctx, [ctx, stream = std::move(stream)]{...}); The code becomes a bit more complicated, but it points explicitly to the potential risk. Also it disallows to make this trick to multiple lambdas at the same time. Of course using the lambda context to control the QGrpcOperation lifetime in this case is not necessary. But even if users will decide to manage the QGrpcOperation lifetime differently, the use of std::unique_ptr will clearly point to the ownership. [ChangeLog][Grpc] All generated RPC methods now return std::unique_ptr instead of std::shared_ptr. This change explicitly defines that caller takes the ownership of the returned pointers. Pick-to: 6.8 Change-Id: I271b91454f0c1b12b77127a7e025fa493367e279 Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
2024-08-28 15:15:33 +00:00
auto *operationPtr = operation.get();
QtGrpcQuickFunctional::validateEngineAndOperation(jsEngine, operationPtr);
auto finishConnection = std::make_shared<QMetaObject::Connection>();
Migrate to std::unique_ptr return value for all RPCs The use of shared pointers has potential risk of storing the QGrpcOperation children forever and leaking in user code. The problem is clearly in the lambda connections that we encourage to use in the docs and examples: auto stream = testStream(...); QObject::connect(stream.get(), &QGrpcOperation::finished, ctx, [ctx, stream]{...}); The above code will hold the 'stream' forever, unless user will make the explicit disconnect in the lambda. By using std::unique_ptr we partially solve this, or at least convince user to solve this. When user creates lambda he knows the 'stream' lifetime and most probably should consider that after the move, lambda is owning the QGrpcOperation, so the need of disconnect is more clear in this case: auto stream = testStream(...); auto *streamPtr = stream.get(); QObject::connect(streamPtr, &QGrpcOperation::finished, ctx, [ctx, stream = std::move(stream)]{...}); The code becomes a bit more complicated, but it points explicitly to the potential risk. Also it disallows to make this trick to multiple lambdas at the same time. Of course using the lambda context to control the QGrpcOperation lifetime in this case is not necessary. But even if users will decide to manage the QGrpcOperation lifetime differently, the use of std::unique_ptr will clearly point to the ownership. [ChangeLog][Grpc] All generated RPC methods now return std::unique_ptr instead of std::shared_ptr. This change explicitly defines that caller takes the ownership of the returned pointers. Pick-to: 6.8 Change-Id: I271b91454f0c1b12b77127a7e025fa493367e279 Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
2024-08-28 15:15:33 +00:00
*finishConnection = QObject::connect(operationPtr, &QGrpcOperation::finished, jsEngine,
[successCallback, errorCallback, jsEngine,
finishConnection,
Migrate to std::unique_ptr return value for all RPCs The use of shared pointers has potential risk of storing the QGrpcOperation children forever and leaking in user code. The problem is clearly in the lambda connections that we encourage to use in the docs and examples: auto stream = testStream(...); QObject::connect(stream.get(), &QGrpcOperation::finished, ctx, [ctx, stream]{...}); The above code will hold the 'stream' forever, unless user will make the explicit disconnect in the lambda. By using std::unique_ptr we partially solve this, or at least convince user to solve this. When user creates lambda he knows the 'stream' lifetime and most probably should consider that after the move, lambda is owning the QGrpcOperation, so the need of disconnect is more clear in this case: auto stream = testStream(...); auto *streamPtr = stream.get(); QObject::connect(streamPtr, &QGrpcOperation::finished, ctx, [ctx, stream = std::move(stream)]{...}); The code becomes a bit more complicated, but it points explicitly to the potential risk. Also it disallows to make this trick to multiple lambdas at the same time. Of course using the lambda context to control the QGrpcOperation lifetime in this case is not necessary. But even if users will decide to manage the QGrpcOperation lifetime differently, the use of std::unique_ptr will clearly point to the ownership. [ChangeLog][Grpc] All generated RPC methods now return std::unique_ptr instead of std::shared_ptr. This change explicitly defines that caller takes the ownership of the returned pointers. Pick-to: 6.8 Change-Id: I271b91454f0c1b12b77127a7e025fa493367e279 Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
2024-08-28 15:15:33 +00:00
operation = std::move(operation)](const QGrpcStatus
&status) {
// We take 'operation' by copy so that its lifetime
// is extended until this lambda is destroyed.
if (QtGrpcQuickFunctional::
checkReceivedStatus(jsEngine, status,
errorCallback)
&& successCallback.isCallable()) {
successCallback.call();
}
QObject::disconnect(*finishConnection);
});
}
void handleReceivedMessageImpl(QJSEngine *jsEngine, std::optional<QJSValue> message,
const QJSValue &successCallback, const QJSValue &errorCallback)
{
if (!successCallback.isCallable())
return;
if (message)
successCallback.call(QJSValueList{ *message });
else
QtGrpcQuickFunctional::handleDeserializationError(jsEngine, errorCallback);
}
} // namespace QtGrpcQuickFunctional
QT_END_NAMESPACE