summaryrefslogtreecommitdiffstats
path: root/src/serviceframework/qservicereply.cpp
blob: 58548731afa951d56f60f999491f900be4bf4410 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtSystems module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qservicereply.h"
#include "qservicereply_p.h"

#include <QThread>

#define Q_SERVICE_REPLY_DEBUG 1

#ifdef Q_SERVICE_REPLY_DEBUG
#include <QDebug>
#endif

/*!
    \class QServiceReplyBase
    \ingroup servicefw
    \inmodule QtServiceFramework
    \brief The QServiceReplyBase class tracks non-blocking service framework calls.

    The QServiceReplyBase class is a data-carrying class.  Each instance is short-lived
    and only exists during the lifetime of a QServiceManager call.  The QServiceReplyBase
    instance never owns any of the data it points to, it just serves to carry the payload
    from the background request back to the caller.

    When an instance is first created, after being returned from QServiceManager::loadInterface(),
    that instance will return false from both the isRunning() and isFinished() functions.
    Then the request is started, and it will emit the started() signal.  After that, and
    until the request is completed, the isRunning() function will return true.
    Finally the request is completed, and it will emit the finished() signal.  After that
    the isRunning() function will return false, and the isFinished() function will return
    true.  At this point client code can access the proxyObject() function to obtain the
    payload of the service request.

    Typically there should be no reason to construct a QServiceReplyBase (or sub-class)
    instance: instead simply use the instances returned from the QServiceManager::loadInterface()
    function.

    The service QObject returned from the proxyObject() function is owned by the caller of
    the original QServiceManager::loadInterface() function which resulted in the
    QServiceReplyBase instance.  Likewise the QServiceObjectBase instance itself is owned
    by the caller and after the payload is retrieved, it should be queued for destruction
    with deleteLater() in the function which handles the finished() signal.

    As a convenience the manager() function will return the QServiceManager associated with
    the request, and the request() function will return a QString indicating the details of
    the request itself.

    For performance reasons the QServiceReplyBase object is \bold{not synchronized}, and
    thread-safety is ensured by observing the following invariant condition:
    \list
        \o all calls to non-const methods are serialised
    \endlist
    In general client code should never have to worry about this, since the private slots
    which can modify the reply are called via queued signal-slot connections behind the scenes
    ensuring that such accesses are serialised.

    In the case of a request based on an interface name, request() will return the interface name;
    otherwise in the case of a request based on a descriptor it will return the interface name
    of the descriptor.

    \sa QServiceReplyTyped, QServiceManager
*/

/*!
    \class QServiceReplyTyped
    \ingroup servicefw
    \inmodule QtServiceFramework
    \brief The QServiceReplyTyped class tracks typed non-blocking service framework calls.

    This templated sub-class of QServiceReplyBase returns QObjects that are
    already conveniently cast to the templated type.  In all other respects instances of this
    class function exactly the same as QServiceReplyBase.

    To obtain a typed payload class, rather than just a QObject instance, you can use
    one of QServiceManager's typed request functions and a typed QServiceReplyTyped
    instantiation will be returned.

    To obtain the untyped version of the QObject service, call the baseObject() function as
    for the QServiceReplyBase class.

    \sa QServiceReplyBase, QServiceManager
*/

/*!
    Constructs a new QServiceReplyBase object.  All values are set to defaults.  Generally
    creating QServiceReplayBase instances should be left to the QServiceManager.
*/
QServiceReplyBase::QServiceReplyBase(QObject *parent)
    : QObject(parent),
      d(new QServiceReplyPrivate)
{
    // nothing to do here
}

/*!
    Destroys this object recovering all resources.
*/
QServiceReplyBase::~QServiceReplyBase()
{
    delete d;
}

/*!
    Convenience function that returns an informative string of the request which was
    issued when this reply was created.  This string is not used by the request processor
    in any way and exists mainly for logging and debugging purposes.
*/
QString QServiceReplyBase::request() const
{
    return d->request;
}

/*!
    Sets the informative \a request string for this reply.  This function is called by the
    QServiceManager object when the request is created.  In general client code
    should not need to call this function.
*/
void QServiceReplyBase::setRequest(const QString &request)
{
    Q_ASSERT_X(thread() == QThread::currentThread(), Q_FUNC_INFO, "Reply object access violation!");
    d->request = request;
}

/*!
    Returns true if the QServiceReplyBase isNoError completed.  When this is true, the
    baseObject() and proxyObject() functions may be called to retrieve the payload
    of the reply.  Note that you should check the value of the error() function
    to see if the request completed successfully.

    \sa isRunning(), error()
*/
bool QServiceReplyBase::isFinished() const
{
    return d->finished;
}

/*!
    Returns true if the QServiceReplyBase is being processed.  When this is true,
    the baseObject() and proxyObject() should not be accessed as they are in an
    undefined state.  Instead wait for the finished() signal to be emitted and
    access those value then.

    \sa isFinished()
*/
bool QServiceReplyBase::isRunning() const
{
    return d->running;
}

/*!
    Returns any error state that may have been set on this reply.

    \sa isFinished()
*/
QServiceManager::Error QServiceReplyBase::error() const
{
    return d->error;
}

/*!
    \internal
    Sets the error condition of the reply to \a error, indicating that processing of
    the associated request has encountered a problem.

    Note that this is a private slot, and should be called by a queued connection, so
    that any data modification is only done in the objects own thread.
*/
void QServiceReplyBase::setError(QServiceManager::Error error)
{
    Q_ASSERT_X(thread() == QThread::currentThread(), Q_FUNC_INFO, "Reply object access violation!");
    if (d->error != error) {
        d->error = error;
        emit errorChanged();
    }
}

/*!
    \internal
    Starts the reply, indicating that processing of the associated request has begun.

    Note that this is a private slot, and should be called by a queued connection, so
    that any data modification is only done in the objects own thread.
*/
void QServiceReplyBase::start()
{
    Q_ASSERT_X(thread() == QThread::currentThread(), Q_FUNC_INFO, "Reply object access violation!");
    if (!d->running) {
        d->running = true;
        emit started();
    }
#ifdef Q_SERVICE_REPLY_DEBUG
    else
    {
        qWarning() << "Starting request that is" << ((d->finished) ? "finished:" : "started:") << d->request;
    }
    Q_ASSERT(!d->finished);
#endif
}

/*!
    \internal
    Finishes the reply, indicating that processing of the associated request has completed.

    Note that this is a private slot, and should be called by a queued connection, so
    that any data modification is only done in the objects own thread.
*/
void QServiceReplyBase::finish()
{
    Q_ASSERT_X(thread() == QThread::currentThread(), Q_FUNC_INFO, "Reply object access violation!");
    if (!d->finished) {
        d->running = false;
        d->finished = true;
        emit finished();
    }
#ifdef Q_SERVICE_REPLY_DEBUG
    else
    {
        qWarning() << "Attempt to finish request that has already finished:" << d->request;
    }
    Q_ASSERT(!d->running);
#endif
}