Chromium Code Reviews| Index: dbus/bus.cc |
| diff --git a/dbus/bus.cc b/dbus/bus.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b71ce5ad086e4870939bd2c51bfd0261e8c27c88 |
| --- /dev/null |
| +++ b/dbus/bus.cc |
| @@ -0,0 +1,234 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "dbus/bus.h" |
| + |
| +#include "base/logging.h" |
| +#include "base/message_loop.h" |
| +#include "base/threading/thread.h" |
| +#include "base/threading/thread_restrictions.h" |
| +#include "dbus/object_proxy.h" |
| + |
| +namespace dbus { |
| + |
| +namespace { |
| + |
| +// The class is used for watching the file descriptor used for D-Bus |
| +// communication. |
| +class Watch : public base::MessagePumpLibevent::Watcher { |
| + public: |
| + Watch(DBusWatch* watch) |
| + : raw_watch_(watch) { |
| + dbus_watch_set_data(raw_watch_, this, NULL); |
| + } |
| + |
| + ~Watch() { |
| + dbus_watch_set_data(raw_watch_, NULL, NULL); |
| + } |
| + |
| + // Returns true if the underlying file descriptor is ready to be watched. |
| + bool IsReadyToBeWatched() { |
| + return dbus_watch_get_enabled(raw_watch_); |
| + } |
| + |
| + // Starts watching the underlying file descriptor. |
| + void StartWatching() { |
| + const int fd = dbus_watch_get_unix_fd(raw_watch_); |
| + const int flags = dbus_watch_get_flags(raw_watch_); |
| + |
| + MessageLoopForIO::Mode mode; |
| + if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) |
| + mode = MessageLoopForIO::WATCH_READ_WRITE; |
| + if (flags & DBUS_WATCH_READABLE) |
|
stevenjb
2011/07/29 21:54:17
else if
satorux1
2011/08/01 19:56:41
Done.
|
| + mode = MessageLoopForIO::WATCH_READ; |
| + if (flags & DBUS_WATCH_WRITABLE) |
|
stevenjb
2011/07/29 21:54:17
else if
satorux1
2011/08/01 19:56:41
Done.
|
| + mode = MessageLoopForIO::WATCH_WRITE; |
|
stevenjb
2011/07/29 21:54:17
else NOTREACHED()
satorux1
2011/08/01 19:56:41
Done.
|
| + |
| + const bool persistent = true; // Watch persistently. |
| + const bool success = MessageLoopForIO::current()->WatchFileDescriptor( |
| + fd, |
| + persistent, |
| + mode, |
| + &file_descriptor_watcher_, |
| + this); |
| + DCHECK(success) << "Unable to allocate memory"; |
| + } |
| + |
| + // Stops watching the underlying file descriptor. |
| + void StopWatching() { |
| + file_descriptor_watcher_->StopWatchingFileDescriptor(); |
| + } |
| + |
| + private: |
| + // Implement MessagePumpLibevent::Watcher. |
| + virtual void OnFileCanReadWithoutBlocking(int fd) { |
| + const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_READABLE); |
| + DCHECK(success) << "Unable to allocate memory"; |
| + } |
| + |
| + // Implement MessagePumpLibevent::Watcher. |
| + virtual void OnFileCanWriteWithoutBlocking(int fd) { |
| + const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_WRITABLE); |
| + DCHECK(success) << "Unable to allocate memory"; |
| + } |
| + |
| + DBusWatch* raw_watch_; |
| + base::MessagePumpLibevent::FileDescriptorWatcher file_descriptor_watcher_; |
| +}; |
| + |
| +} // namespace |
| + |
| +Bus::Options::Options() |
| + : bus_type(SESSION), |
| + connection_type(PRIVATE), |
| + io_thread(NULL) { |
| +} |
| + |
| +Bus::Bus(const Options& options) |
| + : bus_type_(options.bus_type), |
| + connection_type_(options.connection_type), |
| + io_thread_(options.io_thread), |
| + connection_(NULL) { |
| + if (io_thread_) { |
| + DCHECK(io_thread_->IsRunning()) |
| + << "The IO thread should be running"; |
| + DCHECK_EQ(MessageLoop::TYPE_IO, |
| + io_thread_->message_loop()->type()) |
| + << "The IO thread should have an MessageLoopForIO attached"; |
| + } |
| +} |
| + |
| +Bus::~Bus() { |
| + // Private connection should be closed. |
| + if (connection_ && connection_type_ == PRIVATE) { |
| + dbus_connection_close(connection_); |
| + } |
| +} |
| + |
| +ObjectProxy* Bus::GetObjectProxy(const std::string& service_name, |
| + const std::string& object_path) { |
| + return new ObjectProxy(this, service_name, object_path); |
| +} |
| + |
| +bool Bus::Init() { |
| + // dbus_bus_get_private() and dbus_bus_get() are blocking calls. |
| + base::ThreadRestrictions::AssertIOAllowed(); |
| + |
| + // Check if it's already initialized. |
| + if (connection_) |
| + return true; |
| + |
| + DBusError error = {}; |
|
stevenjb
2011/07/29 21:54:17
nit: don't need to initialize error (and better no
satorux1
2011/08/01 19:56:41
Done.
|
| + dbus_error_init(&error); |
| + const DBusBusType dbus_bus_type = static_cast<DBusBusType>(bus_type_); |
| + if (connection_type_ == PRIVATE) { |
| + connection_ = dbus_bus_get_private(dbus_bus_type, &error); |
| + } else { |
| + connection_ = dbus_bus_get(dbus_bus_type, &error); |
| + } |
| + if (!connection_) { |
| + if (dbus_error_is_set(&error)) { |
| + LOG(ERROR) << error.message; |
| + } |
| + return false; |
| + } |
| + |
| + // This is unnecessary if client only uses synchronous APIs, but it |
| + // won't hurt. |
| + SetUpAsyncOperations(); |
| + |
| + return true; |
| +} |
| + |
| +void Bus::SetUpAsyncOperations() { |
| + // Process all the incoming data if any, so that OnDispatchStatus() will |
| + // be called when the incoming data is ready. |
| + ProcessAllIncomingDataIfAny(connection_); |
| + |
| + dbus_connection_set_watch_functions(connection_, |
| + &Bus::OnAddWatchStub, |
| + &Bus::OnRemoveWatchStub, |
| + &Bus::OnToggleWatchStub, |
| + this, |
| + NULL); |
| + // TODO(satorux): Timeout is not yet implemented. |
| + dbus_connection_set_timeout_functions(connection_, |
| + NULL, |
| + NULL, |
| + NULL, |
| + NULL, |
| + NULL); |
| + dbus_connection_set_dispatch_status_function( |
| + connection_, |
| + &Bus::OnDispatchStatusChangedStub, |
| + connection_, |
| + NULL); |
| +} |
| + |
| +void Bus::ProcessAllIncomingDataIfAny(DBusConnection* connection) { |
| + if (dbus_connection_get_dispatch_status(connection) == |
| + DBUS_DISPATCH_DATA_REMAINS) { |
| + while (dbus_connection_dispatch(connection) == |
| + DBUS_DISPATCH_DATA_REMAINS); |
| + } |
| +} |
| + |
| +void Bus::PostTaskToIoThread(const tracked_objects::Location& from_here, |
| + const base::Closure& task) { |
| + DCHECK(io_thread_); |
| + io_thread_->message_loop()->PostTask(from_here, task); |
| +} |
| + |
| +dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) { |
| + Watch* watch = new Watch(raw_watch); |
|
stevenjb
2011/07/29 21:54:17
This confused me for a while re: when Watch gets d
satorux1
2011/08/01 19:56:41
Good point. I think adding a comment is sufficient
|
| + if (watch->IsReadyToBeWatched()) { |
| + watch->StartWatching(); |
| + } |
| + return true; |
| +} |
| + |
| +void Bus::OnRemoveWatch(DBusWatch* raw_watch) { |
| + Watch* watch = reinterpret_cast<Watch*>(dbus_watch_get_data(raw_watch)); |
|
stevenjb
2011/07/29 21:54:17
dbus_watch_get_data should return a void*, so stat
satorux1
2011/08/01 19:56:41
You are right. Done.
|
| + delete watch; |
| +} |
| + |
| +void Bus::OnToggleWatch(DBusWatch* raw_watch) { |
| + Watch* watch = reinterpret_cast<Watch*>(dbus_watch_get_data(raw_watch)); |
| + if (watch->IsReadyToBeWatched()) { |
| + watch->StartWatching(); |
| + } else { |
| + watch->StopWatching(); |
|
stevenjb
2011/07/29 21:54:17
Is it safe to call this if StartWatching() was nev
satorux1
2011/08/01 19:56:41
Good point. Yes it's safe. Added a comment about i
|
| + } |
| +} |
| + |
| +void Bus::OnDispatchStatusChanged(DBusConnection* connection, |
| + DBusDispatchStatus status) { |
| + if (!dbus_connection_get_is_connected(connection)) |
| + return; |
| + ProcessAllIncomingDataIfAny(connection); |
| +} |
| + |
| +dbus_bool_t Bus::OnAddWatchStub(DBusWatch* raw_watch, void* data) { |
|
stevenjb
2011/07/29 21:54:17
These could be local functions instead of class st
satorux1
2011/08/01 19:56:41
Here, we need to call self->OnAddWatch(), which is
|
| + Bus* self = reinterpret_cast<Bus*>(data); |
| + return self->OnAddWatch(raw_watch); |
| +} |
| + |
| +void Bus::OnRemoveWatchStub(DBusWatch* raw_watch, void* data) { |
| + Bus* self = reinterpret_cast<Bus*>(data); |
| + return self->OnRemoveWatch(raw_watch); |
| +} |
| + |
| +void Bus::OnToggleWatchStub(DBusWatch* raw_watch, void* data) { |
| + Bus* self = reinterpret_cast<Bus*>(data); |
| + return self->OnToggleWatch(raw_watch); |
| +} |
| + |
| +void Bus::OnDispatchStatusChangedStub(DBusConnection* connection, |
| + DBusDispatchStatus status, |
| + void* data) { |
| + Bus* self = reinterpret_cast<Bus*>(data); |
| + return self->OnDispatchStatusChanged(connection, status); |
| +} |
| + |
| +} // namespace dbus |