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