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 "SkLazyPtr.h"
12#include "SkTDArray.h"
13#include "SkThread.h"
14#include "SkTypes.h"
15
16template <typename Message>
17class SkMessageBus : SkNoncopyable {
18public:
19    // Post a message to be received by all Inboxes for this Message type.  Threadsafe.
20    static void Post(const Message& m);
21
22    class Inbox {
23    public:
24        Inbox();
25        ~Inbox();
26
27        // Overwrite out with all the messages we've received since the last call.  Threadsafe.
28        void poll(SkTDArray<Message>* out);
29
30    private:
31        SkTDArray<Message> fMessages;
32        SkMutex            fMessagesMutex;
33
34        friend class SkMessageBus;
35        void receive(const Message& m);  // SkMessageBus is a friend only to call this.
36    };
37
38private:
39    SkMessageBus();
40    static SkMessageBus* Get();
41    static SkMessageBus* New();
42
43    SkTDArray<Inbox*> fInboxes;
44    SkMutex           fInboxesMutex;
45};
46
47// This must go in a single .cpp file, not some .h, or we risk creating more than one global
48// SkMessageBus per type when using shared libraries.
49#define DECLARE_SKMESSAGEBUS_MESSAGE(Message)                        \
50    template <>                                                      \
51    SkMessageBus<Message>* SkMessageBus<Message>::Get() {            \
52        SK_DECLARE_STATIC_LAZY_PTR(SkMessageBus<Message>, bus, New); \
53        return bus.get();                                            \
54    }
55
56//   ----------------------- Implementation of SkMessageBus::Inbox -----------------------
57
58template<typename Message>
59SkMessageBus<Message>::Inbox::Inbox() {
60    // Register ourselves with the corresponding message bus.
61    SkMessageBus<Message>* bus = SkMessageBus<Message>::Get();
62    SkAutoMutexAcquire lock(bus->fInboxesMutex);
63    bus->fInboxes.push(this);
64}
65
66template<typename Message>
67SkMessageBus<Message>::Inbox::~Inbox() {
68    // Remove ourselves from the corresponding message bus.
69    SkMessageBus<Message>* bus = SkMessageBus<Message>::Get();
70    SkAutoMutexAcquire lock(bus->fInboxesMutex);
71    // This is a cheaper fInboxes.remove(fInboxes.find(this)) when order doesn't matter.
72    for (int i = 0; i < bus->fInboxes.count(); i++) {
73        if (this == bus->fInboxes[i]) {
74            bus->fInboxes.removeShuffle(i);
75            break;
76        }
77    }
78}
79
80template<typename Message>
81void SkMessageBus<Message>::Inbox::receive(const Message& m) {
82    SkAutoMutexAcquire lock(fMessagesMutex);
83    fMessages.push(m);
84}
85
86template<typename Message>
87void SkMessageBus<Message>::Inbox::poll(SkTDArray<Message>* messages) {
88    SkASSERT(NULL != messages);
89    messages->reset();
90    SkAutoMutexAcquire lock(fMessagesMutex);
91    messages->swap(fMessages);
92}
93
94//   ----------------------- Implementation of SkMessageBus -----------------------
95
96template <typename Message>
97SkMessageBus<Message>::SkMessageBus() {}
98
99template <typename Message>
100/*static*/ SkMessageBus<Message>* SkMessageBus<Message>::New() {
101    return SkNEW(SkMessageBus<Message>);
102}
103
104template <typename Message>
105/*static*/ void SkMessageBus<Message>::Post(const Message& m) {
106    SkMessageBus<Message>* bus = SkMessageBus<Message>::Get();
107    SkAutoMutexAcquire lock(bus->fInboxesMutex);
108    for (int i = 0; i < bus->fInboxes.count(); i++) {
109        bus->fInboxes[i]->receive(m);
110    }
111}
112
113#endif  // SkMessageBus_DEFINED
114