xref: /aosp_15_r20/external/skia/src/core/SkMessageBus.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkMessageBus_DEFINED
9 #define SkMessageBus_DEFINED
10 
11 #include <type_traits>
12 
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkTypes.h"
15 #include "include/private/base/SkMutex.h"
16 #include "include/private/base/SkNoncopyable.h"
17 #include "include/private/base/SkOnce.h"
18 #include "include/private/base/SkTArray.h"
19 #include "include/private/base/SkTDArray.h"
20 
21 /**
22  * The following method must have a specialization for type 'Message':
23  *
24  *     bool SkShouldPostMessageToBus(const Message&, IDType msgBusUniqueID)
25  *
26  * We may want to consider providing a default template implementation, to avoid this requirement by
27  * sending to all inboxes when the specialization for type 'Message' is not present.
28  */
29 template <typename Message, typename IDType, bool AllowCopyableMessage = true>
30 class SkMessageBus : SkNoncopyable {
31 public:
32     template <typename T> struct is_sk_sp : std::false_type {};
33     template <typename T> struct is_sk_sp<sk_sp<T>> : std::true_type {};
34 
35     // We want to make sure the caller of Post() method will not keep a ref or copy of the message,
36     // so the message type must be sk_sp or non copyable.
37     static_assert(AllowCopyableMessage || is_sk_sp<Message>::value ||
38                           !std::is_copy_constructible<Message>::value,
39                   "The message type must be sk_sp or non copyable.");
40 
41     // Post a message to be received by Inboxes for this Message type. Checks
42     // SkShouldPostMessageToBus() for each inbox. Threadsafe.
43     static void Post(Message m);
44 
45     class Inbox {
46     public:
47         Inbox(IDType uniqueID);
48         ~Inbox();
49 
50         IDType uniqueID() const { return fUniqueID; }
51 
52         // Overwrite out with all the messages we've received since the last call.  Threadsafe.
53         void poll(skia_private::TArray<Message>* out);
54 
55     private:
56         skia_private::TArray<Message> fMessages;
57         SkMutex                       fMessagesMutex;
58         const IDType                  fUniqueID;
59 
60         friend class SkMessageBus;
61         void receive(Message m);  // SkMessageBus is a friend only to call this.
62     };
63 
64 private:
65     SkMessageBus();
66     static SkMessageBus* Get();
67 
68     SkTDArray<Inbox*> fInboxes;
69     SkMutex           fInboxesMutex;
70 };
71 
72 // This must go in a single .cpp file, not some .h, or we risk creating more than one global
73 // SkMessageBus per type when using shared libraries.  NOTE: at most one per file will compile.
74 #define DECLARE_SKMESSAGEBUS_MESSAGE(Message, IDType, AllowCopyableMessage)            \
75     template <>                                                                        \
76     SkMessageBus<Message, IDType, AllowCopyableMessage>*                               \
77     SkMessageBus<Message, IDType, AllowCopyableMessage>::Get() {                       \
78         static SkOnce once;                                                            \
79         static SkMessageBus<Message, IDType, AllowCopyableMessage>* bus;               \
80         once([] { bus = new SkMessageBus<Message, IDType, AllowCopyableMessage>(); }); \
81         return bus;                                                                    \
82     }
83 
84 //   ----------------------- Implementation of SkMessageBus::Inbox -----------------------
85 
86 template <typename Message, typename IDType, bool AllowCopyableMessage>
87 SkMessageBus<Message, IDType, AllowCopyableMessage>::Inbox::Inbox(IDType uniqueID)
88         : fUniqueID(uniqueID) {
89     // Register ourselves with the corresponding message bus.
90     auto* bus = SkMessageBus<Message, IDType, AllowCopyableMessage>::Get();
91     SkAutoMutexExclusive lock(bus->fInboxesMutex);
92     bus->fInboxes.push_back(this);
93 }
94 
95 template <typename Message, typename IDType, bool AllowCopyableMessage>
96 SkMessageBus<Message, IDType, AllowCopyableMessage>::Inbox::~Inbox() {
97     // Remove ourselves from the corresponding message bus.
98     auto* bus = SkMessageBus<Message, IDType, AllowCopyableMessage>::Get();
99     SkAutoMutexExclusive lock(bus->fInboxesMutex);
100     // This is a cheaper fInboxes.remove(fInboxes.find(this)) when order doesn't matter.
101     for (int i = 0; i < bus->fInboxes.size(); i++) {
102         if (this == bus->fInboxes[i]) {
103             bus->fInboxes.removeShuffle(i);
104             break;
105         }
106     }
107 }
108 
109 template <typename Message, typename IDType, bool AllowCopyableMessage>
110 void SkMessageBus<Message, IDType, AllowCopyableMessage>::Inbox::receive(Message m) {
111     SkAutoMutexExclusive lock(fMessagesMutex);
112     fMessages.push_back(std::move(m));
113 }
114 
115 template <typename Message, typename IDType, bool AllowCopyableMessage>
116 void SkMessageBus<Message, IDType, AllowCopyableMessage>::Inbox::poll(
117         skia_private::TArray<Message>* messages) {
118     SkASSERT(messages);
119     messages->clear();
120     SkAutoMutexExclusive lock(fMessagesMutex);
121     fMessages.swap(*messages);
122 }
123 
124 //   ----------------------- Implementation of SkMessageBus -----------------------
125 
126 template <typename Message, typename IDType, bool AllowCopyableMessage>
127 SkMessageBus<Message, IDType, AllowCopyableMessage>::SkMessageBus() = default;
128 
129 template <typename Message, typename IDType, bool AllowCopyableMessage>
130 /*static*/ void SkMessageBus<Message, IDType, AllowCopyableMessage>::Post(Message m) {
131     auto* bus = SkMessageBus<Message, IDType, AllowCopyableMessage>::Get();
132     SkAutoMutexExclusive lock(bus->fInboxesMutex);
133     for (int i = 0; i < bus->fInboxes.size(); i++) {
134         if (SkShouldPostMessageToBus(m, bus->fInboxes[i]->fUniqueID)) {
135             if constexpr (AllowCopyableMessage) {
136                 bus->fInboxes[i]->receive(m);
137             } else {
138                 if constexpr (is_sk_sp<Message>::value) {
139                     SkASSERT(m->unique());
140                 }
141                 bus->fInboxes[i]->receive(std::move(m));
142                 break;
143             }
144         }
145     }
146 
147     if constexpr (is_sk_sp<Message>::value && !AllowCopyableMessage) {
148         // Make sure sk_sp has been sent to an inbox.
149         SkASSERT(!m);  // NOLINT(bugprone-use-after-move)
150     }
151 }
152 
153 #endif  // SkMessageBus_DEFINED
154