1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtDBus module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
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 | |
53 | QT_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 | |
128 | void QDBusPendingCallWatcherHelper::add(QDBusPendingCallWatcher *watcher) |
129 | { |
130 | connect(this, SIGNAL(finished()), watcher, SLOT(_q_finished()), Qt::QueuedConnection); |
131 | } |
132 | |
133 | QDBusPendingCallPrivate::~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 | |
142 | bool 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 | if (count == 0) { |
184 | setMetaTypes(count, 0); |
185 | } else { |
186 | QVector<int> types = QVector<int>::fromList(metaTypes); |
187 | setMetaTypes(count, types.constData() + 1); |
188 | } |
189 | return true; |
190 | } |
191 | |
192 | void QDBusPendingCallPrivate::setMetaTypes(int count, const int *types) |
193 | { |
194 | expectedReplyCount = count; |
195 | if (count == 0) { |
196 | expectedReplySignature = QLatin1String("" ); // not null |
197 | return; |
198 | } |
199 | |
200 | QByteArray sig; |
201 | sig.reserve(count + count / 2); |
202 | for (int i = 0; i < count; ++i) { |
203 | const char *typeSig = QDBusMetaType::typeToSignature(types[i]); |
204 | if (!typeSig) { |
205 | qFatal("QDBusPendingReply: type %s is not registered with QtDBus" , |
206 | QMetaType::typeName(types[i])); |
207 | } |
208 | sig += typeSig; |
209 | } |
210 | |
211 | expectedReplySignature = QString::fromLatin1(sig); |
212 | } |
213 | |
214 | void QDBusPendingCallPrivate::checkReceivedSignature() |
215 | { |
216 | // MUST BE CALLED WITH A LOCKED MUTEX! |
217 | |
218 | if (replyMessage.type() == QDBusMessage::InvalidMessage) |
219 | return; // not yet finished - no message to |
220 | // validate against |
221 | if (replyMessage.type() == QDBusMessage::ErrorMessage) |
222 | return; // we don't have to check the signature of an error reply |
223 | |
224 | if (expectedReplySignature.isNull()) |
225 | return; // no signature to validate against |
226 | |
227 | // can't use startsWith here because a null string doesn't start or end with an empty string |
228 | if (replyMessage.signature().indexOf(expectedReplySignature) != 0) { |
229 | QString errorMsg = QLatin1String("Unexpected reply signature: got \"%1\", " |
230 | "expected \"%2\"" ); |
231 | replyMessage = QDBusMessage::createError( |
232 | QDBusError::InvalidSignature, |
233 | errorMsg.arg(replyMessage.signature(), expectedReplySignature)); |
234 | |
235 | } |
236 | } |
237 | |
238 | void QDBusPendingCallPrivate::waitForFinished() |
239 | { |
240 | QMutexLocker locker(&mutex); |
241 | |
242 | if (replyMessage.type() != QDBusMessage::InvalidMessage) |
243 | return; // already finished |
244 | |
245 | connection->waitForFinished(this); |
246 | } |
247 | |
248 | /*! |
249 | Creates a copy of the \a other pending asynchronous call. Note |
250 | that both objects will refer to the same pending call. |
251 | */ |
252 | QDBusPendingCall::QDBusPendingCall(const QDBusPendingCall &other) |
253 | : d(other.d) |
254 | { |
255 | } |
256 | |
257 | /*! |
258 | \internal |
259 | */ |
260 | QDBusPendingCall::QDBusPendingCall(QDBusPendingCallPrivate *dd) |
261 | : d(dd) |
262 | { |
263 | if (dd) { |
264 | bool r = dd->ref.deref(); |
265 | Q_ASSERT(r); |
266 | Q_UNUSED(r); |
267 | } |
268 | } |
269 | |
270 | /*! |
271 | Destroys this copy of the QDBusPendingCall object. If this copy is |
272 | also the last copy of a pending asynchronous call, the call will |
273 | be canceled and no further notifications will be received. There |
274 | will be no way of accessing the reply's contents when it arrives. |
275 | */ |
276 | QDBusPendingCall::~QDBusPendingCall() |
277 | { |
278 | // d deleted by QExplicitlySharedDataPointer |
279 | } |
280 | |
281 | |
282 | /*! |
283 | Creates a copy of the \a other pending asynchronous call and drops |
284 | the reference to the previously-referenced call. Note that both |
285 | objects will refer to the same pending call after this function. |
286 | |
287 | If this object contained the last reference of a pending |
288 | asynchronous call, the call will be canceled and no further |
289 | notifications will be received. There will be no way of accessing |
290 | the reply's contents when it arrives. |
291 | */ |
292 | QDBusPendingCall &QDBusPendingCall::operator=(const QDBusPendingCall &other) |
293 | { |
294 | d = other.d; |
295 | return *this; |
296 | } |
297 | |
298 | /*! |
299 | \fn bool QDBusPendingCallWatcher::isFinished() const |
300 | |
301 | Returns true if the pending call has finished processing and the |
302 | reply has been received. |
303 | |
304 | Note that this function only changes state if you call |
305 | waitForFinished() or if an external D-Bus event happens, which in |
306 | general only happens if you return to the event loop execution. |
307 | |
308 | \sa QDBusPendingReply::isFinished() |
309 | */ |
310 | /*! |
311 | \fn bool QDBusPendingReply::isFinished() const |
312 | |
313 | Returns true if the pending call has finished processing and the |
314 | reply has been received. If this function returns true, the |
315 | isError(), error() and reply() methods should return valid |
316 | information. |
317 | |
318 | Note that this function only changes state if you call |
319 | waitForFinished() or if an external D-Bus event happens, which in |
320 | general only happens if you return to the event loop execution. |
321 | |
322 | \sa QDBusPendingCallWatcher::isFinished() |
323 | */ |
324 | |
325 | bool QDBusPendingCall::isFinished() const |
326 | { |
327 | if (!d) |
328 | return true; // considered finished |
329 | |
330 | QMutexLocker locker(&d->mutex); |
331 | return d->replyMessage.type() != QDBusMessage::InvalidMessage; |
332 | } |
333 | |
334 | void QDBusPendingCall::waitForFinished() |
335 | { |
336 | if (d) d->waitForFinished(); |
337 | } |
338 | |
339 | /*! |
340 | \fn bool QDBusPendingReply::isValid() const |
341 | |
342 | Returns true if the reply contains a normal reply message, false |
343 | if it contains anything else. |
344 | |
345 | If the pending call has not finished processing, this function |
346 | return false. |
347 | */ |
348 | bool QDBusPendingCall::isValid() const |
349 | { |
350 | if (!d) |
351 | return false; |
352 | QMutexLocker locker(&d->mutex); |
353 | return d->replyMessage.type() == QDBusMessage::ReplyMessage; |
354 | } |
355 | |
356 | /*! |
357 | \fn bool QDBusPendingReply::isError() const |
358 | |
359 | Returns true if the reply contains an error message, false if it |
360 | contains a normal method reply. |
361 | |
362 | If the pending call has not finished processing, this function |
363 | also returns true. |
364 | */ |
365 | bool QDBusPendingCall::isError() const |
366 | { |
367 | if (!d) |
368 | return true; // considered finished and an error |
369 | QMutexLocker locker(&d->mutex); |
370 | return d->replyMessage.type() == QDBusMessage::ErrorMessage; |
371 | } |
372 | |
373 | /*! |
374 | \fn QDBusError QDBusPendingReply::error() const |
375 | |
376 | Retrieves the error content of the reply message, if it has |
377 | finished processing. If the reply message has not finished |
378 | processing or if it contains a normal reply message (non-error), |
379 | this function returns an invalid QDBusError. |
380 | */ |
381 | QDBusError QDBusPendingCall::error() const |
382 | { |
383 | if (d) { |
384 | QMutexLocker locker(&d->mutex); |
385 | return d->replyMessage; |
386 | } |
387 | |
388 | // not connected, return an error |
389 | QDBusError err = QDBusError(QDBusError::Disconnected, |
390 | QLatin1String("Not connected to D-Bus server" )); |
391 | return err; |
392 | } |
393 | |
394 | /*! |
395 | \fn QDBusMessage QDBusPendingReply::reply() const |
396 | |
397 | Retrieves the reply message received for the asynchronous call |
398 | that was sent, if it has finished processing. If the pending call |
399 | is not finished, this function returns a QDBusMessage of type |
400 | QDBusMessage::InvalidMessage. |
401 | |
402 | After it has finished processing, the message type will either be |
403 | an error message or a normal method reply message. |
404 | */ |
405 | QDBusMessage QDBusPendingCall::reply() const |
406 | { |
407 | if (!d) |
408 | return QDBusMessage::createError(error()); |
409 | QMutexLocker locker(&d->mutex); |
410 | return d->replyMessage; |
411 | } |
412 | |
413 | #if 0 |
414 | /*! |
415 | Sets the slot \a member in object \a target to be called when the |
416 | reply arrives. The slot's parameter list must match the reply |
417 | message's arguments for it to be called. |
418 | |
419 | It may, optionally, contain a QDBusMessage final parameter. If it |
420 | is present, the parameter will contain the reply message object. |
421 | |
422 | The callback will not be called if the reply is an error message. |
423 | |
424 | This function returns true if it could set the callback, false |
425 | otherwise. It is not a guarantee that the callback will be |
426 | called. |
427 | |
428 | \warning QDBusPendingCall only supports one callback per pending |
429 | asynchronous call, even if multiple QDBusPendingCall |
430 | objects are referencing the same pending call. |
431 | */ |
432 | bool QDBusPendingCall::setReplyCallback(QObject *target, const char *member) |
433 | { |
434 | if (!d) |
435 | return false; |
436 | |
437 | return d->setReplyCallback(target, member); |
438 | } |
439 | #endif |
440 | |
441 | /*! |
442 | \since 4.6 |
443 | Creates a QDBusPendingCall object based on the error condition |
444 | \a error. The resulting pending call object will be in the |
445 | "finished" state and QDBusPendingReply::isError() will return true. |
446 | |
447 | \sa fromCompletedCall() |
448 | */ |
449 | QDBusPendingCall QDBusPendingCall::fromError(const QDBusError &error) |
450 | { |
451 | return fromCompletedCall(QDBusMessage::createError(error)); |
452 | } |
453 | |
454 | /*! |
455 | \since 4.6 |
456 | Creates a QDBusPendingCall object based on the message \a msg. |
457 | The message must be of type QDBusMessage::ErrorMessage or |
458 | QDBusMessage::ReplyMessage (that is, a message that is typical |
459 | of a completed call). |
460 | |
461 | This function is useful for code that requires simulating a pending |
462 | call, but that has already finished. |
463 | |
464 | \sa fromError() |
465 | */ |
466 | QDBusPendingCall QDBusPendingCall::fromCompletedCall(const QDBusMessage &msg) |
467 | { |
468 | QDBusPendingCallPrivate *d = 0; |
469 | if (msg.type() == QDBusMessage::ErrorMessage || |
470 | msg.type() == QDBusMessage::ReplyMessage) { |
471 | d = new QDBusPendingCallPrivate(QDBusMessage(), 0); |
472 | d->replyMessage = msg; |
473 | d->ref = 1; |
474 | } |
475 | |
476 | return QDBusPendingCall(d); |
477 | } |
478 | |
479 | |
480 | class QDBusPendingCallWatcherPrivate: public QObjectPrivate |
481 | { |
482 | public: |
483 | void _q_finished(); |
484 | |
485 | Q_DECLARE_PUBLIC(QDBusPendingCallWatcher) |
486 | }; |
487 | |
488 | inline void QDBusPendingCallWatcherPrivate::_q_finished() |
489 | { |
490 | Q_Q(QDBusPendingCallWatcher); |
491 | emit q->finished(q); |
492 | } |
493 | |
494 | /*! |
495 | Creates a QDBusPendingCallWatcher object to watch for replies on the |
496 | asynchronous pending call \a call and sets this object's parent |
497 | to \a parent. |
498 | */ |
499 | QDBusPendingCallWatcher::QDBusPendingCallWatcher(const QDBusPendingCall &call, QObject *parent) |
500 | : QObject(*new QDBusPendingCallWatcherPrivate, parent), QDBusPendingCall(call) |
501 | { |
502 | if (d) { // QDBusPendingCall::d |
503 | QMutexLocker locker(&d->mutex); |
504 | if (!d->watcherHelper) { |
505 | d->watcherHelper = new QDBusPendingCallWatcherHelper; |
506 | if (d->replyMessage.type() != QDBusMessage::InvalidMessage) { |
507 | // cause a signal emission anyways |
508 | QMetaObject::invokeMethod(d->watcherHelper, "finished" , Qt::QueuedConnection); |
509 | } |
510 | } |
511 | d->watcherHelper->add(this); |
512 | } |
513 | } |
514 | |
515 | /*! |
516 | Destroys this object. If this QDBusPendingCallWatcher object was the |
517 | last reference to the unfinished pending call, the call will be |
518 | canceled. |
519 | */ |
520 | QDBusPendingCallWatcher::~QDBusPendingCallWatcher() |
521 | { |
522 | } |
523 | |
524 | /*! |
525 | \fn void QDBusPendingCallWatcher::waitForFinished() |
526 | |
527 | Suspends the execution of the calling thread until the reply is |
528 | received and processed. After this function returns, isFinished() |
529 | should return true, indicating the reply's contents are ready to |
530 | be processed. |
531 | |
532 | \sa QDBusPendingReply::waitForFinished() |
533 | */ |
534 | void QDBusPendingCallWatcher::waitForFinished() |
535 | { |
536 | if (d) { |
537 | d->waitForFinished(); |
538 | |
539 | // our signals were queued, so deliver them |
540 | QCoreApplication::sendPostedEvents(d->watcherHelper, QEvent::MetaCall); |
541 | QCoreApplication::sendPostedEvents(this, QEvent::MetaCall); |
542 | } |
543 | } |
544 | QT_END_NAMESPACE |
545 | |
546 | #endif // QT_NO_DBUS |
547 | |
548 | #include "moc_qdbuspendingcall.cpp" |
549 | |