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