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