Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(195)

Side by Side Diff: chrome/browser/multi_process_notification_linux.cc

Issue 6320011: Multi process notifications on linux (dbus) (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: nanosleep + comments Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | chrome/browser/multi_process_notification_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/multi_process_notification.h" 5 #include "chrome/browser/multi_process_notification.h"
6 6
7 #include <dbus/dbus.h>
8 #include <set>
9
10 #include "base/basictypes.h"
11 #include "base/file_path.h"
7 #include "base/logging.h" 12 #include "base/logging.h"
13 #include "base/message_loop_proxy.h"
14 #include "base/path_service.h"
15 #include "base/stringprintf.h"
16 #include "base/threading/simple_thread.h"
17 #include "chrome/common/chrome_paths.h"
8 18
9 namespace multi_process_notification { 19 namespace multi_process_notification {
10 20
11 bool Post(const std::string& name, Domain domain) { 21 const char* kNotifyPath = "/modules/notify";
12 // TODO(dmaclach): Implement 22 const char* kNotifyInterface = "org.chromium.Notify";
13 NOTIMPLEMENTED(); 23 const char* kNotifySignalName = "Notify";
14 return false; 24
15 } 25 // A simple thread to run the DBus main loop.
16 26 class ListenerThread : public base::SimpleThread {
27 public:
28 ListenerThread();
29
30 bool Init();
31
32 bool AddListener(ListenerImpl* listner);
33 bool RemoveListener(ListenerImpl* listner);
34
35 // SimpleThread overrides
36 virtual void Run();
37
38 private:
39 DBusConnection* connection_;
40
41 Lock listeners_lock_;
42 std::set<ListenerImpl*> listeners_;
43 };
44
45 // This does all the heavy lifting for Listener class.
17 class ListenerImpl { 46 class ListenerImpl {
18 public: 47 public:
19 ListenerImpl(const std::string& name, 48 ListenerImpl(const std::string& name,
20 Domain domain, 49 Domain domain,
21 Listener::Delegate* delegate); 50 Listener::Delegate* delegate);
51 virtual ~ListenerImpl();
dmac 2011/01/21 22:28:33 no need for this to be virtual. Nothing else is he
garykac 2011/01/22 00:09:05 Done.
22 52
23 bool Start(MessageLoop* io_loop_to_listen_on); 53 bool Start(MessageLoop* io_loop_to_listen_on);
54 void OnListen(const std::string& name);
dmac 2011/01/21 22:28:33 can OnListen be private?
garykac 2011/01/22 00:09:05 ListenerThread calls it.
24 55
25 std::string name() const { return name_; } 56 std::string name() const { return name_; }
26 Domain domain() const { return domain_; } 57 Domain domain() const { return domain_; }
27 58
28 private: 59 private:
60 void StartListener();
61
29 std::string name_; 62 std::string name_;
30 Domain domain_; 63 Domain domain_;
31 Listener::Delegate* delegate_; 64 Listener::Delegate* delegate_;
32 65
66 // Full name of signal (= domain-prefix + user-specified-name).
67 std::string fullname_;
68
69 DBusError error_;
70 DBusConnection* connection_;
71
72 Lock thread_lock_;
73 static ListenerThread* g_thread_;
74
75 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
76
33 DISALLOW_COPY_AND_ASSIGN(ListenerImpl); 77 DISALLOW_COPY_AND_ASSIGN(ListenerImpl);
34 }; 78 };
35 79
80 ListenerThread* ListenerImpl::g_thread_ = NULL;
81
82 // Return the fullname for this signal by taking the user-specified name and
83 // adding a prefix based on the domain.
84 std::string AddDomainPrefixToNotification(const std::string& name,
85 Domain domain) {
86 std::string prefix;
87 switch (domain) {
88 case multi_process_notification::ProfileDomain: {
89 FilePath user_data_dir;
90 if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
91 NOTREACHED();
92 }
93 prefix = StringPrintf("user.%u.%s.", getuid(),
94 user_data_dir.value().c_str());
95 break;
96 }
97 case multi_process_notification::UserDomain:
98 prefix = StringPrintf("user.%u.", getuid());
99 break;
100 case multi_process_notification::SystemDomain:
101 prefix = "";
102 break;
103 }
104 return prefix + name;
105 }
106
107 bool Post(const std::string& name, Domain domain) {
108 DBusError error;
109 dbus_error_init(&error);
110
111 // Get a connection to the DBus.
112 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
113 if (dbus_error_is_set(&error)) {
114 LOG(ERROR) << "Failed to create initial dbus connection: " << error.message;
115 dbus_error_free(&error);
116 return false;
117 }
118 if (!connection) {
119 LOG(ERROR) << "Failed to create initial dbus connection";
120 return false;
121 }
122
123 std::string fullname = AddDomainPrefixToNotification(name, domain);
124
125 // Create the Notify signal.
126 bool success = true;
127 DBusMessage* message = dbus_message_new_signal(kNotifyPath, kNotifyInterface,
128 kNotifySignalName);
129 if (!message) {
130 LOG(ERROR) << "Failed to create dbus message for signal: "
131 << kNotifySignalName << " (" << kNotifyInterface << ")";
132 success = false;
133 }
134
135 // Add the full signal name as an argument to the Notify signal.
136 if (success) {
137 DBusMessageIter args;
138 const char* cname = fullname.c_str();
139 dbus_message_iter_init_append(message, &args);
140 if (!dbus_message_iter_append_basic(&args,
141 DBUS_TYPE_STRING, &cname)) {
142 LOG(ERROR) << "Failed to set signal name: " << fullname;
143 success = false;
144 }
145 }
146
147 // Actually send the signal.
148 if (success) {
149 dbus_uint32_t serial = 0;
150 if (!dbus_connection_send(connection, message, &serial)) {
151 LOG(ERROR) << "Unable to send dbus message for " << fullname;
152 success = false;
153 }
154 }
155
156 if (success) {
157 dbus_connection_flush(connection);
158 }
159 dbus_message_unref(message);
160
161 return success;
162 }
163
164 ListenerThread::ListenerThread()
165 : base::SimpleThread("ListenerThread"),
166 connection_(NULL) {
167 }
168
169 bool ListenerThread::Init() {
170 DBusError error;
171 dbus_error_init(&error);
172
173 // Get a connection to the DBus.
174 connection_ = dbus_bus_get(DBUS_BUS_SESSION, &error);
175 if (dbus_error_is_set(&error)) {
176 LOG(ERROR) << "Failed to create initial dbus connection: " << error.message;
177 dbus_error_free(&error);
178 return false;
179 }
180 if (!connection_) {
181 LOG(ERROR) << "Failed to create initial dbus connection";
182 return false;
183 }
184
185 // Create matching rule for our signal type.
186 std::string match_rule = StringPrintf("type='signal',interface='%s'",
187 kNotifyInterface);
188 dbus_bus_add_match(connection_, match_rule.c_str(), &error);
189 dbus_connection_flush(connection_);
190 if (dbus_error_is_set(&error)) {
191 LOG(ERROR) << "Failed to add dbus match rule for "
192 << kNotifyInterface << ": "
193 << error.message;
194 dbus_error_free(&error);
195 return false;
196 }
197
198 return true;
199 }
200
201 bool ListenerThread::AddListener(ListenerImpl* listener) {
202 base::AutoLock autolock(listeners_lock_);
203 listeners_.insert(listener);
204 return true;
205 }
206
207 bool ListenerThread::RemoveListener(ListenerImpl* listener) {
208 base::AutoLock autolock(listeners_lock_);
209 listeners_.erase(listener);
210 return true;
211 }
212
213 void ListenerThread::Run() {
214 bool done = false;
215
216 while (!done) {
217 // Get next available message.
218 // Using -1 for a timeout to make this a blocking call - but it blocks all
219 // dbus calls on this connection. Thus, we use a timeout of 0 and a sleep.
220 // TODO(garykac): Ugh. Fix this to use file descriptors if possible.
221 dbus_connection_read_write(connection_, 0);
222
223 DBusMessage* message = dbus_connection_pop_message(connection_);
224 if (!message) {
225 struct timespec delay = {0};
226 delay.tv_sec = 0;
227 delay.tv_nsec = 500 * 1000 * 1000;
228 nanosleep(&delay, NULL); // nanoYuck!
229 continue;
230 }
231
232 // Process all queued up messages.
233 while (message) {
234 if (dbus_message_is_signal(message,
235 kNotifyInterface, kNotifySignalName)) {
236 // Get user-defined name from the signal arguments.
237 DBusMessageIter args;
238 if (!dbus_message_iter_init(message, &args)) {
239 LOG(ERROR) << "Params missing from dbus signal";
240 } else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
241 LOG(ERROR) << "Dbus signal param is not string type";
242 } else {
243 char* name;
244 dbus_message_iter_get_basic(&args, &name);
245 { // Scope for lock
246 base::AutoLock autolock(listeners_lock_);
247 std::set<ListenerImpl*>::iterator it;
248 // Note that this doesn't scale well to a large number of listeners.
249 // But we should only have a couple active at a time.
250 for (it=listeners_.begin(); it!=listeners_.end(); it++) {
251 (*it)->OnListen(name);
252 }
253 }
254 }
255 }
256
257 dbus_message_unref(message);
258 message = dbus_connection_pop_message(connection_);
259 }
260 }
261 }
262
36 ListenerImpl::ListenerImpl(const std::string& name, 263 ListenerImpl::ListenerImpl(const std::string& name,
37 Domain domain, 264 Domain domain,
38 Listener::Delegate* delegate) 265 Listener::Delegate* delegate)
39 : name_(name), domain_(domain), delegate_(delegate) { 266 : name_(name),
267 domain_(domain),
268 delegate_(delegate),
269 fullname_(name),
dmac 2011/01/21 22:28:33 why bother with fullname_ you use it in two places
garykac 2011/01/22 00:09:05 Done.
270 connection_(NULL) {
271 }
272
273 ListenerImpl::~ListenerImpl() {
274 if (g_thread_) {
dmac 2011/01/21 22:28:33 do you need to lock around this reference to g_thr
garykac 2011/01/22 00:09:05 Done. I checked the Mac code and it looks correct
275 g_thread_->RemoveListener(this);
276 }
40 } 277 }
41 278
42 bool ListenerImpl::Start(MessageLoop* io_loop_to_listen_on) { 279 bool ListenerImpl::Start(MessageLoop* io_loop_to_listen_on) {
43 // TODO(dmaclach): Implement 280 if (io_loop_to_listen_on->type() != MessageLoop::TYPE_IO) {
44 NOTIMPLEMENTED(); 281 DLOG(ERROR) << "io_loop_to_listen_on must be TYPE_IO";
45 return false; 282 return false;
283 }
284
285 // Record the fullname for this signal.
286 fullname_ = AddDomainPrefixToNotification(name_, domain_);
287
288 // Start the listener on the IO thread.
289 message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread();
290 Task* task = NewRunnableMethod(this, &ListenerImpl::StartListener);
291 io_loop_to_listen_on->PostTask(FROM_HERE, task);
292 return true;
293 }
294
295 void ListenerImpl::StartListener() {
296 DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
297 bool success = true;
298
299 if (!g_thread_) {
dmac 2011/01/21 22:28:33 double checked lock... not going to work unfortuna
garykac 2011/01/22 00:09:05 Damn.
300 // No listener thread. Get the lock and check again.
301 base::AutoLock autolock(thread_create_lock_);
302
303 // Start the main dbus loop if needed.
304 if (!g_thread_) {
305 g_thread_ = new ListenerThread();
306 success = g_thread_->Init();
307 if (success) {
308 g_thread_->Start();
309 }
310 }
311 }
312
313 // Register ourselves as a listener of signals.
314 if (success) {
315 g_thread_->AddListener(this);
316 }
317
318 // Send initialization success/fail status to delegate.
319 Task* task = new Listener::ListenerStartedTask(name_, domain_, delegate_,
320 success);
321 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task));
322 }
323
324 void ListenerImpl::OnListen(const std::string& name) {
325 // Ignore the signal unless it matches our name.
326 if (name == fullname_) {
327 Task* task =
328 new Listener::NotificationReceivedTask(name_, domain_, delegate_);
329 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task));
330 }
46 } 331 }
47 332
48 Listener::Listener(const std::string& name, 333 Listener::Listener(const std::string& name,
49 Domain domain, 334 Domain domain,
50 Listener::Delegate* delegate) 335 Listener::Delegate* delegate)
51 : impl_(new ListenerImpl(name, domain, delegate)) { 336 : impl_(new ListenerImpl(name, domain, delegate)) {
52 } 337 }
53 338
54 Listener::~Listener() { 339 Listener::~Listener() {
55 } 340 }
56 341
57 bool Listener::Start(MessageLoop* io_loop_to_listen_on) { 342 bool Listener::Start(MessageLoop* io_loop_to_listen_on) {
58 return impl_->Start(io_loop_to_listen_on); 343 return impl_->Start(io_loop_to_listen_on);
59 } 344 }
60 345
61 std::string Listener::name() const { 346 std::string Listener::name() const {
62 return impl_->name(); 347 return impl_->name();
63 } 348 }
64 349
65 Domain Listener::domain() const { 350 Domain Listener::domain() const {
66 return impl_->domain(); 351 return impl_->domain();
67 } 352 }
68 353
69 } // namespace multi_process_notification 354 } // namespace multi_process_notification
355
356 DISABLE_RUNNABLE_METHOD_REFCOUNT(multi_process_notification::ListenerImpl);
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/multi_process_notification_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698