summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/core/SkMessageBus.h
blob: 3b78793da688d827ab03d7928be26cb0f2622fb4 (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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkMessageBus_DEFINED
#define SkMessageBus_DEFINED

#include <type_traits>

#include "include/core/SkRefCnt.h"
#include "include/core/SkTypes.h"
#include "include/private/base/SkMutex.h"
#include "include/private/base/SkNoncopyable.h"
#include "include/private/base/SkOnce.h"
#include "include/private/base/SkTArray.h"
#include "include/private/base/SkTDArray.h"

/**
 * The following method must have a specialization for type 'Message':
 *
 *     bool SkShouldPostMessageToBus(const Message&, IDType msgBusUniqueID)
 *
 * We may want to consider providing a default template implementation, to avoid this requirement by
 * sending to all inboxes when the specialization for type 'Message' is not present.
 */
template <typename Message, typename IDType, bool AllowCopyableMessage = true>
class SkMessageBus : SkNoncopyable {
public:
    template <typename T> struct is_sk_sp : std::false_type {};
    template <typename T> struct is_sk_sp<sk_sp<T>> : std::true_type {};

    // We want to make sure the caller of Post() method will not keep a ref or copy of the message,
    // so the message type must be sk_sp or non copyable.
    static_assert(AllowCopyableMessage || is_sk_sp<Message>::value ||
                          !std::is_copy_constructible<Message>::value,
                  "The message type must be sk_sp or non copyable.");

    // Post a message to be received by Inboxes for this Message type. Checks
    // SkShouldPostMessageToBus() for each inbox. Threadsafe.
    static void Post(Message m);

    class Inbox {
    public:
        Inbox(IDType uniqueID);
        ~Inbox();

        IDType uniqueID() const { return fUniqueID; }

        // Overwrite out with all the messages we've received since the last call.  Threadsafe.
        void poll(skia_private::TArray<Message>* out);

    private:
        skia_private::TArray<Message> fMessages;
        SkMutex                       fMessagesMutex;
        const IDType                  fUniqueID;

        friend class SkMessageBus;
        void receive(Message m);  // SkMessageBus is a friend only to call this.
    };

private:
    SkMessageBus();
    static SkMessageBus* Get();

    SkTDArray<Inbox*> fInboxes;
    SkMutex           fInboxesMutex;
};

// This must go in a single .cpp file, not some .h, or we risk creating more than one global
// SkMessageBus per type when using shared libraries.  NOTE: at most one per file will compile.
#define DECLARE_SKMESSAGEBUS_MESSAGE(Message, IDType, AllowCopyableMessage)            \
    template <>                                                                        \
    SkMessageBus<Message, IDType, AllowCopyableMessage>*                               \
    SkMessageBus<Message, IDType, AllowCopyableMessage>::Get() {                       \
        static SkOnce once;                                                            \
        static SkMessageBus<Message, IDType, AllowCopyableMessage>* bus;               \
        once([] { bus = new SkMessageBus<Message, IDType, AllowCopyableMessage>(); }); \
        return bus;                                                                    \
    }

//   ----------------------- Implementation of SkMessageBus::Inbox -----------------------

template <typename Message, typename IDType, bool AllowCopyableMessage>
SkMessageBus<Message, IDType, AllowCopyableMessage>::Inbox::Inbox(IDType uniqueID)
        : fUniqueID(uniqueID) {
    // Register ourselves with the corresponding message bus.
    auto* bus = SkMessageBus<Message, IDType, AllowCopyableMessage>::Get();
    SkAutoMutexExclusive lock(bus->fInboxesMutex);
    bus->fInboxes.push_back(this);
}

template <typename Message, typename IDType, bool AllowCopyableMessage>
SkMessageBus<Message, IDType, AllowCopyableMessage>::Inbox::~Inbox() {
    // Remove ourselves from the corresponding message bus.
    auto* bus = SkMessageBus<Message, IDType, AllowCopyableMessage>::Get();
    SkAutoMutexExclusive lock(bus->fInboxesMutex);
    // This is a cheaper fInboxes.remove(fInboxes.find(this)) when order doesn't matter.
    for (int i = 0; i < bus->fInboxes.size(); i++) {
        if (this == bus->fInboxes[i]) {
            bus->fInboxes.removeShuffle(i);
            break;
        }
    }
}

template <typename Message, typename IDType, bool AllowCopyableMessage>
void SkMessageBus<Message, IDType, AllowCopyableMessage>::Inbox::receive(Message m) {
    SkAutoMutexExclusive lock(fMessagesMutex);
    fMessages.push_back(std::move(m));
}

template <typename Message, typename IDType, bool AllowCopyableMessage>
void SkMessageBus<Message, IDType, AllowCopyableMessage>::Inbox::poll(
        skia_private::TArray<Message>* messages) {
    SkASSERT(messages);
    messages->clear();
    SkAutoMutexExclusive lock(fMessagesMutex);
    fMessages.swap(*messages);
}

//   ----------------------- Implementation of SkMessageBus -----------------------

template <typename Message, typename IDType, bool AllowCopyableMessage>
SkMessageBus<Message, IDType, AllowCopyableMessage>::SkMessageBus() = default;

template <typename Message, typename IDType, bool AllowCopyableMessage>
/*static*/ void SkMessageBus<Message, IDType, AllowCopyableMessage>::Post(Message m) {
    auto* bus = SkMessageBus<Message, IDType, AllowCopyableMessage>::Get();
    SkAutoMutexExclusive lock(bus->fInboxesMutex);
    for (int i = 0; i < bus->fInboxes.size(); i++) {
        if (SkShouldPostMessageToBus(m, bus->fInboxes[i]->fUniqueID)) {
            if constexpr (AllowCopyableMessage) {
                bus->fInboxes[i]->receive(m);
            } else {
                if constexpr (is_sk_sp<Message>::value) {
                    SkASSERT(m->unique());
                }
                bus->fInboxes[i]->receive(std::move(m));
                break;
            }
        }
    }

    if constexpr (is_sk_sp<Message>::value && !AllowCopyableMessage) {
        // Make sure sk_sp has been sent to an inbox.
        SkASSERT(!m);  // NOLINT(bugprone-use-after-move)
    }
}

#endif  // SkMessageBus_DEFINED