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