OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 // | |
5 // TODO(satorux): | |
6 // - Handle "disconnected" signal. | |
7 // - Add support for signal sending | |
8 // - Add support for signal monitoring | |
9 // - Collect metrics (ex. # of method calls, method call time, etc.) | |
10 | |
11 #include "dbus/bus.h" | |
12 | |
13 #include "base/bind.h" | |
14 #include "base/logging.h" | |
15 #include "base/message_loop.h" | |
16 #include "base/stl_util.h" | |
17 #include "base/threading/thread.h" | |
18 #include "base/threading/thread_restrictions.h" | |
19 #include "dbus/error.h" | |
20 #include "dbus/exported_object.h" | |
21 #include "dbus/object_proxy.h" | |
22 | |
23 namespace dbus { | |
24 | |
25 namespace { | |
26 | |
27 // The class is used for watching the file descriptor used for D-Bus | |
28 // communication. | |
29 class Watch : public base::MessagePumpLibevent::Watcher { | |
30 public: | |
31 Watch(DBusWatch* watch) | |
32 : raw_watch_(watch) { | |
33 dbus_watch_set_data(raw_watch_, this, NULL); | |
34 } | |
35 | |
36 ~Watch() { | |
37 dbus_watch_set_data(raw_watch_, NULL, NULL); | |
38 } | |
39 | |
40 // Returns true if the underlying file descriptor is ready to be watched. | |
41 bool IsReadyToBeWatched() { | |
42 return dbus_watch_get_enabled(raw_watch_); | |
43 } | |
44 | |
45 // Starts watching the underlying file descriptor. | |
46 void StartWatching() { | |
47 const int file_descriptor = dbus_watch_get_unix_fd(raw_watch_); | |
48 const int flags = dbus_watch_get_flags(raw_watch_); | |
49 | |
50 MessageLoopForIO::Mode mode = MessageLoopForIO::WATCH_READ; | |
51 if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) | |
52 mode = MessageLoopForIO::WATCH_READ_WRITE; | |
53 else if (flags & DBUS_WATCH_READABLE) | |
54 mode = MessageLoopForIO::WATCH_READ; | |
55 else if (flags & DBUS_WATCH_WRITABLE) | |
56 mode = MessageLoopForIO::WATCH_WRITE; | |
57 else | |
58 NOTREACHED(); | |
59 | |
60 const bool persistent = true; // Watch persistently. | |
61 const bool success = MessageLoopForIO::current()->WatchFileDescriptor( | |
62 file_descriptor, | |
63 persistent, | |
64 mode, | |
65 &file_descriptor_watcher_, | |
66 this); | |
67 CHECK(success) << "Unable to allocate memory"; | |
68 } | |
69 | |
70 // Stops watching the underlying file descriptor. | |
71 void StopWatching() { | |
72 file_descriptor_watcher_.StopWatchingFileDescriptor(); | |
73 } | |
74 | |
75 private: | |
76 // Implement MessagePumpLibevent::Watcher. | |
77 virtual void OnFileCanReadWithoutBlocking(int file_descriptor) { | |
78 const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_READABLE); | |
79 CHECK(success) << "Unable to allocate memory"; | |
80 } | |
81 | |
82 // Implement MessagePumpLibevent::Watcher. | |
83 virtual void OnFileCanWriteWithoutBlocking(int file_descriptor) { | |
84 const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_WRITABLE); | |
85 CHECK(success) << "Unable to allocate memory"; | |
86 } | |
87 | |
88 DBusWatch* raw_watch_; | |
89 base::MessagePumpLibevent::FileDescriptorWatcher file_descriptor_watcher_; | |
90 }; | |
91 | |
92 // The class is used for monitoring the timeout used for D-Bus method | |
93 // calls. | |
94 // | |
95 // Unlike Watch, Timeout is a ref counted object, to ensure that |this| of | |
96 // the object is is alive when HandleTimeout() is called. It's unlikely | |
97 // but it may be possible that HandleTimeout() is called after | |
98 // Bus::OnRemoveTimeout(). That's why we don't simply delete the object in | |
99 // Bus::OnRemoveTimeout(). | |
100 class Timeout : public base::RefCountedThreadSafe<Timeout> { | |
101 public: | |
102 Timeout(DBusTimeout* timeout) | |
103 : raw_timeout_(timeout), | |
104 monitoring_is_active_(false), | |
105 is_completed(false) { | |
106 dbus_timeout_set_data(raw_timeout_, this, NULL); | |
107 AddRef(); // Balanced on Complete(). | |
108 } | |
109 | |
110 // Returns true if the timeout is ready to be monitored. | |
111 bool IsReadyToBeMonitored() { | |
112 return dbus_timeout_get_enabled(raw_timeout_); | |
113 } | |
114 | |
115 // Starts monitoring the timeout. | |
116 void StartMonitoring(dbus::Bus* bus) { | |
117 bus->PostDelayedTaskToDBusThread(FROM_HERE, | |
118 base::Bind(&Timeout::HandleTimeout, | |
119 this), | |
120 GetIntervalInMs()); | |
121 monitoring_is_active_ = true; | |
122 } | |
123 | |
124 // Stops monitoring the timeout. | |
125 void StopMonitoring() { | |
126 // We cannot take back the delayed task we posted in | |
127 // StartMonitoring(), so we just mark the monitoring is inactive now. | |
128 monitoring_is_active_ = false; | |
129 } | |
130 | |
131 // Returns the interval in milliseconds. | |
132 int GetIntervalInMs() { | |
133 return dbus_timeout_get_interval(raw_timeout_); | |
134 } | |
135 | |
136 // Cleans up the raw_timeout and marks that timeout is completed. | |
137 // See the class comment above for why we are doing this. | |
138 void Complete() { | |
139 dbus_timeout_set_data(raw_timeout_, NULL, NULL); | |
140 is_completed = true; | |
141 Release(); | |
142 } | |
143 | |
144 private: | |
145 friend class base::RefCountedThreadSafe<Timeout>; | |
146 ~Timeout() { | |
147 } | |
148 | |
149 // Handles the timeout. | |
150 void HandleTimeout() { | |
151 // If the timeout is marked completed, we should do nothing. This can | |
152 // occur if this function is called after Bus::OnRemoveTimeout(). | |
153 if (is_completed) | |
154 return; | |
155 // Skip if monitoring is cancled. | |
156 if (!monitoring_is_active_) | |
157 return; | |
158 | |
159 const bool success = dbus_timeout_handle(raw_timeout_); | |
160 CHECK(success) << "Unable to allocate memory"; | |
161 } | |
162 | |
163 DBusTimeout* raw_timeout_; | |
164 bool monitoring_is_active_; | |
165 bool is_completed; | |
166 }; | |
167 | |
168 } // namespace | |
169 | |
170 Bus::Options::Options() | |
171 : bus_type(SESSION), | |
172 connection_type(PRIVATE), | |
173 dbus_thread(NULL) { | |
174 } | |
175 | |
176 Bus::Options::~Options() { | |
177 } | |
178 | |
179 Bus::Bus(const Options& options) | |
180 : bus_type_(options.bus_type), | |
181 connection_type_(options.connection_type), | |
182 dbus_thread_(options.dbus_thread), | |
183 connection_(NULL), | |
184 origin_loop_(MessageLoop::current()), | |
185 origin_thread_id_(base::PlatformThread::CurrentId()), | |
186 dbus_thread_id_(base::kInvalidThreadId), | |
187 async_operations_are_set_up_(false), | |
188 num_pending_watches_(0), | |
189 num_pending_timeouts_(0) { | |
190 if (dbus_thread_) { | |
191 dbus_thread_id_ = dbus_thread_->thread_id(); | |
192 DCHECK(dbus_thread_->IsRunning()) | |
193 << "The D-Bus thread should be running"; | |
194 DCHECK_EQ(MessageLoop::TYPE_IO, | |
195 dbus_thread_->message_loop()->type()) | |
196 << "The D-Bus thread should have an MessageLoopForIO attached"; | |
197 } | |
198 | |
199 // This is safe to call multiple times. | |
200 dbus_threads_init_default(); | |
201 } | |
202 | |
203 Bus::~Bus() { | |
204 DCHECK(!connection_); | |
205 DCHECK(owned_service_names_.empty()); | |
206 DCHECK_EQ(0, num_pending_watches_); | |
207 DCHECK_EQ(0, num_pending_timeouts_); | |
208 } | |
209 | |
210 ObjectProxy* Bus::GetObjectProxy(const std::string& service_name, | |
211 const std::string& object_path) { | |
212 AssertOnOriginThread(); | |
213 | |
214 scoped_refptr<ObjectProxy> object_proxy = | |
215 new ObjectProxy(this, service_name, object_path); | |
216 object_proxies_.push_back(object_proxy); | |
217 | |
218 return object_proxy; | |
219 } | |
220 | |
221 ExportedObject* Bus::GetExportedObject(const std::string& service_name, | |
222 const std::string& object_path) { | |
223 AssertOnOriginThread(); | |
224 | |
225 scoped_refptr<ExportedObject> exported_object = | |
226 new ExportedObject(this, service_name, object_path); | |
227 exported_objects_.push_back(exported_object); | |
228 | |
229 return exported_object; | |
230 } | |
231 | |
232 bool Bus::Connect() { | |
233 // dbus_bus_get_private() and dbus_bus_get() are blocking calls. | |
234 AssertOnDBusThread(); | |
235 | |
236 // Check if it's already initialized. | |
237 if (connection_) | |
238 return true; | |
239 | |
240 ScopedDBusError error; | |
241 const DBusBusType dbus_bus_type = static_cast<DBusBusType>(bus_type_); | |
242 if (connection_type_ == PRIVATE) { | |
243 connection_ = dbus_bus_get_private(dbus_bus_type, error.get()); | |
244 } else { | |
245 connection_ = dbus_bus_get(dbus_bus_type, error.get()); | |
246 } | |
247 if (!connection_) { | |
248 LOG(ERROR) << "Failed to connect to the bus: " | |
249 << (dbus_error_is_set(error.get()) ? error.message() : ""); | |
250 return false; | |
251 } | |
252 // We shouldn't exit on the disconnected signal. | |
253 dbus_connection_set_exit_on_disconnect(connection_, false); | |
254 | |
255 return true; | |
256 } | |
257 | |
258 void Bus::ShutdownAndBlock() { | |
259 AssertOnDBusThread(); | |
260 | |
261 // Delete exported objects. Need to unregister them beforehand. | |
stevenjb
2011/08/15 21:42:04
Currently we're not actually deleting the object h
satorux1
2011/08/16 22:25:37
Good catch. The comment was obsolete. Updated.
| |
262 for (size_t i = 0; i < exported_objects_.size(); ++i) { | |
263 exported_objects_[i]->Unregister(); | |
264 } | |
265 | |
266 // Release all service names. | |
267 for (std::set<std::string>::iterator iter = owned_service_names_.begin(); | |
268 iter != owned_service_names_.end();) { | |
269 // This is a bit tricky but we should increment the iter here as | |
270 // ReleaseOwnership() may remove |service_name| from the set. | |
271 const std::string& service_name = *iter++; | |
272 ReleaseOwnership(service_name); | |
273 } | |
274 if (!owned_service_names_.empty()) { | |
275 LOG(ERROR) << "Failed to release all service names. # of services left: " | |
276 << owned_service_names_.size(); | |
277 } | |
278 | |
279 // Private connection should be closed. | |
280 if (connection_ && connection_type_ == PRIVATE) { | |
281 dbus_connection_close(connection_); | |
282 } | |
283 // dbus_connection_close() won't unref. | |
284 dbus_connection_unref(connection_); | |
285 | |
286 connection_ = NULL; | |
287 } | |
288 | |
289 void Bus::Shutdown(OnShutdownCallback callback) { | |
290 AssertOnOriginThread(); | |
291 | |
292 PostTaskToDBusThread(FROM_HERE, base::Bind(&Bus::ShutdownInternal, | |
293 this, | |
294 callback)); | |
295 } | |
296 | |
297 bool Bus::RequestOwnership(const std::string& service_name) { | |
298 DCHECK(connection_); | |
299 // dbus_bus_request_name() is a blocking call. | |
300 AssertOnDBusThread(); | |
301 | |
302 // Check if we already own the service name. | |
303 if (owned_service_names_.find(service_name) != owned_service_names_.end()) { | |
304 return true; | |
305 } | |
306 | |
307 ScopedDBusError error; | |
308 const int result = dbus_bus_request_name(connection_, | |
309 service_name.c_str(), | |
310 DBUS_NAME_FLAG_DO_NOT_QUEUE, | |
311 error.get()); | |
312 if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { | |
313 LOG(ERROR) << "Failed to get the onwership of " << service_name << ": " | |
314 << (dbus_error_is_set(error.get()) ? error.message() : ""); | |
315 return false; | |
316 } | |
317 owned_service_names_.insert(service_name); | |
318 return true; | |
319 } | |
320 | |
321 bool Bus::ReleaseOwnership(const std::string& service_name) { | |
322 DCHECK(connection_); | |
323 // dbus_bus_request_name() is a blocking call. | |
324 AssertOnDBusThread(); | |
325 | |
326 // Check if we already own the service name. | |
stevenjb
2011/08/15 21:42:04
Minor optimization: set
std::set<std::string>::ite
satorux1
2011/08/16 22:25:37
Done.
| |
327 if (owned_service_names_.find(service_name) == owned_service_names_.end()) { | |
328 LOG(ERROR) << service_name << " is not owned by the bus"; | |
329 return false; | |
330 } | |
331 | |
332 ScopedDBusError error; | |
333 const int result = dbus_bus_release_name(connection_, service_name.c_str(), | |
334 error.get()); | |
335 if (result == DBUS_RELEASE_NAME_REPLY_RELEASED) { | |
336 owned_service_names_.erase(service_name); | |
337 return true; | |
338 } else { | |
339 LOG(ERROR) << "Failed to release the onwership of " << service_name << ": " | |
340 << (error.is_set() ? error.message() : ""); | |
341 return false; | |
342 } | |
343 } | |
344 | |
345 bool Bus::SetUpAsyncOperations() { | |
346 DCHECK(connection_); | |
347 AssertOnDBusThread(); | |
348 | |
349 if (async_operations_are_set_up_) | |
350 return true; | |
351 | |
352 // Process all the incoming data if any, so that OnDispatchStatus() will | |
353 // be called when the incoming data is ready. | |
354 ProcessAllIncomingDataIfAny(); | |
355 | |
356 bool success = dbus_connection_set_watch_functions(connection_, | |
357 &Bus::OnAddWatchThunk, | |
358 &Bus::OnRemoveWatchThunk, | |
359 &Bus::OnToggleWatchThunk, | |
360 this, | |
361 NULL); | |
362 CHECK(success) << "Unable to allocate memory"; | |
363 | |
364 // TODO(satorux): Timeout is not yet implemented. | |
365 success = dbus_connection_set_timeout_functions(connection_, | |
366 &Bus::OnAddTimeoutThunk, | |
367 &Bus::OnRemoveTimeoutThunk, | |
368 &Bus::OnToggleTimeoutThunk, | |
369 this, | |
370 NULL); | |
371 CHECK(success) << "Unable to allocate memory"; | |
372 | |
373 dbus_connection_set_dispatch_status_function( | |
374 connection_, | |
375 &Bus::OnDispatchStatusChangedThunk, | |
376 this, | |
377 NULL); | |
378 | |
379 async_operations_are_set_up_ = true; | |
380 | |
381 return true; | |
382 } | |
383 | |
384 DBusMessage* Bus::SendWithReplyAndBlock(DBusMessage* request, | |
385 int timeout_ms, | |
386 DBusError* error) { | |
387 DCHECK(connection_); | |
388 AssertOnDBusThread(); | |
389 | |
390 return dbus_connection_send_with_reply_and_block( | |
391 connection_, request, timeout_ms, error); | |
392 } | |
393 | |
394 void Bus::SendWithReply(DBusMessage* request, | |
395 DBusPendingCall** pending_call, | |
396 int timeout_ms) { | |
397 DCHECK(connection_); | |
398 AssertOnDBusThread(); | |
399 | |
400 const bool success = dbus_connection_send_with_reply( | |
401 connection_, request, pending_call, timeout_ms); | |
402 CHECK(success) << "Unable to allocate memory"; | |
403 } | |
404 | |
405 bool Bus::TryRegisterObjectPath(const std::string& object_path, | |
406 const DBusObjectPathVTable* vtable, | |
407 void* user_data, | |
408 DBusError* error) { | |
409 DCHECK(connection_); | |
410 AssertOnDBusThread(); | |
411 | |
412 return dbus_connection_try_register_object_path( | |
413 connection_, | |
414 object_path.c_str(), | |
415 vtable, | |
416 user_data, | |
417 error); | |
418 } | |
419 | |
420 void Bus::UnregisterObjectPath(const std::string& object_path) { | |
421 DCHECK(connection_); | |
422 AssertOnDBusThread(); | |
423 | |
424 const bool success = dbus_connection_unregister_object_path( | |
425 connection_, | |
426 object_path.c_str()); | |
427 CHECK(success) << "Unable to allocate memory"; | |
428 } | |
429 | |
430 void Bus::ShutdownInternal(OnShutdownCallback callback) { | |
431 AssertOnDBusThread(); | |
432 | |
433 ShutdownAndBlock(); | |
434 PostTaskToOriginThread(FROM_HERE, callback); | |
435 } | |
436 | |
437 void Bus::ProcessAllIncomingDataIfAny() { | |
438 AssertOnDBusThread(); | |
439 | |
440 // As mentioned at the class comment in .h file, connection_ can be NULL. | |
441 if (!connection_ || !dbus_connection_get_is_connected(connection_)) | |
442 return; | |
443 | |
444 if (dbus_connection_get_dispatch_status(connection_) == | |
445 DBUS_DISPATCH_DATA_REMAINS) { | |
446 while (dbus_connection_dispatch(connection_) == | |
447 DBUS_DISPATCH_DATA_REMAINS); | |
448 } | |
449 } | |
450 | |
451 void Bus::PostTaskToOriginThread(const tracked_objects::Location& from_here, | |
452 const base::Closure& task) { | |
453 origin_loop_->PostTask(from_here, task); | |
454 } | |
455 | |
456 void Bus::PostTaskToDBusThread(const tracked_objects::Location& from_here, | |
457 const base::Closure& task) { | |
458 if (dbus_thread_) | |
459 dbus_thread_->message_loop()->PostTask(from_here, task); | |
460 else | |
461 origin_loop_->PostTask(from_here, task); | |
462 } | |
463 | |
464 void Bus::PostDelayedTaskToDBusThread( | |
465 const tracked_objects::Location& from_here, | |
466 const base::Closure& task, | |
467 int delay_ms) { | |
468 if (dbus_thread_) | |
469 dbus_thread_->message_loop()->PostDelayedTask(from_here, task, delay_ms); | |
470 else | |
471 origin_loop_->PostDelayedTask(from_here, task, delay_ms); | |
472 } | |
473 | |
474 void Bus::AssertOnOriginThread() { | |
475 DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId()); | |
476 } | |
477 | |
478 void Bus::AssertOnDBusThread() { | |
479 base::ThreadRestrictions::AssertIOAllowed(); | |
480 | |
481 if (dbus_thread_) { | |
482 DCHECK_EQ(dbus_thread_id_, base::PlatformThread::CurrentId()); | |
483 } else { | |
484 AssertOnOriginThread(); | |
485 } | |
486 } | |
487 | |
488 dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) { | |
489 AssertOnDBusThread(); | |
490 | |
491 // watch will be deleted when raw_watch is removed in OnRemoveWatch(). | |
492 Watch* watch = new Watch(raw_watch); | |
493 if (watch->IsReadyToBeWatched()) { | |
494 watch->StartWatching(); | |
495 } | |
496 ++num_pending_watches_; | |
497 return true; | |
498 } | |
499 | |
500 void Bus::OnRemoveWatch(DBusWatch* raw_watch) { | |
501 AssertOnDBusThread(); | |
502 | |
503 Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch)); | |
504 delete watch; | |
505 --num_pending_watches_; | |
506 } | |
507 | |
508 void Bus::OnToggleWatch(DBusWatch* raw_watch) { | |
509 AssertOnDBusThread(); | |
510 | |
511 Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch)); | |
512 if (watch->IsReadyToBeWatched()) { | |
513 watch->StartWatching(); | |
514 } else { | |
515 // It's safe to call this if StartWatching() wasn't called, per | |
516 // message_pump_libevent.h. | |
517 watch->StopWatching(); | |
518 } | |
519 } | |
520 | |
521 dbus_bool_t Bus::OnAddTimeout(DBusTimeout* raw_timeout) { | |
522 AssertOnDBusThread(); | |
523 | |
524 // timeout will be deleted when raw_timeout is removed in | |
525 // OnRemoveTimeoutThunk(). | |
526 Timeout* timeout = new Timeout(raw_timeout); | |
527 if (timeout->IsReadyToBeMonitored()) { | |
528 timeout->StartMonitoring(this); | |
529 } | |
530 ++num_pending_timeouts_; | |
531 return true; | |
532 } | |
533 | |
534 void Bus::OnRemoveTimeout(DBusTimeout* raw_timeout) { | |
535 AssertOnDBusThread(); | |
536 | |
537 Timeout* timeout = static_cast<Timeout*>(dbus_timeout_get_data(raw_timeout)); | |
538 timeout->Complete(); | |
539 --num_pending_timeouts_; | |
540 } | |
541 | |
542 void Bus::OnToggleTimeout(DBusTimeout* raw_timeout) { | |
543 AssertOnDBusThread(); | |
544 | |
545 Timeout* timeout = static_cast<Timeout*>(dbus_timeout_get_data(raw_timeout)); | |
546 if (timeout->IsReadyToBeMonitored()) { | |
547 timeout->StartMonitoring(this); | |
548 } else { | |
549 timeout->StopMonitoring(); | |
550 } | |
551 } | |
552 | |
553 void Bus::OnDispatchStatusChanged(DBusConnection* connection, | |
554 DBusDispatchStatus status) { | |
555 DCHECK_EQ(connection, connection_); | |
556 AssertOnDBusThread(); | |
557 | |
558 if (!dbus_connection_get_is_connected(connection)) | |
559 return; | |
560 | |
561 // We cannot call ProcessAllIncomingDataIfAny() here, as calling | |
562 // dbus_connection_dispatch() inside DBusDispatchStatusFunction is | |
563 // prohibited by the D-Bus library. Hence, we post a task here instead. | |
564 // See comments for dbus_connection_set_dispatch_status_function(). | |
565 PostTaskToDBusThread(FROM_HERE, | |
566 base::Bind(&Bus::ProcessAllIncomingDataIfAny, | |
567 this)); | |
568 } | |
569 | |
570 dbus_bool_t Bus::OnAddWatchThunk(DBusWatch* raw_watch, void* data) { | |
571 Bus* self = static_cast<Bus*>(data); | |
572 return self->OnAddWatch(raw_watch); | |
573 } | |
574 | |
575 void Bus::OnRemoveWatchThunk(DBusWatch* raw_watch, void* data) { | |
576 Bus* self = static_cast<Bus*>(data); | |
577 return self->OnRemoveWatch(raw_watch); | |
578 } | |
579 | |
580 void Bus::OnToggleWatchThunk(DBusWatch* raw_watch, void* data) { | |
581 Bus* self = static_cast<Bus*>(data); | |
582 return self->OnToggleWatch(raw_watch); | |
583 } | |
584 | |
585 dbus_bool_t Bus::OnAddTimeoutThunk(DBusTimeout* raw_timeout, void* data) { | |
586 Bus* self = static_cast<Bus*>(data); | |
587 return self->OnAddTimeout(raw_timeout); | |
588 } | |
589 | |
590 void Bus::OnRemoveTimeoutThunk(DBusTimeout* raw_timeout, void* data) { | |
591 Bus* self = static_cast<Bus*>(data); | |
592 return self->OnRemoveTimeout(raw_timeout); | |
593 } | |
594 | |
595 void Bus::OnToggleTimeoutThunk(DBusTimeout* raw_timeout, void* data) { | |
596 Bus* self = static_cast<Bus*>(data); | |
597 return self->OnToggleTimeout(raw_timeout); | |
598 } | |
599 | |
600 void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection, | |
601 DBusDispatchStatus status, | |
602 void* data) { | |
603 Bus* self = static_cast<Bus*>(data); | |
604 return self->OnDispatchStatusChanged(connection, status); | |
605 } | |
606 | |
607 } // namespace dbus | |
OLD | NEW |