diff options
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, ¶m, &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); }; |