QGpgME 2.0.0-unknown
Qt API for GpgME
Loading...
Searching...
No Matches
threadedjobmixin.h
1/*
2 threadedjobmixin.h
3
4 This file is part of qgpgme, the Qt API binding for gpgme
5 Copyright (c) 2008 Klarälvdalens Datakonsult AB
6 Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik
7 Software engineering by Intevation GmbH
8
9 QGpgME is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version.
13
14 QGpgME is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23 In addition, as a special exception, the copyright holders give
24 permission to link the code of this program with any edition of
25 the Qt library by Trolltech AS, Norway (or with modified versions
26 of Qt that use the same license as Qt), and distribute linked
27 combinations including the two. You must obey the GNU General
28 Public License in all respects for all of the code used other than
29 Qt. If you modify this file, you may extend this exception to
30 your version of the file, but you are not obligated to do so. If
31 you do not wish to do so, delete this exception statement from
32 your version.
33*/
34
35#ifndef __QGPGME_THREADEDJOBMIXING_H__
36#define __QGPGME_THREADEDJOBMIXING_H__
37
38#include <QMutex>
39#include <QMutexLocker>
40#include <QThread>
41#include <QString>
42#include <QIODevice>
43
44#include <gpgme++/context.h>
45#include <gpgme++/interfaces/progressprovider.h>
46
47#include "job.h"
48
49#include <cassert>
50#include <functional>
51
52namespace QGpgME
53{
54namespace _detail
55{
56
57QString audit_log_as_html(GpgME::Context *ctx, GpgME::Error &err);
58
59class PatternConverter
60{
61 const QList<QByteArray> m_list;
62 mutable const char **m_patterns;
63public:
64 explicit PatternConverter(const QByteArray &ba);
65 explicit PatternConverter(const QString &s);
66 explicit PatternConverter(const QList<QByteArray> &lba);
67 explicit PatternConverter(const QStringList &sl);
68 ~PatternConverter();
69
70 const char **patterns() const;
71};
72
73class ToThreadMover
74{
75 QObject *const m_object;
76 QThread *const m_thread;
77public:
78 ToThreadMover(QObject *o, QThread *t) : m_object(o), m_thread(t) {}
79 ToThreadMover(QObject &o, QThread *t) : m_object(&o), m_thread(t) {}
80 ToThreadMover(const std::shared_ptr<QObject> &o, QThread *t) : m_object(o.get()), m_thread(t) {}
81 ~ToThreadMover()
82 {
83 if (m_object && m_thread) {
84 m_object->moveToThread(m_thread);
85 }
86 }
87};
88
89template <typename T_result>
90class Thread : public QThread
91{
92public:
93 explicit Thread(QObject *parent = nullptr) : QThread(parent) {}
94
95 void setFunction(const std::function<T_result()> &function)
96 {
97 const QMutexLocker locker(&m_mutex);
98 m_function = function;
99 }
100
101 bool hasFunction()
102 {
103 const QMutexLocker locker(&m_mutex);
104 return static_cast<bool>(m_function);
105 }
106
107 T_result result() const
108 {
109 const QMutexLocker locker(&m_mutex);
110 return m_result;
111 }
112
113private:
114 void run() override {
115 const QMutexLocker locker(&m_mutex);
116 m_result = m_function();
117 }
118private:
119 mutable QMutex m_mutex;
120 std::function<T_result()> m_function;
121 T_result m_result;
122};
123
124template <typename T_base, typename T_private = void, typename T_result = std::tuple<GpgME::Error, QString, GpgME::Error>>
125class ThreadedJobMixin : public T_base, public GpgME::ProgressProvider
126{
127public:
128 typedef ThreadedJobMixin<T_base, T_private, T_result> mixin_type;
129 typedef T_result result_type;
130
131 void run()
132 {
133 Q_ASSERT(m_thread.hasFunction() && "Call setWorkerFunction() before run()");
134 m_thread.start();
135 }
136
137protected:
138 static_assert(std::tuple_size<T_result>::value > 2,
139 "Result tuple too small");
140 static_assert(std::is_same <
141 typename std::tuple_element <
142 std::tuple_size<T_result>::value - 2,
143 T_result
144 >::type,
145 QString
146 >::value,
147 "Second to last result type not a QString");
148 static_assert(std::is_same <
149 typename std::tuple_element <
150 std::tuple_size<T_result>::value - 1,
151 T_result
152 >::type,
153 GpgME::Error
154 >::value,
155 "Last result type not a GpgME::Error");
156
157 // Constructor used if a private class is specified (i.e. T_private is not void)
158 template<typename T_private_ = T_private,
159 std::enable_if_t<!std::is_void_v<T_private_>, bool> = true>
160 explicit ThreadedJobMixin(GpgME::Context *ctx)
161 : T_base(std::make_unique<T_private>(), nullptr), m_ctx(ctx), m_thread(), m_auditLog(), m_auditLogError()
162 {
163 }
164
165 // Constructor used if no private class is specified (i.e. T_private is void)
166 template<typename T_private_ = T_private,
167 std::enable_if_t<std::is_void_v<T_private_>, bool> = true>
168 explicit ThreadedJobMixin(GpgME::Context *ctx)
169 : T_base(nullptr), m_ctx(ctx), m_thread(), m_auditLog(), m_auditLogError()
170 {
171 }
172
173 void lateInitialization()
174 {
175 assert(m_ctx);
176 QObject::connect(&m_thread, &QThread::finished, this,
177 &mixin_type::slotFinished);
178 m_ctx->setProgressProvider(this);
179 QGpgME::g_context_map.insert(this, m_ctx.get());
180 }
181
182 ~ThreadedJobMixin()
183 {
184 QGpgME::g_context_map.remove(this);
185 }
186
187 template <typename T_binder>
188 void setWorkerFunction(const T_binder &func)
189 {
190 m_thread.setFunction([this, func]() { return func(this->context()); });
191 }
192
193public:
194 template <typename T_binder>
195 void run(const T_binder &func)
196 {
197 m_thread.setFunction(std::bind(func, this->context()));
198 m_thread.start();
199 }
200 template <typename T_binder>
201 void run(const T_binder &func, const std::shared_ptr<QIODevice> &io)
202 {
203 if (io) {
204 io->moveToThread(&m_thread);
205 }
206 // the arguments passed here to the functor are stored in a QThread, and are not
207 // necessarily destroyed (living outside the UI thread) at the time the result signal
208 // is emitted and the signal receiver wants to clean up IO devices.
209 // To avoid such races, we pass std::weak_ptr's to the functor.
210 m_thread.setFunction(std::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io)));
211 m_thread.start();
212 }
213 template <typename T_binder>
214 void run(const T_binder &func, const std::shared_ptr<QIODevice> &io1, const std::shared_ptr<QIODevice> &io2)
215 {
216 if (io1) {
217 io1->moveToThread(&m_thread);
218 }
219 if (io2) {
220 io2->moveToThread(&m_thread);
221 }
222 // the arguments passed here to the functor are stored in a QThread, and are not
223 // necessarily destroyed (living outside the UI thread) at the time the result signal
224 // is emitted and the signal receiver wants to clean up IO devices.
225 // To avoid such races, we pass std::weak_ptr's to the functor.
226 m_thread.setFunction(std::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io1), std::weak_ptr<QIODevice>(io2)));
227 m_thread.start();
228 }
229
230protected:
231 GpgME::Context *context() const
232 {
233 return m_ctx.get();
234 }
235
236 virtual void resultHook(const result_type &) {}
237
238 void slotFinished()
239 {
240 const T_result r = m_thread.result();
241 m_auditLog = std::get < std::tuple_size<T_result>::value - 2 > (r);
242 m_auditLogError = std::get < std::tuple_size<T_result>::value - 1 > (r);
243 resultHook(r);
244 Q_EMIT this->done();
245 doEmitResult(r);
246 this->deleteLater();
247 }
248 void slotCancel() override {
249 if (m_ctx)
250 {
251 m_ctx->cancelPendingOperation();
252 }
253 }
254 QString auditLogAsHtml() const override
255 {
256 return m_auditLog;
257 }
258 GpgME::Error auditLogError() const override
259 {
260 return m_auditLogError;
261 }
262 void showProgress(const char *what,
263 int type, int current, int total) override {
264 QMetaObject::invokeMethod(this, [this, current, total]() {
265 Q_EMIT this->jobProgress(current, total);
266 }, Qt::QueuedConnection);
267 const QString what_ = QString::fromUtf8(what);
268 QMetaObject::invokeMethod(this, [this, what_, type, current, total]() {
269 Q_EMIT this->rawProgress(what_, type, current, total);
270 }, Qt::QueuedConnection);
271 QMetaObject::invokeMethod(this, [this, what_, current, total]() {
272 QT_WARNING_PUSH
273 QT_WARNING_DISABLE_DEPRECATED
274 Q_EMIT this->progress(what_, current, total);
275 QT_WARNING_POP
276 }, Qt::QueuedConnection);
277 }
278private:
279 template <typename T1, typename T2>
280 void doEmitResult(const std::tuple<T1, T2> &tuple)
281 {
282 Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple));
283 }
284
285 template <typename T1, typename T2, typename T3>
286 void doEmitResult(const std::tuple<T1, T2, T3> &tuple)
287 {
288 Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple));
289 }
290
291 template <typename T1, typename T2, typename T3, typename T4>
292 void doEmitResult(const std::tuple<T1, T2, T3, T4> &tuple)
293 {
294 Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple));
295 }
296
297 template <typename T1, typename T2, typename T3, typename T4, typename T5>
298 void doEmitResult(const std::tuple<T1, T2, T3, T4, T5> &tuple)
299 {
300 Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple));
301 }
302
303private:
304 std::shared_ptr<GpgME::Context> m_ctx;
305 Thread<T_result> m_thread;
306 QString m_auditLog;
307 GpgME::Error m_auditLogError;
308};
309
310}
311}
312
313#endif /* __QGPGME_THREADEDJOBMIXING_H__ */
Definition threadedjobmixin.h:91