summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-15 22:25:11 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-01-15 22:25:11 +0000
commit1c4947ffee0b7f5672737b542eb6adf466fcb223 (patch)
treeca2b1631479523a3a397fac842fe0870f422193f
parentc0ce93d64e2374f29acf2130c5324dc54762f0fa (diff)
downloadchromium_src-1c4947ffee0b7f5672737b542eb6adf466fcb223.zip
chromium_src-1c4947ffee0b7f5672737b542eb6adf466fcb223.tar.gz
chromium_src-1c4947ffee0b7f5672737b542eb6adf466fcb223.tar.bz2
WaitableEvent is the replacement for Windows events. Previously in the code, a HANDLE from CreateEvent was used for signaling, both within a process and across processes.
WaitableEvent is the cross platform replacement for this. To convert: * HANDLE -> base::WaitableEvent* * ScopedHandle -> scoped_ptr<base::WaitableEvent> * CreateEvent -> new base::WaitableEvent * SetEvent -> base::WaitableEvent::Signal * ResetEvent -> base::WaitableEvent::Reset * ObjectWatcher -> base::WaitableEventWatcher * WaitForMultipleObjects -> static base::WaitableEvent::WaitMany ObjectWatcher remains for Windows specific code. WaitableEventWatcher has an identical interface save, * It uses WaitableEvents, not HANDLEs * It returns void from StartWatching and StopWatcher, rather than errors. System internal errors are fatal to the address space IMPORTANT: There are semantic differences between the different platforms. WaitableEvents on Windows are implemented on top of events. Windows events work across process and this is used mostly for modal dialog support. Windows events can be duplicated with DuplicateHandle. On other platforms, WaitableEvent works only within a single process. In the future we shall have to replace the current uses of cross-process events with IPCs. BEWARE: HANDLE, on Windows, is a void *. Since any pointer type coerces to void *, you can pass a WaitableEvent * where a HANDLE is expected without any build-time errors. Review URL: http://codereview.chromium.org/16554 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@8126 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/atomic_ref_count.h10
-rw-r--r--base/base_lib.scons4
-rw-r--r--base/base_unittests.scons1
-rw-r--r--base/build/base.vcproj8
-rw-r--r--base/waitable_event.h87
-rw-r--r--base/waitable_event_generic.cc71
-rw-r--r--base/waitable_event_posix.cc392
-rw-r--r--base/waitable_event_unittest.cc55
-rw-r--r--base/waitable_event_watcher.h148
-rw-r--r--base/waitable_event_watcher_posix.cc253
-rw-r--r--base/waitable_event_watcher_unittest.cc136
-rw-r--r--base/waitable_event_watcher_win.cc60
-rw-r--r--base/waitable_event_win.cc40
-rw-r--r--chrome/browser/browser_process.h5
-rw-r--r--chrome/browser/browser_process_impl.cc3
-rw-r--r--chrome/browser/browser_process_impl.h4
-rw-r--r--chrome/browser/browser_shutdown.cc3
-rw-r--r--chrome/browser/render_view_host.cc26
-rw-r--r--chrome/browser/render_view_host.h12
-rw-r--r--chrome/browser/render_view_host_delegate.h9
-rw-r--r--chrome/browser/render_view_host_manager.cc10
-rw-r--r--chrome/browser/render_view_host_manager.h4
-rw-r--r--chrome/browser/tab_contents/web_contents.cc2
-rw-r--r--chrome/browser/tab_contents/web_contents.h6
-rw-r--r--chrome/browser/tab_contents/web_contents_unittest.cc6
-rw-r--r--chrome/browser/tab_contents/web_contents_view.cc3
-rw-r--r--chrome/browser/tab_contents/web_contents_view.h11
-rw-r--r--chrome/browser/tab_contents/web_contents_view_win.cc2
-rw-r--r--chrome/browser/tab_contents/web_contents_view_win.h2
-rw-r--r--chrome/common/child_process.cc11
-rw-r--r--chrome/common/child_process.h7
-rw-r--r--chrome/common/common.scons2
-rw-r--r--chrome/common/ipc_logging.h7
-rw-r--r--chrome/common/ipc_message.h3
-rw-r--r--chrome/common/ipc_sync_channel.cc94
-rw-r--r--chrome/common/ipc_sync_channel.h42
-rw-r--r--chrome/common/ipc_sync_message.cc12
-rw-r--r--chrome/common/ipc_sync_message.h21
-rw-r--r--chrome/plugin/npobject_proxy.cc11
-rw-r--r--chrome/plugin/npobject_proxy.h11
-rw-r--r--chrome/plugin/npobject_util.cc2
-rw-r--r--chrome/plugin/npobject_util.h5
-rw-r--r--chrome/plugin/webplugin_proxy.cc12
-rw-r--r--chrome/plugin/webplugin_proxy.h7
-rw-r--r--chrome/renderer/render_thread.cc4
-rw-r--r--chrome/renderer/render_view.cc18
-rw-r--r--chrome/renderer/render_view.h14
-rw-r--r--chrome/renderer/webplugin_delegate_proxy.cc6
-rw-r--r--chrome/renderer/webplugin_delegate_proxy.h6
-rw-r--r--chrome/test/testing_browser_process.h16
50 files changed, 1418 insertions, 266 deletions
diff --git a/base/atomic_ref_count.h b/base/atomic_ref_count.h
index af83572..66fe07b 100644
--- a/base/atomic_ref_count.h
+++ b/base/atomic_ref_count.h
@@ -13,12 +13,12 @@
namespace base {
-typedef base::subtle::Atomic32 AtomicRefCount;
+typedef subtle::Atomic32 AtomicRefCount;
// Increment a reference count by "increment", which must exceed 0.
inline void AtomicRefCountIncN(volatile AtomicRefCount *ptr,
AtomicRefCount increment) {
- base::subtle::NoBarrier_AtomicIncrement(ptr, increment);
+ subtle::NoBarrier_AtomicIncrement(ptr, increment);
}
// Decrement a reference count by "decrement", which must exceed 0,
@@ -27,7 +27,7 @@ inline void AtomicRefCountIncN(volatile AtomicRefCount *ptr,
// became zero will be visible to a thread that has just made the count zero.
inline bool AtomicRefCountDecN(volatile AtomicRefCount *ptr,
AtomicRefCount decrement) {
- return base::subtle::Barrier_AtomicIncrement(ptr, -decrement) != 0;
+ return subtle::Barrier_AtomicIncrement(ptr, -decrement) != 0;
}
// Increment a reference count by 1.
@@ -49,14 +49,14 @@ inline bool AtomicRefCountDec(volatile AtomicRefCount *ptr) {
// needed for the owning thread to act on the object, knowing that it has
// exclusive access to the object.
inline bool AtomicRefCountIsOne(volatile AtomicRefCount *ptr) {
- return base::subtle::Acquire_Load(ptr) == 1;
+ return subtle::Acquire_Load(ptr) == 1;
}
// Return whether the reference count is zero. With conventional object
// referencing counting, the object will be destroyed, so the reference count
// should never be zero. Hence this is generally used for a debug check.
inline bool AtomicRefCountIsZero(volatile AtomicRefCount *ptr) {
- return base::subtle::Acquire_Load(ptr) == 0;
+ return subtle::Acquire_Load(ptr) == 0;
}
} // namespace base
diff --git a/base/base_lib.scons b/base/base_lib.scons
index ee52dbf..573eea3 100644
--- a/base/base_lib.scons
+++ b/base/base_lib.scons
@@ -320,6 +320,7 @@ if not env.Bit('windows'):
'third_party/purify/pure_api.c',
'time_win.cc',
'waitable_event_win.cc',
+ 'waitable_event_watcher_win.cc',
'win_util.cc',
'wmi_util.cc',
'worker_pool.cc',
@@ -343,7 +344,8 @@ if env.Bit('posix'):
'thread_local_storage_posix.cc',
'thread_local_posix.cc',
'time_posix.cc',
- 'waitable_event_generic.cc',
+ 'waitable_event_posix.cc',
+ 'waitable_event_watcher_posix.cc',
])
if env.Bit('mac'):
diff --git a/base/base_unittests.scons b/base/base_unittests.scons
index 69172e9..a7ea02f 100644
--- a/base/base_unittests.scons
+++ b/base/base_unittests.scons
@@ -112,6 +112,7 @@ input_files = ChromeFileList([
'tuple_unittest.cc',
'values_unittest.cc',
'waitable_event_unittest.cc',
+ 'waitable_event_watcher_unittest.cc',
'watchdog_unittest.cc',
'win_util_unittest.cc',
'wmi_util_unittest.cc',
diff --git a/base/build/base.vcproj b/base/build/base.vcproj
index c5de938..2bfc20a 100644
--- a/base/build/base.vcproj
+++ b/base/build/base.vcproj
@@ -954,6 +954,14 @@
>
</File>
<File
+ RelativePath="..\waitable_event_watcher.h"
+ >
+ </File>
+ <File
+ RelativePath="..\waitable_event_watcher_win.cc"
+ >
+ </File>
+ <File
RelativePath="..\waitable_event_win.cc"
>
</File>
diff --git a/base/waitable_event.h b/base/waitable_event.h
index b723653..84feedc 100644
--- a/base/waitable_event.h
+++ b/base/waitable_event.h
@@ -8,18 +8,27 @@
#include "base/basictypes.h"
#if defined(OS_WIN)
-typedef void* HANDLE;
-#else
+#include <windows.h>
+#endif
+
+#if defined(OS_POSIX)
+#include <list>
+#include <utility>
#include "base/condition_variable.h"
#include "base/lock.h"
+#include "base/ref_counted.h"
#endif
+#include "base/message_loop.h"
+
namespace base {
class TimeDelta;
// A WaitableEvent can be a useful thread synchronization tool when you want to
-// allow one thread to wait for another thread to finish some work.
+// allow one thread to wait for another thread to finish some work. For
+// non-Windows systems, this can only be used from within a single address
+// space.
//
// Use a WaitableEvent when you would otherwise use a Lock+ConditionVariable to
// protect a simple boolean value. However, if you find yourself using a
@@ -31,7 +40,6 @@ class TimeDelta;
// by a Windows event object. This is intentional. If you are writing Windows
// specific code and you need other features of a Windows event, then you might
// be better off just using an Windows event directly.
-//
class WaitableEvent {
public:
// If manual_reset is true, then to set the event state to non-signaled, a
@@ -40,6 +48,13 @@ class WaitableEvent {
// waiting thread has been released.
WaitableEvent(bool manual_reset, bool initially_signaled);
+#if defined(OS_WIN)
+ // Create a WaitableEvent from an Event HANDLE which has already been
+ // created. This objects takes ownership of the HANDLE and will close it when
+ // deleted.
+ explicit WaitableEvent(HANDLE event_handle);
+#endif
+
// WARNING: Destroying a WaitableEvent while threads are waiting on it is not
// supported. Doing so will cause crashes or other instability.
~WaitableEvent();
@@ -64,14 +79,70 @@ class WaitableEvent {
// does not necessarily mean that max_time was exceeded.
bool TimedWait(const TimeDelta& max_time);
+#if defined(OS_WIN)
+ HANDLE handle() const { return handle_; }
+#endif
+
+ // Wait, synchronously, on multiple events.
+ // waitables: an array of WaitableEvent pointers
+ // count: the number of elements in @waitables
+ //
+ // returns: the index of a WaitableEvent which has been signaled.
+ static size_t WaitMany(WaitableEvent** waitables, size_t count);
+
+ // For asynchronous waiting, see WaitableEventWatcher
+
+ // This is a private helper class. It's here because it's used by friends of
+ // this class (such as WaitableEventWatcher) to be able to enqueue elements
+ // of the wait-list
+ class Waiter {
+ public:
+ // Signal the waiter to wake up.
+ //
+ // Consider the case of a Waiter which is in multiple WaitableEvent's
+ // wait-lists. Each WaitableEvent is automatic-reset and two of them are
+ // signaled at the same time. Now, each will wake only the first waiter in
+ // the wake-list before resetting. However, if those two waiters happen to
+ // be the same object (as can happen if another thread didn't have a chance
+ // to dequeue the waiter from the other wait-list in time), two auto-resets
+ // will have happened, but only one waiter has been signaled!
+ //
+ // Because of this, a Waiter may "reject" a wake by returning false. In
+ // this case, the auto-reset WaitableEvent shouldn't act as if anything has
+ // been notified.
+ virtual bool Fire(WaitableEvent* signaling_event) = 0;
+
+ // Waiters may implement this in order to provide an extra condition for
+ // two Waiters to be considered equal. In WaitableEvent::Dequeue, if the
+ // pointers match then this function is called as a final check. See the
+ // comments in ~Handle for why.
+ virtual bool Compare(void* tag) = 0;
+ };
+
private:
+ friend class WaitableEventWatcher;
+
#if defined(OS_WIN)
- HANDLE event_;
+ HANDLE handle_;
#else
- Lock lock_; // Needs to be listed first so it will be constructed first.
- ConditionVariable cvar_;
+ bool SignalAll();
+ bool SignalOne();
+ void Enqueue(Waiter* waiter);
+ bool Dequeue(Waiter* waiter, void* tag);
+
+ // When dealing with arrays of WaitableEvent*, we want to sort by the address
+ // of the WaitableEvent in order to have a globally consistent locking order.
+ // In that case we keep them, in sorted order, in an array of pairs where the
+ // second element is the index of the WaitableEvent in the original,
+ // unsorted, array.
+ typedef std::pair<WaitableEvent*, size_t> WaiterAndIndex;
+ static unsigned EnqueueMany(WaiterAndIndex* waitables,
+ size_t count, Waiter* waiter);
+
+ Lock lock_;
bool signaled_;
- bool manual_reset_;
+ const bool manual_reset_;
+ std::list<Waiter*> waiters_;
#endif
DISALLOW_COPY_AND_ASSIGN(WaitableEvent);
diff --git a/base/waitable_event_generic.cc b/base/waitable_event_generic.cc
deleted file mode 100644
index 61eeeb8..0000000
--- a/base/waitable_event_generic.cc
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "base/waitable_event.h"
-
-namespace base {
-
-WaitableEvent::WaitableEvent(bool manual_reset, bool signaled)
- : lock_(),
- cvar_(&lock_),
- signaled_(signaled),
- manual_reset_(manual_reset) {
-}
-
-WaitableEvent::~WaitableEvent() {
- // Members are destroyed in the reverse of their initialization order, so we
- // should not have to worry about lock_ being destroyed before cvar_.
-}
-
-void WaitableEvent::Reset() {
- AutoLock locked(lock_);
- signaled_ = false;
-}
-
-void WaitableEvent::Signal() {
- AutoLock locked(lock_);
- if (!signaled_) {
- signaled_ = true;
- if (manual_reset_) {
- cvar_.Broadcast();
- } else {
- cvar_.Signal();
- }
- }
-}
-
-bool WaitableEvent::IsSignaled() {
- return TimedWait(TimeDelta::FromMilliseconds(0));
-}
-
-bool WaitableEvent::Wait() {
- AutoLock locked(lock_);
- while (!signaled_)
- cvar_.Wait();
- if (!manual_reset_)
- signaled_ = false;
- return true;
-}
-
-bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
- AutoLock locked(lock_);
- // In case of spurious wake-ups, we need to adjust the amount of time that we
- // spend sleeping.
- TimeDelta total_time;
- for (;;) {
- TimeTicks start = TimeTicks::Now();
- cvar_.TimedWait(max_time - total_time);
- if (signaled_)
- break;
- total_time += TimeTicks::Now() - start;
- if (total_time >= max_time)
- break;
- }
- bool result = signaled_;
- if (!manual_reset_)
- signaled_ = false;
- return result;
-}
-
-} // namespace base
diff --git a/base/waitable_event_posix.cc b/base/waitable_event_posix.cc
new file mode 100644
index 0000000..be5af6e
--- /dev/null
+++ b/base/waitable_event_posix.cc
@@ -0,0 +1,392 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/waitable_event.h"
+
+#include "base/condition_variable.h"
+#include "base/lock.h"
+#include "base/message_loop.h"
+
+// -----------------------------------------------------------------------------
+// A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't
+// support cross-process events (where one process can signal an event which
+// others are waiting on). Because of this, we can avoid having one thread per
+// listener in several cases.
+//
+// The WaitableEvent maintains a list of waiters, protected by a lock. Each
+// waiter is either an async wait, in which case we have a Task and the
+// MessageLoop to run it on, or a blocking wait, in which case we have the
+// condition variable to signal.
+//
+// Waiting involves grabbing the lock and adding oneself to the wait list. Async
+// waits can be canceled, which means grabbing the lock and removing oneself
+// from the list.
+//
+// Waiting on multiple events is handled by adding a single, synchronous wait to
+// the wait-list of many events. An event passes a pointer to itself when
+// firing a waiter and so we can store that pointer to find out which event
+// triggered.
+// -----------------------------------------------------------------------------
+
+namespace base {
+
+// -----------------------------------------------------------------------------
+// This is just an abstract base class for waking the two types of waiters
+// -----------------------------------------------------------------------------
+WaitableEvent::WaitableEvent(bool manual_reset, bool initially_signaled)
+ : signaled_(false),
+ manual_reset_(manual_reset) {
+ DCHECK(!initially_signaled) << "Not implemented";
+}
+
+WaitableEvent::~WaitableEvent() {
+ DCHECK(waiters_.empty()) << "Deleting WaitableEvent with listeners!";
+}
+
+void WaitableEvent::Reset() {
+ AutoLock locked(lock_);
+ signaled_ = false;
+}
+
+void WaitableEvent::Signal() {
+ AutoLock locked(lock_);
+
+ if (signaled_)
+ return;
+
+ if (manual_reset_) {
+ SignalAll();
+ signaled_ = true;
+ } else {
+ // In the case of auto reset, if no waiters were woken, we remain
+ // signaled.
+ if (!SignalOne())
+ signaled_ = true;
+ }
+}
+
+bool WaitableEvent::IsSignaled() {
+ AutoLock locked(lock_);
+
+ const bool result = signaled_;
+ if (result && !manual_reset_)
+ signaled_ = false;
+ return result;
+}
+
+// -----------------------------------------------------------------------------
+// Synchronous waits
+
+// -----------------------------------------------------------------------------
+// This is an synchronous waiter. The thread is waiting on the given condition
+// variable and the fired flag in this object.
+// -----------------------------------------------------------------------------
+class SyncWaiter : public WaitableEvent::Waiter {
+ public:
+ SyncWaiter(ConditionVariable* cv, Lock* lock)
+ : fired_(false),
+ cv_(cv),
+ lock_(lock),
+ signaling_event_(NULL) { }
+
+ bool Fire(WaitableEvent *signaling_event) {
+ lock_->Acquire();
+ const bool previous_value = fired_;
+ fired_ = true;
+ if (!previous_value)
+ signaling_event_ = signaling_event;
+ lock_->Release();
+
+ if (previous_value)
+ return false;
+
+ cv_->Broadcast();
+
+ // SyncWaiters are stack allocated on the stack of the blocking thread.
+ return true;
+ }
+
+ WaitableEvent* signaled_event() const {
+ return signaling_event_;
+ }
+
+ // ---------------------------------------------------------------------------
+ // These waiters are always stack allocated and don't delete themselves. Thus
+ // there's no problem and the ABA tag is the same as the object pointer.
+ // ---------------------------------------------------------------------------
+ bool Compare(void* tag) {
+ return this == tag;
+ }
+
+ // ---------------------------------------------------------------------------
+ // Called with lock held.
+ // ---------------------------------------------------------------------------
+ bool fired() const {
+ return fired_;
+ }
+
+ // ---------------------------------------------------------------------------
+ // During a TimedWait, we need a way to make sure that an auto-reset
+ // WaitableEvent doesn't think that this event has been signaled between
+ // unlocking it and removing it from the wait-list. Called with lock held.
+ // ---------------------------------------------------------------------------
+ void Disable() {
+ fired_ = true;
+ }
+
+ private:
+ bool fired_;
+ ConditionVariable *const cv_;
+ Lock *const lock_;
+ WaitableEvent* signaling_event_; // The WaitableEvent which woke us
+};
+
+bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
+ const Time end_time(Time::Now() + max_time);
+
+ lock_.Acquire();
+ if (signaled_) {
+ if (!manual_reset_) {
+ // In this case we were signaled when we had no waiters. Now that
+ // someone has waited upon us, we can automatically reset.
+ signaled_ = false;
+ }
+
+ lock_.Release();
+ return true;
+ }
+
+ Lock lock;
+ lock.Acquire();
+ ConditionVariable cv(&lock);
+ SyncWaiter sw(&cv, &lock);
+
+ Enqueue(&sw);
+ lock_.Release();
+ // We are violating locking order here by holding the SyncWaiter lock but not
+ // the WaitableEvent lock. However, this is safe because we don't lock @lock_
+ // again before unlocking it.
+
+ for (;;) {
+ if (sw.fired()) {
+ lock.Release();
+ return true;
+ }
+
+ if (max_time.ToInternalValue() < 0) {
+ cv.Wait();
+ } else {
+ const Time current_time(Time::Now());
+ if (current_time >= end_time) {
+ // We can't acquire @lock_ before releasing @lock (because of locking
+ // order), however, inbetween the two a signal could be fired and @sw
+ // would accept it, however we will still return false, so the signal
+ // would be lost on an auto-reset WaitableEvent. Thus we call Disable
+ // which makes sw::Fire return false.
+ sw.Disable();
+ lock.Release();
+
+ lock_.Acquire();
+ Dequeue(&sw, &sw);
+ lock_.Release();
+ return false;
+ }
+ const TimeDelta max_wait(end_time - current_time);
+
+ cv.TimedWait(max_wait);
+ }
+ }
+}
+
+bool WaitableEvent::Wait() {
+ return TimedWait(TimeDelta::FromSeconds(-1));
+}
+
+// -----------------------------------------------------------------------------
+
+
+// -----------------------------------------------------------------------------
+// Synchronous waiting on multiple objects.
+
+static bool // StrictWeakOrdering
+cmp_fst_addr(const std::pair<WaitableEvent*, unsigned> &a,
+ const std::pair<WaitableEvent*, unsigned> &b) {
+ return a.first < b.first;
+}
+
+// static
+size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables,
+ size_t count) {
+ DCHECK(count) << "Cannot wait on no events";
+
+ // We need to acquire the locks in a globally consistent order. Thus we sort
+ // the array of waitables by address. We actually sort a pairs so that we can
+ // map back to the original index values later.
+ std::vector<std::pair<WaitableEvent*, size_t> > waitables;
+ waitables.reserve(count);
+ for (size_t i = 0; i < count; ++i)
+ waitables.push_back(std::make_pair(raw_waitables[i], i));
+
+ DCHECK_EQ(count, waitables.size());
+
+ sort(waitables.begin(), waitables.end(), cmp_fst_addr);
+
+ // The set of waitables must be distinct. Since we have just sorted by
+ // address, we can check this cheaply by comparing pairs of consecutive
+ // elements.
+ for (size_t i = 0; i < waitables.size() - 1; ++i) {
+ DCHECK(waitables[i].first != waitables[i+1].first);
+ }
+
+ Lock lock;
+ ConditionVariable cv(&lock);
+ SyncWaiter sw(&cv, &lock);
+
+ const size_t r = EnqueueMany(&waitables[0], count, &sw);
+ if (r) {
+ // One of the events is already signaled. The SyncWaiter has not been
+ // enqueued anywhere. EnqueueMany returns the count of remaining waitables
+ // when the signaled one was seen, so the index of the signaled event is
+ // @count - @r.
+ return waitables[count - r].second;
+ }
+
+ // At this point, we hold the locks on all the WaitableEvents and we have
+ // enqueued our waiter in them all.
+ lock.Acquire();
+ // Release the WaitableEvent locks in the reverse order
+ for (size_t i = 0; i < count; ++i) {
+ waitables[count - (1 + i)].first->lock_.Release();
+ }
+
+ for (;;) {
+ if (sw.fired())
+ break;
+
+ cv.Wait();
+ }
+ lock.Release();
+
+ // The address of the WaitableEvent which fired is stored in the SyncWaiter.
+ WaitableEvent *const signaled_event = sw.signaled_event();
+ // This will store the index of the raw_waitables which fired.
+ size_t signaled_index;
+
+ // Take the locks of each WaitableEvent in turn (except the signaled one) and
+ // remove our SyncWaiter from the wait-list
+ for (size_t i = 0; i < count; ++i) {
+ if (raw_waitables[i] != signaled_event) {
+ raw_waitables[i]->lock_.Acquire();
+ // There's no possible ABA issue with the address of the SyncWaiter here
+ // because it lives on the stack. Thus the tag value is just the pointer
+ // value again.
+ raw_waitables[i]->Dequeue(&sw, &sw);
+ raw_waitables[i]->lock_.Release();
+ } else {
+ signaled_index = i;
+ }
+ }
+
+ return signaled_index;
+}
+
+// -----------------------------------------------------------------------------
+// If return value == 0:
+// The locks of the WaitableEvents have been taken in order and the Waiter has
+// been enqueued in the wait-list of each. None of the WaitableEvents are
+// currently signaled
+// else:
+// None of the WaitableEvent locks are held. The Waiter has not been enqueued
+// in any of them and the return value is the index of the first WaitableEvent
+// which was signaled, from the end of the array.
+// -----------------------------------------------------------------------------
+// static
+unsigned WaitableEvent::EnqueueMany
+ (std::pair<WaitableEvent*, unsigned>* waitables,
+ unsigned count, Waiter* waiter) {
+ if (!count)
+ return 0;
+
+ waitables[0].first->lock_.Acquire();
+ if (waitables[0].first->signaled_) {
+ if (!waitables[0].first->manual_reset_)
+ waitables[0].first->signaled_ = false;
+ waitables[0].first->lock_.Release();
+ return count;
+ }
+
+ const unsigned r = EnqueueMany(waitables + 1, count - 1, waiter);
+ if (r) {
+ waitables[0].first->lock_.Release();
+ } else {
+ waitables[0].first->Enqueue(waiter);
+ }
+
+ return r;
+}
+
+// -----------------------------------------------------------------------------
+
+
+// -----------------------------------------------------------------------------
+// Private functions...
+
+// -----------------------------------------------------------------------------
+// Wake all waiting waiters. Called with lock held.
+// -----------------------------------------------------------------------------
+bool WaitableEvent::SignalAll() {
+ bool signaled_at_least_one = false;
+
+ for (std::list<Waiter*>::iterator
+ i = waiters_.begin(); i != waiters_.end(); ++i) {
+ if ((*i)->Fire(this))
+ signaled_at_least_one = true;
+ }
+
+ waiters_.clear();
+ return signaled_at_least_one;
+}
+
+// ---------------------------------------------------------------------------
+// Try to wake a single waiter. Return true if one was woken. Called with lock
+// held.
+// ---------------------------------------------------------------------------
+bool WaitableEvent::SignalOne() {
+ for (;;) {
+ if (waiters_.empty())
+ return false;
+
+ const bool r = (*waiters_.begin())->Fire(this);
+ waiters_.pop_front();
+ if (r)
+ return true;
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Add a waiter to the list of those waiting. Called with lock held.
+// -----------------------------------------------------------------------------
+void WaitableEvent::Enqueue(Waiter* waiter) {
+ waiters_.push_back(waiter);
+}
+
+// -----------------------------------------------------------------------------
+// Remove a waiter from the list of those waiting. Return true if the waiter was
+// actually removed. Called with lock held.
+// -----------------------------------------------------------------------------
+bool WaitableEvent::Dequeue(Waiter* waiter, void* tag) {
+ for (std::list<Waiter*>::iterator
+ i = waiters_.begin(); i != waiters_.end(); ++i) {
+ if (*i == waiter && (*i)->Compare(tag)) {
+ waiters_.erase(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+// -----------------------------------------------------------------------------
+
+} // namespace base
diff --git a/base/waitable_event_unittest.cc b/base/waitable_event_unittest.cc
index e7bab49..b2590a8 100644
--- a/base/waitable_event_unittest.cc
+++ b/base/waitable_event_unittest.cc
@@ -4,6 +4,7 @@
#include "base/time.h"
#include "base/waitable_event.h"
+#include "base/platform_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::TimeDelta;
@@ -52,3 +53,57 @@ TEST(WaitableEventTest, AutoBasics) {
EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10)));
}
+TEST(WaitableEventTest, WaitManyShortcut) {
+ WaitableEvent* ev[5];
+ for (unsigned i = 0; i < 5; ++i)
+ ev[i] = new WaitableEvent(false, false);
+
+ ev[3]->Signal();
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 3u);
+
+ ev[3]->Signal();
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 3u);
+
+ ev[4]->Signal();
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 4u);
+
+ ev[0]->Signal();
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 0u);
+
+ for (unsigned i = 0; i < 5; ++i)
+ delete ev[i];
+}
+
+class WaitableEventSignaler : public PlatformThread::Delegate {
+ public:
+ WaitableEventSignaler(double seconds, WaitableEvent* ev)
+ : seconds_(seconds),
+ ev_(ev) {
+ }
+
+ void ThreadMain() {
+ PlatformThread::Sleep(static_cast<int>(seconds_ * 1000));
+ ev_->Signal();
+ }
+
+ private:
+ const double seconds_;
+ WaitableEvent *const ev_;
+};
+
+TEST(WaitableEventTest, WaitMany) {
+ WaitableEvent* ev[5];
+ for (unsigned i = 0; i < 5; ++i)
+ ev[i] = new WaitableEvent(false, false);
+
+ WaitableEventSignaler signaler(0.1, ev[2]);
+ PlatformThreadHandle thread;
+ PlatformThread::Create(0, &signaler, &thread);
+
+ EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 2u);
+
+ PlatformThread::Join(thread);
+
+ for (unsigned i = 0; i < 5; ++i)
+ delete ev[i];
+}
diff --git a/base/waitable_event_watcher.h b/base/waitable_event_watcher.h
new file mode 100644
index 0000000..3e17a10
--- /dev/null
+++ b/base/waitable_event_watcher.h
@@ -0,0 +1,148 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_WAITABLE_EVENT_WATCHER_H_
+#define BASE_WAITABLE_EVENT_WATCHER_H_
+
+#include "build/build_config.h"
+
+#if defined(OS_WIN)
+#include "base/object_watcher.h"
+#else
+#include "base/message_loop.h"
+#endif
+
+namespace base {
+
+class WaitableEvent;
+class Flag;
+class AsyncWaiter;
+class AsyncCallbackTask;
+
+// -----------------------------------------------------------------------------
+// This class provides a way to wait on a WaitableEvent asynchronously.
+//
+// Each instance of this object can be waiting on a single WaitableEvent. When
+// the waitable event is signaled, a callback is made in the thread of a given
+// MessageLoop. This callback can be deleted by deleting the waiter.
+//
+// Typical usage:
+//
+// class MyClass : public base::WaitableEventWatcher::Delegate {
+// public:
+// void DoStuffWhenSignaled(WaitableEvent *waitable_event) {
+// watcher_.StartWatching(waitable_event, this);
+// }
+// virtual void OnWaitableEventSignaled(WaitableEvent* waitable_event) {
+// // OK, time to do stuff!
+// }
+// private:
+// base::WaitableEventWatcher watcher_;
+// };
+//
+// In the above example, MyClass wants to "do stuff" when waitable_event
+// becomes signaled. WaitableEventWatcher makes this task easy. When MyClass
+// goes out of scope, the watcher_ will be destroyed, and there is no need to
+// worry about OnWaitableEventSignaled being called on a deleted MyClass
+// pointer.
+//
+// BEWARE: With automatically reset WaitableEvents, a signal may be lost if it
+// occurs just before a WaitableEventWatcher is deleted. There is currently no
+// safe way to stop watching an automatic reset WaitableEvent without possibly
+// missing a signal.
+// -----------------------------------------------------------------------------
+
+class WaitableEventWatcher
+#if defined(OS_POSIX)
+ : public MessageLoop::DestructionObserver
+#endif
+{
+ public:
+
+ WaitableEventWatcher();
+ ~WaitableEventWatcher();
+
+ class Delegate {
+ public:
+ virtual ~Delegate() { }
+
+ // -------------------------------------------------------------------------
+ // This is called on the MessageLoop thread when WaitableEvent has been
+ // signaled.
+ //
+ // Note: the event may not be signaled by the time that this function is
+ // called. This indicates only that it has been signaled at some point in
+ // the past.
+ // -------------------------------------------------------------------------
+ virtual void OnWaitableEventSignaled(WaitableEvent* waitable_event) = 0;
+ };
+
+ // ---------------------------------------------------------------------------
+ // When @event is signaled, the given delegate is called on the thread of the
+ // current message loop when StartWatching is called. The delegate is not
+ // deleted.
+ // ---------------------------------------------------------------------------
+ bool StartWatching(WaitableEvent* event, Delegate* delegate);
+
+ // ---------------------------------------------------------------------------
+ // Cancel the current watch. Must be called from the same thread which
+ // started the watch.
+ //
+ // Does nothing if no event is being watched, nor if the watch has completed.
+ // The delegate will *not* be called for the current watch after this
+ // function returns. Since the delegate runs on the same thread as this
+ // function, it cannot be called during this function either.
+ // ---------------------------------------------------------------------------
+ void StopWatching();
+
+ // ---------------------------------------------------------------------------
+ // Return the currently watched event, or NULL if no object is currently being
+ // watched.
+ // ---------------------------------------------------------------------------
+ WaitableEvent* GetWatchedEvent();
+
+ private:
+ WaitableEvent* event_;
+
+#if defined(OS_WIN)
+ // ---------------------------------------------------------------------------
+ // The helper class exists because, if WaitableEventWatcher were to inherit
+ // from ObjectWatcher::Delegate, then it couldn't also have an inner class
+ // called Delegate (at least on Windows). Thus this object exists to proxy
+ // the callback function
+ // ---------------------------------------------------------------------------
+ class ObjectWatcherHelper : public ObjectWatcher::Delegate {
+ public:
+ ObjectWatcherHelper(WaitableEventWatcher* watcher);
+
+ // -------------------------------------------------------------------------
+ // Implementation of ObjectWatcher::Delegate
+ // -------------------------------------------------------------------------
+ void OnObjectSignaled(HANDLE h);
+
+ private:
+ WaitableEventWatcher *const watcher_;
+ };
+
+ void OnObjectSignaled();
+
+ Delegate* delegate_;
+ ObjectWatcherHelper helper_;
+ ObjectWatcher watcher_;
+#else
+ // ---------------------------------------------------------------------------
+ // Implementation of MessageLoop::DestructionObserver
+ // ---------------------------------------------------------------------------
+ void WillDestroyCurrentMessageLoop();
+
+ MessageLoop* message_loop_;
+ scoped_refptr<Flag> cancel_flag_;
+ AsyncWaiter* waiter_;
+ AsyncCallbackTask* callback_task_;
+#endif
+};
+
+} // namespace base
+
+#endif // BASE_WAITABLE_EVENT_WATCHER_H_
diff --git a/base/waitable_event_watcher_posix.cc b/base/waitable_event_watcher_posix.cc
new file mode 100644
index 0000000..e4e1a7f
--- /dev/null
+++ b/base/waitable_event_watcher_posix.cc
@@ -0,0 +1,253 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/waitable_event_watcher.h"
+
+#include "base/condition_variable.h"
+#include "base/lock.h"
+#include "base/message_loop.h"
+#include "base/waitable_event.h"
+
+namespace base {
+
+// -----------------------------------------------------------------------------
+// WaitableEventWatcher (async waits).
+//
+// The basic design is that we add an AsyncWaiter to the wait-list of the event.
+// That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it.
+// The MessageLoop ends up running the task, which calls the delegate.
+//
+// Since the wait can be canceled, we have a thread-safe Flag object which is
+// set when the wait has been canceled. At each stage in the above, we check the
+// flag before going onto the next stage. Since the wait may only be canceled in
+// the MessageLoop which runs the Task, we are assured that the delegate cannot
+// be called after canceling...
+
+// -----------------------------------------------------------------------------
+// A thread-safe, reference-counted, write-once flag.
+// -----------------------------------------------------------------------------
+class Flag : public RefCountedThreadSafe<Flag> {
+ public:
+ Flag() { flag_ = false; }
+
+ void Set() {
+ AutoLock locked(lock_);
+ flag_ = true;
+ }
+
+ bool value() const {
+ AutoLock locked(lock_);
+ return flag_;
+ }
+
+ private:
+ mutable Lock lock_;
+ bool flag_;
+};
+
+// -----------------------------------------------------------------------------
+// This is an asynchronous waiter which posts a task to a MessageLoop when
+// fired. An AsyncWaiter may only be in a single wait-list.
+// -----------------------------------------------------------------------------
+class AsyncWaiter : public WaitableEvent::Waiter {
+ public:
+ AsyncWaiter(MessageLoop* message_loop, Task* task, Flag* flag)
+ : message_loop_(message_loop),
+ cb_task_(task),
+ flag_(flag) { }
+
+ bool Fire(WaitableEvent* event) {
+ if (flag_->value()) {
+ // If the callback has been canceled, we don't enqueue the task, we just
+ // delete it instead.
+ delete cb_task_;
+ } else {
+ message_loop_->PostTask(FROM_HERE, cb_task_);
+ }
+
+ // We are removed from the wait-list by the WaitableEvent itself. It only
+ // remains to delete ourselves.
+ delete this;
+
+ // We can always return true because an AsyncWaiter is never in two
+ // different wait-lists at the same time.
+ return true;
+ }
+
+ // See StopWatching for discussion
+ bool Compare(void* tag) {
+ return tag == flag_.get();
+ }
+
+ MessageLoop *const message_loop_;
+ Task *const cb_task_;
+ scoped_refptr<Flag> flag_;
+};
+
+// -----------------------------------------------------------------------------
+// For async waits we need to make a callback in a MessageLoop thread. We do
+// this by posting this task, which calls the delegate and keeps track of when
+// the event is canceled.
+// -----------------------------------------------------------------------------
+class AsyncCallbackTask : public Task {
+ public:
+ AsyncCallbackTask(Flag* flag, WaitableEventWatcher::Delegate* delegate,
+ WaitableEvent* event)
+ : flag_(flag),
+ delegate_(delegate),
+ event_(event) {
+ }
+
+ void Run() {
+ // Runs in MessageLoop thread.
+ if (!flag_->value())
+ delegate_->OnWaitableEventSignaled(event_);
+
+ // This is to let the WaitableEventWatcher know that the event has occured
+ // because it needs to be able to return NULL from GetWatchedEvent
+ flag_->Set();
+
+ // We are deleted by the MessageLoop
+ }
+
+ private:
+ scoped_refptr<Flag> flag_;
+ WaitableEventWatcher::Delegate *const delegate_;
+ WaitableEvent *const event_;
+};
+
+WaitableEventWatcher::WaitableEventWatcher()
+ : event_(NULL),
+ message_loop_(NULL),
+ cancel_flag_(NULL),
+ callback_task_(NULL) {
+}
+
+WaitableEventWatcher::~WaitableEventWatcher() {
+ StopWatching();
+}
+
+// -----------------------------------------------------------------------------
+// The Handle is how the user cancels a wait. After deleting the Handle we
+// insure that the delegate cannot be called.
+// -----------------------------------------------------------------------------
+bool WaitableEventWatcher::StartWatching
+ (WaitableEvent* event, WaitableEventWatcher::Delegate* delegate) {
+ MessageLoop *const current_ml = MessageLoop::current();
+ DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a "
+ "current MessageLoop";
+
+ DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching";
+
+ cancel_flag_ = new Flag;
+ callback_task_ = new AsyncCallbackTask(cancel_flag_, delegate, event);
+
+ AutoLock locked(event->lock_);
+
+ if (event->signaled_) {
+ if (!event->manual_reset_)
+ event->signaled_ = false;
+
+ // No hairpinning - we can't call the delegate directly here. We have to
+ // enqueue a task on the MessageLoop as normal.
+ current_ml->PostTask(FROM_HERE, callback_task_);
+ return true;
+ }
+
+ message_loop_ = current_ml;
+ current_ml->AddDestructionObserver(this);
+
+ event_ = event;
+ waiter_ = new AsyncWaiter(current_ml, callback_task_, cancel_flag_);
+ event->Enqueue(waiter_);
+
+ return true;
+}
+
+void WaitableEventWatcher::StopWatching() {
+ if (message_loop_) {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+ }
+
+ if (!cancel_flag_.get()) // if not currently watching...
+ return;
+
+ if (!event_) {
+ // We have no WaitableEvent. This means that we never enqueued a Waiter on
+ // an event because the event was already signaled when StartWatching was
+ // called.
+ //
+ // In this case, a task was enqueued on the MessageLoop and will run.
+ // We set the flag in case the task hasn't yet run. The flag will stop the
+ // delegate getting called. If the task has run then we have the last
+ // reference to the flag and it will be deleted immedately after.
+ cancel_flag_->Set();
+ cancel_flag_ = NULL;
+ return;
+ }
+
+ AutoLock locked(event_->lock_);
+ // We have a lock on the WaitableEvent. No one else can signal the event while
+ // we have it.
+
+ // We have a possible ABA issue here. If Dequeue was to compare only the
+ // pointer values then it's possible that the AsyncWaiter could have been
+ // fired, freed and the memory reused for a different Waiter which was
+ // enqueued in the same wait-list. We would think that that waiter was our
+ // AsyncWaiter and remove it.
+ //
+ // To stop this, Dequeue also takes a tag argument which is passed to the
+ // virtual Compare function before the two are considered a match. So we need
+ // a tag which is good for the lifetime of this handle: the Flag. Since we
+ // have a reference to the Flag, its memory cannot be reused while this object
+ // still exists. So if we find a waiter with the correct pointer value, and
+ // which shares a Flag pointer, we have a real match.
+ if (event_->Dequeue(waiter_, cancel_flag_.get())) {
+ // Case 2: the waiter hasn't been signaled yet; it was still on the wait
+ // list. We've removed it, thus we can delete it and the task (which cannot
+ // have been enqueued with the MessageLoop because the waiter was never
+ // signaled)
+ delete waiter_;
+ delete callback_task_;
+ cancel_flag_ = NULL;
+ return;
+ }
+
+ // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may
+ // not have run yet, so we set the flag to tell it not to bother enqueuing the
+ // task on the MessageLoop, but to delete it instead. The Waiter deletes
+ // itself once run.
+ cancel_flag_->Set();
+ cancel_flag_ = NULL;
+
+ // If the waiter has already run then the task has been enqueued. If the Task
+ // hasn't yet run, the flag will stop the delegate from getting called. (This
+ // is thread safe because one may only delete a Handle from the MessageLoop
+ // thread.)
+ //
+ // If the delegate has already been called then we have nothing to do. The
+ // task has been deleted by the MessageLoop.
+}
+
+WaitableEvent* WaitableEventWatcher::GetWatchedEvent() {
+ if (!cancel_flag_.get())
+ return NULL;
+
+ if (cancel_flag_->value())
+ return NULL;
+
+ return event_;
+}
+
+// -----------------------------------------------------------------------------
+// This is called when the MessageLoop which the callback will be run it is
+// deleted. We need to cancel the callback as if we had been deleted, but we
+// will still be deleted at some point in the future.
+// -----------------------------------------------------------------------------
+void WaitableEventWatcher::WillDestroyCurrentMessageLoop() {
+ StopWatching();
+}
+
+} // namespace base
diff --git a/base/waitable_event_watcher_unittest.cc b/base/waitable_event_watcher_unittest.cc
new file mode 100644
index 0000000..c50807f
--- /dev/null
+++ b/base/waitable_event_watcher_unittest.cc
@@ -0,0 +1,136 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/message_loop.h"
+#include "base/platform_thread.h"
+#include "base/waitable_event.h"
+#include "base/waitable_event_watcher.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::WaitableEvent;
+using base::WaitableEventWatcher;
+
+namespace {
+
+class QuitDelegate : public WaitableEventWatcher::Delegate {
+ public:
+ virtual void OnWaitableEventSignaled(WaitableEvent* event) {
+ MessageLoop::current()->Quit();
+ }
+};
+
+class DecrementCountDelegate : public WaitableEventWatcher::Delegate {
+ public:
+ DecrementCountDelegate(int* counter) : counter_(counter) {
+ }
+ virtual void OnWaitableEventSignaled(WaitableEvent* object) {
+ --(*counter_);
+ }
+ private:
+ int* counter_;
+};
+
+} // namespace
+
+void RunTest_BasicSignal(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ // A manual-reset event that is not yet signaled.
+ WaitableEvent event(true, false);
+
+ WaitableEventWatcher watcher;
+ EXPECT_EQ(NULL, watcher.GetWatchedObject());
+
+ QuitDelegate delegate;
+ watcher.StartWatching(&event, &delegate);
+ EXPECT_EQ(&event, watcher.GetWatchedObject());
+
+ event.Signal();
+
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(NULL, watcher.GetWatchedObject());
+}
+
+void RunTest_BasicCancel(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ // A manual-reset event that is not yet signaled.
+ WaitableEvent event(true, false);
+
+ WaitableEventWatcher watcher;
+
+ QuitDelegate delegate;
+ watcher.StartWatching(&event, &delegate);
+
+ watcher.StopWatching();
+}
+
+void RunTest_CancelAfterSet(MessageLoop::Type message_loop_type) {
+ MessageLoop message_loop(message_loop_type);
+
+ // A manual-reset event that is not yet signaled.
+ WaitableEvent event(true, false);
+
+ WaitableEventWatcher watcher;
+
+ int counter = 1;
+ DecrementCountDelegate delegate(&counter);
+
+ watcher.StartWatching(&event, &delegate);
+
+ event.Signal();
+
+ // Let the background thread do its business
+ PlatformThread::Sleep(30);
+
+ watcher.StopWatching();
+
+ MessageLoop::current()->RunAllPending();
+
+ // Our delegate should not have fired.
+ EXPECT_EQ(1, counter);
+}
+
+void RunTest_OutlivesMessageLoop(MessageLoop::Type message_loop_type) {
+ // Simulate a MessageLoop that dies before an WaitableEventWatcher. This
+ // ordinarily doesn't happen when people use the Thread class, but it can
+ // happen when people use the Singleton pattern or atexit.
+ WaitableEvent event(true, false);
+ {
+ WaitableEventWatcher watcher;
+ {
+ MessageLoop message_loop(message_loop_type);
+
+ QuitDelegate delegate;
+ watcher.StartWatching(&event, &delegate);
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+
+TEST(ObjectWatcherTest, BasicSignal) {
+ RunTest_BasicSignal(MessageLoop::TYPE_DEFAULT);
+ RunTest_BasicSignal(MessageLoop::TYPE_IO);
+ RunTest_BasicSignal(MessageLoop::TYPE_UI);
+}
+
+TEST(ObjectWatcherTest, BasicCancel) {
+ RunTest_BasicCancel(MessageLoop::TYPE_DEFAULT);
+ RunTest_BasicCancel(MessageLoop::TYPE_IO);
+ RunTest_BasicCancel(MessageLoop::TYPE_UI);
+}
+
+TEST(ObjectWatcherTest, CancelAfterSet) {
+ RunTest_CancelAfterSet(MessageLoop::TYPE_DEFAULT);
+ RunTest_CancelAfterSet(MessageLoop::TYPE_IO);
+ RunTest_CancelAfterSet(MessageLoop::TYPE_UI);
+}
+
+TEST(ObjectWatcherTest, OutlivesMessageLoop) {
+ RunTest_OutlivesMessageLoop(MessageLoop::TYPE_DEFAULT);
+ RunTest_OutlivesMessageLoop(MessageLoop::TYPE_IO);
+ RunTest_OutlivesMessageLoop(MessageLoop::TYPE_UI);
+}
diff --git a/base/waitable_event_watcher_win.cc b/base/waitable_event_watcher_win.cc
new file mode 100644
index 0000000..7619aa4
--- /dev/null
+++ b/base/waitable_event_watcher_win.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/waitable_event_watcher.h"
+
+#include "base/compiler_specific.h"
+#include "base/object_watcher.h"
+#include "base/waitable_event.h"
+
+namespace base {
+
+WaitableEventWatcher::ObjectWatcherHelper::ObjectWatcherHelper(
+ WaitableEventWatcher* watcher)
+ : watcher_(watcher) {
+};
+
+void WaitableEventWatcher::ObjectWatcherHelper::OnObjectSignaled(HANDLE h) {
+ watcher_->OnObjectSignaled();
+}
+
+
+WaitableEventWatcher::WaitableEventWatcher()
+ : event_(NULL),
+ ALLOW_THIS_IN_INITIALIZER_LIST(helper_(this)),
+ delegate_(NULL) {
+}
+
+WaitableEventWatcher::~WaitableEventWatcher() {
+}
+
+bool WaitableEventWatcher::StartWatching(WaitableEvent* event,
+ Delegate* delegate) {
+ delegate_ = delegate;
+ event_ = event;
+
+ return watcher_.StartWatching(event->handle(), &helper_);
+}
+
+void WaitableEventWatcher::StopWatching() {
+ delegate_ = NULL;
+ event_ = NULL;
+ watcher_.StopWatching();
+}
+
+WaitableEvent* WaitableEventWatcher::GetWatchedEvent() {
+ return event_;
+}
+
+void WaitableEventWatcher::OnObjectSignaled() {
+ WaitableEvent* event = event_;
+ Delegate* delegate = delegate_;
+ event_ = NULL;
+ delegate_ = NULL;
+ DCHECK(event);
+
+ delegate->OnWaitableEventSignaled(event);
+}
+
+} // namespace base
diff --git a/base/waitable_event_win.cc b/base/waitable_event_win.cc
index 257f145..001a5df 100644
--- a/base/waitable_event_win.cc
+++ b/base/waitable_event_win.cc
@@ -13,22 +13,27 @@
namespace base {
WaitableEvent::WaitableEvent(bool manual_reset, bool signaled)
- : event_(CreateEvent(NULL, manual_reset, signaled, NULL)) {
+ : handle_(CreateEvent(NULL, manual_reset, signaled, NULL)) {
// We're probably going to crash anyways if this is ever NULL, so we might as
// well make our stack reports more informative by crashing here.
- CHECK(event_);
+ CHECK(handle_);
+}
+
+WaitableEvent::WaitableEvent(HANDLE handle)
+ : handle_(handle) {
+ CHECK(handle) << "Tried to create WaitableEvent from NULL handle";
}
WaitableEvent::~WaitableEvent() {
- CloseHandle(event_);
+ CloseHandle(handle_);
}
void WaitableEvent::Reset() {
- ResetEvent(event_);
+ ResetEvent(handle_);
}
void WaitableEvent::Signal() {
- SetEvent(event_);
+ SetEvent(handle_);
}
bool WaitableEvent::IsSignaled() {
@@ -36,7 +41,7 @@ bool WaitableEvent::IsSignaled() {
}
bool WaitableEvent::Wait() {
- DWORD result = WaitForSingleObject(event_, INFINITE);
+ DWORD result = WaitForSingleObject(handle_, INFINITE);
// It is most unexpected that this should ever fail. Help consumers learn
// about it if it should ever fail.
DCHECK(result == WAIT_OBJECT_0) << "WaitForSingleObject failed";
@@ -49,7 +54,7 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
// is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6?
// It should be 6 to avoid returning too early.
double timeout = ceil(max_time.InMillisecondsF());
- DWORD result = WaitForSingleObject(event_, static_cast<DWORD>(timeout));
+ DWORD result = WaitForSingleObject(handle_, static_cast<DWORD>(timeout));
switch (result) {
case WAIT_OBJECT_0:
return true;
@@ -62,4 +67,25 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) {
return false;
}
+// static
+size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) {
+ HANDLE handles[MAXIMUM_WAIT_OBJECTS];
+ CHECK(count <= MAXIMUM_WAIT_OBJECTS)
+ << "Can only wait on " << MAXIMUM_WAIT_OBJECTS << " with WaitMany";
+
+ for (size_t i = 0; i < count; ++i)
+ handles[i] = events[i]->handle();
+
+ DWORD result =
+ WaitForMultipleObjects(count, handles,
+ FALSE, // don't wait for all the objects
+ INFINITE); // no timeout
+ if (result < WAIT_OBJECT_0 || result >= WAIT_OBJECT_0 + count) {
+ NOTREACHED() << "WaitForMultipleObjects failed: " << GetLastError();
+ return 0;
+ }
+
+ return result - WAIT_OBJECT_0;
+}
+
} // namespace base
diff --git a/chrome/browser/browser_process.h b/chrome/browser/browser_process.h
index 18ce8be..adff28b 100644
--- a/chrome/browser/browser_process.h
+++ b/chrome/browser/browser_process.h
@@ -36,6 +36,7 @@ class SuspendController;
namespace base {
class Thread;
+class WaitableEvent;
}
namespace sandbox {
class BrokerServices;
@@ -128,10 +129,10 @@ class BrowserProcess {
ResourceDispatcherHost* rdh = resource_dispatcher_host();
return rdh ? rdh->download_request_manager() : NULL;
}
+#endif
// Returns an event that is signaled when the browser shutdown.
- virtual HANDLE shutdown_event() = 0;
-#endif
+ virtual base::WaitableEvent* shutdown_event() = 0;
// Returns a reference to the user-data-dir based profiles vector.
std::vector<std::wstring>& user_data_dir_profiles() {
diff --git a/chrome/browser/browser_process_impl.cc b/chrome/browser/browser_process_impl.cc
index 904de03..bccf10b 100644
--- a/chrome/browser/browser_process_impl.cc
+++ b/chrome/browser/browser_process_impl.cc
@@ -7,6 +7,7 @@
#include "base/command_line.h"
#include "base/path_service.h"
#include "base/thread.h"
+#include "base/waitable_event.h"
#include "chrome/browser/automation/automation_provider_list.h"
#include "chrome/browser/browser_trial.h"
#include "chrome/browser/chrome_thread.h"
@@ -127,7 +128,7 @@ BrowserProcessImpl::BrowserProcessImpl(CommandLine& command_line)
memory_model_ = MEDIUM_MEMORY_MODEL;
}
- shutdown_event_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
+ shutdown_event_ = new base::WaitableEvent(true, false);
}
BrowserProcessImpl::~BrowserProcessImpl() {
diff --git a/chrome/browser/browser_process_impl.h b/chrome/browser/browser_process_impl.h
index 6f51102..3b8732c 100644
--- a/chrome/browser/browser_process_impl.h
+++ b/chrome/browser/browser_process_impl.h
@@ -167,7 +167,7 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe {
return memory_model_;
}
- virtual HANDLE shutdown_event() { return shutdown_event_; }
+ virtual base::WaitableEvent* shutdown_event() { return shutdown_event_; }
private:
void CreateResourceDispatcherHost();
@@ -242,7 +242,7 @@ class BrowserProcessImpl : public BrowserProcess, public NonThreadSafe {
bool using_new_frames_;
// An event that notifies when we are shutting-down.
- HANDLE shutdown_event_;
+ base::WaitableEvent* shutdown_event_;
DISALLOW_EVIL_CONSTRUCTORS(BrowserProcessImpl);
};
diff --git a/chrome/browser/browser_shutdown.cc b/chrome/browser/browser_shutdown.cc
index e192688..e0417fa 100644
--- a/chrome/browser/browser_shutdown.cc
+++ b/chrome/browser/browser_shutdown.cc
@@ -9,6 +9,7 @@
#include "base/path_service.h"
#include "base/string_util.h"
#include "base/time.h"
+#include "base/waitable_event.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/first_run.h"
#include "chrome/browser/jankometer.h"
@@ -86,7 +87,7 @@ void Shutdown() {
DCHECK(g_browser_process);
// Notifies we are going away.
- ::SetEvent(g_browser_process->shutdown_event());
+ g_browser_process->shutdown_event()->Signal();
PluginService* plugin_service = PluginService::GetInstance();
if (plugin_service) {
diff --git a/chrome/browser/render_view_host.cc b/chrome/browser/render_view_host.cc
index 5ace177..399beee 100644
--- a/chrome/browser/render_view_host.cc
+++ b/chrome/browser/render_view_host.cc
@@ -8,6 +8,7 @@
#include <vector>
#include "base/string_util.h"
+#include "base/waitable_event.h"
#include "chrome/app/result_codes.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/cross_site_request_manager.h"
@@ -74,7 +75,7 @@ RenderViewHost* RenderViewHost::FromID(int render_process_id,
RenderViewHost::RenderViewHost(SiteInstance* instance,
RenderViewHostDelegate* delegate,
int routing_id,
- HANDLE modal_dialog_event)
+ base::WaitableEvent* modal_dialog_event)
: RenderWidgetHost(instance->GetProcess(), routing_id),
instance_(instance),
enable_dom_ui_bindings_(false),
@@ -93,9 +94,9 @@ RenderViewHost::RenderViewHost(SiteInstance* instance,
DCHECK(instance_);
DCHECK(delegate_);
if (modal_dialog_event == NULL)
- modal_dialog_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ modal_dialog_event = new base::WaitableEvent(true, false);
- modal_dialog_event_.Set(modal_dialog_event);
+ modal_dialog_event_.reset(modal_dialog_event);
#ifdef CHROME_PERSONALIZATION
personalization_ = Personalization::CreateHostPersonalization(this);
#endif
@@ -134,7 +135,7 @@ bool RenderViewHost::CreateRenderView() {
renderer_process_handle = GetCurrentProcess();
BOOL result = DuplicateHandle(GetCurrentProcess(),
- modal_dialog_event_.Get(),
+ modal_dialog_event_->handle(),
renderer_process_handle,
&modal_dialog_event,
SYNCHRONIZE,
@@ -498,7 +499,7 @@ void RenderViewHost::JavaScriptMessageBoxClosed(IPC::Message* reply_msg,
}
if (--modal_dialog_count_ == 0)
- ResetEvent(modal_dialog_event_.Get());
+ modal_dialog_event_->Reset();
ViewHostMsg_RunJavaScriptMessage::WriteReplyParams(reply_msg, success, prompt);
Send(reply_msg);
}
@@ -509,7 +510,7 @@ void RenderViewHost::ModalHTMLDialogClosed(IPC::Message* reply_msg,
StartHangMonitorTimeout(TimeDelta::FromMilliseconds(kUnloadTimeoutMS));
if (--modal_dialog_count_ == 0)
- ResetEvent(modal_dialog_event_.Get());
+ modal_dialog_event_->Reset();
ViewHostMsg_ShowModalHTMLDialog::WriteReplyParams(reply_msg, json_retval);
Send(reply_msg);
@@ -742,7 +743,7 @@ void RenderViewHost::Shutdown() {
// If we are being run modally (see RunModal), then we need to cleanup.
if (run_modal_reply_msg_) {
if (--modal_dialog_count_ == 0)
- ResetEvent(modal_dialog_event_.Get());
+ modal_dialog_event_->Reset();
Send(run_modal_reply_msg_);
run_modal_reply_msg_ = NULL;
}
@@ -753,7 +754,8 @@ void RenderViewHost::OnMsgCreateWindow(int route_id,
HANDLE modal_dialog_event) {
RenderViewHostDelegate::View* view = delegate_->GetViewDelegate();
if (view)
- view->CreateNewWindow(route_id, modal_dialog_event);
+ view->CreateNewWindow(route_id,
+ new base::WaitableEvent(modal_dialog_event));
}
void RenderViewHost::OnMsgCreateWidget(int route_id, bool activatable) {
@@ -781,7 +783,7 @@ void RenderViewHost::OnMsgShowWidget(int route_id,
void RenderViewHost::OnMsgRunModal(IPC::Message* reply_msg) {
DCHECK(!run_modal_reply_msg_);
if (modal_dialog_count_++ == 0)
- SetEvent(modal_dialog_event_.Get());
+ modal_dialog_event_->Reset();
run_modal_reply_msg_ = reply_msg;
// TODO(darin): Bug 1107929: Need to inform our delegate to show this view in
@@ -1085,7 +1087,7 @@ void RenderViewHost::OnMsgRunJavaScriptMessage(
IPC::Message* reply_msg) {
StopHangMonitorTimeout();
if (modal_dialog_count_++ == 0)
- SetEvent(modal_dialog_event_.Get());
+ modal_dialog_event_->Signal();
bool did_suppress_message = false;
delegate_->RunJavaScriptMessage(message, default_prompt, flags, reply_msg,
&are_javascript_messages_suppressed_);
@@ -1095,7 +1097,7 @@ void RenderViewHost::OnMsgRunBeforeUnloadConfirm(const std::wstring& message,
IPC::Message* reply_msg) {
StopHangMonitorTimeout();
if (modal_dialog_count_++ == 0)
- SetEvent(modal_dialog_event_.Get());
+ modal_dialog_event_->Signal();
delegate_->RunBeforeUnloadConfirm(message, reply_msg);
}
@@ -1104,7 +1106,7 @@ void RenderViewHost::OnMsgShowModalHTMLDialog(
IPC::Message* reply_msg) {
StopHangMonitorTimeout();
if (modal_dialog_count_++ == 0)
- SetEvent(modal_dialog_event_.Get());
+ modal_dialog_event_->Signal();
delegate_->ShowModalHTMLDialog(url, width, height, json_arguments, reply_msg);
}
diff --git a/chrome/browser/render_view_host.h b/chrome/browser/render_view_host.h
index f95720e..7b0ef7a 100644
--- a/chrome/browser/render_view_host.h
+++ b/chrome/browser/render_view_host.h
@@ -8,7 +8,7 @@
#include <string>
#include <vector>
-#include "base/scoped_handle.h"
+#include "base/scoped_ptr.h"
#include "chrome/browser/render_view_host_delegate.h"
#include "chrome/browser/render_widget_host.h"
#include "chrome/common/page_zoom.h"
@@ -33,6 +33,10 @@ struct WebDropData;
struct WebPreferences;
enum WindowOpenDisposition;
+namespace base {
+class WaitableEvent;
+}
+
namespace gfx {
class Point;
}
@@ -86,7 +90,7 @@ class RenderViewHost : public RenderWidgetHost {
explicit RenderViewHost(SiteInstance* instance,
RenderViewHostDelegate* delegate,
int routing_id,
- HANDLE modal_dialog_event);
+ base::WaitableEvent* modal_dialog_event);
virtual ~RenderViewHost();
SiteInstance* site_instance() const { return instance_; }
@@ -578,7 +582,7 @@ class RenderViewHost : public RenderWidgetHost {
// Handle to an event that's set when the page is showing a modal dialog box
// (or equivalent constrained window). The renderer and plugin processes
// check this to know if they should pump messages/tasks then.
- ScopedHandle modal_dialog_event_;
+ scoped_ptr<base::WaitableEvent> modal_dialog_event_;
// Multiple dialog boxes can be shown before the first one is finished,
// so we keep a counter to know when we can reset the modal dialog event.
@@ -618,7 +622,7 @@ class RenderViewHostFactory {
SiteInstance* instance,
RenderViewHostDelegate* delegate,
int routing_id,
- HANDLE modal_dialog_event) = 0;
+ base::WaitableEvent* modal_dialog_event) = 0;
};
#endif // CHROME_BROWSER_RENDER_VIEW_HOST_H__
diff --git a/chrome/browser/render_view_host_delegate.h b/chrome/browser/render_view_host_delegate.h
index 1fd0c41..34aba46 100644
--- a/chrome/browser/render_view_host_delegate.h
+++ b/chrome/browser/render_view_host_delegate.h
@@ -22,6 +22,10 @@ class WebContents;
struct WebDropData;
enum WindowOpenDisposition;
+namespace base {
+class WaitableEvent;
+}
+
namespace IPC {
class Message;
}
@@ -56,7 +60,10 @@ class RenderViewHostDelegate {
//
// Note: this is not called "CreateWindow" because that will clash with
// the Windows function which is actually a #define.
- virtual void CreateNewWindow(int route_id, HANDLE modal_dialog_event) = 0;
+ //
+ // NOTE: this takes ownership of @modal_dialog_event
+ virtual void CreateNewWindow(int route_id,
+ base::WaitableEvent* modal_dialog_event) = 0;
// The page is trying to open a new widget (e.g. a select popup). The
// widget should be created associated with the given route, but it should
diff --git a/chrome/browser/render_view_host_manager.cc b/chrome/browser/render_view_host_manager.cc
index f7edacf..622df9e 100644
--- a/chrome/browser/render_view_host_manager.cc
+++ b/chrome/browser/render_view_host_manager.cc
@@ -15,6 +15,10 @@
#include "chrome/common/chrome_switches.h"
#include "chrome/common/notification_service.h"
+namespace base {
+class WaitableEvent;
+}
+
RenderViewHostManager::RenderViewHostManager(
RenderViewHostFactory* render_view_factory,
RenderViewHostDelegate* render_view_delegate,
@@ -37,7 +41,7 @@ RenderViewHostManager::~RenderViewHostManager() {
void RenderViewHostManager::Init(Profile* profile,
SiteInstance* site_instance,
int routing_id,
- HANDLE modal_dialog_event) {
+ base::WaitableEvent* modal_dialog_event) {
// Create a RenderViewHost, once we have an instance. It is important to
// immediately give this SiteInstance to a RenderViewHost so that it is
// ref counted.
@@ -55,7 +59,7 @@ void RenderViewHostManager::Shutdown() {
render_view_host_->Shutdown();
render_view_host_ = NULL;
}
-
+
RenderViewHost* RenderViewHostManager::Navigate(const NavigationEntry& entry) {
RenderViewHost* dest_render_view_host = UpdateRendererStateNavigate(entry);
if (!dest_render_view_host)
@@ -347,7 +351,7 @@ bool RenderViewHostManager::CreatePendingRenderView(SiteInstance* instance) {
RenderViewHost* RenderViewHostManager::CreateRenderViewHost(
SiteInstance* instance,
int routing_id,
- HANDLE modal_dialog_event) {
+ base::WaitableEvent* modal_dialog_event) {
if (render_view_factory_) {
return render_view_factory_->CreateRenderViewHost(
instance, render_view_delegate_, routing_id, modal_dialog_event);
diff --git a/chrome/browser/render_view_host_manager.h b/chrome/browser/render_view_host_manager.h
index 3c2f61d..ee0e16b 100644
--- a/chrome/browser/render_view_host_manager.h
+++ b/chrome/browser/render_view_host_manager.h
@@ -69,7 +69,7 @@ class RenderViewHostManager {
void Init(Profile* profile,
SiteInstance* site_instance,
int routing_id,
- HANDLE modal_dialog_event);
+ base::WaitableEvent* modal_dialog_event);
// Schedules all RenderViewHosts for destruction.
void Shutdown();
@@ -179,7 +179,7 @@ class RenderViewHostManager {
// factory is NULL).
RenderViewHost* CreateRenderViewHost(SiteInstance* instance,
int routing_id,
- HANDLE modal_dialog_event);
+ base::WaitableEvent* modal_dialog_event);
// Replaces the currently shown render_view_host_ with the RenderViewHost in
// the field pointed to by |new_render_view_host|, and then NULLs the field.
diff --git a/chrome/browser/tab_contents/web_contents.cc b/chrome/browser/tab_contents/web_contents.cc
index 77faa55..638d370 100644
--- a/chrome/browser/tab_contents/web_contents.cc
+++ b/chrome/browser/tab_contents/web_contents.cc
@@ -178,7 +178,7 @@ WebContents::WebContents(Profile* profile,
SiteInstance* site_instance,
RenderViewHostFactory* render_view_factory,
int routing_id,
- HANDLE modal_dialog_event)
+ base::WaitableEvent* modal_dialog_event)
: TabContents(TAB_CONTENTS_WEB),
view_(new WebContentsViewWin(this)),
ALLOW_THIS_IN_INITIALIZER_LIST(
diff --git a/chrome/browser/tab_contents/web_contents.h b/chrome/browser/tab_contents/web_contents.h
index 7b44e79..86ce3bf 100644
--- a/chrome/browser/tab_contents/web_contents.h
+++ b/chrome/browser/tab_contents/web_contents.h
@@ -24,6 +24,10 @@ class RenderViewHostFactory;
class RenderWidgetHost;
class WebContentsView;
+namespace base {
+class WaitableEvent;
+}
+
// WebContents represents the contents of a tab that shows web pages. It embeds
// a RenderViewHost (via RenderViewHostManager) to actually display the page.
class WebContents : public TabContents,
@@ -41,7 +45,7 @@ class WebContents : public TabContents,
SiteInstance* instance,
RenderViewHostFactory* render_view_factory,
int routing_id,
- HANDLE modal_dialog_event);
+ base::WaitableEvent* modal_dialog_event);
static void RegisterUserPrefs(PrefService* prefs);
diff --git a/chrome/browser/tab_contents/web_contents_unittest.cc b/chrome/browser/tab_contents/web_contents_unittest.cc
index df018c1..fbb3ad6 100644
--- a/chrome/browser/tab_contents/web_contents_unittest.cc
+++ b/chrome/browser/tab_contents/web_contents_unittest.cc
@@ -82,7 +82,7 @@ class TestRenderViewHost : public RenderViewHost {
SiteInstance* instance,
RenderViewHostDelegate* delegate,
int routing_id,
- HANDLE modal_dialog_event)
+ base::WaitableEvent* modal_dialog_event)
: RenderViewHost(instance, delegate, routing_id, modal_dialog_event),
is_loading(false),
is_created(false),
@@ -156,7 +156,7 @@ class TestRenderViewHostFactory : public RenderViewHostFactory {
SiteInstance* instance,
RenderViewHostDelegate* delegate,
int routing_id,
- HANDLE modal_dialog_event) {
+ base::WaitableEvent* modal_dialog_event) {
return new TestRenderViewHost(
instance, delegate, routing_id, modal_dialog_event);
}
@@ -217,7 +217,7 @@ class TestWebContents : public WebContents {
RenderViewHost* CreateRenderViewHost(SiteInstance* instance,
RenderViewHostDelegate* delegate,
int routing_id,
- HANDLE modal_dialog_event) {
+ base::WaitableEvent* modal_dialog_event) {
return new TestRenderViewHost(
instance, delegate, routing_id, modal_dialog_event);
}
diff --git a/chrome/browser/tab_contents/web_contents_view.cc b/chrome/browser/tab_contents/web_contents_view.cc
index d95fd78..c3c8e8b 100644
--- a/chrome/browser/tab_contents/web_contents_view.cc
+++ b/chrome/browser/tab_contents/web_contents_view.cc
@@ -16,7 +16,8 @@ void WebContentsView::RenderWidgetHostDestroyed(RenderWidgetHost* host) {
}
}
-void WebContentsView::CreateNewWindow(int route_id, HANDLE modal_dialog_event) {
+void WebContentsView::CreateNewWindow(int route_id,
+ base::WaitableEvent* modal_dialog_event) {
// Save the created window associated with the route so we can show it later.
pending_contents_[route_id] = CreateNewWindowInternal(route_id,
modal_dialog_event);
diff --git a/chrome/browser/tab_contents/web_contents_view.h b/chrome/browser/tab_contents/web_contents_view.h
index 232b449..dc5362c 100644
--- a/chrome/browser/tab_contents/web_contents_view.h
+++ b/chrome/browser/tab_contents/web_contents_view.h
@@ -24,6 +24,10 @@ class WebContents;
struct WebDropData;
class WebKeyboardEvent;
+namespace base {
+class WaitableEvent;
+}
+
// The WebContentsView is an interface that is implemented by the platform-
// dependent web contents views. The WebContents uses this interface to talk to
// them. View-related messages will also get forwarded directly to this class
@@ -152,8 +156,8 @@ class WebContentsView : public RenderViewHostDelegate::View {
// created objects so that they can be associated with the given routes. When
// they are shown later, we'll look them up again and pass the objects to
// the Show functions rather than the route ID.
- virtual WebContents* CreateNewWindowInternal(int route_id,
- HANDLE modal_dialog_event) = 0;
+ virtual WebContents* CreateNewWindowInternal
+ (int route_id, base::WaitableEvent* modal_dialog_event) = 0;
virtual RenderWidgetHostView* CreateNewWidgetInternal(int route_id,
bool activatable) = 0;
virtual void ShowCreatedWindowInternal(WebContents* new_web_contents,
@@ -167,7 +171,8 @@ class WebContentsView : public RenderViewHostDelegate::View {
// We implement these functions on RenderViewHostDelegate::View directly and
// do some book-keeping associated with the request. The request is then
// forwarded to *Internal which does platform-specific work.
- virtual void CreateNewWindow(int route_id, HANDLE modal_dialog_event);
+ virtual void CreateNewWindow(int route_id,
+ base::WaitableEvent* modal_dialog_event);
virtual void CreateNewWidget(int route_id, bool activatable);
virtual void ShowCreatedWindow(int route_id,
WindowOpenDisposition disposition,
diff --git a/chrome/browser/tab_contents/web_contents_view_win.cc b/chrome/browser/tab_contents/web_contents_view_win.cc
index ab244cc..29af1f6 100644
--- a/chrome/browser/tab_contents/web_contents_view_win.cc
+++ b/chrome/browser/tab_contents/web_contents_view_win.cc
@@ -326,7 +326,7 @@ void WebContentsViewWin::ShowContextMenu(
WebContents* WebContentsViewWin::CreateNewWindowInternal(
int route_id,
- HANDLE modal_dialog_event) {
+ base::WaitableEvent* modal_dialog_event) {
// Create the new web contents. This will automatically create the new
// WebContentsView. In the future, we may want to create the view separately.
WebContents* new_contents =
diff --git a/chrome/browser/tab_contents/web_contents_view_win.h b/chrome/browser/tab_contents/web_contents_view_win.h
index 137674a..a94b601 100644
--- a/chrome/browser/tab_contents/web_contents_view_win.h
+++ b/chrome/browser/tab_contents/web_contents_view_win.h
@@ -48,7 +48,7 @@ class WebContentsViewWin : public WebContentsView,
// Backend implementation of RenderViewHostDelegate::View.
virtual WebContents* CreateNewWindowInternal(
- int route_id, HANDLE modal_dialog_event);
+ int route_id, base::WaitableEvent* modal_dialog_event);
virtual RenderWidgetHostView* CreateNewWidgetInternal(int route_id,
bool activatable);
virtual void ShowCreatedWindowInternal(WebContents* new_web_contents,
diff --git a/chrome/common/child_process.cc b/chrome/common/child_process.cc
index 979418f..539d8a3 100644
--- a/chrome/common/child_process.cc
+++ b/chrome/common/child_process.cc
@@ -10,13 +10,14 @@
#include "base/basictypes.h"
#include "base/command_line.h"
#include "base/string_util.h"
+#include "base/waitable_event.h"
#include "chrome/common/chrome_switches.h"
#include "webkit/glue/webkit_glue.h"
ChildProcess* ChildProcess::child_process_;
MessageLoop* ChildProcess::main_thread_loop_;
static base::AtomicRefCount ref_count;
-HANDLE ChildProcess::shutdown_event_;
+base::WaitableEvent* ChildProcess::shutdown_event_;
ChildProcess::ChildProcess() {
@@ -51,7 +52,7 @@ void ChildProcess::OnFinalRelease() {
main_thread_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask());
}
-HANDLE ChildProcess::GetShutDownEvent() {
+base::WaitableEvent* ChildProcess::GetShutDownEvent() {
return shutdown_event_;
}
@@ -74,7 +75,7 @@ bool ChildProcess::GlobalInit(const std::wstring &channel_name,
main_thread_loop_ = MessageLoop::current();
// An event that will be signalled when we shutdown.
- shutdown_event_ = CreateEvent(NULL, TRUE, FALSE, NULL);
+ shutdown_event_ = new base::WaitableEvent(true, false);
child_process_ = factory->Create(channel_name);
@@ -92,7 +93,7 @@ void ChildProcess::GlobalCleanup() {
// background threads.
// For example, in the renderer the RenderThread instances will be able to
// notice shutdown before the render process begins waiting for them to exit.
- SetEvent(shutdown_event_);
+ shutdown_event_->Signal();
// Destroy the child process first to force all background threads to
// terminate before we bring down other resources. (We null pointers
@@ -103,7 +104,7 @@ void ChildProcess::GlobalCleanup() {
main_thread_loop_ = NULL;
- CloseHandle(shutdown_event_);
+ delete shutdown_event_;
shutdown_event_ = NULL;
}
diff --git a/chrome/common/child_process.h b/chrome/common/child_process.h
index a74205a..4a63efb 100644
--- a/chrome/common/child_process.h
+++ b/chrome/common/child_process.h
@@ -10,6 +10,9 @@
#include "base/basictypes.h"
#include "base/message_loop.h"
+namespace base {
+ class WaitableEvent;
+};
class ChildProcess;
@@ -53,7 +56,7 @@ class ChildProcess {
// up waiting.
// For example, see the renderer code used to implement
// webkit_glue::GetCookies.
- static HANDLE GetShutDownEvent();
+ static base::WaitableEvent* GetShutDownEvent();
// You must call Init after creating this object before it will be valid
ChildProcess();
@@ -80,7 +83,7 @@ class ChildProcess {
// Derived classes can override this to handle any cleanup, called by
// GlobalCleanup.
virtual void Cleanup() {}
- static HANDLE shutdown_event_;
+ static base::WaitableEvent* shutdown_event_;
DISALLOW_EVIL_CONSTRUCTORS(ChildProcess);
};
diff --git a/chrome/common/common.scons b/chrome/common/common.scons
index 86255d8..c59ca6c 100644
--- a/chrome/common/common.scons
+++ b/chrome/common/common.scons
@@ -55,6 +55,7 @@ input_files.extend([
'ipc_channel_proxy.cc',
'ipc_message.cc',
'ipc_message_utils.cc',
+ 'ipc_sync_channel.cc',
'ipc_sync_message.cc',
'jpeg_codec.cc',
'json_value_serializer.cc',
@@ -99,7 +100,6 @@ if env.Bit('windows'):
'gfx/path.cc',
'gfx/text_elider.cc',
'ipc_logging.cc',
- 'ipc_sync_channel.cc',
'jstemplate_builder.cc',
'os_exchange_data.cc',
'plugin_messages.cc',
diff --git a/chrome/common/ipc_logging.h b/chrome/common/ipc_logging.h
index 2736402..567ba84 100644
--- a/chrome/common/ipc_logging.h
+++ b/chrome/common/ipc_logging.h
@@ -5,13 +5,14 @@
#ifndef CHROME_COMMON_IPC_LOGGING_H_
#define CHROME_COMMON_IPC_LOGGING_H_
-#include "base/lock.h"
-#include "base/object_watcher.h"
-#include "base/singleton.h"
#include "chrome/common/ipc_message.h" // For IPC_MESSAGE_LOG_ENABLED.
#ifdef IPC_MESSAGE_LOG_ENABLED
+#include "base/lock.h"
+#include "base/object_watcher.h"
+#include "base/singleton.h"
+
class MessageLoop;
namespace IPC {
diff --git a/chrome/common/ipc_message.h b/chrome/common/ipc_message.h
index 8163d4f..040a346 100644
--- a/chrome/common/ipc_message.h
+++ b/chrome/common/ipc_message.h
@@ -11,9 +11,12 @@
#include "base/pickle.h"
#include "testing/gtest/include/gtest/gtest_prod.h"
+#if defined(OS_WIN)
+// TODO(port): IPC message logging hasn't been ported to other platforms yet.
#ifndef NDEBUG
#define IPC_MESSAGE_LOG_ENABLED
#endif
+#endif
namespace IPC {
diff --git a/chrome/common/ipc_sync_channel.cc b/chrome/common/ipc_sync_channel.cc
index 45e42d6..4f1e71f 100644
--- a/chrome/common/ipc_sync_channel.cc
+++ b/chrome/common/ipc_sync_channel.cc
@@ -2,19 +2,24 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include <windows.h>
-
#include "chrome/common/ipc_sync_channel.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/thread_local.h"
-#include "chrome/common/child_process.h"
+#include "base/message_loop.h"
+#include "base/waitable_event.h"
+#include "base/waitable_event_watcher.h"
#include "chrome/common/ipc_logging.h"
#include "chrome/common/ipc_sync_message.h"
+#if !defined(OS_WIN)
+#define INFINITE -1
+#endif
+
using base::TimeDelta;
using base::TimeTicks;
+using base::WaitableEvent;
namespace IPC {
// When we're blocked in a Send(), we need to process incoming synchronous
@@ -34,8 +39,6 @@ namespace IPC {
// SyncChannel objects on the same thread (since one object can receive a
// sync message while another one is blocked).
-class SyncChannel::ReceivedSyncMsgQueue;
-
class SyncChannel::ReceivedSyncMsgQueue :
public base::RefCountedThreadSafe<ReceivedSyncMsgQueue> {
public:
@@ -70,7 +73,7 @@ class SyncChannel::ReceivedSyncMsgQueue :
message_queue_.push_back(QueuedMessage(new Message(msg), context));
}
- SetEvent(dispatch_event_);
+ dispatch_event_.Signal();
if (!was_task_pending) {
listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
this, &ReceivedSyncMsgQueue::DispatchMessagesTask));
@@ -143,7 +146,7 @@ class SyncChannel::ReceivedSyncMsgQueue :
}
}
- HANDLE dispatch_event() { return dispatch_event_; }
+ WaitableEvent* dispatch_event() { return &dispatch_event_; }
MessageLoop* listener_message_loop() { return listener_message_loop_; }
// Holds a pointer to the per-thread ReceivedSyncMsgQueue object.
@@ -167,9 +170,9 @@ class SyncChannel::ReceivedSyncMsgQueue :
// See the comment in SyncChannel::SyncChannel for why this event is created
// as manual reset.
ReceivedSyncMsgQueue() :
- dispatch_event_(CreateEvent(NULL, TRUE, FALSE, NULL)),
- task_pending_(false),
+ dispatch_event_(true, false),
listener_message_loop_(MessageLoop::current()),
+ task_pending_(false),
listener_count_(0) {
}
@@ -181,14 +184,14 @@ class SyncChannel::ReceivedSyncMsgQueue :
};
typedef std::deque<QueuedMessage> SyncMessageQueue;
- SyncMessageQueue message_queue_;
+ SyncMessageQueue message_queue_;
std::vector<QueuedMessage> received_replies_;
// Set when we got a synchronous message that we must respond to as the
// sender needs its reply before it can reply to our original synchronous
// message.
- ScopedHandle dispatch_event_;
+ WaitableEvent dispatch_event_;
MessageLoop* listener_message_loop_;
Lock message_lock_;
bool task_pending_;
@@ -202,10 +205,10 @@ SyncChannel::SyncContext::SyncContext(
Channel::Listener* listener,
MessageFilter* filter,
MessageLoop* ipc_thread,
- HANDLE shutdown_event)
+ WaitableEvent* shutdown_event)
: ChannelProxy::Context(listener, filter, ipc_thread),
- shutdown_event_(shutdown_event),
- received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()){
+ received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()),
+ shutdown_event_(shutdown_event) {
}
SyncChannel::SyncContext::~SyncContext() {
@@ -217,13 +220,13 @@ SyncChannel::SyncContext::~SyncContext() {
// we know how to deserialize the reply. Returns a handle that's set when
// the reply has arrived.
void SyncChannel::SyncContext::Push(SyncMessage* sync_msg) {
- // The event is created as manual reset because in between SetEvent and
+ // The event is created as manual reset because in between Signal and
// OnObjectSignalled, another Send can happen which would stop the watcher
// from being called. The event would get watched later, when the nested
// Send completes, so the event will need to remain set.
PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg),
sync_msg->GetReplyDeserializer(),
- CreateEvent(NULL, TRUE, FALSE, NULL));
+ new WaitableEvent(true, false));
AutoLock auto_lock(deserializers_lock_);
deserializers_.push_back(pending);
}
@@ -234,7 +237,8 @@ bool SyncChannel::SyncContext::Pop() {
AutoLock auto_lock(deserializers_lock_);
PendingSyncMsg msg = deserializers_.back();
delete msg.deserializer;
- CloseHandle(msg.done_event);
+ delete msg.done_event;
+ msg.done_event = NULL;
deserializers_.pop_back();
result = msg.send_result;
}
@@ -250,12 +254,12 @@ bool SyncChannel::SyncContext::Pop() {
return result;
}
-HANDLE SyncChannel::SyncContext::GetSendDoneEvent() {
+WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() {
AutoLock auto_lock(deserializers_lock_);
return deserializers_.back().done_event;
}
-HANDLE SyncChannel::SyncContext::GetDispatchEvent() {
+WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() {
return received_sync_msgs_->dispatch_event();
}
@@ -274,7 +278,7 @@ bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) {
deserializers_.back().send_result = deserializers_.back().deserializer->
SerializeOutputParameters(*msg);
}
- SetEvent(deserializers_.back().done_event);
+ deserializers_.back().done_event->Signal();
return true;
}
@@ -327,7 +331,7 @@ void SyncChannel::SyncContext::OnSendTimeout(int message_id) {
PendingSyncMessageQueue::iterator iter;
for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) {
if (iter->id == message_id) {
- SetEvent(iter->done_event);
+ iter->done_event->Signal();
break;
}
}
@@ -337,11 +341,11 @@ void SyncChannel::SyncContext::CancelPendingSends() {
AutoLock auto_lock(deserializers_lock_);
PendingSyncMessageQueue::iterator iter;
for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++)
- SetEvent(iter->done_event);
+ iter->done_event->Signal();
}
-void SyncChannel::SyncContext::OnObjectSignaled(HANDLE object) {
- DCHECK(object == shutdown_event_);
+void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) {
+ DCHECK(event == shutdown_event_);
// Process shut down before we can get a reply to a synchronous message.
// Cancel pending Send calls, which will end up setting the send done event.
CancelPendingSends();
@@ -351,7 +355,8 @@ void SyncChannel::SyncContext::OnObjectSignaled(HANDLE object) {
SyncChannel::SyncChannel(
const std::wstring& channel_id, Channel::Mode mode,
Channel::Listener* listener, MessageFilter* filter,
- MessageLoop* ipc_message_loop, bool create_pipe_now, HANDLE shutdown_event)
+ MessageLoop* ipc_message_loop, bool create_pipe_now,
+ WaitableEvent* shutdown_event)
: ChannelProxy(
channel_id, mode, ipc_message_loop,
new SyncContext(listener, filter, ipc_message_loop, shutdown_event),
@@ -362,7 +367,7 @@ SyncChannel::SyncChannel(
// message loop running under it or not, so we wouldn't know whether to
// stop or keep watching. So we always watch it, and create the event as
// manual reset since the object watcher might otherwise reset the event
- // when we're doing a WaitForMultipleObjects.
+ // when we're doing a WaitMany.
dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(), this);
}
@@ -381,7 +386,7 @@ bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) {
// *this* might get deleted in WaitForReply.
scoped_refptr<SyncContext> context(sync_context());
- if (WaitForSingleObject(context->shutdown_event(), 0) == WAIT_OBJECT_0) {
+ if (context->shutdown_event()->IsSignaled()) {
delete message;
return false;
}
@@ -390,7 +395,7 @@ bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) {
SyncMessage* sync_msg = static_cast<SyncMessage*>(message);
context->Push(sync_msg);
int message_id = SyncMessage::GetMessageId(*sync_msg);
- HANDLE pump_messages_event = sync_msg->pump_messages_event();
+ WaitableEvent* pump_messages_event = sync_msg->pump_messages_event();
ChannelProxy::Send(message);
@@ -409,22 +414,25 @@ bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) {
return context->Pop();
}
-void SyncChannel::WaitForReply(HANDLE pump_messages_event) {
+void SyncChannel::WaitForReply(WaitableEvent* pump_messages_event) {
while (true) {
- HANDLE objects[] = { sync_context()->GetDispatchEvent(),
- sync_context()->GetSendDoneEvent(),
- pump_messages_event };
- uint32 count = pump_messages_event ? 3: 2;
- DWORD result = WaitForMultipleObjects(count, objects, FALSE, INFINITE);
- if (result == WAIT_OBJECT_0) {
+ WaitableEvent* objects[] = {
+ sync_context()->GetDispatchEvent(),
+ sync_context()->GetSendDoneEvent(),
+ pump_messages_event
+ };
+
+ unsigned count = pump_messages_event ? 3: 2;
+ unsigned result = WaitableEvent::WaitMany(objects, count);
+ if (result == 0 /* dispatch event */) {
// We're waiting for a reply, but we received a blocking synchronous
// call. We must process it or otherwise a deadlock might occur.
- ResetEvent(sync_context()->GetDispatchEvent());
+ sync_context()->GetDispatchEvent()->Reset();
sync_context()->DispatchMessages();
continue;
}
- if (result == WAIT_OBJECT_0 + 2)
+ if (result == 2 /* pump_messages_event */)
WaitForReplyWithNestedMessageLoop(); // Start a nested message loop.
break;
@@ -432,7 +440,7 @@ void SyncChannel::WaitForReply(HANDLE pump_messages_event) {
}
void SyncChannel::WaitForReplyWithNestedMessageLoop() {
- HANDLE old_done_event = send_done_watcher_.GetWatchedObject();
+ WaitableEvent* old_done_event = send_done_watcher_.GetWatchedEvent();
send_done_watcher_.StopWatching();
send_done_watcher_.StartWatching(sync_context()->GetSendDoneEvent(), this);
bool old_state = MessageLoop::current()->NestableTasksAllowed();
@@ -443,17 +451,17 @@ void SyncChannel::WaitForReplyWithNestedMessageLoop() {
send_done_watcher_.StartWatching(old_done_event, this);
}
-void SyncChannel::OnObjectSignaled(HANDLE object) {
- HANDLE dispatch_event = sync_context()->GetDispatchEvent();
- if (object == dispatch_event) {
+void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) {
+ WaitableEvent* dispatch_event = sync_context()->GetDispatchEvent();
+ if (event == dispatch_event) {
// The call to DispatchMessages might delete this object, so reregister
// the object watcher first.
- ResetEvent(dispatch_event);
+ dispatch_event->Reset();
dispatch_watcher_.StartWatching(dispatch_event, this);
sync_context()->DispatchMessages();
} else {
// We got the reply, timed out or the process shutdown.
- DCHECK(object == sync_context()->GetSendDoneEvent());
+ DCHECK(event == sync_context()->GetSendDoneEvent());
MessageLoop::current()->Quit();
}
}
diff --git a/chrome/common/ipc_sync_channel.h b/chrome/common/ipc_sync_channel.h
index 6c03745..725fb33 100644
--- a/chrome/common/ipc_sync_channel.h
+++ b/chrome/common/ipc_sync_channel.h
@@ -5,19 +5,20 @@
#ifndef CHROME_COMMON_IPC_SYNC_SENDER_H__
#define CHROME_COMMON_IPC_SYNC_SENDER_H__
-#include <windows.h>
#include <string>
#include <deque>
#include "base/basictypes.h"
#include "base/lock.h"
-#include "base/object_watcher.h"
#include "base/ref_counted.h"
#include "base/scoped_handle.h"
+#include "base/waitable_event.h"
+#include "base/waitable_event_watcher.h"
#include "chrome/common/ipc_channel_proxy.h"
namespace IPC {
class SyncMessage;
+class MessageReplyDeserializer;
// This is similar to IPC::ChannelProxy, with the added feature of supporting
// sending synchronous messages.
@@ -26,12 +27,12 @@ class SyncMessage;
// is running and it's used to send a message, then it will use the invalid
// message loop pointer to proxy it to the ipc thread.
class SyncChannel : public ChannelProxy,
- public base::ObjectWatcher::Delegate {
+ public base::WaitableEventWatcher::Delegate {
public:
SyncChannel(const std::wstring& channel_id, Channel::Mode mode,
Channel::Listener* listener, MessageFilter* filter,
MessageLoop* ipc_message_loop, bool create_pipe_now,
- HANDLE shutdown_event);
+ base::WaitableEvent* shutdown_event);
~SyncChannel();
virtual bool Send(Message* message);
@@ -50,12 +51,12 @@ class SyncChannel : public ChannelProxy,
// can be deleted while it's being used in a different thread. See
// ChannelProxy::Context for more information.
class SyncContext : public Context,
- public base::ObjectWatcher::Delegate {
+ public base::WaitableEventWatcher::Delegate {
public:
SyncContext(Channel::Listener* listener,
MessageFilter* filter,
MessageLoop* ipc_thread,
- HANDLE shutdown_event);
+ base::WaitableEvent* shutdown_event);
~SyncContext();
@@ -69,11 +70,11 @@ class SyncChannel : public ChannelProxy,
// Returns an event that's set when the send is complete, timed out or the
// process shut down.
- HANDLE GetSendDoneEvent();
+ base::WaitableEvent* GetSendDoneEvent();
// Returns an event that's set when an incoming message that's not the reply
// needs to get dispatched (by calling SyncContext::DispatchMessages).
- HANDLE GetDispatchEvent();
+ base::WaitableEvent* GetDispatchEvent();
void DispatchMessages();
@@ -86,7 +87,7 @@ class SyncChannel : public ChannelProxy,
// times out.
void OnSendTimeout(int message_id);
- HANDLE shutdown_event() { return shutdown_event_; }
+ base::WaitableEvent* shutdown_event() { return shutdown_event_; }
private:
// IPC::ChannelProxy methods that we override.
@@ -103,17 +104,18 @@ class SyncChannel : public ChannelProxy,
// Cancels all pending Send calls.
void CancelPendingSends();
- // ObjectWatcher::Delegate implementation.
- virtual void OnObjectSignaled(HANDLE object);
+ // WaitableEventWatcher::Delegate implementation.
+ virtual void OnWaitableEventSignaled(base::WaitableEvent* arg);
// When sending a synchronous message, this structure contains an object that
// knows how to deserialize the response.
struct PendingSyncMsg {
- PendingSyncMsg(int id, IPC::MessageReplyDeserializer* d, HANDLE e) :
+ PendingSyncMsg(int id, IPC::MessageReplyDeserializer* d,
+ base::WaitableEvent* e) :
id(id), deserializer(d), done_event(e), send_result(false) { }
int id;
IPC::MessageReplyDeserializer* deserializer;
- HANDLE done_event;
+ base::WaitableEvent* done_event;
bool send_result;
};
@@ -123,19 +125,19 @@ class SyncChannel : public ChannelProxy,
scoped_refptr<ReceivedSyncMsgQueue> received_sync_msgs_;
- HANDLE shutdown_event_;
- base::ObjectWatcher shutdown_watcher_;
+ base::WaitableEvent* shutdown_event_;
+ base::WaitableEventWatcher shutdown_watcher_;
};
private:
- // ObjectWatcher::Delegate implementation.
- virtual void OnObjectSignaled(HANDLE object);
+ // WaitableEventWatcher::Delegate implementation.
+ virtual void OnWaitableEventSignaled(base::WaitableEvent* arg);
SyncContext* sync_context() { return reinterpret_cast<SyncContext*>(context()); }
// Both these functions wait for a reply, timeout or process shutdown. The
// latter one also runs a nested message loop in the meantime.
- void WaitForReply(HANDLE pump_messages_event);
+ void WaitForReply(base::WaitableEvent* pump_messages_event);
// Runs a nested message loop until a reply arrives, times out, or the process
// shuts down.
@@ -144,8 +146,8 @@ class SyncChannel : public ChannelProxy,
bool sync_messages_with_no_timeout_allowed_;
// Used to signal events between the IPC and listener threads.
- base::ObjectWatcher send_done_watcher_;
- base::ObjectWatcher dispatch_watcher_;
+ base::WaitableEventWatcher send_done_watcher_;
+ base::WaitableEventWatcher dispatch_watcher_;
DISALLOW_EVIL_CONSTRUCTORS(SyncChannel);
};
diff --git a/chrome/common/ipc_sync_message.cc b/chrome/common/ipc_sync_message.cc
index 4a851e5..a07ea15 100644
--- a/chrome/common/ipc_sync_message.cc
+++ b/chrome/common/ipc_sync_message.cc
@@ -9,8 +9,9 @@
#endif
#include <stack>
-#include "chrome/common/ipc_sync_message.h"
#include "base/logging.h"
+#include "base/waitable_event.h"
+#include "chrome/common/ipc_sync_message.h"
namespace IPC {
@@ -20,7 +21,7 @@ uint32 SyncMessage::next_id_ = 0;
#if defined(OS_WIN)
// TODO(playmobil): reinstantiate once ObjectWatcher is ported.
// A dummy handle used by EnableMessagePumping.
-HANDLE dummy_event = ::CreateEvent(NULL, TRUE, TRUE, NULL);
+base::WaitableEvent* dummy_event = new base::WaitableEvent(true, true);
#endif
SyncMessage::SyncMessage(
@@ -29,11 +30,8 @@ SyncMessage::SyncMessage(
PriorityValue priority,
MessageReplyDeserializer* deserializer)
: Message(routing_id, type, priority),
- deserializer_(deserializer)
-#if defined(OS_WIN)
- // TODO(playmobil): reinstantiate once ObjectWatcher is ported.
- , pump_messages_event_(NULL)
-#endif
+ deserializer_(deserializer),
+ pump_messages_event_(NULL)
{
set_sync();
set_unblock(true);
diff --git a/chrome/common/ipc_sync_message.h b/chrome/common/ipc_sync_message.h
index 116e6c7..034de07 100644
--- a/chrome/common/ipc_sync_message.h
+++ b/chrome/common/ipc_sync_message.h
@@ -12,6 +12,10 @@
#include "base/basictypes.h"
#include "chrome/common/ipc_message.h"
+namespace base {
+class WaitableEvent;
+}
+
namespace IPC {
class MessageReplyDeserializer;
@@ -26,15 +30,13 @@ class SyncMessage : public Message {
// for deleting the deserializer when they're done.
MessageReplyDeserializer* GetReplyDeserializer();
-// TODO(playmobil): reimplement on POSIX.
-#if defined(OS_WIN)
// If this message can cause the receiver to block while waiting for user
// input (i.e. by calling MessageBox), then the caller needs to pump window
// messages and dispatch asynchronous messages while waiting for the reply.
- // If this handle is passed in, then window messages will start being pumped
+ // If this event is passed in, then window messages will start being pumped
// when it's set. Note that this behavior will continue even if the event is
- // later reset. The handle must be valid until after the Send call returns.
- void set_pump_messages_event(HANDLE event) {
+ // later reset. The event must be valid until after the Send call returns.
+ void set_pump_messages_event(base::WaitableEvent* event) {
pump_messages_event_ = event;
if (event) {
header()->flags |= PUMPING_MSGS_BIT;
@@ -47,8 +49,9 @@ class SyncMessage : public Message {
// or set_pump_messages_event but not both.
void EnableMessagePumping();
- HANDLE pump_messages_event() const { return pump_messages_event_; }
-#endif // defined(OS_WIN)
+ base::WaitableEvent* pump_messages_event() const {
+ return pump_messages_event_;
+ }
// Returns true if the message is a reply to the given request id.
static bool IsMessageReplyTo(const Message& msg, int request_id);
@@ -73,9 +76,7 @@ class SyncMessage : public Message {
static bool WriteSyncHeader(Message* msg, const SyncHeader& header);
MessageReplyDeserializer* deserializer_;
-#if defined(OS_WIN)
- HANDLE pump_messages_event_;
-#endif
+ base::WaitableEvent* pump_messages_event_;
static uint32 next_id_; // for generation of unique ids
};
diff --git a/chrome/plugin/npobject_proxy.cc b/chrome/plugin/npobject_proxy.cc
index 7df4e5c..3c4f811 100644
--- a/chrome/plugin/npobject_proxy.cc
+++ b/chrome/plugin/npobject_proxy.cc
@@ -4,6 +4,7 @@
#include "chrome/plugin/npobject_proxy.h"
+#include "base/waitable_event.h"
#include "chrome/common/plugin_messages.h"
#include "chrome/common/win_util.h"
#include "chrome/plugin/npobject_util.h"
@@ -48,7 +49,7 @@ NPObjectProxy::NPObjectProxy(
PluginChannelBase* channel,
int route_id,
void* npobject_ptr,
- HANDLE modal_dialog_event)
+ base::WaitableEvent* modal_dialog_event)
: channel_(channel),
route_id_(route_id),
npobject_ptr_(npobject_ptr),
@@ -67,7 +68,7 @@ NPObjectProxy::~NPObjectProxy() {
NPObject* NPObjectProxy::Create(PluginChannelBase* channel,
int route_id,
void* npobject_ptr,
- HANDLE modal_dialog_event) {
+ base::WaitableEvent* modal_dialog_event) {
NPObjectWrapper* obj = reinterpret_cast<NPObjectWrapper*>(
NPN_CreateObject(0, &npclass_proxy_));
obj->proxy = new NPObjectProxy(
@@ -178,7 +179,7 @@ bool NPObjectProxy::NPInvokePrivate(NPP npp,
// messages are pumped).
msg->set_pump_messages_event(proxy->modal_dialog_event_);
- HANDLE modal_dialog_event_handle = proxy->modal_dialog_event_;
+ base::WaitableEvent* modal_dialog_event_handle = proxy->modal_dialog_event_;
proxy->Send(msg);
@@ -237,7 +238,7 @@ bool NPObjectProxy::NPGetProperty(NPObject *obj,
CreateNPIdentifierParam(name, &name_param);
NPVariant_Param param;
- HANDLE modal_dialog_event_handle = proxy->modal_dialog_event_;
+ base::WaitableEvent* modal_dialog_event_handle = proxy->modal_dialog_event_;
scoped_refptr<PluginChannelBase> channel(proxy->channel_);
proxy->Send(new NPObjectMsg_GetProperty(
proxy->route_id(), name_param, &param, &result));
@@ -367,7 +368,7 @@ bool NPObjectProxy::NPNEvaluate(NPP npp,
// the reasoning behind setting the pump messages event in the sync message.
msg->set_pump_messages_event(proxy->modal_dialog_event_);
scoped_refptr<PluginChannelBase> channel(proxy->channel_);
- HANDLE modal_dialog_event_handle = proxy->modal_dialog_event_;
+ base::WaitableEvent* modal_dialog_event_handle = proxy->modal_dialog_event_;
proxy->Send(msg);
// Send may delete proxy.
proxy = NULL;
diff --git a/chrome/plugin/npobject_proxy.h b/chrome/plugin/npobject_proxy.h
index 4157717..dcc4d01 100644
--- a/chrome/plugin/npobject_proxy.h
+++ b/chrome/plugin/npobject_proxy.h
@@ -16,6 +16,10 @@ class PluginChannelBase;
struct NPObject;
struct NPVariant_Param;
+namespace base {
+class WaitableEvent;
+}
+
// When running a plugin in a different process from the renderer, we need to
// proxy calls to NPObjects across process boundaries. This happens both ways,
// as a plugin can get an NPObject for the window, and a page can get an
@@ -29,10 +33,11 @@ class NPObjectProxy : public IPC::Channel::Listener,
public:
~NPObjectProxy();
+ // modal_dialog_event_ is must be valid for the lifetime of the NPObjectProxy.
static NPObject* Create(PluginChannelBase* channel,
int route_id,
void* npobject_ptr,
- HANDLE modal_dialog_event);
+ base::WaitableEvent* modal_dialog_event);
// IPC::Message::Sender implementation:
bool Send(IPC::Message* msg);
@@ -93,7 +98,7 @@ class NPObjectProxy : public IPC::Channel::Listener,
NPObjectProxy(PluginChannelBase* channel,
int route_id,
void* npobject_ptr,
- HANDLE modal_dialog_event);
+ base::WaitableEvent* modal_dialog_event);
// IPC::Channel::Listener implementation:
void OnMessageReceived(const IPC::Message& msg);
@@ -109,7 +114,7 @@ class NPObjectProxy : public IPC::Channel::Listener,
int route_id_;
void* npobject_ptr_;
scoped_refptr<PluginChannelBase> channel_;
- HANDLE modal_dialog_event_;
+ base::WaitableEvent* modal_dialog_event_;
};
#endif // CHROME_PLUGIN_NPOBJECT_PROXY_H_
diff --git a/chrome/plugin/npobject_util.cc b/chrome/plugin/npobject_util.cc
index 8936471..f5283f9 100644
--- a/chrome/plugin/npobject_util.cc
+++ b/chrome/plugin/npobject_util.cc
@@ -200,7 +200,7 @@ void CreateNPVariantParam(const NPVariant& variant,
void CreateNPVariant(const NPVariant_Param& param,
PluginChannelBase* channel,
NPVariant* result,
- HANDLE modal_dialog_event) {
+ base::WaitableEvent* modal_dialog_event) {
switch (param.type) {
case NPVARIANT_PARAM_VOID:
result->type = NPVariantType_Void;
diff --git a/chrome/plugin/npobject_util.h b/chrome/plugin/npobject_util.h
index cc9347e..ba74a6b 100644
--- a/chrome/plugin/npobject_util.h
+++ b/chrome/plugin/npobject_util.h
@@ -22,6 +22,9 @@ struct NPIdentifier_Param;
struct NPVariant_Param;
typedef void *NPIdentifier;
+namespace base {
+class WaitableEvent;
+}
// Needs to be called early in the plugin process lifetime, before any
// plugin instances are initialized.
@@ -52,7 +55,7 @@ void CreateNPVariantParam(const NPVariant& variant,
void CreateNPVariant(const NPVariant_Param& param,
PluginChannelBase* channel,
NPVariant* result,
- HANDLE modal_dialog_event);
+ base::WaitableEvent* modal_dialog_event);
// Given a plugin's HWND, returns an event associated with the WebContents
// that's set when inside a messagebox. This tells the plugin process that
diff --git a/chrome/plugin/webplugin_proxy.cc b/chrome/plugin/webplugin_proxy.cc
index 0bd6ed8..bb7ee5c 100644
--- a/chrome/plugin/webplugin_proxy.cc
+++ b/chrome/plugin/webplugin_proxy.cc
@@ -8,6 +8,7 @@
#include "base/scoped_handle.h"
#include "base/shared_memory.h"
#include "base/singleton.h"
+#include "base/waitable_event.h"
#include "chrome/common/gfx/chrome_canvas.h"
#include "chrome/common/plugin_messages.h"
#include "chrome/common/win_util.h"
@@ -47,7 +48,7 @@ WebPluginProxy::WebPluginProxy(
FALSE,
0);
DCHECK(result) << "Couldn't duplicate the modal dialog handle for the plugin.";
- modal_dialog_event_.Set(event);
+ modal_dialog_event_.reset(new base::WaitableEvent(event));
}
WebPluginProxy::~WebPluginProxy() {
@@ -121,7 +122,7 @@ NPObject* WebPluginProxy::GetWindowScriptNPObject() {
window_npobject_ = NPObjectProxy::Create(channel_,
npobject_route_id,
npobject_ptr,
- modal_dialog_event_.Get());
+ modal_dialog_event_.get());
return window_npobject_;
}
@@ -141,7 +142,7 @@ NPObject* WebPluginProxy::GetPluginElement() {
plugin_element_ = NPObjectProxy::Create(channel_,
npobject_route_id,
npobject_ptr,
- modal_dialog_event_.Get());
+ modal_dialog_event_.get());
return plugin_element_;
}
@@ -170,8 +171,9 @@ void WebPluginProxy::ShowModalHTMLDialog(const GURL& url, int width, int height,
// Create a new event and set it. This forces us to pump messages while
// waiting for a response (which won't come until the dialog is closed). This
// avoids a deadlock.
- ScopedHandle event(CreateEvent(NULL, FALSE, TRUE, NULL));
- msg->set_pump_messages_event(event);
+ scoped_ptr<base::WaitableEvent> event(
+ new base::WaitableEvent(false, true));
+ msg->set_pump_messages_event(event.get());
Send(msg);
}
diff --git a/chrome/plugin/webplugin_proxy.h b/chrome/plugin/webplugin_proxy.h
index c2cefc7..7828abf 100644
--- a/chrome/plugin/webplugin_proxy.h
+++ b/chrome/plugin/webplugin_proxy.h
@@ -8,12 +8,17 @@
#include "base/hash_tables.h"
#include "base/ref_counted.h"
#include "base/scoped_handle.h"
+#include "base/scoped_ptr.h"
#include "base/shared_memory.h"
#include "base/timer.h"
#include "chrome/common/ipc_message.h"
#include "chrome/common/chrome_plugin_api.h"
#include "webkit/glue/webplugin.h"
+namespace base {
+class WaitableEvent;
+}
+
class PluginChannel;
class WebPluginDelegateImpl;
@@ -126,7 +131,7 @@ class WebPluginProxy : public WebPlugin {
gfx::Rect damaged_rect_;
bool waiting_for_paint_;
uint32 cp_browsing_context_;
- ScopedHandle modal_dialog_event_;
+ scoped_ptr<base::WaitableEvent> modal_dialog_event_;
// Variables used for desynchronized windowless plugin painting. See note in
// webplugin_delegate_proxy.h for how this works.
diff --git a/chrome/renderer/render_thread.cc b/chrome/renderer/render_thread.cc
index 311e865..e0108e4 100644
--- a/chrome/renderer/render_thread.cc
+++ b/chrome/renderer/render_thread.cc
@@ -198,8 +198,10 @@ void RenderThread::OnCreateNewView(HWND parent_hwnd,
int32 view_id) {
// TODO(darin): once we have a RenderThread per RenderView, this will need to
// change to assert that we are not creating more than one view.
+ base::WaitableEvent* waitable_event =
+ new base::WaitableEvent(modal_dialog_event);
RenderView::Create(
- this, parent_hwnd, modal_dialog_event, MSG_ROUTING_NONE, webkit_prefs,
+ this, parent_hwnd, waitable_event, MSG_ROUTING_NONE, webkit_prefs,
new SharedRenderViewCounter(0), view_id);
}
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc
index 708453f..7c85b65 100644
--- a/chrome/renderer/render_view.cc
+++ b/chrome/renderer/render_view.cc
@@ -197,7 +197,7 @@ RenderView::~RenderView() {
RenderView* RenderView::Create(
RenderThreadBase* render_thread,
HWND parent_hwnd,
- HANDLE modal_dialog_event,
+ base::WaitableEvent* modal_dialog_event,
int32 opener_id,
const WebPreferences& webkit_prefs,
SharedRenderViewCounter* counter,
@@ -245,7 +245,7 @@ void RenderView::JSOutOfMemory() {
}
void RenderView::Init(HWND parent_hwnd,
- HANDLE modal_dialog_event,
+ base::WaitableEvent* modal_dialog_event,
int32 opener_id,
const WebPreferences& webkit_prefs,
SharedRenderViewCounter* counter,
@@ -287,7 +287,7 @@ void RenderView::Init(HWND parent_hwnd,
}
host_window_ = parent_hwnd;
- modal_dialog_event_.Set(modal_dialog_event);
+ modal_dialog_event_.reset(modal_dialog_event);
CommandLine command_line;
enable_dom_automation_ =
@@ -1648,7 +1648,7 @@ bool RenderView::RunJavaScriptMessage(int type,
IPC::SyncMessage* msg = new ViewHostMsg_RunJavaScriptMessage(
routing_id_, message, default_value, type, &success, result);
- msg->set_pump_messages_event(modal_dialog_event_);
+ msg->set_pump_messages_event(modal_dialog_event_.get());
Send(msg);
return success;
@@ -1669,7 +1669,7 @@ bool RenderView::RunBeforeUnloadConfirm(WebView* webview,
IPC::SyncMessage* msg = new ViewHostMsg_RunBeforeUnloadConfirm(
routing_id_, message, &success, &ignored_result);
- msg->set_pump_messages_event(modal_dialog_event_);
+ msg->set_pump_messages_event(modal_dialog_event_.get());
Send(msg);
return success;
@@ -1716,7 +1716,7 @@ void RenderView::ShowModalHTMLDialog(const GURL& url, int width, int height,
IPC::SyncMessage* msg = new ViewHostMsg_ShowModalHTMLDialog(
routing_id_, url, width, height, json_arguments, json_retval);
- msg->set_pump_messages_event(modal_dialog_event_);
+ msg->set_pump_messages_event(modal_dialog_event_.get());
Send(msg);
}
@@ -1803,8 +1803,10 @@ WebView* RenderView::CreateWebView(WebView* webview, bool user_gesture) {
// The WebView holds a reference to this new RenderView
const WebPreferences& prefs = webview->GetPreferences();
+ base::WaitableEvent* waitable_event =
+ new base::WaitableEvent(modal_dialog_event);
RenderView* view = RenderView::Create(render_thread_,
- NULL, modal_dialog_event, routing_id_,
+ NULL, waitable_event, routing_id_,
prefs, shared_popup_counter_,
routing_id);
view->set_opened_by_user_gesture(user_gesture);
@@ -1938,7 +1940,7 @@ void RenderView::RunModal(WebWidget* webwidget) {
IPC::SyncMessage* msg = new ViewHostMsg_RunModal(routing_id_);
- msg->set_pump_messages_event(modal_dialog_event_);
+ msg->set_pump_messages_event(modal_dialog_event_.get());
Send(msg);
}
diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h
index ef0307d..26e3b97 100644
--- a/chrome/renderer/render_view.h
+++ b/chrome/renderer/render_view.h
@@ -48,6 +48,10 @@ class WebPluginDelegate;
class WebPluginDelegateProxy;
enum WebRequestCachePolicy;
+namespace base {
+class WaitableEvent;
+}
+
namespace webkit_glue {
struct FileUploadData;
}
@@ -85,7 +89,7 @@ class RenderView : public RenderWidget,
static RenderView* Create(
RenderThreadBase* render_thread,
HWND parent_hwnd,
- HANDLE modal_dialog_event,
+ base::WaitableEvent* modal_dialog_event, // takes ownership
int32 opener_id,
const WebPreferences& webkit_prefs,
SharedRenderViewCounter* counter,
@@ -108,8 +112,8 @@ class RenderView : public RenderWidget,
return host_window_;
}
- HANDLE modal_dialog_event() {
- return modal_dialog_event_.Get();
+ base::WaitableEvent* modal_dialog_event() {
+ return modal_dialog_event_.get();
}
// IPC::Channel::Listener
@@ -328,7 +332,7 @@ class RenderView : public RenderWidget,
// set to 'MSG_ROUTING_NONE' if the true ID is not yet known. In this case,
// CompleteInit must be called later with the true ID.
void Init(HWND parent,
- HANDLE modal_dialog_event,
+ base::WaitableEvent* modal_dialog_event, // takes ownership
int32 opener_id,
const WebPreferences& webkit_prefs,
SharedRenderViewCounter* counter,
@@ -659,7 +663,7 @@ class RenderView : public RenderWidget,
// Handle to an event that's set when the page is showing a modal dialog (or
// equivalent constrained window). The renderer and any plugin processes
// check this to know if they should pump messages/tasks then.
- ScopedHandle modal_dialog_event_;
+ scoped_ptr<base::WaitableEvent> modal_dialog_event_;
// Document width when in print CSS media type. 0 otherwise.
int printed_document_width_;
diff --git a/chrome/renderer/webplugin_delegate_proxy.cc b/chrome/renderer/webplugin_delegate_proxy.cc
index 22ece4b..f4c61a2 100644
--- a/chrome/renderer/webplugin_delegate_proxy.cc
+++ b/chrome/renderer/webplugin_delegate_proxy.cc
@@ -246,7 +246,7 @@ bool WebPluginDelegateProxy::Initialize(const GURL& url, char** argn,
}
}
params.load_manually = load_manually;
- params.modal_dialog_event = render_view_->modal_dialog_event();
+ params.modal_dialog_event = render_view_->modal_dialog_event()->handle();
plugin_ = plugin;
@@ -571,7 +571,7 @@ bool WebPluginDelegateProxy::HandleEvent(NPEvent* event, WebCursor* cursor) {
IPC::SyncMessage* message = new PluginMsg_HandleEvent(instance_id_,
*event, &handled,
cursor);
- message->set_pump_messages_event(modal_loop_pump_messages_event_);
+ message->set_pump_messages_event(modal_loop_pump_messages_event_.get());
Send(message);
return handled;
}
@@ -587,7 +587,7 @@ void WebPluginDelegateProxy::OnSetWindow(
plugin_->SetWindow(window, modal_loop_pump_messages_event);
DCHECK(modal_loop_pump_messages_event_ == NULL);
- modal_loop_pump_messages_event_.Set(modal_loop_pump_messages_event);
+ modal_loop_pump_messages_event_.reset();
}
void WebPluginDelegateProxy::OnCancelResource(int id) {
diff --git a/chrome/renderer/webplugin_delegate_proxy.h b/chrome/renderer/webplugin_delegate_proxy.h
index 4f8b757..521b763 100644
--- a/chrome/renderer/webplugin_delegate_proxy.h
+++ b/chrome/renderer/webplugin_delegate_proxy.h
@@ -23,6 +23,10 @@ struct PluginHostMsg_RouteToFrame_Params;
class RenderView;
class SkBitmap;
+namespace base {
+class WaitableEvent;
+}
+
namespace skia {
class PlatformCanvasWin;
}
@@ -167,7 +171,7 @@ class WebPluginDelegateProxy : public WebPluginDelegate,
// Event passed in by the plugin process and is used to decide if
// messages need to be pumped in the NPP_HandleEvent sync call.
- ScopedHandle modal_loop_pump_messages_event_;
+ scoped_ptr<base::WaitableEvent> modal_loop_pump_messages_event_;
// Bitmap for crashed plugin
SkBitmap* sad_plugin_;
diff --git a/chrome/test/testing_browser_process.h b/chrome/test/testing_browser_process.h
index 4e756f1..76f349c 100644
--- a/chrome/test/testing_browser_process.h
+++ b/chrome/test/testing_browser_process.h
@@ -15,17 +15,17 @@
#include <string>
#include "base/string_util.h"
+#include "base/waitable_event.h"
#include "chrome/browser/browser_process.h"
#include "chrome/common/notification_service.h"
#include "base/logging.h"
class TestingBrowserProcess : public BrowserProcess {
public:
- TestingBrowserProcess() {
-#if defined(OS_WIN)
- shutdown_event_ = ::CreateEvent(NULL, TRUE, FALSE, NULL);
-#endif
+ TestingBrowserProcess()
+ : shutdown_event_(new base::WaitableEvent(true, false)) {
}
+
virtual ~TestingBrowserProcess() {
}
@@ -122,15 +122,11 @@ class TestingBrowserProcess : public BrowserProcess {
virtual MemoryModel memory_model() { return HIGH_MEMORY_MODEL; }
-#if defined(OS_WIN)
- virtual HANDLE shutdown_event() { return shutdown_event_; }
-#endif
+ virtual base::WaitableEvent* shutdown_event() { return shutdown_event_.get(); }
private:
NotificationService notification_service_;
-#if defined(OS_WIN)
- HANDLE shutdown_event_;
-#endif
+ scoped_ptr<base::WaitableEvent> shutdown_event_;
DISALLOW_COPY_AND_ASSIGN(TestingBrowserProcess);
};