summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/condition_variable_unittest.cc38
-rw-r--r--base/lock.h2
-rw-r--r--base/lock_impl.h4
-rw-r--r--base/lock_impl_win.cc2
4 files changed, 35 insertions, 11 deletions
diff --git a/base/condition_variable_unittest.cc b/base/condition_variable_unittest.cc
index 8e47114..6bdb96b 100644
--- a/base/condition_variable_unittest.cc
+++ b/base/condition_variable_unittest.cc
@@ -77,12 +77,12 @@ class WorkQueue : public PlatformThread::Delegate {
int task_count() const;
bool allow_help_requests() const; // Workers can signal more workers.
bool shutdown() const; // Check if shutdown has been requested.
- int shutdown_task_count() const;
void thread_shutting_down();
+
//----------------------------------------------------------------------------
- // Worker threads can call them but not needed to acquire a lock
+ // Worker threads can call them but not needed to acquire a lock.
Lock* lock();
ConditionVariable* work_is_available();
@@ -103,8 +103,14 @@ class WorkQueue : public PlatformThread::Delegate {
void SetTaskCount(int count);
void SetAllowHelp(bool allow);
+ // Caller must acquire lock before calling.
void SetShutdown();
+ // Compares the |shutdown_task_count_| to the |thread_count| and returns true
+ // if they are equal. This check will acquire the |lock_| so the caller
+ // should not hold the lock when calling this method.
+ bool ThreadSafeCheckShutdown(int thread_count);
+
private:
// Both worker threads and controller use the following to synchronize.
Lock lock_;
@@ -176,6 +182,12 @@ TEST_F(ConditionVariableTest, TimeoutTest) {
// This test is flaky due to excessive timing sensitivity.
// http://code.google.com/p/chromium/issues/detail?id=3599
+// TODO(jar): A recent change to the WorkQueue fixed flakyness for the test
+// LargeFastTaskTest. Specifically, the test was accessing the member variable
+// WorkQueue::shutdown_task_count_ without a lock held. The MultiThreadConsumer
+// test now ran successfully for 500 times on RalphL's machine. RalphL did not
+// want to blindly re-enable this test if you know of other issues with it, but
+// it appears that the flakyness is gone, but I'll leave that to you to verify.
TEST_F(ConditionVariableTest, DISABLED_MultiThreadConsumerTest) {
const int kThreadCount = 10;
WorkQueue queue(kThreadCount); // Start the threads.
@@ -336,7 +348,7 @@ TEST_F(ConditionVariableTest, DISABLED_MultiThreadConsumerTest) {
queue.work_is_available()->Broadcast(); // Force check for shutdown.
SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
- queue.shutdown_task_count() == kThreadCount);
+ queue.ThreadSafeCheckShutdown(kThreadCount));
PlatformThread::Sleep(10); // Be sure they're all shutdown.
}
@@ -436,7 +448,7 @@ TEST_F(ConditionVariableTest, LargeFastTaskTest) {
// Wait for shutdowns to complete.
SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1),
- queue.shutdown_task_count() == kThreadCount);
+ queue.ThreadSafeCheckShutdown(kThreadCount));
PlatformThread::Sleep(10); // Be sure they're all shutdown.
}
@@ -519,16 +531,27 @@ bool WorkQueue::allow_help_requests() const {
}
bool WorkQueue::shutdown() const {
+ lock_.AssertAcquired();
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
return shutdown_;
}
-int WorkQueue::shutdown_task_count() const {
- DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
- return shutdown_task_count_;
+// Because this method is called from the test's main thread we need to actually
+// take the lock. Threads will call the thread_shutting_down() method with the
+// lock already acquired.
+bool WorkQueue::ThreadSafeCheckShutdown(int thread_count) {
+ bool all_shutdown;
+ AutoLock auto_lock(lock_);
+ {
+ // Declare in scope so DFAKE is guranteed to be destroyed before AutoLock.
+ DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
+ all_shutdown = (shutdown_task_count_ == thread_count);
+ }
+ return all_shutdown;
}
void WorkQueue::thread_shutting_down() {
+ lock_.AssertAcquired();
DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_);
shutdown_task_count_++;
}
@@ -606,6 +629,7 @@ void WorkQueue::SetAllowHelp(bool allow) {
}
void WorkQueue::SetShutdown() {
+ lock_.AssertAcquired();
shutdown_ = true;
}
diff --git a/base/lock.h b/base/lock.h
index f3653cf..31ad9a0 100644
--- a/base/lock.h
+++ b/base/lock.h
@@ -23,7 +23,7 @@ class Lock {
// calling thread. If the lock has not been acquired, then the method
// will DCHECK(). In non-debug builds, the LockImpl's implementation of
// AssertAcquired() is an empty inline method.
- void AssertAcquired() { return lock_.AssertAcquired(); }
+ void AssertAcquired() const { return lock_.AssertAcquired(); }
// Return the underlying lock implementation.
// TODO(awalker): refactor lock and condition variables so that this is
diff --git a/base/lock_impl.h b/base/lock_impl.h
index d3fcc19..2d4a921 100644
--- a/base/lock_impl.h
+++ b/base/lock_impl.h
@@ -47,9 +47,9 @@ class LockImpl {
// through the os_lock() method, runtime assertions can not be done on those
// builds.
#if defined(NDEBUG) || !defined(OS_WIN)
- void AssertAcquired() {}
+ void AssertAcquired() const {}
#else
- void AssertAcquired();
+ void AssertAcquired() const;
#endif
// Return the native underlying lock. Not supported for Windows builds.
diff --git a/base/lock_impl_win.cc b/base/lock_impl_win.cc
index 503f105..0f0e424 100644
--- a/base/lock_impl_win.cc
+++ b/base/lock_impl_win.cc
@@ -66,7 +66,7 @@ void LockImpl::Unlock() {
// In non-debug builds, this method is declared as an empty inline method.
#ifndef NDEBUG
-void LockImpl::AssertAcquired() {
+void LockImpl::AssertAcquired() const {
DCHECK(recursion_count_shadow_ > 0);
DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId());
}