1 | /* |
2 | * BluezQt - Asynchronous BlueZ wrapper library |
3 | * |
4 | * SPDX-FileCopyrightText: 2014 Alejandro Fiestas Olivares <afiestas@kde.org> |
5 | * SPDX-FileCopyrightText: 2014-2015 David Rosca <nowrep@gmail.com> |
6 | * |
7 | * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
8 | */ |
9 | |
10 | #ifndef BLUEZQT_JOB_H |
11 | #define BLUEZQT_JOB_H |
12 | |
13 | #include <QObject> |
14 | |
15 | #include "bluezqt_export.h" |
16 | |
17 | #include <memory> |
18 | |
19 | namespace BluezQt |
20 | { |
21 | class JobPrivate; |
22 | |
23 | /*! |
24 | * \inmodule BluezQt |
25 | * \class BluezQt::Job |
26 | * \inheaderfile BluezQt/Job |
27 | * \brief Async job. |
28 | * |
29 | * This class represents an asynchronous job performed by BluezQt, |
30 | * it is usually not used directly but instead it is inherit by some |
31 | * other class. |
32 | * |
33 | * There are two ways of using this class, one is via exec() which will block |
34 | * the thread until a result is fetched, the other is via connecting to the |
35 | * signal result() |
36 | * |
37 | * Please, think twice before using exec(), it should be used only in either |
38 | * unittest or cli apps. |
39 | * |
40 | * \note Job and its subclasses are meant to be used in a fire-and-forget way. |
41 | * Jobs will delete themselves when they finish using deleteLater(). |
42 | * |
43 | * \note Even given their asynchronous nature, Jobs are still executed in the |
44 | * main thread, so any blocking code executed in it will block the app calling it. |
45 | * |
46 | * \sa InitManagerJob |
47 | * \sa InitObexManagerJob |
48 | */ |
49 | class BLUEZQT_EXPORT Job : public QObject |
50 | { |
51 | Q_OBJECT |
52 | /*! \property BluezQt::Job::error */ |
53 | Q_PROPERTY(int error READ error) |
54 | /*! \property BluezQt::Job::errorText */ |
55 | Q_PROPERTY(QString errorText READ errorText) |
56 | /*! \property BluezQt::Job::running */ |
57 | Q_PROPERTY(bool running READ isRunning) |
58 | /*! \property BluezQt::Job::finished */ |
59 | Q_PROPERTY(bool finished READ isFinished) |
60 | |
61 | public: |
62 | /*! |
63 | * Creates a new Job object as a child of \a parent. |
64 | */ |
65 | explicit Job(QObject *parent = nullptr); |
66 | |
67 | ~Job() override; |
68 | |
69 | /*! |
70 | * \enum BluezQt::Job::Error |
71 | * \value NoError |
72 | * Indicates there is no error. |
73 | * \value UserDefinedError |
74 | * Subclasses should define error codes starting at this value. |
75 | * \sa error() |
76 | */ |
77 | enum Error { |
78 | NoError = 0, |
79 | UserDefinedError = 100 |
80 | }; |
81 | Q_ENUM(Error) |
82 | |
83 | /*! |
84 | * Executes the job synchronously. |
85 | * |
86 | * This will start a nested QEventLoop internally. Nested event loop can be dangerous and |
87 | * can have unintended side effects, you should avoid calling exec() whenever you can and use the |
88 | * asynchronous interface of Job instead. |
89 | * |
90 | * Should you indeed call this method, you need to make sure that all callers are reentrant, |
91 | * so that events delivered by the inner event loop don't cause non-reentrant functions to be |
92 | * called, which usually wreaks havoc. |
93 | * |
94 | * Note that the event loop started by this method does not process user input events, which means |
95 | * your user interface will effectively be blocked. Other events like paint or network events are |
96 | * still being processed. The advantage of not processing user input events is that the chance of |
97 | * accidental reentrancy is greatly reduced. Still you should avoid calling this function. |
98 | * |
99 | * \warning This method blocks until the job finishes! |
100 | * |
101 | * Returns \c true if the job has been executed without error, \c false otherwise. |
102 | */ |
103 | bool exec(); |
104 | |
105 | /*! |
106 | * Returns the error code for this job if there has been an error. |
107 | * |
108 | * Make sure to call this once result() has been emitted. |
109 | */ |
110 | int error() const; |
111 | |
112 | /*! |
113 | * Returns the error text if there has been an error. |
114 | * |
115 | * Only call if error is not 0. |
116 | * |
117 | * This is usually some extra data associated with the error, |
118 | * such as a URL. Use errorString() to get a human-readable, |
119 | * translated message. |
120 | */ |
121 | QString errorText() const; |
122 | |
123 | /*! |
124 | * Returns whether the job is currently running. |
125 | */ |
126 | bool isRunning() const; |
127 | |
128 | /*! |
129 | * Returns whether the job has already finished. |
130 | */ |
131 | bool isFinished() const; |
132 | |
133 | public Q_SLOTS: |
134 | /*! |
135 | * Starts the job asynchronously. |
136 | * |
137 | * This method will schedule doStart() to be executed in the next |
138 | * loop. This is done so this method returns as soon as possible. |
139 | * |
140 | * When the job is finished, result() is emitted. |
141 | */ |
142 | void start(); |
143 | |
144 | /*! |
145 | * Kills the job. |
146 | * |
147 | * This method will kill the job and then call deleteLater(). |
148 | * Only jobs started with start() can be killed. |
149 | * |
150 | * It will not emit the result signal. |
151 | */ |
152 | void kill(); |
153 | |
154 | protected Q_SLOTS: |
155 | /*! |
156 | * Implementation for start() that will be executed in next loop |
157 | * |
158 | * This slot is always called in the next loop, triggered by start(). |
159 | * |
160 | * When implementing this method is important to remember that jobs |
161 | * are not executed on a different thread (unless done that way), so any |
162 | * blocking task has to be done in a different thread or process. |
163 | */ |
164 | virtual void doStart() = 0; |
165 | |
166 | protected: |
167 | /*! |
168 | * Sets the \a errorCode. |
169 | * |
170 | * It should be called when an error |
171 | * is encountered in the job, just before calling emitResult(). |
172 | * |
173 | * You should define an enum of error codes, |
174 | * with values starting at Job::UserDefinedError, and use |
175 | * those. For example: |
176 | * \code |
177 | * enum ExampleErrors{ |
178 | * InvalidFoo = UserDefinedError, |
179 | * BarNotFound |
180 | * }; |
181 | * \endcode |
182 | * |
183 | * \sa emitResult() |
184 | */ |
185 | void setError(int errorCode); |
186 | |
187 | /*! |
188 | * Sets the \a errorText. |
189 | * |
190 | * It should be called when an error |
191 | * is encountered in the job, just before calling emitResult(). |
192 | * |
193 | * Provides extra information about the error that cannot be |
194 | * determined directly from the error code. For example, a |
195 | * URL or filename. This string is not normally translatable. |
196 | * |
197 | * \sa emitResult(), setError() |
198 | */ |
199 | void setErrorText(const QString &errorText); |
200 | |
201 | /*! |
202 | * Utility function to emit the result signal, and remove this job. |
203 | * |
204 | * \note Deletes this job using deleteLater(). |
205 | * \sa result() |
206 | */ |
207 | void emitResult(); |
208 | |
209 | /*! |
210 | * Implementation for emitting the result signal. |
211 | * |
212 | * This function is needed to be able to emit result() signal |
213 | * with the job pointer's type being subclass. |
214 | */ |
215 | virtual void doEmitResult() = 0; |
216 | |
217 | private: |
218 | std::unique_ptr<JobPrivate> const d_ptr; |
219 | |
220 | Q_DECLARE_PRIVATE(Job) |
221 | }; |
222 | |
223 | } // namespace BluezQt |
224 | |
225 | #endif // BLUEZQT_JOB_H |
226 | |