summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/object_watcher.cc74
-rw-r--r--base/object_watcher.h76
-rw-r--r--base/object_watcher_unittest.cc112
-rw-r--r--base/thread.cc20
-rw-r--r--base/tracked.cc1
5 files changed, 126 insertions, 157 deletions
diff --git a/base/object_watcher.cc b/base/object_watcher.cc
index c16f917..5e8c27b 100644
--- a/base/object_watcher.cc
+++ b/base/object_watcher.cc
@@ -41,7 +41,7 @@ struct ObjectWatcher::Watch : public Task {
HANDLE object; // The object being watched
HANDLE wait_object; // Returned by RegisterWaitForSingleObject
MessageLoop* origin_loop; // Used to get back to the origin thread
- scoped_ptr<Task> task; // Task to notify when signaled
+ Delegate* delegate; // Delegate to notify when signaled
bool did_signal; // DoneWaiting was called
virtual void Run() {
@@ -50,42 +50,33 @@ struct ObjectWatcher::Watch : public Task {
if (!watcher)
return;
- // Put this on the stack since CancelWatch deletes task. It is a good
- // to call CancelWatch before running the task because we want to allow
- // the consumer to call AddWatch again inside Run.
- Task* task_to_run = task.release();
-
- watcher->CancelWatch(object);
+ DCHECK(did_signal);
+ watcher->StopWatching();
- task_to_run->ResetBirthTime();
- task_to_run->Run();
- delete task_to_run;
+ delegate->OnObjectSignaled(object);
}
};
//-----------------------------------------------------------------------------
-ObjectWatcher::ObjectWatcher() {
+ObjectWatcher::ObjectWatcher() : watch_(NULL) {
}
ObjectWatcher::~ObjectWatcher() {
- // Cancel any watches that may still exist.
- while (!watches_.empty())
- CancelWatch(watches_.begin()->first);
+ StopWatching();
}
-bool ObjectWatcher::AddWatch(const tracked_objects::Location& from_here,
- HANDLE object, Task* task) {
- task->SetBirthPlace(from_here);
-
- linked_ptr<Watch>& watch = watches_[object];
- CHECK(!watch.get()) << "Already watched!";
+bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) {
+ if (watch_) {
+ NOTREACHED() << "Already watching an object";
+ return false;
+ }
- watch.reset(new Watch());
+ Watch* watch = new Watch;
watch->watcher = this;
watch->object = object;
watch->origin_loop = MessageLoop::current();
- watch->task.reset(task);
+ watch->delegate = delegate;
watch->did_signal = false;
// Since our job is to just notice when an object is signaled and report the
@@ -93,44 +84,46 @@ bool ObjectWatcher::AddWatch(const tracked_objects::Location& from_here,
DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE;
if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting,
- watch.get(), INFINITE, wait_flags)) {
+ watch, INFINITE, wait_flags)) {
NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError();
- watches_.erase(object);
+ delete watch;
return false;
}
+ watch_ = watch;
return true;
}
-bool ObjectWatcher::CancelWatch(HANDLE object) {
- WatchMap::iterator i = watches_.find(object);
- if (i == watches_.end())
+bool ObjectWatcher::StopWatching() {
+ if (!watch_)
return false;
- Watch* watch = i->second.get();
+ // Make sure ObjectWatcher is used in a single-threaded fashion.
+ DCHECK(watch_->origin_loop == MessageLoop::current());
// If DoneWaiting is in progress, we wait for it to finish. We know whether
// DoneWaiting happened or not by inspecting the did_signal flag.
- if (!UnregisterWaitEx(watch->wait_object, INVALID_HANDLE_VALUE)) {
+ if (!UnregisterWaitEx(watch_->wait_object, INVALID_HANDLE_VALUE)) {
NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError();
return false;
}
- // If DoneWaiting was called, then the watch would have been posted as a
- // task, and will therefore be deleted by the MessageLoop. Otherwise, we
- // need to take care to delete it here.
- if (watch->did_signal)
- i->second.release();
+ // Make sure that we see any mutation to did_signal. This should be a no-op
+ // since we expect that UnregisterWaitEx resulted in a memory barrier, but
+ // just to be sure, we're going to be explicit.
+ MemoryBarrier();
// If the watch has been posted, then we need to make sure it knows not to do
// anything once it is run.
- watch->watcher = NULL;
+ watch_->watcher = NULL;
- // Delete the task object now so that everything, from the perspective of the
- // consumer, is cleaned up once we return from CancelWatch.
- watch->task.reset();
+ // If DoneWaiting was called, then the watch would have been posted as a
+ // task, and will therefore be deleted by the MessageLoop. Otherwise, we
+ // need to take care to delete it here.
+ if (!watch_->did_signal)
+ delete watch_;
- watches_.erase(i);
+ watch_ = NULL;
return true;
}
@@ -143,6 +136,9 @@ void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) {
// Record that we ran this function.
watch->did_signal = true;
+ // We rely on the locking in PostTask() to ensure that a memory barrier is
+ // provided, which in turn ensures our change to did_signal can be observed
+ // on the target thread.
watch->origin_loop->PostTask(FROM_HERE, watch);
}
diff --git a/base/object_watcher.h b/base/object_watcher.h
index 58431b1..de4816a 100644
--- a/base/object_watcher.h
+++ b/base/object_watcher.h
@@ -32,53 +32,77 @@
#include <windows.h>
-#include <map>
-
-#include "base/linked_ptr.h"
-#include "base/tracked.h"
-
-class Task;
+#include "base/basictypes.h"
namespace base {
-// A class that enables support for asynchronously waiting for Windows objects
-// to become signaled. Supports waiting on more than 64 objects.
+// A class that provides a means to asynchronously wait for a Windows object to
+// become signaled. It is an abstraction around RegisterWaitForSingleObject
+// that provides a notification callback, OnObjectSignaled, that runs back on
+// the origin thread (i.e., the thread that called StartWatching).
+//
+// This class acts like a smart pointer such that when it goes out-of-scope,
+// UnregisterWaitEx is automatically called, and any in-flight notification is
+// suppressed.
+//
+// Typical usage:
+//
+// class MyClass : public base::ObjectWatcher::Delegate {
+// public:
+// void DoStuffWhenSignaled(HANDLE object) {
+// watcher_.StartWatching(object, this);
+// }
+// virtual void OnObjectSignaled(HANDLE object) {
+// // OK, time to do stuff!
+// }
+// private:
+// base::ObjectWatcher watcher_;
+// };
+//
+// In the above example, MyClass wants to "do stuff" when object becomes
+// signaled. ObjectWatcher makes this task easy. When MyClass goes out of
+// 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 {
public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+ // Called from the MessageLoop when a signaled object is detected. To
+ // continue watching the object, AddWatch must be called again.
+ virtual void OnObjectSignaled(HANDLE object) = 0;
+ };
+
ObjectWatcher();
~ObjectWatcher();
- // When the object is signaled, the given task is run on the thread where
- // Watch is called. The ObjectWatcher assumes ownership of the task and
- // will ensure that it gets deleted eventually.
+ // When the object is signaled, the given delegate is notified on the thread
+ // where StartWatching is called. The ObjectWatcher is not responsible for
+ // deleting the delegate.
//
- // NOTE: It is an error to call this method on an object that is already
- // being watched by this ObjectWatcher.
+ // Returns true if the watch was started. Otherwise, false is returned.
//
- // Returns true if the watch was added. Otherwise, false is returned.
- //
- bool AddWatch(const tracked_objects::Location& from_here, HANDLE object,
- Task* task);
+ bool StartWatching(HANDLE object, Delegate* delegate);
- // Stops watching the given object. Does nothing if the watch has already
- // completed. If the watch is still active, then it is canceled, and the
- // associated task is deleted.
+ // Stops watching. Does nothing if the watch has already completed. If the
+ // watch is still active, then it is canceled, and the associated delegate is
+ // not notified.
//
// Returns true if the watch was canceled. Otherwise, false is returned.
//
- bool CancelWatch(HANDLE object);
+ bool StopWatching();
private:
// Called on a background thread when done waiting.
static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out);
- // Passed as the param argument to the above methods.
+ // Internal state.
struct Watch;
+ Watch* watch_;
- typedef std::map<HANDLE, linked_ptr<Watch>> WatchMap;
- WatchMap watches_;
-};
-
+ DISALLOW_COPY_AND_ASSIGN(ObjectWatcher);
+};
} // namespace base
#endif // BASE_OBJECT_WATCHER_H_
diff --git a/base/object_watcher_unittest.cc b/base/object_watcher_unittest.cc
index 16e6ae5..51ed968 100644
--- a/base/object_watcher_unittest.cc
+++ b/base/object_watcher_unittest.cc
@@ -34,13 +34,19 @@
namespace {
typedef testing::Test ObjectWatcherTest;
-class DecrementCountTask : public Task {
+class QuitDelegate : public base::ObjectWatcher::Delegate {
public:
- DecrementCountTask(int* counter) : counter_(counter) {
+ virtual void OnObjectSignaled(HANDLE object) {
+ MessageLoop::current()->Quit();
}
- virtual void Run() {
- if (--(*counter_) == 0)
- MessageLoop::current()->Quit();
+};
+
+class DecrementCountDelegate : public base::ObjectWatcher::Delegate {
+ public:
+ DecrementCountDelegate(int* counter) : counter_(counter) {
+ }
+ virtual void OnObjectSignaled(HANDLE object) {
+ --(*counter_);
}
private:
int* counter_;
@@ -54,7 +60,8 @@ TEST(ObjectWatcherTest, BasicSignal) {
// A manual-reset event that is not yet signaled.
HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL);
- bool ok = watcher.AddWatch(FROM_HERE, event, new MessageLoop::QuitTask());
+ QuitDelegate delegate;
+ bool ok = watcher.StartWatching(event, &delegate);
EXPECT_TRUE(ok);
SetEvent(event);
@@ -70,98 +77,39 @@ TEST(ObjectWatcherTest, BasicCancel) {
// A manual-reset event that is not yet signaled.
HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL);
- bool ok = watcher.AddWatch(FROM_HERE, event, new MessageLoop::QuitTask());
+ QuitDelegate delegate;
+ bool ok = watcher.StartWatching(event, &delegate);
EXPECT_TRUE(ok);
- watcher.CancelWatch(event);
+ watcher.StopWatching();
CloseHandle(event);
}
-TEST(ObjectWatcherTest, ManySignal) {
- base::ObjectWatcher watcher;
-
- const int kNumObjects = 2000;
-
- int counter = kNumObjects;
- HANDLE events[kNumObjects];
-
- for (int i = 0; i < kNumObjects; ++i) {
- // A manual-reset event that is not yet signaled.
- events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
-
- bool ok = watcher.AddWatch(
- FROM_HERE, events[i], new DecrementCountTask(&counter));
- EXPECT_TRUE(ok);
- }
-
- for (int i = 0; i < kNumObjects; ++i)
- SetEvent(events[i]);
-
- MessageLoop::current()->Run();
-
- for (int i = 0; i < kNumObjects; ++i)
- CloseHandle(events[i]);
-}
-
-TEST(ObjectWatcherTest, ManyCancel) {
- base::ObjectWatcher watcher;
-
- const int kNumObjects = 2000;
-
- int counter = kNumObjects;
- HANDLE events[kNumObjects];
-
- for (int i = 0; i < kNumObjects; ++i) {
- // A manual-reset event that is not yet signaled.
- events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
-
- bool ok = watcher.AddWatch(
- FROM_HERE, events[i], new DecrementCountTask(&counter));
- EXPECT_TRUE(ok);
- }
-
- for (int i = 0; i < kNumObjects; ++i)
- watcher.CancelWatch(events[i]);
-
- for (int i = 0; i < kNumObjects; ++i)
- CloseHandle(events[i]);
-}
TEST(ObjectWatcherTest, CancelAfterSet) {
base::ObjectWatcher watcher;
- const int kNumObjects = 50;
+ int counter = 1;
+ DecrementCountDelegate delegate(&counter);
- int counter = kNumObjects;
- HANDLE events[kNumObjects];
-
- for (int i = 0; i < kNumObjects; ++i) {
// A manual-reset event that is not yet signaled.
- events[i] = CreateEvent(NULL, TRUE, FALSE, NULL);
+ HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL);
- bool ok = watcher.AddWatch(
- FROM_HERE, events[i], new DecrementCountTask(&counter));
- EXPECT_TRUE(ok);
- }
+ bool ok = watcher.StartWatching(event, &delegate);
+ EXPECT_TRUE(ok);
- for (int i = 0; i < kNumObjects; ++i) {
- SetEvent(events[i]);
+ SetEvent(event);
- // Let the background thread do its business
- SleepEx(10, TRUE);
+ // Let the background thread do its business
+ Sleep(30);
- // Occasionally pump some tasks. Sometimes we cancel a watch after its
- // task has run and other times we do nothing.
- if (i % 3 == 0)
- MessageLoop::current()->RunAllPending();
-
- if (watcher.CancelWatch(events[i]))
- --counter;
- }
+ watcher.StopWatching();
- EXPECT_EQ(0, counter);
+ MessageLoop::current()->RunAllPending();
- for (int i = 0; i < kNumObjects; ++i)
- CloseHandle(events[i]);
+ // Our delegate should not have fired.
+ EXPECT_EQ(1, counter);
+
+ CloseHandle(event);
}
diff --git a/base/thread.cc b/base/thread.cc
index cec558b..25379e1 100644
--- a/base/thread.cc
+++ b/base/thread.cc
@@ -33,10 +33,13 @@
#include "base/thread.h"
#include "base/message_loop.h"
+#include "base/object_watcher.h"
#include "base/ref_counted.h"
#include "base/string_util.h"
#include "base/win_util.h"
+namespace {
+
// This class is used when starting a thread. It passes information to the
// thread function. It is referenced counted so we can cleanup the event
// object used to synchronize thread startup properly.
@@ -65,20 +68,15 @@ class ThreadQuitTask : public Task {
};
// Once an object is signaled, quits the current inner message loop.
-class QuitOnSignal : public MessageLoop::Watcher {
+class QuitOnSignal : public base::ObjectWatcher::Delegate {
public:
- explicit QuitOnSignal(HANDLE signal) : signal_(signal) {
- }
virtual void OnObjectSignaled(HANDLE object) {
- DCHECK_EQ(object, signal_);
- MessageLoop::current()->WatchObject(signal_, NULL);
MessageLoop::current()->Quit();
}
- private:
- HANDLE signal_;
- DISALLOW_EVIL_CONSTRUCTORS(QuitOnSignal);
};
+} // namespace
+
Thread::Thread(const char *name)
: thread_(NULL),
thread_id_(0),
@@ -198,8 +196,10 @@ void Thread::InternalStop(bool run_message_loop) {
message_loop_->PostTask(FROM_HERE, new ThreadQuitTask());
if (run_message_loop) {
- QuitOnSignal signal_watcher(thread_);
- MessageLoop::current()->WatchObject(thread_, &signal_watcher);
+ QuitOnSignal quit_on_signal;
+ base::ObjectWatcher signal_watcher;
+ CHECK(signal_watcher.StartWatching(thread_, &quit_on_signal));
+
bool old_state = MessageLoop::current()->NestableTasksAllowed();
MessageLoop::current()->SetNestableTasksAllowed(true);
MessageLoop::current()->Run();
diff --git a/base/tracked.cc b/base/tracked.cc
index 86d634b..b1a2bff 100644
--- a/base/tracked.cc
+++ b/base/tracked.cc
@@ -74,6 +74,7 @@ void Location::WriteFunctionName(std::string* output) const {
Tracked::Tracked() {}
Tracked::~Tracked() {}
void Tracked::SetBirthPlace(const Location& from_here) {}
+void Tracked::ResetBirthTime() {}
bool Tracked::MissingBirthplace() const { return false; }
void Tracked::ResetBirthTime() {}