source: trunk/src/dbus/qdbuspendingcall.cpp@ 890

Last change on this file since 890 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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