|  | /* | 
|  | * 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/SkMutex.h" | 
|  | #include "include/private/SkNoncopyable.h" | 
|  | #include "include/private/SkOnce.h" | 
|  | #include "include/private/SkTArray.h" | 
|  | #include "include/private/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(SkTArray<Message>* out); | 
|  |  | 
|  | private: | 
|  | SkTArray<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.count(); 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(SkTArray<Message>* messages) { | 
|  | SkASSERT(messages); | 
|  | messages->reset(); | 
|  | 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.count(); 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 |