diff options
-rw-r--r-- | base/base.xcodeproj/project.pbxproj | 6 | ||||
-rw-r--r-- | base/condition_variable_test.cc | 167 | ||||
-rw-r--r-- | base/spin_wait.h | 3 |
3 files changed, 87 insertions, 89 deletions
diff --git a/base/base.xcodeproj/project.pbxproj b/base/base.xcodeproj/project.pbxproj index 3ae34ed..6a5372c 100644 --- a/base/base.xcodeproj/project.pbxproj +++ b/base/base.xcodeproj/project.pbxproj @@ -133,6 +133,7 @@ ABFBD3E60DC793C600E164CB /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825403290D92D2090006B936 /* md5.cc */; }; BA739A020E5E3242009842A7 /* tracked_objects_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA739A000E5E3242009842A7 /* tracked_objects_test.cc */; }; BA739A030E5E3242009842A7 /* timer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA739A010E5E3242009842A7 /* timer_unittest.cc */; }; + BA73AA330E5F614B00A20026 /* condition_variable_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = BA73AA320E5F614B00A20026 /* condition_variable_test.cc */; }; E45062A60E40A9BE0025A81A /* base_switches.cc in Sources */ = {isa = PBXBuildFile; fileRef = 825402CB0D92D1390006B936 /* base_switches.cc */; }; E48A05F70E3F61B300172919 /* command_line.cc in Sources */ = {isa = PBXBuildFile; fileRef = E4A133490E37A41D00110AA2 /* command_line.cc */; }; E48A06710E3F70E200172919 /* convolver.cc in Sources */ = {isa = PBXBuildFile; fileRef = E48A06680E3F70B500172919 /* convolver.cc */; }; @@ -513,6 +514,8 @@ ABF4B9B40DC2BC9F00A6E319 /* path_service.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = path_service.cc; sourceTree = "<group>"; }; BA739A000E5E3242009842A7 /* tracked_objects_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tracked_objects_test.cc; sourceTree = "<group>"; }; BA739A010E5E3242009842A7 /* timer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = timer_unittest.cc; sourceTree = "<group>"; }; + BA73AA320E5F614B00A20026 /* condition_variable_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = condition_variable_test.cc; sourceTree = "<group>"; }; + BA73AA420E5F62F400A20026 /* spin_wait.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = spin_wait.h; sourceTree = "<group>"; }; E45629E40E27C058005E4685 /* rect_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = rect_unittest.cc; sourceTree = "<group>"; }; E4562A200E27C8C1005E4685 /* png_codec_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = png_codec_unittest.cc; sourceTree = "<group>"; }; E4562A2A0E27CA2F005E4685 /* libpng.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = libpng.xcodeproj; path = third_party/libpng/libpng.xcodeproj; sourceTree = "<group>"; }; @@ -703,6 +706,7 @@ 7BAFFC8A0E5E0CC000797CC4 /* compiler_specific.h */, 825402EB0D92D1940006B936 /* condition_variable.h */, 824653670DC12CEC007C2BAA /* condition_variable_posix.cc */, + BA73AA320E5F614B00A20026 /* condition_variable_test.cc */, 825402F40D92D1AC0006B936 /* debug_on_start.cc */, 825402F30D92D1AC0006B936 /* debug_on_start.h */, 825402F20D92D1AC0006B936 /* debug_util.cc */, @@ -806,6 +810,7 @@ 824653730DC12D0E007C2BAA /* shared_memory_posix.cc */, 7BD9E84E0DA447F800FC7A01 /* singleton.h */, E4AFA4BE0E50DE7400201347 /* singleton_unittest.cc */, + BA73AA420E5F62F400A20026 /* spin_wait.h */, 825403770D92D2CF0006B936 /* stack_container.h */, E491165C0E48A51E001EE8C3 /* stack_container_unittest.cc */, 825403780D92D2CF0006B936 /* stats_counters.h */, @@ -1211,6 +1216,7 @@ files = ( 7B78D38E0E54FE0100609465 /* at_exit_unittest.cc in Sources */, 7B78D38F0E54FE0100609465 /* command_line_unittest.cc in Sources */, + BA73AA330E5F614B00A20026 /* condition_variable_test.cc in Sources */, 7B8505D40E5B43FE00730B43 /* convolver_unittest.cc in Sources */, A5CE1D2B0E55F4D800AD0606 /* file_util_unittest.cc in Sources */, 7B78D3910E54FE0100609465 /* file_version_info_unittest.cc in Sources */, diff --git a/base/condition_variable_test.cc b/base/condition_variable_test.cc index ff4d71e..6952567 100644 --- a/base/condition_variable_test.cc +++ b/base/condition_variable_test.cc @@ -35,6 +35,7 @@ #include "base/condition_variable.h" #include "base/logging.h" +#include "base/platform_thread.h" #include "base/scoped_ptr.h" #include "base/spin_wait.h" #include "testing/gtest/include/gtest/gtest.h" @@ -73,17 +74,18 @@ class ConditionVariableTest : public testing::Test { // cases will validate that the WorkQueue has records showing that the desired // activities were performed. //------------------------------------------------------------------------------ -// Forward declare the WorkerProcess task -static DWORD WINAPI WorkerProcess(void* p); // 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 { +class WorkQueue : public PlatformThread::Delegate { public: explicit WorkQueue(int thread_count); ~WorkQueue(); + // PlatformThread::Delegate interface. + void ThreadMain(); + //---------------------------------------------------------------------------- // Worker threads only call the following methods. // They should use the lock to get exclusive access. @@ -130,7 +132,7 @@ class WorkQueue { ConditionVariable no_more_tasks_; // Task count is zero. const int thread_count_; - scoped_array<HANDLE> handles_; + scoped_array<PlatformThreadHandle> thread_handles_; std::vector<int> assignment_history_; // Number of assignment per worker. std::vector<int> completion_history_; // Number of completions per worker. int thread_started_counter_; // Used to issue unique id to workers. @@ -142,72 +144,6 @@ class WorkQueue { }; //------------------------------------------------------------------------------ -// 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). - -// WorkerProcess() implements the above task for all threads. -// It calls the controlling object to tell the creator about progress, and to -// ask about tasks. -static DWORD WINAPI WorkerProcess(void* p) { - int thread_id; - class WorkQueue* queue = reinterpret_cast<WorkQueue*>(p); - { - AutoLock auto_lock(*queue->lock()); - thread_id = queue->GetThreadId(); - if (queue->EveryIdWasAllocated()) - queue->all_threads_have_ids()->Signal(); // Tell creator we're ready. - } - - Lock private_lock; // Used to waste time on "our work". - while (1) { // This is the main consumer loop. - TimeDelta work_time; - bool could_use_help; - { - AutoLock auto_lock(*queue->lock()); - while (0 == queue->task_count() && !queue->shutdown()) { - queue->work_is_available()->Wait(); - } - if (queue->shutdown()) { - // Ack the notification of a shutdown message back to the controller. - queue->thread_shutting_down(); - return 0; // Terminate. - } - // Get our task duration from the queue. - work_time = queue->GetAnAssignment(thread_id); - could_use_help = (queue->task_count() > 0) && - queue->allow_help_requests(); - } // Release lock - - // Do work (outside of locked region. - if (could_use_help) - queue->work_is_available()->Signal(); // Get help from other threads. - - if (work_time > TimeDelta::FromMilliseconds(0)) { - // We could just sleep(), but we'll instead further exercise the - // condition variable class, and do a timed wait. - AutoLock auto_lock(private_lock); - ConditionVariable private_cv(&private_lock); - private_cv.TimedWait(work_time); // Unsynchronized waiting. - } - - { - AutoLock auto_lock(*queue->lock()); - // Send notification that we completed our "work." - queue->WorkIsCompleted(thread_id); - } - } -} -//------------------------------------------------------------------------------ // The next section contains the actual tests. //------------------------------------------------------------------------------ @@ -414,7 +350,7 @@ TEST_F(ConditionVariableTest, MultiThreadConsumerTest) { SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1), queue.shutdown_task_count() == kThreadCount); - Sleep(10); // Be sure they're all shutdown. + PlatformThread::Sleep(10); // Be sure they're all shutdown. } TEST_F(ConditionVariableTest, LargeFastTaskTest) { @@ -514,7 +450,7 @@ TEST_F(ConditionVariableTest, LargeFastTaskTest) { // Wait for shutdowns to complete. SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1), queue.shutdown_task_count() == kThreadCount); - Sleep(10); // Be sure they're all shutdown. + PlatformThread::Sleep(10); // Be sure they're all shutdown. } //------------------------------------------------------------------------------ @@ -527,7 +463,7 @@ WorkQueue::WorkQueue(int thread_count) all_threads_have_ids_(&lock_), no_more_tasks_(&lock_), thread_count_(thread_count), - handles_(new HANDLE[thread_count]), + thread_handles_(new PlatformThreadHandle[thread_count]), assignment_history_(thread_count), completion_history_(thread_count), thread_started_counter_(0), @@ -541,13 +477,9 @@ WorkQueue::WorkQueue(int thread_count) SetWorkTime(TimeDelta::FromMilliseconds(30)); for (int i = 0; i < thread_count_; ++i) { - handles_[i] = CreateThread(NULL, // security. - 0, // <64K stack size. - WorkerProcess, // Static function. - reinterpret_cast<void*>(this), - 0, // Create running process. - NULL); // OS version of thread id. - EXPECT_NE(reinterpret_cast<void*>(NULL), handles_[i]); + PlatformThreadHandle pth; + EXPECT_TRUE(PlatformThread::Create(0, this, &pth)); + thread_handles_[i] = pth; } } @@ -557,16 +489,9 @@ WorkQueue::~WorkQueue() { SetShutdown(); } work_is_available_.Broadcast(); // Tell them all to terminate. - DWORD result = WaitForMultipleObjects( - thread_count_, - &handles_[0], - true, // Wait for all - 10000); // Ten seconds max. for (int i = 0; i < thread_count_; ++i) { - int ret_value = CloseHandle(handles_[i]); - CHECK(ret_value); - handles_[i] = NULL; + PlatformThread::Join(thread_handles_[i]); } } @@ -688,4 +613,70 @@ void WorkQueue::SetShutdown() { shutdown_ = true; } +//------------------------------------------------------------------------------ +// 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() { + int thread_id; + { + AutoLock auto_lock(lock_); + thread_id = GetThreadId(); + if (EveryIdWasAllocated()) + all_threads_have_ids()->Signal(); // Tell creator we're ready. + } + + Lock private_lock; // Used to waste time on "our work". + while (1) { // This is the main consumer loop. + TimeDelta work_time; + bool could_use_help; + { + AutoLock auto_lock(lock_); + while (0 == task_count() && !shutdown()) { + work_is_available()->Wait(); + } + if (shutdown()) { + // Ack the notification of a shutdown message back to the controller. + thread_shutting_down(); + return; // Terminate. + } + // Get our task duration from the queue. + work_time = GetAnAssignment(thread_id); + could_use_help = (task_count() > 0) && allow_help_requests(); + } // Release lock + + // Do work (outside of locked region. + if (could_use_help) + work_is_available()->Signal(); // Get help from other threads. + + if (work_time > TimeDelta::FromMilliseconds(0)) { + // We could just sleep(), but we'll instead further exercise the + // condition variable class, and do a timed wait. + AutoLock auto_lock(private_lock); + ConditionVariable private_cv(&private_lock); + private_cv.TimedWait(work_time); // Unsynchronized waiting. + } + + { + AutoLock auto_lock(lock_); + // Send notification that we completed our "work." + WorkIsCompleted(thread_id); + } + } +} + } // namespace diff --git a/base/spin_wait.h b/base/spin_wait.h index 7226387..31628bb 100644 --- a/base/spin_wait.h +++ b/base/spin_wait.h @@ -40,6 +40,7 @@ #ifndef BASE_SPIN_WAIT_H__ #define BASE_SPIN_WAIT_H__ +#include "base/platform_thread.h" #include "base/time.h" // Provide a macro that will wait no longer than 1 second for an asynchronous @@ -66,7 +67,7 @@ kTimeout.InMilliseconds()) << "Timed out"; \ break; \ } \ - Sleep(50); \ + PlatformThread::Sleep(50); \ } \ } \ while(0) |