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

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