// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Multi-threaded tests of ConditionVariable class. #include "base/synchronization/condition_variable.h" #include <time.h> #include <algorithm> #include <memory> #include <vector> #include "base/functional/bind.h" #include "base/location.h" #include "base/logging.h" #include "base/synchronization/lock.h" #include "base/task/single_thread_task_runner.h" #include "base/test/spin_wait.h" #include "base/threading/platform_thread.h" #include "base/threading/thread.h" #include "base/threading/thread_collision_warner.h" #include "base/time/time.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" namespace base { namespace { //------------------------------------------------------------------------------ // Define our test class, with several common variables. //------------------------------------------------------------------------------ class ConditionVariableTest : public PlatformTest { … }; //------------------------------------------------------------------------------ // Define a class that will control activities an several multi-threaded tests. // The general structure of multi-threaded tests is that a test case will // construct an instance of a WorkQueue. The WorkQueue will spin up some // threads and control them throughout their lifetime, as well as maintaining // a central repository of the work thread's activity. Finally, the WorkQueue // will command the worker threads to terminate. At that point, the test // cases will validate that the WorkQueue has records showing that the desired // activities were performed. //------------------------------------------------------------------------------ // Callers are responsible for synchronizing access to the following class. // The WorkQueue::lock_, as accessed via WorkQueue::lock(), should be used for // all synchronized access. class WorkQueue : public PlatformThread::Delegate { … }; //------------------------------------------------------------------------------ // The next section contains the actual tests. //------------------------------------------------------------------------------ TEST_F(ConditionVariableTest, StartupShutdownTest) { … } // Call for cv destruction. TEST_F(ConditionVariableTest, TimeoutTest) { … } #if BUILDFLAG(IS_POSIX) const int kDiscontinuitySeconds = …; void BackInTime(Lock* lock) { … } // Tests that TimedWait ignores changes to the system clock. // Test is disabled by default, because it needs to run as root to muck with the // system clock. // http://crbug.com/293736 TEST_F(ConditionVariableTest, DISABLED_TimeoutAcrossSetTimeOfDay) { … } #endif // Test serial task servicing, as well as two parallel task servicing methods. TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { … } TEST_F(ConditionVariableTest, LargeFastTaskTest) { … } //------------------------------------------------------------------------------ // Finally we provide the implementation for the methods in the WorkQueue class. //------------------------------------------------------------------------------ WorkQueue::WorkQueue(int thread_count) : … { … } WorkQueue::~WorkQueue() { … } int WorkQueue::GetThreadId() { … } bool WorkQueue::EveryIdWasAllocated() const { … } TimeDelta WorkQueue::GetAnAssignment(int thread_id) { … } void WorkQueue::WorkIsCompleted(int thread_id) { … } int WorkQueue::task_count() const { … } bool WorkQueue::allow_help_requests() const { … } bool WorkQueue::shutdown() const { … } // 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) { … } void WorkQueue::thread_shutting_down() { … } Lock* WorkQueue::lock() { … } ConditionVariable* WorkQueue::work_is_available() { … } ConditionVariable* WorkQueue::all_threads_have_ids() { … } ConditionVariable* WorkQueue::no_more_tasks() { … } void WorkQueue::ResetHistory() { … } int WorkQueue::GetMinCompletionsByWorkerThread() const { … } int WorkQueue::GetMaxCompletionsByWorkerThread() const { … } int WorkQueue::GetNumThreadsTakingAssignments() const { … } int WorkQueue::GetNumThreadsCompletingTasks() const { … } int WorkQueue::GetNumberOfCompletedTasks() const { … } void WorkQueue::SetWorkTime(TimeDelta delay) { … } void WorkQueue::SetTaskCount(int count) { … } void WorkQueue::SetAllowHelp(bool allow) { … } void WorkQueue::SetShutdown() { … } void WorkQueue::SpinUntilAllThreadsAreWaiting() { … } void WorkQueue::SpinUntilTaskCountLessThan(int task_count) { … } //------------------------------------------------------------------------------ // Define the standard worker task. Several tests will spin out many of these // threads. //------------------------------------------------------------------------------ // The multithread tests involve several threads with a task to perform as // directed by an instance of the class WorkQueue. // The task is to: // a) Check to see if there are more tasks (there is a task counter). // a1) Wait on condition variable if there are no tasks currently. // b) Call a function to see what should be done. // c) Do some computation based on the number of milliseconds returned in (b). // d) go back to (a). // WorkQueue::ThreadMain() implements the above task for all threads. // It calls the controlling object to tell the creator about progress, and to // ask about tasks. void WorkQueue::ThreadMain() { … } } // namespace } // namespace base