Skip to content

Commit

Permalink
Migrate to base::FileDescriptionWatcher in dbus/bus.cc.
Browse files Browse the repository at this point in the history
Before this CL, dbus::Bus::OnAddWatch called
MessageLoopForIO::current()->WatchFileDescriptor(), which is invalid from a
non-MessageLoopForIO thread. This caused a crash when dbus::Bus::OnAddWatch
was called from a TaskScheduler thread.

With this CL, dbus::Bus::OnAddWatch uses
base::FileDescriptorWatcher::WatchReadable/Writable instead of
MessageLoopForIO::current()->WatchFileDescriptor(). This works from
base::Threads that run a MessageLoopForIO and TaskScheduler threads.

This CL also instantiates a base::FileDescriptorWatcher in tests that use
dbus::Bus::OnAddWatch. This is required to allow usage of
base::FileDescriptorWatcher::WatchReadable/Writable on the main thread
of a test.

BUG=708142

Review-Url: https://codereview.chromium.org/2808983002
Cr-Commit-Position: refs/heads/master@{#465215}
  • Loading branch information
fdoray authored and Commit bot committed Apr 18, 2017
1 parent f8ebda9 commit ebc379c
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 42 deletions.
68 changes: 30 additions & 38 deletions dbus/bus.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@

#include <stddef.h>

#include <memory>

#include "base/bind.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread.h"
Expand Down Expand Up @@ -42,14 +44,13 @@ const char kServiceNameOwnerChangeMatchRule[] =

// The class is used for watching the file descriptor used for D-Bus
// communication.
class Watch : public base::MessagePumpLibevent::Watcher {
class Watch {
public:
explicit Watch(DBusWatch* watch)
: raw_watch_(watch), file_descriptor_watcher_(FROM_HERE) {
explicit Watch(DBusWatch* watch) : raw_watch_(watch) {
dbus_watch_set_data(raw_watch_, this, NULL);
}

~Watch() override { dbus_watch_set_data(raw_watch_, NULL, NULL); }
~Watch() { dbus_watch_set_data(raw_watch_, NULL, NULL); }

// Returns true if the underlying file descriptor is ready to be watched.
bool IsReadyToBeWatched() {
Expand All @@ -59,44 +60,38 @@ class Watch : public base::MessagePumpLibevent::Watcher {
// Starts watching the underlying file descriptor.
void StartWatching() {
const int file_descriptor = dbus_watch_get_unix_fd(raw_watch_);
const int flags = dbus_watch_get_flags(raw_watch_);

base::MessageLoopForIO::Mode mode = base::MessageLoopForIO::WATCH_READ;
if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE))
mode = base::MessageLoopForIO::WATCH_READ_WRITE;
else if (flags & DBUS_WATCH_READABLE)
mode = base::MessageLoopForIO::WATCH_READ;
else if (flags & DBUS_WATCH_WRITABLE)
mode = base::MessageLoopForIO::WATCH_WRITE;
else
NOTREACHED();

const bool persistent = true; // Watch persistently.
const bool success = base::MessageLoopForIO::current()->WatchFileDescriptor(
file_descriptor, persistent, mode, &file_descriptor_watcher_, this);
CHECK(success) << "Unable to allocate memory";
const unsigned int flags = dbus_watch_get_flags(raw_watch_);

// Using base::Unretained(this) is safe because watches are automatically
// canceled when |read_watcher_| and |write_watcher_| are destroyed.
if (flags & DBUS_WATCH_READABLE) {
read_watcher_ = base::FileDescriptorWatcher::WatchReadable(
file_descriptor,
base::Bind(&Watch::OnFileReady, base::Unretained(this),
DBUS_WATCH_READABLE));
}
if (flags & DBUS_WATCH_WRITABLE) {
write_watcher_ = base::FileDescriptorWatcher::WatchWritable(
file_descriptor,
base::Bind(&Watch::OnFileReady, base::Unretained(this),
DBUS_WATCH_WRITABLE));
}
}

// Stops watching the underlying file descriptor.
void StopWatching() {
file_descriptor_watcher_.StopWatchingFileDescriptor();
read_watcher_.reset();
write_watcher_.reset();
}

private:
// Implement MessagePumpLibevent::Watcher.
void OnFileCanReadWithoutBlocking(int file_descriptor) override {
const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_READABLE);
CHECK(success) << "Unable to allocate memory";
}

// Implement MessagePumpLibevent::Watcher.
void OnFileCanWriteWithoutBlocking(int file_descriptor) override {
const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_WRITABLE);
CHECK(success) << "Unable to allocate memory";
void OnFileReady(unsigned int flags) {
CHECK(dbus_watch_handle(raw_watch_, flags)) << "Unable to allocate memory";
}

DBusWatch* raw_watch_;
base::MessagePumpLibevent::FileDescriptorWatcher file_descriptor_watcher_;
std::unique_ptr<base::FileDescriptorWatcher::Controller> read_watcher_;
std::unique_ptr<base::FileDescriptorWatcher::Controller> write_watcher_;
};

// The class is used for monitoring the timeout used for D-Bus method
Expand Down Expand Up @@ -1048,13 +1043,10 @@ void Bus::OnToggleWatch(DBusWatch* raw_watch) {
AssertOnDBusThread();

Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch));
if (watch->IsReadyToBeWatched()) {
if (watch->IsReadyToBeWatched())
watch->StartWatching();
} else {
// It's safe to call this if StartWatching() wasn't called, per
// message_pump_libevent.h.
else
watch->StopWatching();
}
}

dbus_bool_t Bus::OnAddTimeout(DBusTimeout* raw_timeout) {
Expand Down
9 changes: 6 additions & 3 deletions dbus/bus_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "dbus/bus.h"

#include "base/bind.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop/message_loop.h"
Expand Down Expand Up @@ -318,9 +319,11 @@ TEST(BusTest, DoubleAddAndRemoveMatch) {
}

TEST(BusTest, ListenForServiceOwnerChange) {
// Setup the current thread's MessageLoop. Must be of TYPE_IO for the
// listeners to work.
base::MessageLoop message_loop(base::MessageLoop::TYPE_IO);
base::MessageLoopForIO message_loop;

// This enables FileDescriptorWatcher, which is required by dbus::Watch.
base::FileDescriptorWatcher file_descriptor_watcher(&message_loop);

RunLoopWithExpectedCount run_loop_state;

// Create the bus.
Expand Down
9 changes: 8 additions & 1 deletion dbus/object_proxy_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "dbus/object_proxy.h"
#include "base/bind.h"
#include "base/files/file_descriptor_watcher_posix.h"
#include "base/memory/ref_counted.h"
#include "base/run_loop.h"
#include "dbus/bus.h"
#include "dbus/object_proxy.h"
#include "dbus/test_service.h"
#include "testing/gtest/include/gtest/gtest.h"

Expand All @@ -15,6 +16,8 @@ namespace {

class ObjectProxyTest : public testing::Test {
protected:
ObjectProxyTest() : file_descriptor_watcher_(&message_loop_) {}

void SetUp() override {
Bus::Options bus_options;
bus_options.bus_type = Bus::SESSION;
Expand All @@ -25,6 +28,10 @@ class ObjectProxyTest : public testing::Test {
void TearDown() override { bus_->ShutdownAndBlock(); }

base::MessageLoopForIO message_loop_;

// This enables FileDescriptorWatcher, which is required by dbus::Watch.
base::FileDescriptorWatcher file_descriptor_watcher_;

scoped_refptr<Bus> bus_;
};

Expand Down

0 comments on commit ebc379c

Please sign in to comment.