Index: mojo/common/handle_watcher.cc |
diff --git a/mojo/common/handle_watcher.cc b/mojo/common/handle_watcher.cc |
index 84e8b64fb9ad7a93fd0fb5ca1b211178a5f3bb6b..c8c4ac236194c13c711bda62087a7639b7a7c788 100644 |
--- a/mojo/common/handle_watcher.cc |
+++ b/mojo/common/handle_watcher.cc |
@@ -13,6 +13,7 @@ |
#include "base/memory/weak_ptr.h" |
#include "base/message_loop/message_loop.h" |
#include "base/message_loop/message_loop_proxy.h" |
+#include "base/synchronization/condition_variable.h" |
#include "base/synchronization/lock.h" |
#include "base/threading/thread.h" |
#include "base/time/time.h" |
@@ -57,6 +58,20 @@ struct WatchData { |
scoped_refptr<base::MessageLoopProxy> message_loop; |
}; |
+// WaitState ------------------------------------------------------------------- |
+ |
+// Used when removing a handle. The main thread blocks until the background |
+// thread successfully removes the handle from the set of handles being watched. |
+// When the background thread removes the handle it sets |removed| and signals |
+// |condition|. |
+struct WaitState { |
darin (slow to review)
2014/07/22 17:42:56
nit: You could also just use base::WaitableEvent h
sky
2014/07/22 18:20:50
I thought there was something like that. Done!
|
+ WaitState() : condition(&lock), removed(false) {} |
+ |
+ base::Lock lock; |
+ base::ConditionVariable condition; |
+ bool removed; |
+}; |
+ |
// WatcherBackend -------------------------------------------------------------- |
// WatcherBackend is responsible for managing the requests and interacting with |
@@ -68,7 +83,10 @@ class WatcherBackend : public MessagePumpMojoHandler { |
virtual ~WatcherBackend(); |
void StartWatching(const WatchData& data); |
- void StopWatching(WatcherID watcher_id); |
+ |
+ // Cancels a previously schedule request to start a watch. When done sets |
+ // |wait_state->removed| and signals the condition. |
+ void StopWatching(WatcherID watcher_id, WaitState* wait_state); |
private: |
typedef std::map<Handle, WatchData> HandleToWatchDataMap; |
@@ -107,15 +125,19 @@ void WatcherBackend::StartWatching(const WatchData& data) { |
data.deadline); |
} |
-void WatcherBackend::StopWatching(WatcherID watcher_id) { |
+void WatcherBackend::StopWatching(WatcherID watcher_id, WaitState* wait_state) { |
// Because of the thread hop it is entirely possible to get here and not |
// have a valid handle registered for |watcher_id|. |
Handle handle; |
- if (!GetMojoHandleByWatcherID(watcher_id, &handle)) |
- return; |
- |
- handle_to_data_.erase(handle); |
- message_pump_mojo->RemoveHandler(handle); |
+ if (GetMojoHandleByWatcherID(watcher_id, &handle)) { |
+ handle_to_data_.erase(handle); |
+ message_pump_mojo->RemoveHandler(handle); |
+ } |
+ { |
+ base::AutoLock auto_lock(wait_state->lock); |
+ wait_state->removed = true; |
+ wait_state->condition.Signal(); |
+ } |
} |
void WatcherBackend::RemoveAndNotify(const Handle& handle, |
@@ -208,7 +230,7 @@ WatcherID WatcherThreadManager::StartWatching( |
data.message_loop = base::MessageLoopProxy::current(); |
DCHECK_NE(static_cast<base::MessageLoopProxy*>(NULL), |
data.message_loop.get()); |
- // We outlive |thread_|, so it's safe to use Unretained() here. |
+ // We own |thread_|, so it's safe to use Unretained() here. |
thread_.message_loop()->PostTask( |
FROM_HERE, |
base::Bind(&WatcherBackend::StartWatching, |
@@ -218,12 +240,21 @@ WatcherID WatcherThreadManager::StartWatching( |
} |
void WatcherThreadManager::StopWatching(WatcherID watcher_id) { |
- // We outlive |thread_|, so it's safe to use Unretained() here. |
+ // We own |thread_|, so it's safe to use Unretained() here. |
+ WaitState wait_state; |
thread_.message_loop()->PostTask( |
FROM_HERE, |
base::Bind(&WatcherBackend::StopWatching, |
base::Unretained(&backend_), |
- watcher_id)); |
+ watcher_id, |
+ &wait_state)); |
+ |
+ // We need to block until the handle is actually removed. |
+ { |
+ base::AutoLock auto_lock(wait_state.lock); |
+ while (!wait_state.removed) |
+ wait_state.condition.Wait(); |
+ } |
} |
WatcherThreadManager::WatcherThreadManager() |