Blame lang/qt/src/threadedjobmixin.h

Packit d7e8d0
/*
Packit d7e8d0
    threadedjobmixin.h
Packit d7e8d0
Packit d7e8d0
    This file is part of qgpgme, the Qt API binding for gpgme
Packit d7e8d0
    Copyright (c) 2008 Klarälvdalens Datakonsult AB
Packit d7e8d0
    Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik
Packit d7e8d0
    Software engineering by Intevation GmbH
Packit d7e8d0
Packit d7e8d0
    QGpgME is free software; you can redistribute it and/or
Packit d7e8d0
    modify it under the terms of the GNU General Public License as
Packit d7e8d0
    published by the Free Software Foundation; either version 2 of the
Packit d7e8d0
    License, or (at your option) any later version.
Packit d7e8d0
Packit d7e8d0
    QGpgME is distributed in the hope that it will be useful,
Packit d7e8d0
    but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit d7e8d0
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit d7e8d0
    General Public License for more details.
Packit d7e8d0
Packit d7e8d0
    You should have received a copy of the GNU General Public License
Packit d7e8d0
    along with this program; if not, write to the Free Software
Packit d7e8d0
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit d7e8d0
Packit d7e8d0
    In addition, as a special exception, the copyright holders give
Packit d7e8d0
    permission to link the code of this program with any edition of
Packit d7e8d0
    the Qt library by Trolltech AS, Norway (or with modified versions
Packit d7e8d0
    of Qt that use the same license as Qt), and distribute linked
Packit d7e8d0
    combinations including the two.  You must obey the GNU General
Packit d7e8d0
    Public License in all respects for all of the code used other than
Packit d7e8d0
    Qt.  If you modify this file, you may extend this exception to
Packit d7e8d0
    your version of the file, but you are not obligated to do so.  If
Packit d7e8d0
    you do not wish to do so, delete this exception statement from
Packit d7e8d0
    your version.
Packit d7e8d0
*/
Packit d7e8d0
Packit d7e8d0
#ifndef __QGPGME_THREADEDJOBMIXING_H__
Packit d7e8d0
#define __QGPGME_THREADEDJOBMIXING_H__
Packit d7e8d0
Packit d7e8d0
#include <QMutex>
Packit d7e8d0
#include <QMutexLocker>
Packit d7e8d0
#include <QThread>
Packit d7e8d0
#include <QString>
Packit d7e8d0
#include <QIODevice>
Packit d7e8d0
Packit d7e8d0
#ifdef BUILDING_QGPGME
Packit d7e8d0
# include "context.h"
Packit d7e8d0
# include "interfaces/progressprovider.h"
Packit d7e8d0
#else
Packit d7e8d0
# include <gpgme++/context.h>
Packit d7e8d0
# include <gpgme++/interfaces/progressprovider.h>
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include "job.h"
Packit d7e8d0
Packit d7e8d0
#include <cassert>
Packit d7e8d0
#include <functional>
Packit d7e8d0
Packit d7e8d0
namespace QGpgME
Packit d7e8d0
{
Packit d7e8d0
namespace _detail
Packit d7e8d0
{
Packit d7e8d0
Packit d7e8d0
QString audit_log_as_html(GpgME::Context *ctx, GpgME::Error &err;;
Packit d7e8d0
Packit d7e8d0
class PatternConverter
Packit d7e8d0
{
Packit d7e8d0
    const QList<QByteArray> m_list;
Packit d7e8d0
    mutable const char **m_patterns;
Packit d7e8d0
public:
Packit d7e8d0
    explicit PatternConverter(const QByteArray &ba);
Packit d7e8d0
    explicit PatternConverter(const QString &s);
Packit d7e8d0
    explicit PatternConverter(const QList<QByteArray> &lba);
Packit d7e8d0
    explicit PatternConverter(const QStringList &sl);
Packit d7e8d0
    ~PatternConverter();
Packit d7e8d0
Packit d7e8d0
    const char **patterns() const;
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
class ToThreadMover
Packit d7e8d0
{
Packit d7e8d0
    QObject *const m_object;
Packit d7e8d0
    QThread *const m_thread;
Packit d7e8d0
public:
Packit d7e8d0
    ToThreadMover(QObject *o, QThread *t) : m_object(o), m_thread(t) {}
Packit d7e8d0
    ToThreadMover(QObject &o, QThread *t) : m_object(&o), m_thread(t) {}
Packit d7e8d0
    ToThreadMover(const std::shared_ptr<QObject> &o, QThread *t) : m_object(o.get()), m_thread(t) {}
Packit d7e8d0
    ~ToThreadMover()
Packit d7e8d0
    {
Packit d7e8d0
        if (m_object && m_thread) {
Packit d7e8d0
            m_object->moveToThread(m_thread);
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
template <typename T_result>
Packit d7e8d0
class Thread : public QThread
Packit d7e8d0
{
Packit d7e8d0
public:
Packit d7e8d0
    explicit Thread(QObject *parent = Q_NULLPTR) : QThread(parent) {}
Packit d7e8d0
Packit d7e8d0
    void setFunction(const std::function<T_result()> &function)
Packit d7e8d0
    {
Packit d7e8d0
        const QMutexLocker locker(&m_mutex);
Packit d7e8d0
        m_function = function;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    T_result result() const
Packit d7e8d0
    {
Packit d7e8d0
        const QMutexLocker locker(&m_mutex);
Packit d7e8d0
        return m_result;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
private:
Packit d7e8d0
    void run() Q_DECL_OVERRIDE {
Packit d7e8d0
        const QMutexLocker locker(&m_mutex);
Packit d7e8d0
        m_result = m_function();
Packit d7e8d0
    }
Packit d7e8d0
private:
Packit d7e8d0
    mutable QMutex m_mutex;
Packit d7e8d0
    std::function<T_result()> m_function;
Packit d7e8d0
    T_result m_result;
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
template <typename T_base, typename T_result = std::tuple<GpgME::Error, QString, GpgME::Error> >
Packit d7e8d0
class ThreadedJobMixin : public T_base, public GpgME::ProgressProvider
Packit d7e8d0
{
Packit d7e8d0
public:
Packit d7e8d0
    typedef ThreadedJobMixin<T_base, T_result> mixin_type;
Packit d7e8d0
    typedef T_result result_type;
Packit d7e8d0
Packit d7e8d0
protected:
Packit d7e8d0
    static_assert(std::tuple_size<T_result>::value > 2,
Packit d7e8d0
                  "Result tuple too small");
Packit d7e8d0
    static_assert(std::is_same <
Packit d7e8d0
                  typename std::tuple_element <
Packit d7e8d0
                  std::tuple_size<T_result>::value - 2,
Packit d7e8d0
                  T_result
Packit d7e8d0
                  >::type,
Packit d7e8d0
                  QString
Packit d7e8d0
                  >::value,
Packit d7e8d0
                  "Second to last result type not a QString");
Packit d7e8d0
    static_assert(std::is_same <
Packit d7e8d0
                  typename std::tuple_element <
Packit d7e8d0
                  std::tuple_size<T_result>::value - 1,
Packit d7e8d0
                  T_result
Packit d7e8d0
                  >::type,
Packit d7e8d0
                  GpgME::Error
Packit d7e8d0
                  >::value,
Packit d7e8d0
                  "Last result type not a GpgME::Error");
Packit d7e8d0
Packit d7e8d0
    explicit ThreadedJobMixin(GpgME::Context *ctx)
Packit Service 30b792
        : T_base(nullptr), m_ctx(ctx), m_thread(), m_auditLog(), m_auditLogError()
Packit d7e8d0
    {
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    void lateInitialization()
Packit d7e8d0
    {
Packit d7e8d0
        assert(m_ctx);
Packit d7e8d0
        QObject::connect(&m_thread, &QThread::finished, this,
Packit d7e8d0
                         &mixin_type::slotFinished);
Packit d7e8d0
        m_ctx->setProgressProvider(this);
Packit d7e8d0
        QGpgME::g_context_map.insert(this, m_ctx.get());
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    ~ThreadedJobMixin()
Packit d7e8d0
    {
Packit d7e8d0
        QGpgME::g_context_map.remove(this);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    template <typename T_binder>
Packit d7e8d0
    void run(const T_binder &func)
Packit d7e8d0
    {
Packit d7e8d0
        m_thread.setFunction(std::bind(func, this->context()));
Packit d7e8d0
        m_thread.start();
Packit d7e8d0
    }
Packit d7e8d0
    template <typename T_binder>
Packit d7e8d0
    void run(const T_binder &func, const std::shared_ptr<QIODevice> &io)
Packit d7e8d0
    {
Packit d7e8d0
        if (io) {
Packit d7e8d0
            io->moveToThread(&m_thread);
Packit d7e8d0
        }
Packit d7e8d0
        // the arguments passed here to the functor are stored in a QThread, and are not
Packit d7e8d0
        // necessarily destroyed (living outside the UI thread) at the time the result signal
Packit d7e8d0
        // is emitted and the signal receiver wants to clean up IO devices.
Packit d7e8d0
        // To avoid such races, we pass std::weak_ptr's to the functor.
Packit d7e8d0
        m_thread.setFunction(std::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io)));
Packit d7e8d0
        m_thread.start();
Packit d7e8d0
    }
Packit d7e8d0
    template <typename T_binder>
Packit d7e8d0
    void run(const T_binder &func, const std::shared_ptr<QIODevice> &io1, const std::shared_ptr<QIODevice> &io2)
Packit d7e8d0
    {
Packit d7e8d0
        if (io1) {
Packit d7e8d0
            io1->moveToThread(&m_thread);
Packit d7e8d0
        }
Packit d7e8d0
        if (io2) {
Packit d7e8d0
            io2->moveToThread(&m_thread);
Packit d7e8d0
        }
Packit d7e8d0
        // the arguments passed here to the functor are stored in a QThread, and are not
Packit d7e8d0
        // necessarily destroyed (living outside the UI thread) at the time the result signal
Packit d7e8d0
        // is emitted and the signal receiver wants to clean up IO devices.
Packit d7e8d0
        // To avoid such races, we pass std::weak_ptr's to the functor.
Packit d7e8d0
        m_thread.setFunction(std::bind(func, this->context(), this->thread(), std::weak_ptr<QIODevice>(io1), std::weak_ptr<QIODevice>(io2)));
Packit d7e8d0
        m_thread.start();
Packit d7e8d0
    }
Packit d7e8d0
    GpgME::Context *context() const
Packit d7e8d0
    {
Packit d7e8d0
        return m_ctx.get();
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    virtual void resultHook(const result_type &) {}
Packit d7e8d0
Packit d7e8d0
    void slotFinished()
Packit d7e8d0
    {
Packit d7e8d0
        const T_result r = m_thread.result();
Packit d7e8d0
        m_auditLog = std::get < std::tuple_size<T_result>::value - 2 > (r);
Packit d7e8d0
        m_auditLogError = std::get < std::tuple_size<T_result>::value - 1 > (r);
Packit d7e8d0
        resultHook(r);
Packit d7e8d0
        Q_EMIT this->done();
Packit d7e8d0
        doEmitResult(r);
Packit d7e8d0
        this->deleteLater();
Packit d7e8d0
    }
Packit d7e8d0
    void slotCancel() Q_DECL_OVERRIDE {
Packit d7e8d0
        if (m_ctx)
Packit d7e8d0
        {
Packit d7e8d0
            m_ctx->cancelPendingOperation();
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
    QString auditLogAsHtml() const Q_DECL_OVERRIDE
Packit d7e8d0
    {
Packit d7e8d0
        return m_auditLog;
Packit d7e8d0
    }
Packit d7e8d0
    GpgME::Error auditLogError() const Q_DECL_OVERRIDE
Packit d7e8d0
    {
Packit d7e8d0
        return m_auditLogError;
Packit d7e8d0
    }
Packit d7e8d0
    void showProgress(const char * /*what*/,
Packit d7e8d0
                      int /*type*/, int current, int total) Q_DECL_OVERRIDE {
Packit d7e8d0
        // will be called from the thread exec'ing the operation, so
Packit d7e8d0
        // just bounce everything to the owning thread:
Packit d7e8d0
        // ### hope this is thread-safe (meta obj is const, and
Packit d7e8d0
        // ### portEvent is thread-safe, so should be ok)
Packit d7e8d0
        QMetaObject::invokeMethod(this, "progress", Qt::QueuedConnection,
Packit d7e8d0
        // TODO port
Packit d7e8d0
        Q_ARG(QString, QString()),
Packit d7e8d0
        Q_ARG(int, current),
Packit d7e8d0
        Q_ARG(int, total));
Packit d7e8d0
    }
Packit d7e8d0
private:
Packit d7e8d0
    template <typename T1, typename T2>
Packit d7e8d0
    void doEmitResult(const std::tuple<T1, T2> &tuple)
Packit d7e8d0
    {
Packit d7e8d0
        Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple));
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    template <typename T1, typename T2, typename T3>
Packit d7e8d0
    void doEmitResult(const std::tuple<T1, T2, T3> &tuple)
Packit d7e8d0
    {
Packit d7e8d0
        Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple));
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    template <typename T1, typename T2, typename T3, typename T4>
Packit d7e8d0
    void doEmitResult(const std::tuple<T1, T2, T3, T4> &tuple)
Packit d7e8d0
    {
Packit d7e8d0
        Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple));
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
    template <typename T1, typename T2, typename T3, typename T4, typename T5>
Packit d7e8d0
    void doEmitResult(const std::tuple<T1, T2, T3, T4, T5> &tuple)
Packit d7e8d0
    {
Packit d7e8d0
        Q_EMIT this->result(std::get<0>(tuple), std::get<1>(tuple), std::get<2>(tuple), std::get<3>(tuple), std::get<4>(tuple));
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
private:
Packit d7e8d0
    std::shared_ptr<GpgME::Context> m_ctx;
Packit d7e8d0
    Thread<T_result> m_thread;
Packit d7e8d0
    QString m_auditLog;
Packit d7e8d0
    GpgME::Error m_auditLogError;
Packit d7e8d0
};
Packit d7e8d0
Packit d7e8d0
}
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
#endif /* __QGPGME_THREADEDJOBMIXING_H__ */