summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/message_loop.cc18
-rw-r--r--base/message_loop.h29
-rw-r--r--base/object_watcher.cc13
-rw-r--r--base/object_watcher.h7
-rw-r--r--base/object_watcher_unittest.cc33
5 files changed, 94 insertions, 6 deletions
diff --git a/base/message_loop.cc b/base/message_loop.cc
index 7d2fbff..78f820c 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -140,9 +140,17 @@ MessageLoop::MessageLoop() : message_hwnd_(NULL),
MessageLoop::~MessageLoop() {
DCHECK(this == current());
+
+ // Let interested parties have one last shot at accessing this.
+ FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_,
+ WillDestroyCurrentMessageLoop());
+
+ // OK, now make it so that no one can find us.
ThreadLocalStorage::Set(tls_index_, NULL);
+
DCHECK(!dispatcher_);
DCHECK(!quit_received_ && !quit_now_);
+
// Most tasks that have not been Run() are deleted in the |timer_manager_|
// destructor after we remove our tls index. We delete the tasks in our
// queues here so their destuction is similar to the tasks in the
@@ -158,6 +166,16 @@ void MessageLoop::SetThreadName(const std::string& thread_name) {
StartHistogrammer();
}
+void MessageLoop::AddDestructionObserver(DestructionObserver *obs) {
+ DCHECK(this == current());
+ destruction_observers_.AddObserver(obs);
+}
+
+void MessageLoop::RemoveDestructionObserver(DestructionObserver *obs) {
+ DCHECK(this == current());
+ destruction_observers_.RemoveObserver(obs);
+}
+
void MessageLoop::AddObserver(Observer *obs) {
DCHECK(this == current());
observers_.AddObserver(obs);
diff --git a/base/message_loop.h b/base/message_loop.h
index 1610d07..cd7b1c5 100644
--- a/base/message_loop.h
+++ b/base/message_loop.h
@@ -150,6 +150,10 @@ class MessageLoop {
virtual void OnObjectSignaled(HANDLE object) = 0;
};
+ // Have the current thread's message loop watch for a signaled object.
+ // Pass a null watcher to stop watching the object.
+ bool WatchObject(HANDLE, Watcher*);
+
// Dispatcher is used during a nested invocation of Run to dispatch events.
// If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
// dispatch events (or invoke TranslateMessage), rather every message is
@@ -172,9 +176,27 @@ class MessageLoop {
virtual bool Dispatch(const MSG& msg) = 0;
};
- // Have the current thread's message loop watch for a signaled object.
- // Pass a null watcher to stop watching the object.
- bool WatchObject(HANDLE, Watcher*);
+ // A DestructionObserver is notified when the current MessageLoop is being
+ // destroyed. These obsevers are notified prior to MessageLoop::current()
+ // being changed to return NULL. This gives interested parties the chance to
+ // do final cleanup that depends on the MessageLoop.
+ //
+ // NOTE: Any tasks posted to the MessageLoop during this notification will
+ // not be run. Instead, they will be deleted.
+ //
+ class DestructionObserver {
+ public:
+ virtual ~DestructionObserver() {}
+ virtual void WillDestroyCurrentMessageLoop() = 0;
+ };
+
+ // Add a DestructionObserver, which will start receiving notifications
+ // immediately.
+ void AddDestructionObserver(DestructionObserver* destruction_observer);
+
+ // Remove a DestructionObserver. It is safe to call this method while a
+ // DestructionObserver is receiving a notification callback.
+ void RemoveDestructionObserver(DestructionObserver* destruction_observer);
// An Observer is an object that receives global notifications from the
// MessageLoop.
@@ -577,6 +599,7 @@ class MessageLoop {
std::vector<Watcher*> watchers_;
ObserverList<Observer> observers_;
+ ObserverList<DestructionObserver> destruction_observers_;
HWND message_hwnd_;
IDMap<Task> timed_tasks_;
// A recursion block that prevents accidentally running additonal tasks when
diff --git a/base/object_watcher.cc b/base/object_watcher.cc
index 5e8c27b..9872a4c 100644
--- a/base/object_watcher.cc
+++ b/base/object_watcher.cc
@@ -29,7 +29,6 @@
#include "base/object_watcher.h"
-#include "base/message_loop.h"
#include "base/logging.h"
namespace base {
@@ -91,6 +90,10 @@ bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
}
watch_ = watch;
+
+ // We need to know if the current message loop is going away so we can
+ // prevent the wait thread from trying to access a dead message loop.
+ MessageLoop::current()->AddDestructionObserver(this);
return true;
}
@@ -124,6 +127,8 @@ bool ObjectWatcher::StopWatching() {
delete watch_;
watch_ = NULL;
+
+ MessageLoop::current()->RemoveDestructionObserver(this);
return true;
}
@@ -142,4 +147,10 @@ void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
watch->origin_loop->PostTask(FROM_HERE, watch);
}
+void ObjectWatcher::WillDestroyCurrentMessageLoop() {
+ // Need to shutdown the watch so that we don't try to access the MessageLoop
+ // after this point.
+ StopWatching();
+}
+
} // namespace base
diff --git a/base/object_watcher.h b/base/object_watcher.h
index de4816a..9058ab4 100644
--- a/base/object_watcher.h
+++ b/base/object_watcher.h
@@ -32,7 +32,7 @@
#include <windows.h>
-#include "base/basictypes.h"
+#include "base/message_loop.h"
namespace base {
@@ -64,7 +64,7 @@ namespace base {
// scope, the watcher_ will be destroyed, and there is no need to worry about
// OnObjectSignaled being called on a deleted MyClass pointer. Easy!
//
-class ObjectWatcher {
+class ObjectWatcher : public MessageLoop::DestructionObserver {
public:
class Delegate {
public:
@@ -97,6 +97,9 @@ class ObjectWatcher {
// Called on a background thread when done waiting.
static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out);
+ // MessageLoop::DestructionObserver implementation:
+ virtual void WillDestroyCurrentMessageLoop();
+
// Internal state.
struct Watch;
Watch* watch_;
diff --git a/base/object_watcher_unittest.cc b/base/object_watcher_unittest.cc
index 51ed968..6c3fcce 100644
--- a/base/object_watcher_unittest.cc
+++ b/base/object_watcher_unittest.cc
@@ -27,6 +27,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <process.h>
+
#include "base/message_loop.h"
#include "base/object_watcher.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -113,3 +115,34 @@ TEST(ObjectWatcherTest, CancelAfterSet) {
CloseHandle(event);
}
+
+// Used so we can simulate a MessageLoop that dies before an ObjectWatcher.
+// This ordinarily doesn't happen when people use the Thread class, but it can
+// happen when people use the Singleton pattern or atexit.
+static unsigned __stdcall ThreadFunc(void* param) {
+ HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); // not signaled
+ {
+ base::ObjectWatcher watcher;
+ {
+ MessageLoop message_loop;
+
+ QuitDelegate delegate;
+ watcher.StartWatching(event, &delegate);
+ }
+ }
+ CloseHandle(event);
+ return 0;
+}
+
+TEST(ObjectWatcherTest, OutlivesMessageLoop) {
+ unsigned int thread_id;
+ HANDLE thread = reinterpret_cast<HANDLE>(
+ _beginthreadex(NULL,
+ 0,
+ ThreadFunc,
+ NULL,
+ 0,
+ &thread_id));
+ WaitForSingleObject(thread, INFINITE);
+ CloseHandle(thread);
+}