Line data Source code
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 : #ifdef BUILDING_QGPGME
45 : # include "context.h"
46 : # include "interfaces/progressprovider.h"
47 : #else
48 : # include <gpgme++/context.h>
49 : # include <gpgme++/interfaces/progressprovider.h>
50 : #endif
51 :
52 : #include "job.h"
53 :
54 : #include <cassert>
55 : #include <functional>
56 :
57 : namespace QGpgME
58 : {
59 : namespace _detail
60 : {
61 :
62 : QString audit_log_as_html(GpgME::Context *ctx, GpgME::Error &err);
63 :
64 : class PatternConverter
65 : {
66 : const QList<QByteArray> m_list;
67 : mutable const char **m_patterns;
68 : public:
69 : explicit PatternConverter(const QByteArray &ba);
70 : explicit PatternConverter(const QString &s);
71 : explicit PatternConverter(const QList<QByteArray> &lba);
72 : explicit PatternConverter(const QStringList &sl);
73 : ~PatternConverter();
74 :
75 : const char **patterns() const;
76 : };
77 :
78 : class ToThreadMover
79 : {
80 : QObject *const m_object;
81 : QThread *const m_thread;
82 : public:
83 : ToThreadMover(QObject *o, QThread *t) : m_object(o), m_thread(t) {}
84 : ToThreadMover(QObject &o, QThread *t) : m_object(&o), m_thread(t) {}
85 52 : ToThreadMover(const std::shared_ptr<QObject> &o, QThread *t) : m_object(o.get()), m_thread(t) {}
86 52 : ~ToThreadMover()
87 52 : {
88 52 : if (m_object && m_thread) {
89 2 : m_object->moveToThread(m_thread);
90 : }
91 52 : }
92 : };
93 :
94 : template <typename T_result>
95 46 : class Thread : public QThread
96 : {
97 : public:
98 46 : explicit Thread(QObject *parent = Q_NULLPTR) : QThread(parent) {}
99 :
100 7 : void setFunction(const std::function<T_result()> &function)
101 : {
102 14 : const QMutexLocker locker(&m_mutex);
103 7 : m_function = function;
104 7 : }
105 :
106 7 : T_result result() const
107 : {
108 14 : const QMutexLocker locker(&m_mutex);
109 14 : return m_result;
110 : }
111 :
112 : private:
113 7 : void run() Q_DECL_OVERRIDE {
114 14 : const QMutexLocker locker(&m_mutex);
115 7 : m_result = m_function();
116 7 : }
117 : private:
118 : mutable QMutex m_mutex;
119 : std::function<T_result()> m_function;
120 : T_result m_result;
121 : };
122 :
123 : template <typename T_base, typename T_result = std::tuple<GpgME::Error, QString, GpgME::Error> >
124 : class ThreadedJobMixin : public T_base, public GpgME::ProgressProvider
125 : {
126 : public:
127 : typedef ThreadedJobMixin<T_base, T_result> mixin_type;
128 : typedef T_result result_type;
129 :
130 : protected:
131 : static_assert(std::tuple_size<T_result>::value > 2,
132 : "Result tuple too small");
133 : static_assert(std::is_same <
134 : typename std::tuple_element <
135 : std::tuple_size<T_result>::value - 2,
136 : T_result
137 : >::type,
138 : QString
139 : >::value,
140 : "Second to last result type not a QString");
141 : static_assert(std::is_same <
142 : typename std::tuple_element <
143 : std::tuple_size<T_result>::value - 1,
144 : T_result
145 : >::type,
146 : GpgME::Error
147 : >::value,
148 : "Last result type not a GpgME::Error");
149 :
150 46 : explicit ThreadedJobMixin(GpgME::Context *ctx)
151 46 : : T_base(0), m_ctx(ctx), m_thread(), m_auditLog(), m_auditLogError()
152 : {
153 46 : }
154 :
155 46 : void lateInitialization()
156 : {
157 46 : assert(m_ctx);
158 46 : QObject::connect(&m_thread, &QThread::finished, this,
159 : &mixin_type::slotFinished);
160 46 : m_ctx->setProgressProvider(this);
161 46 : QGpgME::g_context_map.insert(this, m_ctx.get());
162 46 : }
163 :
164 46 : ~ThreadedJobMixin()
165 : {
166 46 : QGpgME::g_context_map.remove(this);
167 92 : }
168 :
169 : template <typename T_binder>
170 6 : void run(const T_binder &func)
171 : {
172 6 : m_thread.setFunction(std::bind(func, this->context()));
173 6 : m_thread.start();
174 6 : }
175 : template <typename T_binder>
176 0 : void run(const T_binder &func, const std::shared_ptr<QIODevice> &io)
177 : {
178 0 : if (io) {
179 0 : io->moveToThread(&m_thread);
180 : }
181 : // the arguments passed here to the functor are stored in a QThread, and are not
182 : // necessarily destroyed (living outside the UI thread) at the time the result signal
183 : // is emitted and the signal receiver wants to clean up IO devices.
184 : // To avoid such races, we pass std::weak_ptr's to the functor.
185 0 : m_thread.setFunction(std::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io)));
186 0 : m_thread.start();
187 0 : }
188 : template <typename T_binder>
189 1 : void run(const T_binder &func, const std::shared_ptr<QIODevice> &io1, const std::shared_ptr<QIODevice> &io2)
190 : {
191 1 : if (io1) {
192 1 : io1->moveToThread(&m_thread);
193 : }
194 1 : if (io2) {
195 1 : io2->moveToThread(&m_thread);
196 : }
197 : // the arguments passed here to the functor are stored in a QThread, and are not
198 : // necessarily destroyed (living outside the UI thread) at the time the result signal
199 : // is emitted and the signal receiver wants to clean up IO devices.
200 : // To avoid such races, we pass std::weak_ptr's to the functor.
201 1 : m_thread.setFunction(std::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io1), std::weak_ptr<QIODevice>(io2)));
202 1 : m_thread.start();
203 1 : }
204 53 : GpgME::Context *context() const
205 : {
206 53 : return m_ctx.get();
207 : }
208 :
209 2 : virtual void resultHook(const result_type &) {}
210 :
211 7 : void slotFinished()
212 : {
213 14 : const T_result r = m_thread.result();
214 7 : m_auditLog = std::get < std::tuple_size<T_result>::value - 2 > (r);
215 7 : m_auditLogError = std::get < std::tuple_size<T_result>::value - 1 > (r);
216 7 : resultHook(r);
217 7 : Q_EMIT this->done();
218 7 : doEmitResult(r);
219 7 : this->deleteLater();
220 7 : }
221 0 : void slotCancel() Q_DECL_OVERRIDE {
222 0 : if (m_ctx)
223 : {
224 0 : m_ctx->cancelPendingOperation();
225 : }
226 0 : }
227 0 : QString auditLogAsHtml() const Q_DECL_OVERRIDE
228 : {
229 0 : return m_auditLog;
230 : }
231 0 : GpgME::Error auditLogError() const Q_DECL_OVERRIDE
232 : {
233 0 : return m_auditLogError;
234 : }
235 26 : void showProgress(const char * /*what*/,
236 : int /*type*/, int current, int total) Q_DECL_OVERRIDE {
237 : // will be called from the thread exec'ing the operation, so
238 : // just bounce everything to the owning thread:
239 : // ### hope this is thread-safe (meta obj is const, and
240 : // ### portEvent is thread-safe, so should be ok)
241 26 : QMetaObject::invokeMethod(this, "progress", Qt::QueuedConnection,
242 : // TODO port
243 : Q_ARG(QString, QString()),
244 : Q_ARG(int, current),
245 : Q_ARG(int, total));
246 26 : }
247 : private:
248 : template <typename T1, typename T2>
249 : void doEmitResult(const std::tuple<T1, T2> &tuple)
250 : {
251 : Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple));
252 : }
253 :
254 : template <typename T1, typename T2, typename T3>
255 4 : void doEmitResult(const std::tuple<T1, T2, T3> &tuple)
256 : {
257 4 : Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple));
258 4 : }
259 :
260 : template <typename T1, typename T2, typename T3, typename T4>
261 3 : void doEmitResult(const std::tuple<T1, T2, T3, T4> &tuple)
262 : {
263 3 : Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple));
264 3 : }
265 :
266 : template <typename T1, typename T2, typename T3, typename T4, typename T5>
267 0 : void doEmitResult(const std::tuple<T1, T2, T3, T4, T5> &tuple)
268 : {
269 0 : Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple));
270 0 : }
271 :
272 : private:
273 : std::shared_ptr<GpgME::Context> m_ctx;
274 : Thread<T_result> m_thread;
275 : QString m_auditLog;
276 : GpgME::Error m_auditLogError;
277 : };
278 :
279 : }
280 : }
281 :
282 : #endif /* __QGPGME_THREADEDJOBMIXING_H__ */
|