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