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

source code of qtbase/src/dbus/qdbuspendingcall.cpp