• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/SkMutex.h"
16 #include "include/private/SkNoncopyable.h"
17 #include "include/private/SkOnce.h"
18 #include "include/private/SkTArray.h"
19 #include "include/private/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(SkTArray<Message>* out);
54 
55     private:
56         SkTArray<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.count(); 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(SkTArray<Message>* messages) {
117     SkASSERT(messages);
118     messages->reset();
119     SkAutoMutexExclusive lock(fMessagesMutex);
120     fMessages.swap(*messages);
121 }
122 
123 //   ----------------------- Implementation of SkMessageBus -----------------------
124 
125 template <typename Message, typename IDType, bool AllowCopyableMessage>
126 SkMessageBus<Message, IDType, AllowCopyableMessage>::SkMessageBus() = default;
127 
128 template <typename Message, typename IDType, bool AllowCopyableMessage>
129 /*static*/ void SkMessageBus<Message, IDType, AllowCopyableMessage>::Post(Message m) {
130     auto* bus = SkMessageBus<Message, IDType, AllowCopyableMessage>::Get();
131     SkAutoMutexExclusive lock(bus->fInboxesMutex);
132     for (int i = 0; i < bus->fInboxes.count(); i++) {
133         if (SkShouldPostMessageToBus(m, bus->fInboxes[i]->fUniqueID)) {
134             if constexpr (AllowCopyableMessage) {
135                 bus->fInboxes[i]->receive(m);
136             } else {
137                 if constexpr (is_sk_sp<Message>::value) {
138                     SkASSERT(m->unique());
139                 }
140                 bus->fInboxes[i]->receive(std::move(m));
141                 break;
142             }
143         }
144     }
145 
146     if constexpr (is_sk_sp<Message>::value && !AllowCopyableMessage) {
147         // Make sure sk_sp has been sent to an inbox.
148         SkASSERT(!m);  // NOLINT(bugprone-use-after-move)
149     }
150 }
151 
152 #endif  // SkMessageBus_DEFINED
153