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 "include/core/SkTypes.h"
12#include "include/private/SkMutex.h"
13#include "include/private/SkNoncopyable.h"
14#include "include/private/SkOnce.h"
15#include "include/private/SkTArray.h"
16#include "include/private/SkTDArray.h"
17
18/**
19 * The following method must have a specialization for type 'Message':
20 *
21 * bool SkShouldPostMessageToBus(const Message&, uint32_t msgBusUniqueID)
22 *
23 * We may want to consider providing a default template implementation, to avoid this requirement by
24 * sending to all inboxes when the specialization for type 'Message' is not present.
25 */
26template <typename Message>
27class SkMessageBus : SkNoncopyable {
28public:
29 // Post a message to be received by Inboxes for this Message type. Checks
30 // SkShouldPostMessageToBus() for each inbox. Threadsafe.
31 static void Post(const Message& m);
32
33 class Inbox {
34 public:
35 Inbox(uint32_t uniqueID = SK_InvalidUniqueID);
36 ~Inbox();
37
38 uint32_t uniqueID() const { return fUniqueID; }
39
40 // Overwrite out with all the messages we've received since the last call. Threadsafe.
41 void poll(SkTArray<Message>* out);
42
43 private:
44 SkTArray<Message> fMessages;
45 SkMutex fMessagesMutex;
46 uint32_t fUniqueID;
47
48 friend class SkMessageBus;
49 void receive(const Message& m); // SkMessageBus is a friend only to call this.
50 };
51
52private:
53 SkMessageBus();
54 static SkMessageBus* Get();
55
56 SkTDArray<Inbox*> fInboxes;
57 SkMutex fInboxesMutex;
58};
59
60// This must go in a single .cpp file, not some .h, or we risk creating more than one global
61// SkMessageBus per type when using shared libraries. NOTE: at most one per file will compile.
62#define DECLARE_SKMESSAGEBUS_MESSAGE(Message) \
63 template <> \
64 SkMessageBus<Message>* SkMessageBus<Message>::Get() { \
65 static SkOnce once; \
66 static SkMessageBus<Message>* bus; \
67 once([] { bus = new SkMessageBus<Message>(); }); \
68 return bus; \
69 }
70
71// ----------------------- Implementation of SkMessageBus::Inbox -----------------------
72
73template<typename Message>
74SkMessageBus<Message>::Inbox::Inbox(uint32_t uniqueID) : fUniqueID(uniqueID) {
75 // Register ourselves with the corresponding message bus.
76 SkMessageBus<Message>* bus = SkMessageBus<Message>::Get();
77 SkAutoMutexExclusive lock(bus->fInboxesMutex);
78 bus->fInboxes.push_back(this);
79}
80
81template<typename Message>
82SkMessageBus<Message>::Inbox::~Inbox() {
83 // Remove ourselves from the corresponding message bus.
84 SkMessageBus<Message>* bus = SkMessageBus<Message>::Get();
85 SkAutoMutexExclusive lock(bus->fInboxesMutex);
86 // This is a cheaper fInboxes.remove(fInboxes.find(this)) when order doesn't matter.
87 for (int i = 0; i < bus->fInboxes.count(); i++) {
88 if (this == bus->fInboxes[i]) {
89 bus->fInboxes.removeShuffle(i);
90 break;
91 }
92 }
93}
94
95template<typename Message>
96void SkMessageBus<Message>::Inbox::receive(const Message& m) {
97 SkAutoMutexExclusive lock(fMessagesMutex);
98 fMessages.push_back(m);
99}
100
101template<typename Message>
102void SkMessageBus<Message>::Inbox::poll(SkTArray<Message>* messages) {
103 SkASSERT(messages);
104 messages->reset();
105 SkAutoMutexExclusive lock(fMessagesMutex);
106 fMessages.swap(*messages);
107}
108
109// ----------------------- Implementation of SkMessageBus -----------------------
110
111template <typename Message>
112SkMessageBus<Message>::SkMessageBus() {}
113
114template <typename Message>
115/*static*/ void SkMessageBus<Message>::Post(const Message& m) {
116 SkMessageBus<Message>* bus = SkMessageBus<Message>::Get();
117 SkAutoMutexExclusive lock(bus->fInboxesMutex);
118 for (int i = 0; i < bus->fInboxes.count(); i++) {
119 if (SkShouldPostMessageToBus(m, bus->fInboxes[i]->fUniqueID)) {
120 bus->fInboxes[i]->receive(m);
121 }
122 }
123}
124
125#endif // SkMessageBus_DEFINED
126