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 #include "c_salt/notification_center.h" | |
6 | |
7 #include "c_salt/notification.h" | |
8 | |
9 namespace { | |
10 const int kPthreadMutexSuccess = 0; | |
11 | |
12 // The class singleton and its mutex. | |
13 // TODO(c_salt_authors): This mutex should really be some kind of scoped | |
14 // mutex class, such as boost::mutex and boost::lock_guard. | |
15 pthread_mutex_t notification_center_mutex = PTHREAD_MUTEX_INITIALIZER; | |
16 c_salt::NotificationCenter* notification_center = NULL; | |
17 } // namespace | |
18 | |
19 namespace c_salt { | |
20 | |
21 const char* const NotificationCenter::kAnonymousPublisherName = ""; | |
22 | |
23 NotificationCenter* NotificationCenter::DefaultCenter( | |
24 const c_salt::Instance& instance) { | |
25 // TODO(c_salt_authors): There needs to be one DefaultCenter() per | |
26 // Instance. We need to implement this. | |
27 if (pthread_mutex_lock(¬ification_center_mutex) != kPthreadMutexSuccess) | |
28 return NULL; | |
29 if (notification_center == NULL) { | |
30 notification_center = new NotificationCenter(); | |
31 } | |
32 pthread_mutex_unlock(¬ification_center_mutex); | |
33 return notification_center; | |
34 } | |
35 | |
36 NotificationCenter::NotificationCenter() { | |
37 pthread_mutex_init(&mutex_, NULL); | |
38 } | |
39 | |
40 NotificationCenter::~NotificationCenter() { | |
41 pthread_mutex_destroy(&mutex_); | |
42 } | |
43 | |
44 bool NotificationCenter::AddSubscriberImpl( | |
45 const std::string& notification_name, | |
46 uint64_t subscriber_id, | |
47 boost::function<void(const Notification&)> subscriber_functor, | |
48 const std::string& publisher_name) { | |
49 if (pthread_mutex_lock(&mutex_) != kPthreadMutexSuccess) { | |
50 return false; | |
51 } | |
52 SharedNotificationSlot notification_slot; | |
53 std::pair<NotificationDict::iterator, bool> slot_iter = | |
54 notifications_.insert(NotificationDict::value_type(notification_name, | |
55 notification_slot)); | |
56 // If this is a newly inserted notification signal, then reset the | |
57 // value with a new signal object. | |
58 if (slot_iter.second) { | |
59 slot_iter.first->second.reset(new NotificationSlot()); | |
60 } | |
61 // Grab the actual NotificationSlot and hook up the subscriber. | |
62 notification_slot = slot_iter.first->second; | |
63 boost::signals2::connection connection; | |
64 if (publisher_name.empty()) { | |
65 connection = notification_slot->any_publisher_signal_ | |
66 .connect(subscriber_functor); | |
67 } else { | |
68 // Add a new shared_ptr if there isn't one already. The [] notation on | |
69 // std::map doesn't help here. This is to work around the lack of a copy | |
70 // ctor on boost::signals2::signal. | |
71 SharedNotificationSignal publisher_signal(new NotificationSignal()); | |
72 std::pair<PublisherSignalDict::iterator, bool> pub_sig_iter = | |
73 notification_slot->publisher_signals_.insert( | |
74 PublisherSignalDict::value_type(publisher_name, publisher_signal)); | |
75 connection = pub_sig_iter.first->second->connect(subscriber_functor); | |
76 } | |
77 notification_slot->connections_[subscriber_id].push_back(connection); | |
78 pthread_mutex_unlock(&mutex_); | |
79 return true; | |
80 } | |
81 | |
82 bool NotificationCenter::RemoveSubscriberImpl(uint64_t subscriber_id) { | |
83 if (pthread_mutex_lock(&mutex_) != kPthreadMutexSuccess) { | |
84 return false; | |
85 } | |
86 NotificationDict::iterator note_iter = notifications_.begin(); | |
87 const NotificationDict::const_iterator end = notifications_.end(); | |
88 for (; note_iter != end; ++note_iter) { | |
89 // Disconnect all the connections this subscriber might have for the | |
90 // notification at |note_iter|. | |
91 (note_iter->second)->DisconnectSubscriber(subscriber_id); | |
92 } | |
93 pthread_mutex_unlock(&mutex_); | |
94 return true; | |
95 } | |
96 | |
97 bool NotificationCenter::RemoveSubscriberFromNotificationImpl( | |
98 uint64_t subscriber_id, | |
99 const std::string& notification_name) { | |
100 if (pthread_mutex_lock(&mutex_) != kPthreadMutexSuccess) { | |
101 return false; | |
102 } | |
103 const NotificationDict::iterator note_iter = | |
104 notifications_.find(notification_name); | |
105 if (note_iter != notifications_.end()) { | |
106 // Disconnect all the connections this subscriber might have for the | |
107 // notification at |note_iter|. | |
108 (note_iter->second)->DisconnectSubscriber(subscriber_id); | |
109 } | |
110 pthread_mutex_unlock(&mutex_); | |
111 return true; | |
112 } | |
113 | |
114 bool NotificationCenter::RemoveNotification( | |
115 const std::string& notification_name) { | |
116 if (pthread_mutex_lock(&mutex_) != kPthreadMutexSuccess) { | |
117 return false; | |
118 } | |
119 notifications_.erase(notification_name); | |
120 pthread_mutex_unlock(&mutex_); | |
121 return true; | |
122 } | |
123 | |
124 bool NotificationCenter::PublishNotification( | |
125 const std::string& notification_name, | |
126 const Notification& notification, | |
127 const std::string& publisher_name) { | |
128 if (pthread_mutex_lock(&mutex_) != kPthreadMutexSuccess) { | |
129 return false; | |
130 } | |
131 const NotificationDict::const_iterator note_iter = | |
132 notifications_.find(notification_name); | |
133 SharedNotificationSlot notification_slot; | |
134 SharedNotificationSignal publisher_signal; | |
135 if (note_iter != notifications_.end()) { | |
136 // Make a local copy of the signals, in the event that the | |
137 // notification gets removed before signals can be fired (which will | |
138 // make the iterator invalid). The local copy of the NotificationSlot | |
139 // shared_ptr manages |any_publisher_signal_|. | |
140 notification_slot = note_iter->second; | |
141 // See if there are any publisher-specific signals. The signal itself is | |
142 // managed with a shared_ptr, because boost::signals2::signal does not have | |
143 // a copy ctor. | |
144 PublisherSignalDict::const_iterator sig_iter = | |
145 (note_iter->second)->publisher_signals_.find(publisher_name); | |
146 if (sig_iter != (note_iter->second)->publisher_signals_.end()) { | |
147 publisher_signal = sig_iter->second; | |
148 } | |
149 } | |
150 // Signal the subscribers. boost makes the actual signalling thread-safe, so | |
151 // it is ok to unlock the mutex before signaling the subscribers. Note that | |
152 // it's possible for another thread to disconnect the signal before this gets | |
153 // reached, in which case the signal will not fire. | |
154 pthread_mutex_unlock(&mutex_); | |
155 // Always fire the any-publisher matches. | |
156 if (notification_slot != NULL) { | |
157 (notification_slot->any_publisher_signal_)(notification); | |
158 } | |
159 // Fire any publisher-specific notifications. If there were none, then this | |
160 // signal set will be empty and signaling will have no effect. | |
161 if (publisher_signal != NULL) { | |
162 (*publisher_signal)(notification); | |
163 } | |
164 return true; | |
165 } | |
166 | |
167 NotificationCenter::NotificationSlot::~NotificationSlot() { | |
168 ConnectionDict::iterator conn_iter; | |
169 for (conn_iter = connections_.begin(); | |
170 conn_iter != connections_.end(); | |
171 ++conn_iter) { | |
172 DisconnectAll(conn_iter); | |
173 // No need to erase(conn_iter), since this will be done as part of the | |
174 // std::map<> dtor. | |
175 } | |
176 } | |
177 | |
178 void NotificationCenter::NotificationSlot::DisconnectSubscriber( | |
179 uint64_t subscriber_id) { | |
180 ConnectionDict::iterator conn_iter = connections_.find(subscriber_id); | |
181 if (conn_iter != connections_.end()) { | |
182 DisconnectAll(conn_iter); | |
183 connections_.erase(conn_iter); | |
184 } | |
185 } | |
186 | |
187 void NotificationCenter::NotificationSlot::DisconnectAll( | |
188 const ConnectionDict::iterator& conn_iter) { | |
189 std::vector<boost::signals2::connection>::iterator signal_iter; | |
190 for (signal_iter = conn_iter->second.begin(); | |
191 signal_iter != conn_iter->second.end(); | |
192 ++signal_iter) { | |
193 // Note that ~connection() does *not* disconnect the signal, so this has | |
194 // to be done manually in a loop. | |
195 signal_iter->disconnect(); | |
196 } | |
197 } | |
198 | |
199 } // namespace c_salt | |
OLD | NEW |