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

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: remove statics and fixup locka 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/singleton.h"
16 #include "base/stringprintf.h"
17 #include "base/threading/simple_thread.h"
18 #include "chrome/common/chrome_paths.h"
8 19
9 namespace multi_process_notification { 20 namespace multi_process_notification {
10 21
11 bool Post(const std::string& name, Domain domain) { 22 const char* kNotifyPath = "/modules/notify";
12 // TODO(dmaclach): Implement 23 const char* kNotifyInterface = "org.chromium.Notify";
13 NOTIMPLEMENTED(); 24 const char* kNotifySignalName = "Notify";
14 return false; 25
15 } 26 // A simple thread to run the DBus main loop.
16 27 class ListenerThread : public base::SimpleThread {
28 public:
29 ListenerThread();
30
31 bool Init();
32
33 bool AddListener(ListenerImpl* listner);
34 bool RemoveListener(ListenerImpl* listner);
35
36 // SimpleThread overrides
37 virtual void Run();
38
39 private:
40 friend struct DefaultSingletonTraits<ListenerThread>;
41
42 DBusConnection* connection_;
43
44 // Access to this needs to be protected by ListenerThreadLock::GetLock().
45 std::set<ListenerImpl*> listeners_;
46
47 DISALLOW_COPY_AND_ASSIGN(ListenerThread);
48 };
49
50 // This is a singleton class that owns the lock for the ListenerThread and
51 // the thread.
52 class ListenerThreadLock {
53 public:
54 static ListenerThreadLock* GetInstance();
55
56 // Return a lock that can be used to protect access to the ListenerThread's
57 // member variables.
58 base::Lock& GetLock();
59
60 // Return the global ListenerThread, creating/initializing it as needed.
61 ListenerThread* GetThread();
62
63 private:
64 ListenerThreadLock();
65 friend struct DefaultSingletonTraits<ListenerThreadLock>;
66
67 // This lock is used to protect members in the thread_.
68 // Currently, this is only used to protect access to the listeners_.
69 base::Lock thread_access_lock_;
dmac 2011/01/25 18:06:28 do we really need two locks? One is probably suffi
garykac 2011/01/25 20:39:49 Separated into two locks to distinguish between th
70
71 // This lock is used to protect the creation of thread_.
72 base::Lock thread_create_lock_;
73
74 ListenerThread* thread_;
75
76 DISALLOW_COPY_AND_ASSIGN(ListenerThreadLock);
77 };
78
79 // This does all the heavy lifting for Listener class.
17 class ListenerImpl { 80 class ListenerImpl {
18 public: 81 public:
19 ListenerImpl(const std::string& name, 82 ListenerImpl(const std::string& name,
20 Domain domain, 83 Domain domain,
21 Listener::Delegate* delegate); 84 Listener::Delegate* delegate);
85 ~ListenerImpl();
22 86
23 bool Start(MessageLoop* io_loop_to_listen_on); 87 bool Start(MessageLoop* io_loop_to_listen_on);
88 void OnListen(const std::string& name);
24 89
25 std::string name() const { return name_; } 90 std::string name() const { return name_; }
26 Domain domain() const { return domain_; } 91 Domain domain() const { return domain_; }
27 92
28 private: 93 private:
94 void StartListener();
95
29 std::string name_; 96 std::string name_;
30 Domain domain_; 97 Domain domain_;
31 Listener::Delegate* delegate_; 98 Listener::Delegate* delegate_;
32 99
100 DBusError error_;
101 DBusConnection* connection_;
102
103 scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
104
33 DISALLOW_COPY_AND_ASSIGN(ListenerImpl); 105 DISALLOW_COPY_AND_ASSIGN(ListenerImpl);
34 }; 106 };
35 107
108 // Return the fullname for this signal by taking the user-specified name and
109 // adding a prefix based on the domain.
110 std::string AddDomainPrefixToNotification(const std::string& name,
111 Domain domain) {
112 std::string prefix;
113 switch (domain) {
114 case multi_process_notification::ProfileDomain: {
115 FilePath user_data_dir;
116 if (!PathService::Get(chrome::DIR_USER_DATA, &user_data_dir)) {
117 NOTREACHED();
118 }
119 prefix = StringPrintf("user.%u.%s.", getuid(),
120 user_data_dir.value().c_str());
121 break;
122 }
123 case multi_process_notification::UserDomain:
124 prefix = StringPrintf("user.%u.", getuid());
125 break;
126 case multi_process_notification::SystemDomain:
127 prefix = "";
128 break;
129 }
130 return prefix + name;
131 }
132
133 bool Post(const std::string& name, Domain domain) {
134 DBusError error;
135 dbus_error_init(&error);
136
137 // Get a connection to the DBus.
138 DBusConnection* connection = dbus_bus_get(DBUS_BUS_SESSION, &error);
139 if (dbus_error_is_set(&error)) {
140 LOG(ERROR) << "Failed to create initial dbus connection: " << error.message;
141 dbus_error_free(&error);
142 return false;
143 }
144 if (!connection) {
145 LOG(ERROR) << "Failed to create initial dbus connection";
146 return false;
147 }
148
149 std::string fullname = AddDomainPrefixToNotification(name, domain);
150
151 // Create the Notify signal.
152 bool success = true;
153 DBusMessage* message = dbus_message_new_signal(kNotifyPath, kNotifyInterface,
154 kNotifySignalName);
155 if (!message) {
156 LOG(ERROR) << "Failed to create dbus message for signal: "
157 << kNotifySignalName << " (" << kNotifyInterface << ")";
158 success = false;
159 }
160
161 // Add the full signal name as an argument to the Notify signal.
162 if (success) {
163 DBusMessageIter args;
164 const char* cname = fullname.c_str();
165 dbus_message_iter_init_append(message, &args);
166 if (!dbus_message_iter_append_basic(&args,
167 DBUS_TYPE_STRING, &cname)) {
168 LOG(ERROR) << "Failed to set signal name: " << fullname;
169 success = false;
170 }
171 }
172
173 // Actually send the signal.
174 if (success) {
175 dbus_uint32_t serial = 0;
176 if (!dbus_connection_send(connection, message, &serial)) {
177 LOG(ERROR) << "Unable to send dbus message for " << fullname;
178 success = false;
179 }
180 }
181
182 if (success) {
183 dbus_connection_flush(connection);
184 }
185 dbus_message_unref(message);
186
187 return success;
188 }
189
190 ListenerThread::ListenerThread()
191 : base::SimpleThread("ListenerThread"),
192 connection_(NULL) {
193 }
194
195 bool ListenerThread::Init() {
196 DBusError error;
197 dbus_error_init(&error);
198
199 // Get a connection to the DBus.
200 connection_ = dbus_bus_get(DBUS_BUS_SESSION, &error);
201 if (dbus_error_is_set(&error)) {
202 LOG(ERROR) << "Failed to create initial dbus connection: " << error.message;
203 dbus_error_free(&error);
204 return false;
205 }
206 if (!connection_) {
207 LOG(ERROR) << "Failed to create initial dbus connection";
208 return false;
209 }
210
211 // Create matching rule for our signal type.
212 std::string match_rule = StringPrintf("type='signal',interface='%s'",
213 kNotifyInterface);
214 dbus_bus_add_match(connection_, match_rule.c_str(), &error);
215 dbus_connection_flush(connection_);
216 if (dbus_error_is_set(&error)) {
217 LOG(ERROR) << "Failed to add dbus match rule for "
218 << kNotifyInterface << ": "
219 << error.message;
220 dbus_error_free(&error);
221 return false;
222 }
223
224 return true;
225 }
226
227 bool ListenerThread::AddListener(ListenerImpl* listener) {
228 base::AutoLock autolock(ListenerThreadLock::GetInstance()->GetLock());
229 listeners_.insert(listener);
230 return true;
231 }
232
233 bool ListenerThread::RemoveListener(ListenerImpl* listener) {
234 base::AutoLock autolock(ListenerThreadLock::GetInstance()->GetLock());
235 listeners_.erase(listener);
236 return true;
237 }
238
239 void ListenerThread::Run() {
240 while (true) {
241 // Get next available message.
242 // Using -1 for the timeout (instead of 0) would make this a blocking call.
243 // But it would unfortunately block *all* dbus calls on this connection.
244 // Thus, we use a timeout of 0 and a (nano)sleep.
245 // TODO(garykac): Ugh. Fix this to avoid the call to sleep if possible.
246 dbus_connection_read_write(connection_, 0);
247
248 DBusMessage* message = dbus_connection_pop_message(connection_);
249 if (!message) {
250 struct timespec delay = {0};
251 delay.tv_sec = 0;
252 delay.tv_nsec = 500 * 1000 * 1000;
253 nanosleep(&delay, NULL); // nanoYuck!
254 continue;
255 }
256
257 // Process all queued up messages.
258 while (message) {
259 if (dbus_message_is_signal(message,
260 kNotifyInterface, kNotifySignalName)) {
261 // Get user-defined name from the signal arguments.
262 DBusMessageIter args;
263 if (!dbus_message_iter_init(message, &args)) {
264 LOG(ERROR) << "Params missing from dbus signal";
265 } else if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) {
266 LOG(ERROR) << "Dbus signal param is not string type";
267 } else {
268 char* name;
dmac 2011/01/25 18:06:28 const char *name?
garykac 2011/01/25 20:39:49 Done.
269 dbus_message_iter_get_basic(&args, &name);
270 { // Scope for lock
271 base::AutoLock autolock(ListenerThreadLock::GetInstance()
272 ->GetLock());
273 std::set<ListenerImpl*>::iterator it;
274 // Note that this doesn't scale well to a large number of listeners.
275 // But we should only have a couple active at a time.
276 for (it=listeners_.begin(); it!=listeners_.end(); it++) {
277 (*it)->OnListen(name);
278 }
279 }
280 }
281 }
282
283 dbus_message_unref(message);
284 message = dbus_connection_pop_message(connection_);
285 }
286 }
287 }
288
36 ListenerImpl::ListenerImpl(const std::string& name, 289 ListenerImpl::ListenerImpl(const std::string& name,
37 Domain domain, 290 Domain domain,
38 Listener::Delegate* delegate) 291 Listener::Delegate* delegate)
39 : name_(name), domain_(domain), delegate_(delegate) { 292 : name_(name),
293 domain_(domain),
294 delegate_(delegate),
295 connection_(NULL) {
296 }
297
298 ListenerImpl::~ListenerImpl() {
299 ListenerThreadLock* threadlock = ListenerThreadLock::GetInstance();
300 ListenerThread* thread = threadlock->GetThread();
301 if (thread) {
302 thread->RemoveListener(this);
303 }
40 } 304 }
41 305
42 bool ListenerImpl::Start(MessageLoop* io_loop_to_listen_on) { 306 bool ListenerImpl::Start(MessageLoop* io_loop_to_listen_on) {
43 // TODO(dmaclach): Implement 307 if (io_loop_to_listen_on->type() != MessageLoop::TYPE_IO) {
44 NOTIMPLEMENTED(); 308 DLOG(ERROR) << "io_loop_to_listen_on must be TYPE_IO";
45 return false; 309 return false;
310 }
311
312 // Start the listener on the IO thread.
313 message_loop_proxy_ = base::MessageLoopProxy::CreateForCurrentThread();
314 Task* task = NewRunnableMethod(this, &ListenerImpl::StartListener);
315 io_loop_to_listen_on->PostTask(FROM_HERE, task);
dmac 2011/01/25 18:06:28 you were going to get rid of this use of "io_loop_
garykac 2011/01/25 20:39:49 Breaks unittests - leaving the same for now.
316 return true;
317 }
318
319 void ListenerImpl::StartListener() {
320 DCHECK_EQ(MessageLoop::TYPE_IO, MessageLoop::current()->type());
321
322 ListenerThreadLock* threadlock = ListenerThreadLock::GetInstance();
323 ListenerThread* thread = threadlock->GetThread();
324
325 // Register ourselves as a listener of signals.
326 bool success = false;
327 if (thread) {
328 success = thread->AddListener(this);
329 }
330
331 // Send initialization success/fail status to delegate.
332 Task* task = new Listener::ListenerStartedTask(name_, domain_, delegate_,
333 success);
334 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task));
335 }
336
337 void ListenerImpl::OnListen(const std::string& name) {
338 // Ignore the signal unless it matches our name.
339 std::string fullname = AddDomainPrefixToNotification(name_, domain_);
340 if (name == fullname) {
341 Task* task =
342 new Listener::NotificationReceivedTask(name_, domain_, delegate_);
343 CHECK(message_loop_proxy_->PostTask(FROM_HERE, task));
344 }
46 } 345 }
47 346
48 Listener::Listener(const std::string& name, 347 Listener::Listener(const std::string& name,
49 Domain domain, 348 Domain domain,
50 Listener::Delegate* delegate) 349 Listener::Delegate* delegate)
51 : impl_(new ListenerImpl(name, domain, delegate)) { 350 : impl_(new ListenerImpl(name, domain, delegate)) {
52 } 351 }
53 352
54 Listener::~Listener() { 353 Listener::~Listener() {
55 } 354 }
56 355
57 bool Listener::Start(MessageLoop* io_loop_to_listen_on) { 356 bool Listener::Start(MessageLoop* io_loop_to_listen_on) {
58 return impl_->Start(io_loop_to_listen_on); 357 return impl_->Start(io_loop_to_listen_on);
59 } 358 }
60 359
61 std::string Listener::name() const { 360 std::string Listener::name() const {
62 return impl_->name(); 361 return impl_->name();
63 } 362 }
64 363
65 Domain Listener::domain() const { 364 Domain Listener::domain() const {
66 return impl_->domain(); 365 return impl_->domain();
67 } 366 }
68 367
368 ListenerThreadLock* ListenerThreadLock::GetInstance() {
369 return Singleton<ListenerThreadLock>::get();
370 }
371
372 ListenerThreadLock::ListenerThreadLock()
373 : thread_(NULL) {
374 }
375
376 base::Lock& ListenerThreadLock::GetLock() {
377 return thread_access_lock_;
378 }
379
380 ListenerThread* ListenerThreadLock::GetThread() {
381 base::AutoLock autolock(thread_create_lock_);
382 if (!thread_) {
383 thread_ = new ListenerThread();
384 if (thread_ && thread_->Init()) {
dmac 2011/01/25 18:06:28 if init fails we leak the thread here.
garykac 2011/01/25 20:39:49 Done.
385 thread_->Start();
386 } else {
387 LOG(ERROR) << "Unable to start dbus ListenerThread";
388 return NULL;
389 }
390 }
391 return thread_;
392 }
393
69 } // namespace multi_process_notification 394 } // namespace multi_process_notification
395
396 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