summaryrefslogtreecommitdiffstats
path: root/src/corelib/thread/qrunnable.h
blob: f0dd0a582efc4114df15a668c4f3faec97433f1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QRUNNABLE_H
#define QRUNNABLE_H

#include <QtCore/qcompilerdetection.h>
#include <QtCore/qfunctionaltools_impl.h>
#include <QtCore/qtclasshelpermacros.h>
#include <QtCore/qtcoreexports.h>

#include <functional>
#include <type_traits>

QT_BEGIN_NAMESPACE

class Q_CORE_EXPORT QRunnable
{
    bool m_autoDelete = true;

    Q_DISABLE_COPY(QRunnable)
public:
    virtual void run() = 0;

    constexpr QRunnable() noexcept = default;
    virtual ~QRunnable();
#if QT_CORE_REMOVED_SINCE(6, 6)
    static QRunnable *create(std::function<void()> functionToRun);
#endif
    template <typename Callable>
    using if_callable = std::enable_if_t<std::is_invocable_r_v<void, Callable>, bool>;

    template <typename Callable, if_callable<Callable> = true>
    static QRunnable *create(Callable &&functionToRun);
    static QRunnable *create(std::nullptr_t) = delete;

    bool autoDelete() const { return m_autoDelete; }
    void setAutoDelete(bool autoDelete) { m_autoDelete = autoDelete; }

private:
    static Q_DECL_COLD_FUNCTION QRunnable *warnNullCallable();
    class QGenericRunnable;
};

class Q_CORE_EXPORT QRunnable::QGenericRunnable : public QRunnable
{
    // Type erasure, to only instantiate a non-virtual class per Callable:
    class HelperBase
    {
    protected:
        enum class Op {
            Run,
            Destroy,
        };
        using OpFn = void* (*)(Op, HelperBase *, void*);
        OpFn fn;
    protected:
        constexpr explicit HelperBase(OpFn f) noexcept : fn(f) {}
        ~HelperBase() = default;
    public:
        void run() { fn(Op::Run, this, nullptr); }
        void destroy() { fn(Op::Destroy, this, nullptr); }
    };

    template <typename Callable>
    class Helper : public HelperBase, private QtPrivate::CompactStorage<Callable>
    {
        using Storage = QtPrivate::CompactStorage<Callable>;
        static void *impl(Op op, HelperBase *that, [[maybe_unused]] void *arg)
        {
            const auto _this = static_cast<Helper*>(that);
            switch (op) {
            case Op::Run:     _this->object()(); break;
            case Op::Destroy: delete _this; break;
            }
            return nullptr;
        }
    public:
        template <typename UniCallable>
        explicit Helper(UniCallable &&functionToRun) noexcept
            : HelperBase(&impl),
              Storage{std::forward<UniCallable>(functionToRun)}
        {
        }
    };

    HelperBase *runHelper;
public:
    template <typename Callable, if_callable<Callable> = true>
    explicit QGenericRunnable(Callable &&c)
        : runHelper(new Helper<std::decay_t<Callable>>(std::forward<Callable>(c)))
    {
    }
    ~QGenericRunnable() override;

    void run() override;
};

namespace QtPrivate {

template <typename T>
constexpr inline bool is_function_pointer_v = std::conjunction_v<
        std::is_pointer<T>,
        std::is_function<std::remove_pointer_t<T>>
    >;
template <typename T>
constexpr inline bool is_std_function_v = false;
template <typename T>
constexpr inline bool is_std_function_v<std::function<T>> = true;

} // namespace QtPrivate

template <typename Callable, QRunnable::if_callable<Callable>>
QRunnable *QRunnable::create(Callable &&functionToRun)
{
    using F = std::decay_t<Callable>;
    constexpr bool is_std_function = QtPrivate::is_std_function_v<F>;
    constexpr bool is_function_pointer = QtPrivate::is_function_pointer_v<F>;
    if constexpr (is_std_function || is_function_pointer) {
        bool is_null;
        if constexpr (is_std_function) {
            is_null = !functionToRun;
        } else if constexpr (is_function_pointer) {
            // shut up warnings about functions always having a non-null address:
            const void *functionPtr = reinterpret_cast<void *>(functionToRun);
            is_null = !functionPtr;
        }
        if (is_null)
            return warnNullCallable();
    }

    return new QGenericRunnable(std::forward<Callable>(functionToRun));
}

QT_END_NAMESPACE

#endif