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