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

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