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