OLD | NEW |
| (Empty) |
1 // Copyright 2010 The Ginsu Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can | |
3 // be found in the LICENSE file. | |
4 | |
5 #ifndef C_SALT_NOTIFICATION_CENTER_H_ | |
6 #define C_SALT_NOTIFICATION_CENTER_H_ | |
7 | |
8 #include <pthread.h> | |
9 | |
10 #include <cstddef> | |
11 #include <map> | |
12 #include <string> | |
13 | |
14 #include "boost/noncopyable.hpp" | |
15 #include "boost/function.hpp" | |
16 #include "boost/signals2.hpp" | |
17 #include "c_salt/instance.h" | |
18 #include "c_salt/notification_ptrs.h" | |
19 | |
20 namespace c_salt { | |
21 // NotificationCenter is a simple mechanism for broadcasting notifications | |
22 // within a process. | |
23 // Objects register with the NotificationCenter as subscribers of notifications | |
24 // using string identifiers to specify the notification of interest. | |
25 // Subscribers must subsequently remove themselves if they go out of scope | |
26 // before the NotificationCenter is deleted, or there will be dangling | |
27 // references. | |
28 class NotificationCenter : public boost::noncopyable { | |
29 public: | |
30 static const char* const kAnonymousPublisherName; | |
31 | |
32 // Return the common NotificationCenter used by |instance|. c_salt objects | |
33 // that post notifications all post to the DefaultCenter. This call is | |
34 // mutex locked. | |
35 static NotificationCenter* DefaultCenter(const c_salt::Instance& instance); | |
36 | |
37 NotificationCenter(); | |
38 virtual ~NotificationCenter(); | |
39 | |
40 // Add a subscriber with its method to invoke. The subscriber doesn't have | |
41 // to be created in the same thread as the NotificationCenter. When a | |
42 // notification gets published, however, the handler for the notification is | |
43 // called from the publisher's thread. The |subscriber| gets a copy of the | |
44 // Notification data when |handler| is called, which can in turn be copied | |
45 // to another thread. If |publisher_name| is the enpty string, then | |
46 // |subscriber| will receive notifications named |notification_name| from | |
47 // any publisher. Otherwise, it will only receive notifications from | |
48 // calls to PublishNotification() when |publisher_name| matches. | |
49 // Returns |true| on success. | |
50 template <typename SubscriberType> | |
51 bool AddSubscriber(const std::string& notification_name, | |
52 SubscriberType* subscriber, | |
53 void (SubscriberType::*handler)(const Notification&), | |
54 const std::string& publisher_name) { | |
55 return AddSubscriberImpl(notification_name, | |
56 CreateSubscriberId(subscriber), | |
57 boost::bind(handler, subscriber, _1), | |
58 publisher_name); | |
59 } | |
60 | |
61 // Remove a subscriber from all notifications in the dispatch table. | |
62 // Returns |true| on success. | |
63 template <typename SubscriberType> | |
64 bool RemoveSubscriber(SubscriberType* subscriber) { | |
65 return RemoveSubscriberImpl(CreateSubscriberId(subscriber)); | |
66 } | |
67 | |
68 // Remove a subscriber from the specified notification. Returns |true| on | |
69 // success. | |
70 template <typename SubscriberType> | |
71 bool RemoveSubscriberFromNotification( | |
72 SubscriberType* subscriber, | |
73 const std::string& notification_name) { | |
74 return RemoveSubscriberFromNotificationImpl(CreateSubscriberId(subscriber), | |
75 notification_name); | |
76 } | |
77 // Remove all subscribers from a specified notification and then remove the | |
78 // notification. Future calls to PostNotification() to |notification_name| | |
79 // will have no effect. Returns |true| on success. | |
80 bool RemoveNotification(const std::string& notification_name); | |
81 | |
82 // Publish a notification to be dispatched, specifying notification-specific | |
83 // data. Publishing all happens on a single thread, all subscribers are | |
84 // signaled on the calling thread. To further process a notification on | |
85 // another thread you have to do the inter-thread communication yourself. | |
86 // Returns |true| on success. | |
87 bool PublishNotification(const std::string& notification_name, | |
88 const Notification& notification, | |
89 const std::string& publisher_name); | |
90 | |
91 private: | |
92 typedef boost::signals2::signal<void(const Notification&)> NotificationSignal; | |
93 typedef boost::shared_ptr<NotificationSignal> SharedNotificationSignal; | |
94 // Map publisher ids to a set of boost::signals. This map is used when | |
95 // publishing a notification. Note that boost::signals2::signal<> has no | |
96 // copy ctor, so this dictionary has to use shared_ptrs. | |
97 typedef std::map<std::string, SharedNotificationSignal> PublisherSignalDict; | |
98 typedef std::map<uint64_t, std::vector<boost::signals2::connection> > | |
99 ConnectionDict; | |
100 | |
101 // A small helper class that holds the signal and the function which gets | |
102 // fired during PublishNotification(). A notification is published by firing | |
103 // a signal; there are two possible signals, one that is always fired which | |
104 // publishes a notification to subscribers of any publisher, and then another | |
105 // signal which is fired only if the publisher's id exists in the publisher | |
106 // ==> signal dictionary. In this way, every subscriber that wants | |
107 // notifications from either any publisher or from a specific publisher will | |
108 // get notified. Each NotificationSlot also holds a dictionary which maps | |
109 // subscriber_id ==> vector<connections>. This is done to support the | |
110 // RemoveSubscriber(SubscriberType) and to support disconnection of a | |
111 // boost::signals2::signal without relying on the peculiar equality semantics | |
112 // of boost::function in boost::signals. | |
113 struct NotificationSlot { | |
114 // Dtor disconnects all the signals in |connections_|. Assumes that any | |
115 // necessary mutex locking has been done by the caller. | |
116 ~NotificationSlot(); | |
117 | |
118 // Disconnect all the signals connected to the subscriber represented by | |
119 // this NotificationSlot. Assumes that any necessary mutex locking has | |
120 // been done by the caller. | |
121 void DisconnectSubscriber(uint64_t subscriber_id); | |
122 void DisconnectAll(const ConnectionDict::iterator& conn_iter); | |
123 | |
124 // This signal set is fired every time PublishNotification() is called, | |
125 // and will notify all subscribers to any publisher. | |
126 NotificationSignal any_publisher_signal_; | |
127 // Fire the signal set from this dictionary that matches |publisher_id_|. | |
128 PublisherSignalDict publisher_signals_; | |
129 ConnectionDict connections_; | |
130 }; | |
131 typedef boost::shared_ptr<NotificationSlot> SharedNotificationSlot; | |
132 typedef std::map<std::string, SharedNotificationSlot> NotificationDict; | |
133 | |
134 // Return a unique hash id for |subscriber|. This function has to return a | |
135 // unique id for each subscriber. | |
136 template <typename SubscriberType> | |
137 uint64_t CreateSubscriberId(SubscriberType* subscriber) const { | |
138 return reinterpret_cast<uint64_t>(subscriber); | |
139 } | |
140 | |
141 // The implementation behind the template methods. | |
142 bool AddSubscriberImpl( | |
143 const std::string& notification_name, | |
144 uint64_t subscriber_id, | |
145 boost::function<void(const Notification&)> subscriber_functor, | |
146 const std::string& publisher_name); | |
147 bool RemoveSubscriberImpl(uint64_t subscriber_id); | |
148 bool RemoveSubscriberFromNotificationImpl( | |
149 uint64_t subscriber_id, | |
150 const std::string& notification_name); | |
151 | |
152 mutable pthread_mutex_t mutex_; | |
153 NotificationDict notifications_; | |
154 }; | |
155 } // namespace c_salt | |
156 #endif // C_SALT_NOTIFICATION_CENTER_H_ | |
OLD | NEW |