diff options
author | darin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-11 14:35:15 +0000 |
---|---|---|
committer | darin@google.com <darin@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-08-11 14:35:15 +0000 |
commit | ffd830813ff2ad974e5b8596e7053cbfc77511f2 (patch) | |
tree | be43c75d0ca05361ed0905e8062fe5f62a583ae8 | |
parent | 7ce742d8a2434377005763f7b54e4ae844e3e2bd (diff) | |
download | chromium_src-ffd830813ff2ad974e5b8596e7053cbfc77511f2.zip chromium_src-ffd830813ff2ad974e5b8596e7053cbfc77511f2.tar.gz chromium_src-ffd830813ff2ad974e5b8596e7053cbfc77511f2.tar.bz2 |
Provide cross-platform implementation of WaitableEvent for use on Mac and Linux.
I gave the file the suffix _generic since it is implemented entirely in terms of other APIs in base.
This CL also adds a simple unit test for WaitableEvent, and I switched some code in thread.cc over to using WaitableEvent.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@648 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/SConscript | 2 | ||||
-rw-r--r-- | base/build/base_unittests.vcproj | 4 | ||||
-rw-r--r-- | base/thread.cc | 15 | ||||
-rw-r--r-- | base/waitable_event.h | 17 | ||||
-rw-r--r-- | base/waitable_event_generic.cc | 96 | ||||
-rw-r--r-- | base/waitable_event_unittest.cc | 77 | ||||
-rw-r--r-- | base/waitable_event_win.cc | 3 |
7 files changed, 201 insertions, 13 deletions
diff --git a/base/SConscript b/base/SConscript index b8d9da9..a23672d 100644 --- a/base/SConscript +++ b/base/SConscript @@ -137,6 +137,7 @@ if env['PLATFORM'] in ('darwin', 'linux2'): 'lock_impl_posix.cc', 'thread_posix.cc', 'thread_local_storage_posix.cc', + 'waitable_event_generic.cc', ]) env.ChromeStaticLibrary('base', input_files) @@ -249,6 +250,7 @@ test_files = [ 'time_unittest.cc', 'timer_unittest.cc', 'values_unittest.cc', + 'waitable_event_unittest.cc', 'win_util_unittest.cc', 'word_iterator_unittest.cc', 'wmi_util_unittest.cc', diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj index 9c56e25..648cd17 100644 --- a/base/build/base_unittests.vcproj +++ b/base/build/base_unittests.vcproj @@ -328,6 +328,10 @@ > </File> <File + RelativePath="..\waitable_event_unittest.cc" + > + </File> + <File RelativePath="..\watchdog_test.cc" > </File> diff --git a/base/thread.cc b/base/thread.cc index 0a19a16..ab79a9a 100644 --- a/base/thread.cc +++ b/base/thread.cc @@ -36,6 +36,7 @@ #include "base/object_watcher.h" #include "base/ref_counted.h" #include "base/string_util.h" +#include "base/waitable_event.h" #include "base/win_util.h" namespace { @@ -46,15 +47,9 @@ namespace { class ThreadStartInfo : public base::RefCountedThreadSafe<ThreadStartInfo> { public: Thread* self; - HANDLE start_event; + base::WaitableEvent start_event; - explicit ThreadStartInfo(Thread* t) - : self(t), - start_event(CreateEvent(NULL, FALSE, FALSE, NULL)) { - } - - ~ThreadStartInfo() { - CloseHandle(start_event); + explicit ThreadStartInfo(Thread* t) : self(t), start_event(false, false) { } }; @@ -170,7 +165,7 @@ bool Thread::StartWithStackSize(size_t stack_size) { } // Wait for the thread to start and initialize message_loop_ - WaitForSingleObject(info->start_event, INFINITE); + info->start_event.Wait(); return true; } @@ -232,7 +227,7 @@ unsigned __stdcall Thread::ThreadFunc(void* param) { self->message_loop_ = &message_loop; SetThreadName(self->thread_name().c_str(), GetCurrentThreadId()); message_loop.SetThreadName(self->thread_name()); - SetEvent(info->start_event); + info->start_event.Signal(); } // Let the thread do extra initialization. diff --git a/base/waitable_event.h b/base/waitable_event.h index 778dabf..be0cd68 100644 --- a/base/waitable_event.h +++ b/base/waitable_event.h @@ -30,12 +30,17 @@ #ifndef BASE_WAITABLE_EVENT_H_ #define BASE_WAITABLE_EVENT_H_ -#include "base/time.h" +#include "base/basictypes.h" #if defined(OS_WIN) typedef void* HANDLE; +#else +#include "base/condition_variable.h" +#include "base/lock.h" #endif +class TimeDelta; + namespace base { // A WaitableEvent can be a useful thread synchronization tool when you want to @@ -71,7 +76,8 @@ class WaitableEvent { // to be woken up. void Signal(); - // Returns true if the event is in the signaled state, else false. + // Returns true if the event is in the signaled state, else false. If this + // is not a manual reset event, then this test will cause a reset. bool IsSignaled(); // Wait indefinitely for the event to be signaled. Returns true if the event @@ -86,7 +92,14 @@ class WaitableEvent { private: #if defined(OS_WIN) HANDLE event_; +#else + Lock lock_; // Needs to be listed first so it will be constructed first. + ConditionVariable cvar_; + bool signaled_; + bool manual_reset_; #endif + + DISALLOW_COPY_AND_ASSIGN(WaitableEvent); }; } // namespace base diff --git a/base/waitable_event_generic.cc b/base/waitable_event_generic.cc new file mode 100644 index 0000000..f036f8d --- /dev/null +++ b/base/waitable_event_generic.cc @@ -0,0 +1,96 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#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_unittest.cc b/base/waitable_event_unittest.cc new file mode 100644 index 0000000..c35bd2eb --- /dev/null +++ b/base/waitable_event_unittest.cc @@ -0,0 +1,77 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "base/time.h" +#include "base/waitable_event.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::WaitableEvent; + +namespace { +typedef testing::Test WaitableEventTest; +} + +TEST(WaitableEventTest, ManualBasics) { + WaitableEvent event(true, false); + + EXPECT_FALSE(event.IsSignaled()); + + event.Signal(); + EXPECT_TRUE(event.IsSignaled()); + EXPECT_TRUE(event.IsSignaled()); + + event.Reset(); + EXPECT_FALSE(event.IsSignaled()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.Wait()); + EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10))); +} + +TEST(WaitableEventTest, AutoBasics) { + WaitableEvent event(false, false); + + EXPECT_FALSE(event.IsSignaled()); + + event.Signal(); + EXPECT_TRUE(event.IsSignaled()); + EXPECT_FALSE(event.IsSignaled()); + + event.Reset(); + EXPECT_FALSE(event.IsSignaled()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.Wait()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10))); +} diff --git a/base/waitable_event_win.cc b/base/waitable_event_win.cc index 58fbfde..da2d2d8 100644 --- a/base/waitable_event_win.cc +++ b/base/waitable_event_win.cc @@ -30,7 +30,9 @@ #include "base/waitable_event.h" #include <windows.h> + #include "base/logging.h" +#include "base/time.h" namespace base { @@ -78,7 +80,6 @@ bool WaitableEvent::TimedWait(const TimeDelta& max_time) { // about it if it should ever fail. NOTREACHED() << "WaitForSingleObject failed"; return false; - } } // namespace base |