summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/message_loop_unittest.cc75
-rw-r--r--base/message_pump_libevent.cc41
-rw-r--r--base/message_pump_libevent.h3
3 files changed, 108 insertions, 11 deletions
diff --git a/base/message_loop_unittest.cc b/base/message_loop_unittest.cc
index d959659..ed7b0c1 100644
--- a/base/message_loop_unittest.cc
+++ b/base/message_loop_unittest.cc
@@ -13,6 +13,9 @@
#include "base/message_pump_win.h"
#include "base/scoped_handle.h"
#endif
+#if defined(OS_POSIX)
+#include "base/message_pump_libevent.h"
+#endif
using base::Thread;
using base::Time;
@@ -1381,3 +1384,75 @@ TEST(MessageLoopTest, WaitForIO) {
RunTest_WaitForIO();
}
#endif // defined(OS_WIN)
+
+#if defined(OS_POSIX)
+
+namespace {
+
+class QuitDelegate : public
+ base::MessagePumpLibevent::Watcher {
+ public:
+ virtual void OnFileCanWriteWithoutBlocking(int fd) {
+ MessageLoop::current()->Quit();
+ }
+ virtual void OnFileCanReadWithoutBlocking(int fd) {
+ MessageLoop::current()->Quit();
+ }
+};
+
+} // namespace
+
+TEST(MessageLoopTest, DISABLED_FileDescriptorWatcherOutlivesMessageLoop) {
+ // Simulate a MessageLoop that dies before an FileDescriptorWatcher.
+ // This could happen when people use the Singleton pattern or atexit.
+ // This is disabled for now because it fails (valgrind shows
+ // invalid reads), and it's not clear any code relies on this...
+ // TODO(dkegel): enable if it turns out we rely on this
+
+ // Create a file descriptor. Doesn't need to be readable or writable,
+ // as we don't need to actually get any notifications.
+ // pipe() is just the easiest way to do it.
+ int pipefds[2];
+ int err = pipe(pipefds);
+ ASSERT_TRUE(err == 0);
+ int fd = pipefds[1];
+ {
+ // Arrange for controller to live longer than message loop.
+ base::MessagePumpLibevent::FileDescriptorWatcher controller;
+ {
+ MessageLoopForIO message_loop;
+
+ QuitDelegate delegate;
+ message_loop.WatchFileDescriptor(fd,
+ true, MessageLoopForIO::WATCH_WRITE, &controller, &delegate);
+ // and don't run the message loop, just destroy it.
+ }
+ }
+ close(pipefds[0]);
+ close(pipefds[1]);
+}
+
+TEST(MessageLoopTest, FileDescriptorWatcherDoubleStop) {
+ // Verify that it's ok to call StopWatchingFileDescriptor().
+ // (Errors only showed up in valgrind.)
+ int pipefds[2];
+ int err = pipe(pipefds);
+ ASSERT_TRUE(err == 0);
+ int fd = pipefds[1];
+ {
+ // Arrange for message loop to live longer than controller.
+ MessageLoopForIO message_loop;
+ {
+ base::MessagePumpLibevent::FileDescriptorWatcher controller;
+
+ QuitDelegate delegate;
+ message_loop.WatchFileDescriptor(fd,
+ true, MessageLoopForIO::WATCH_WRITE, &controller, &delegate);
+ controller.StopWatchingFileDescriptor();
+ }
+ }
+ close(pipefds[0]);
+ close(pipefds[1]);
+}
+
+#endif // defined(OS_LINUX)
diff --git a/base/message_pump_libevent.cc b/base/message_pump_libevent.cc
index 2f828ba..f2681f3 100644
--- a/base/message_pump_libevent.cc
+++ b/base/message_pump_libevent.cc
@@ -9,9 +9,28 @@
#include "base/logging.h"
#include "base/scoped_nsautorelease_pool.h"
+#include "base/scoped_ptr.h"
#include "base/time.h"
#include "third_party/libevent/event.h"
+// Lifecycle of struct event
+// Libevent uses two main data structures:
+// struct event_base (of which there is one per message pump), and
+// struct event (of which there is roughly one per socket).
+// The socket's struct event is created in
+// MessagePumpLibevent::WatchFileDescriptor(),
+// is owned by the FileDescriptorWatcher, and is destroyed in
+// StopWatchingFileDescriptor().
+// It is moved into and out of lists in struct event_base by
+// the libevent functions event_add() and event_del().
+//
+// TODO(dkegel):
+// At the moment bad things happen if a FileDescriptorWatcher
+// is active after its MessagePumpLibevent has been destroyed.
+// See MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop
+// Not clear yet whether that situation occurs in practice,
+// but if it does, we need to fix it.
+
namespace base {
// Return 0 on success
@@ -29,7 +48,7 @@ MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher()
}
MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() {
- if (event_.get()) {
+ if (event_) {
StopWatchingFileDescriptor();
}
}
@@ -37,23 +56,27 @@ MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() {
void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e,
bool is_persistent) {
DCHECK(e);
- DCHECK(event_.get() == NULL);
+ DCHECK(event_ == NULL);
is_persistent_ = is_persistent;
- event_.reset(e);
+ event_ = e;
}
event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() {
- return event_.release();
+ struct event *e = event_;
+ event_ = NULL;
+ return e;
}
bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() {
- if (event_.get() == NULL) {
+ event* e = ReleaseEvent();
+ if (e == NULL)
return true;
- }
- // event_del() is a no-op of the event isn't active.
- return (event_del(event_.get()) == 0);
+ // event_del() is a no-op if the event isn't active.
+ int rv = event_del(e);
+ delete e;
+ return (rv == 0);
}
// Called if a byte is received on the wakeup pipe.
@@ -169,7 +192,7 @@ bool MessagePumpLibevent::WatchFileDescriptor(int fd,
return false;
}
- // Transfer ownership of e to controller.
+ // Transfer ownership of evt to controller.
controller->Init(evt.release(), persistent);
return true;
}
diff --git a/base/message_pump_libevent.h b/base/message_pump_libevent.h
index aa5d5f8..a2a4e1c 100644
--- a/base/message_pump_libevent.h
+++ b/base/message_pump_libevent.h
@@ -6,7 +6,6 @@
#define BASE_MESSAGE_PUMP_LIBEVENT_H_
#include "base/message_pump.h"
-#include "base/scoped_ptr.h"
#include "base/time.h"
// Declare structs we need from libevent.h rather than including it
@@ -44,7 +43,7 @@ class MessagePumpLibevent : public MessagePump {
private:
bool is_persistent_; // false if this event is one-shot.
- scoped_ptr<event> event_;
+ event* event_;
DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher);
};