diff options
-rw-r--r-- | base/object_watcher.cc | 74 | ||||
-rw-r--r-- | base/object_watcher.h | 76 | ||||
-rw-r--r-- | base/object_watcher_unittest.cc | 112 | ||||
-rw-r--r-- | base/thread.cc | 20 | ||||
-rw-r--r-- | base/tracked.cc | 1 |
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() {} |