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

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