Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdbuspendingcall.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
7
10#include "qdbusutil_p.h"
12#include "qcoreevent.h"
13#include <private/qobject_p.h>
14#include <private/qlocking_p.h>
15
16#ifndef QT_NO_DBUS
17
19
20using namespace Qt::StringLiterals;
21
22/*!
23 \class QDBusPendingCall
24 \inmodule QtDBus
25 \ingroup shared
26 \since 4.5
27
28 \brief The QDBusPendingCall class refers to one pending asynchronous call.
29
30 A QDBusPendingCall object is a reference to a method call that was
31 sent over D-Bus without waiting for a reply. QDBusPendingCall is an
32 opaque type, meant to be used as a handle for a pending reply.
33
34 In most programs, the QDBusPendingCall class will not be used
35 directly. It can be safely replaced with the template-based
36 QDBusPendingReply, in order to access the contents of the reply or
37 wait for it to be complete.
38
39 The QDBusPendingCallWatcher class allows one to connect to a signal
40 that will indicate when the reply has arrived or if the call has
41 timed out. It also provides the
42 QDBusPendingCallWatcher::waitForFinished() method which will suspend
43 the execution of the program until the reply has arrived.
44
45 \note If you create a copy of a QDBusPendingCall object, all
46 information will be shared among the many copies. Therefore,
47 QDBusPendingCall is an explicitly-shared object and does not
48 provide a method of detaching the copies (since they refer
49 to the same pending call)
50
51 \sa QDBusPendingReply, QDBusPendingCallWatcher
52*/
53
54/*!
55 \class QDBusPendingCallWatcher
56 \inmodule QtDBus
57 \since 4.5
58
59 \brief The QDBusPendingCallWatcher class provides a convenient way for
60 waiting for asynchronous replies.
61
62 The QDBusPendingCallWatcher provides the finished() signal that will be
63 emitted when a reply arrives.
64
65 It is usually used like the following example:
66
67 \snippet code/src_qdbus_qdbuspendingcall.cpp 0
68
69 Note that it is not necessary to keep the original QDBusPendingCall
70 object around since QDBusPendingCallWatcher inherits from that class
71 too.
72
73 The slot connected to by the above code could be something similar
74 to the following:
75
76 \snippet code/src_qdbus_qdbuspendingcall.cpp 1
77
78 Note the use of QDBusPendingReply to validate the argument types in
79 the reply. If the reply did not contain exactly two arguments
80 (one string and one QByteArray), QDBusPendingReply::isError() will
81 return true.
82
83 \sa QDBusPendingReply
84*/
85
86/*!
87 \fn void QDBusPendingCallWatcher::finished(QDBusPendingCallWatcher *self)
88
89 This signal is emitted when the pending call has finished and its
90 reply is available. The \a self parameter is a pointer to the
91 object itself, passed for convenience so that the slot can access
92 the properties and determine the contents of the reply.
93*/
94
95void QDBusPendingCallWatcherHelper::add(QDBusPendingCallWatcher *watcher)
96{
97 connect(this, &QDBusPendingCallWatcherHelper::finished, watcher,
98 [watcher] { Q_EMIT watcher->finished(watcher); }, Qt::QueuedConnection);
99}
100
102{
103 if (pending) {
104 q_dbus_pending_call_cancel(pending);
106 }
107 delete watcherHelper;
108}
109
110bool QDBusPendingCallPrivate::setReplyCallback(QObject *target, const char *member)
111{
112 receiver = target;
113 metaTypes.clear();
114 methodIdx = -1;
115 if (!target)
116 return true;; // unsetting
117
118 if (!member || !*member) {
119 // would not be able to deliver a reply
120 qWarning("QDBusPendingCall::setReplyCallback: error: cannot deliver a reply to %s::%s (%s)",
121 target ? target->metaObject()->className() : "(null)",
122 member ? member + 1 : "(null)",
123 target ? qPrintable(target->objectName()) : "no name");
124 return false;
125 }
126
127 QString errorMsg;
128 methodIdx = QDBusConnectionPrivate::findSlot(target, member + 1, metaTypes, errorMsg);
129 if (methodIdx == -1) {
130 QByteArray normalizedName = QMetaObject::normalizedSignature(member + 1);
131 methodIdx = QDBusConnectionPrivate::findSlot(target, normalizedName, metaTypes, errorMsg);
132 }
133 if (methodIdx == -1) {
134 // would not be able to deliver a reply
135 qWarning("QDBusPendingCall::setReplyCallback: error: cannot deliver a reply to %s::%s (%s) "
136 "because %s",
137 target->metaObject()->className(), member + 1, qPrintable(target->objectName()),
138 qPrintable(errorMsg));
139 return false;
140 }
141
142 // success
143 // construct the expected signature
144 int count = metaTypes.size() - 1;
145 if (count == 1 && metaTypes.at(1) == QDBusMetaTypeId::message()) {
146 // wildcard slot, can receive anything, so don't set the signature
147 return true;
148 }
149
150 if (metaTypes.at(count) == QDBusMetaTypeId::message())
151 --count;
152
153 setMetaTypes(count, count ? metaTypes.constData() + 1 : nullptr);
154 return true;
155}
156
157void QDBusPendingCallPrivate::setMetaTypes(int count, const QMetaType *types)
158{
159 if (count == 0) {
160 expectedReplySignature = ""_L1; // not null
161 return;
162 }
163
164 QByteArray sig;
165 sig.reserve(count + count / 2);
166 for (int i = 0; i < count; ++i) {
167 const char *typeSig = QDBusMetaType::typeToSignature(types[i]);
168 if (Q_UNLIKELY(!typeSig))
169 qFatal("QDBusPendingReply: type %s is not registered with QtDBus", types[i].name());
170 sig += typeSig;
171 }
172
173 expectedReplySignature = QString::fromLatin1(sig);
174}
175
177{
178 // MUST BE CALLED WITH A LOCKED MUTEX!
179
180 if (replyMessage.type() == QDBusMessage::InvalidMessage)
181 return; // not yet finished - no message to
182 // validate against
183 if (replyMessage.type() == QDBusMessage::ErrorMessage)
184 return; // we don't have to check the signature of an error reply
185
186 if (expectedReplySignature.isNull())
187 return; // no signature to validate against
188
189 // can't use startsWith here because a null string doesn't start or end with an empty string
190 if (replyMessage.signature().indexOf(expectedReplySignature) != 0) {
191 const auto errorMsg = "Unexpected reply signature: got \"%1\", expected \"%2\""_L1;
192 replyMessage = QDBusMessage::createError(
193 QDBusError::InvalidSignature,
194 errorMsg.arg(replyMessage.signature(), expectedReplySignature));
195
196 }
197}
198
200{
201 const auto locker = qt_scoped_lock(mutex);
202
203 if (replyMessage.type() != QDBusMessage::InvalidMessage)
204 return; // already finished
205
206 waitForFinishedCondition.wait(&mutex);
207}
208
210{
211 QEventLoop loop;
212
213 {
214 const auto locker = qt_scoped_lock(mutex);
215 if (replyMessage.type() != QDBusMessage::InvalidMessage)
216 return; // already finished
217
218 Q_ASSERT(!watcherHelper);
220 loop.connect(watcherHelper, &QDBusPendingCallWatcherHelper::reply, &loop,
221 &QEventLoop::quit);
222 loop.connect(watcherHelper, &QDBusPendingCallWatcherHelper::error, &loop,
223 &QEventLoop::quit);
224 }
225
226 loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents);
227}
228
229/*!
230 Creates a copy of the \a other pending asynchronous call. Note
231 that both objects will refer to the same pending call.
232*/
233QDBusPendingCall::QDBusPendingCall(const QDBusPendingCall &other)
234 : d(other.d)
235{
236}
237
238/*!
239 \internal
240*/
241QDBusPendingCall::QDBusPendingCall(QDBusPendingCallPrivate *dd)
242 : d(dd)
243{
244 if (dd) {
245 bool r = dd->ref.deref();
246 Q_ASSERT(r);
247 }
248}
249
250/*!
251 Destroys this copy of the QDBusPendingCall object. If this copy is
252 also the last copy of a pending asynchronous call, the call will
253 be canceled and no further notifications will be received. There
254 will be no way of accessing the reply's contents when it arrives.
255*/
256QDBusPendingCall::~QDBusPendingCall()
257{
258 // d deleted by QExplicitlySharedDataPointer
259}
260
261
262/*!
263 Creates a copy of the \a other pending asynchronous call and drops
264 the reference to the previously-referenced call. Note that both
265 objects will refer to the same pending call after this function.
266
267 If this object contained the last reference of a pending
268 asynchronous call, the call will be canceled and no further
269 notifications will be received. There will be no way of accessing
270 the reply's contents when it arrives.
271*/
272QDBusPendingCall &QDBusPendingCall::operator=(const QDBusPendingCall &other)
273{
274 d = other.d;
275 return *this;
276}
277
278/*!
279 \fn void QDBusPendingCall::swap(QDBusPendingCall &other)
280 \since 5.0
281 \memberswap{pending call instance}
282*/
283
284/*!
285 \fn bool QDBusPendingCallWatcher::isFinished() const
286
287 Returns \c true if the pending call has finished processing and the
288 reply has been received.
289
290 Note that this function only changes state if you call
291 waitForFinished() or if an external D-Bus event happens, which in
292 general only happens if you return to the event loop execution.
293
294 \sa QDBusPendingReply::isFinished()
295*/
296
297/*!
298 \fn template <typename... Types> bool QDBusPendingReply<Types...>::isFinished() const
299
300 Returns \c true if the pending call has finished processing and the
301 reply has been received. If this function returns \c true, the
302 isError(), error() and reply() methods should return valid
303 information.
304
305 Note that this function only changes state if you call
306 waitForFinished() or if an external D-Bus event happens, which in
307 general only happens if you return to the event loop execution.
308
309 \sa QDBusPendingCallWatcher::isFinished()
310*/
311
312bool QDBusPendingCall::isFinished() const
313{
314 if (!d)
315 return true; // considered finished
316
317 const auto locker = qt_scoped_lock(d->mutex);
318 return d->replyMessage.type() != QDBusMessage::InvalidMessage;
319}
320
321void QDBusPendingCall::waitForFinished()
322{
323 if (d) d->waitForFinished();
324}
325
326/*!
327 \fn template <typename... Types> bool QDBusPendingReply<Types...>::isValid() const
328
329 Returns \c true if the reply contains a normal reply message, false
330 if it contains anything else.
331
332 If the pending call has not finished processing, this function
333 return false.
334*/
335bool QDBusPendingCall::isValid() const
336{
337 if (!d)
338 return false;
339 const auto locker = qt_scoped_lock(d->mutex);
340 return d->replyMessage.type() == QDBusMessage::ReplyMessage;
341}
342
343/*!
344 \fn template <typename... Types> bool QDBusPendingReply<Types...>::isError() const
345
346 Returns \c true if the reply contains an error message, false if it
347 contains a normal method reply.
348
349 If the pending call has not finished processing, this function
350 also returns \c true.
351*/
352bool QDBusPendingCall::isError() const
353{
354 if (!d)
355 return true; // considered finished and an error
356 const auto locker = qt_scoped_lock(d->mutex);
357 return d->replyMessage.type() == QDBusMessage::ErrorMessage;
358}
359
360/*!
361 \fn template <typename... Types> QDBusError QDBusPendingReply<Types...>::error() const
362
363 Retrieves the error content of the reply message, if it has
364 finished processing. If the reply message has not finished
365 processing or if it contains a normal reply message (non-error),
366 this function returns an invalid QDBusError.
367*/
368QDBusError QDBusPendingCall::error() const
369{
370 if (d) {
371 const auto locker = qt_scoped_lock(d->mutex);
372 return QDBusError(d->replyMessage);
373 }
374
375 // not connected, return an error
376 QDBusError err = QDBusError(QDBusError::Disconnected,
377 QDBusUtil::disconnectedErrorMessage());
378 return err;
379}
380
381/*!
382 \fn template <typename... Types> QDBusMessage QDBusPendingReply<Types...>::reply() const
383
384 Retrieves the reply message received for the asynchronous call
385 that was sent, if it has finished processing. If the pending call
386 is not finished, this function returns a QDBusMessage of type
387 QDBusMessage::InvalidMessage.
388
389 After it has finished processing, the message type will either be
390 an error message or a normal method reply message.
391*/
392QDBusMessage QDBusPendingCall::reply() const
393{
394 if (!d)
395 return QDBusMessage::createError(error());
396 const auto locker = qt_scoped_lock(d->mutex);
397 return d->replyMessage;
398}
399
400#if 0
401/*
402 Sets the slot \a member in object \a target to be called when the
403 reply arrives. The slot's parameter list must match the reply
404 message's arguments for it to be called.
405
406 It may, optionally, contain a QDBusMessage final parameter. If it
407 is present, the parameter will contain the reply message object.
408
409 The callback will not be called if the reply is an error message.
410
411 This function returns \c true if it could set the callback, false
412 otherwise. It is not a guarantee that the callback will be
413 called.
414
415 \warning QDBusPendingCall only supports one callback per pending
416 asynchronous call, even if multiple QDBusPendingCall
417 objects are referencing the same pending call.
418*/
419bool QDBusPendingCall::setReplyCallback(QObject *target, const char *member)
420{
421 if (!d)
422 return false;
423
424 return d->setReplyCallback(target, member);
425}
426#endif
427
428/*!
429 \since 4.6
430 Creates a QDBusPendingCall object based on the error condition
431 \a error. The resulting pending call object will be in the
432 "finished" state and QDBusPendingReply<Types...>::isError() will return true.
433
434 \sa fromCompletedCall()
435*/
436QDBusPendingCall QDBusPendingCall::fromError(const QDBusError &error)
437{
438 return fromCompletedCall(QDBusMessage::createError(error));
439}
440
441/*!
442 \since 4.6
443 Creates a QDBusPendingCall object based on the message \a msg.
444 The message must be of type QDBusMessage::ErrorMessage or
445 QDBusMessage::ReplyMessage (that is, a message that is typical
446 of a completed call).
447
448 This function is useful for code that requires simulating a pending
449 call, but that has already finished.
450
451 \sa fromError()
452*/
453QDBusPendingCall QDBusPendingCall::fromCompletedCall(const QDBusMessage &msg)
454{
455 QDBusPendingCallPrivate *d = nullptr;
456 if (msg.type() == QDBusMessage::ErrorMessage ||
457 msg.type() == QDBusMessage::ReplyMessage) {
458 d = new QDBusPendingCallPrivate(QDBusMessage(), nullptr);
459 d->replyMessage = msg;
460 d->ref.storeRelaxed(1);
461 }
462
463 return QDBusPendingCall(d);
464}
465
466/*!
467 Creates a QDBusPendingCallWatcher object to watch for replies on the
468 asynchronous pending call \a call and sets this object's parent
469 to \a parent.
470*/
471QDBusPendingCallWatcher::QDBusPendingCallWatcher(const QDBusPendingCall &call, QObject *parent)
472 : QObject(parent), QDBusPendingCall(call)
473{
474 if (d) { // QDBusPendingCall::d
475 const auto locker = qt_scoped_lock(d->mutex);
476 if (!d->watcherHelper) {
477 d->watcherHelper = new QDBusPendingCallWatcherHelper;
478 if (d->replyMessage.type() != QDBusMessage::InvalidMessage) {
479 // cause a signal emission anyways
480 QMetaObject::invokeMethod(d->watcherHelper,
481 &QDBusPendingCallWatcherHelper::finished,
482 Qt::QueuedConnection);
483 }
484 }
485 d->watcherHelper->add(this);
486 }
487}
488
489/*!
490 Destroys this object. If this QDBusPendingCallWatcher object was the
491 last reference to the unfinished pending call, the call will be
492 canceled.
493*/
494QDBusPendingCallWatcher::~QDBusPendingCallWatcher()
495{
496}
497
498/*!
499 \fn void QDBusPendingCallWatcher::waitForFinished()
500
501 Suspends the execution of the calling thread until the reply is
502 received and processed. After this function returns, isFinished()
503 should return true, indicating the reply's contents are ready to
504 be processed.
505
506 \sa QDBusPendingReply::waitForFinished()
507*/
508void QDBusPendingCallWatcher::waitForFinished()
509{
510 if (d) {
511 d->waitForFinished();
512
513 // our signals were queued, so deliver them
514 QCoreApplication::sendPostedEvents(d->watcherHelper, QEvent::MetaCall);
515 QCoreApplication::sendPostedEvents(this, QEvent::MetaCall);
516 }
517}
518QT_END_NAMESPACE
519
520#include "moc_qdbuspendingcall_p.cpp"
521
522#endif // QT_NO_DBUS
523
524#include "moc_qdbuspendingcall.cpp"
QDBusPendingCallWatcherHelper * watcherHelper
bool setReplyCallback(QObject *target, const char *member)
void setMetaTypes(int count, const QMetaType *types)
Combined button and popup list for selecting options.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall DBusPendingCall return dbus_pending_call_unref