Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/VLC/modules/gui/qt/util/   (VLC Media Plyer ©)  Datei vom 23.9.2026 mit Größe 7 kB image not shown  

Quelle  asynctask.hpp   Sprache: C

 

/*****************************************************************************
 * Copyright (C) 2020 VLC authors and VideoLAN
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * ( at your option ) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/


#ifndef VLC_ASYNC_TASK_HPP
#define VLC_ASYNC_TASK_HPP

#include <atomic>
#include <cassert>
#include <memory>
#include <QMetaObject>
#include <QObject>
#include <QRunnable>
#include <QThreadPool>

/**
 * This class helps executing a processing on a `QThreadPool` and retrieve the
 * result on the main thread.
 *
 * It allows to properly handle the case where the receiver of the result is
 * destroyed while a processing is still running, without blocking (provided
 * that the `QThreadPool` is not destroyed).
 *
 * Here is how to use it properly.
 *
 * First, write a class inheriting `AsyncTask<T>` (`T` being the type of the
 * result), and implement the `execute()` method:
 *
 * \code{.cpp}
 *     struct MyTask : public AsyncTask<int>
 *     {
 *         int m_num;
 *
 *         MyTask(int num) : m_num(num) {}
 *
 *         int execute() override
 *         {
 *             // This will be executed from a separate thread
 *             sleep(1); // long-running task
 *             return m_num * 2;
 *         }
 *     };
 * \endcode
 *
 * To execute a new task asynchronously, instantiate it, connect the result
 * signal and start it:
 *
 * \code{.cpp}
 *     // To be called from the main thread
 *     void SomeQObject::asyncLoad(int num)
 *     {
 *         // Create a task and keep a reference in a field
 *         m_task = new MyTask(num);
 *
 *         // Connect the signal to be notified of the result
 *         connect(m_task, &BaseAsyncTask::result,
 *                 this, &SomeQObject::onResult);
 *
 *         // Submit the task to some thread pool
 *         m_start->start(m_threadPool);
 *     }
 * \endcode
 *
 * The task might be canceled at any time (it might continue running
 * asynchronously if it is already running, but the caller will not receive the
 * result):
 *
 * \code{.cpp}
 *     // To be called from the main thread
 *     void SomeQObject::cancelCurrentTask()
 *     {
 *         if (m_task)
 *         {
 *             m_task->abandon();
 *             m_task = nullptr;
 *         }
 *     }
 * \endcode
 *
 * The result slot must retrieve the result and _abandon_ the task:
 *
 * \code{.cpp}
 *     // Called on the main thread
 *     void SomeQObject::onResult()
 *     {
 *         // Retrieve the task instance from the slot, if necessary
 *         MyTask *task = static_cast<MyTask *>(sender());
 *
 *         // In basic cases (one task at a time), it should be the same:
 *         assert(task == m_task);
 *
 *         // Retrieve the result (can only be called once)
 *         int result = task->takeResult();
 *
 *         qDebug() << "The result of MyTask(" << task->m_num << ") is"
 *                  << result;
 *
 *         // Abandon the task (so that it can be destroyed)
 *         task->abandon();
 *         m_task = nullptr;
 *     }
 * \endcode
 *
 * The `abandon()` method allows to handle both cancelation and memory
 * management. A task is semantically owned by 2 components:
 *  - the caller
 *  - the QThreadPool
 *
 * Indeed, the caller must be able to _cancel_ it without race conditions, even
 * if the task run() has completed. Conversely, the task must not be destroyed
 * unilaterally by the caller, to avoid crashing if it is currently running.
 *
 * The running state is tracked internally, and `abandon()` allows the caller
 * to declare that it won't use it anymore (and don't want the result). When
 * both conditions are met, the task is deleted via `QObject::deleteLater()`.
 *
 * For convenience, a `TaskHandle` smart pointer is provided to call
 * `abandon()` using RAII.
 *
 * The task result is provided via a separate method `takeResult()` instead of
 * a slot parameter, because otherwise, Qt would require its type to be:
 *  - non-template (like `std::vector<T>`),
 *  - registered to the Qt type system (wrapped into a `QVariant`),
 *  - copiable (not movable-only, like `std::unique_ptr<T>`).
 */


class BaseAsyncTask : public QObject
{
    Q_OBJECT

signals:
    void result();
};

template <typename T>
class AsyncTask : public BaseAsyncTask
{
public:
    virtual ~AsyncTask() = default;

    virtual T execute() = 0;

    /**
     * Start the task on the thread pool
     */

    void start(QThreadPool &threadPool, int priority = 0)
    {
        m_threadPool = &threadPool;
        m_runnable = std::make_unique<Runnable>(this);
        threadPool.start(&*m_runnable, priority);
    }

    /**
     * Abandon the task (cancel and request deletion when possible)
     *
     * This method must be called from the UI thread.
     *
     * After this call, the task must not be used and the `result()` signal will
     * never be triggered.
     */

    void abandon()
    {
        assert(m_runnable);
        assert(m_threadPool);

        bool removed = m_threadPool->tryTake(&*m_runnable);
        if (removed)
        {
            /* run() will never be called */
            deleteLater();
            return;
        }

        m_abandoned = true;
        if (m_completed)
            deleteLater();

        /* Else it will be deleted from the run() function */
    }

    /**
     * Retrieve the result (from the UI thread)
     *
     * This method consumes the result, and must be called exactly once (from
     * the UI thread, in the slot connected to the signal `result()`).
     */

    T takeResult()
    {
        return std::move(m_result);
    }

private:
    class Runnable;
    std::unique_ptr<Runnable> m_runnable;

    QThreadPool *m_threadPool = nullptr;

    /* Only read and written from the main thread */
    bool m_abandoned = false;
    bool m_completed = false;

    T m_result;
};

template <typename T>
class AsyncTask<T>::Runnable : public QRunnable
{
public:
    Runnable(AsyncTask<T> *task) : m_task(task)
    {
        /* The runnable will be owned by the AsyncTask, which needs to keep a
         * reference to be able to cancel it using QThreadPool::tryTake(). */

        setAutoDelete(false);
    }

    void run() override
    {
        m_task->m_result = m_task->execute();

        QMetaObject::invokeMethod(m_task, [task = m_task]
        {
            /* Check on the main thread to avoid a call to abandon() between the
             * call to invokeLater() and the actual execution of the callback */

            task->m_completed = true;
            if (task->m_abandoned)
                task->deleteLater();
            else
                emit task->result();
        });
    }

private:
    friend class AsyncTask<T>;
    AsyncTask<T> *m_task;
};

template <typename T>
struct TaskDeleter
{
    void operator()(T *task) { task->abandon(); }
};

/**
 * Task handle to abandon() a task using RAII
 *
 * It behaves like a unique_ptr, but its deleter calls AsyncTask<T>::abandon(),
 * which indicates that the client cancels the task and requests deletion when
 * possible.
 *
 * The actual deletion may happen later (when the runnable also finished its
 * work).
 */

template <typename T>
using TaskHandle = std::unique_ptr<T, TaskDeleter<T>>;

#endif

Messung
CHZG
861009693

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.