thread_unittest.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/threading/thread.h" 6 7#include <vector> 8 9#include "base/message_loop.h" 10#include "base/third_party/dynamic_annotations/dynamic_annotations.h" 11#include "testing/gtest/include/gtest/gtest.h" 12#include "testing/platform_test.h" 13 14using base::Thread; 15 16typedef PlatformTest ThreadTest; 17 18namespace { 19 20class ToggleValue : public Task { 21 public: 22 explicit ToggleValue(bool* value) : value_(value) { 23 ANNOTATE_BENIGN_RACE(value, "Test-only data race on boolean " 24 "in base/thread_unittest"); 25 } 26 virtual void Run() { 27 *value_ = !*value_; 28 } 29 private: 30 bool* value_; 31}; 32 33class SleepSome : public Task { 34 public: 35 explicit SleepSome(int msec) : msec_(msec) { 36 } 37 virtual void Run() { 38 base::PlatformThread::Sleep(msec_); 39 } 40 private: 41 int msec_; 42}; 43 44class SleepInsideInitThread : public Thread { 45 public: 46 SleepInsideInitThread() : Thread("none") { init_called_ = false; } 47 virtual ~SleepInsideInitThread() { } 48 49 virtual void Init() { 50 base::PlatformThread::Sleep(500); 51 init_called_ = true; 52 } 53 bool InitCalled() { return init_called_; } 54 private: 55 bool init_called_; 56}; 57 58enum ThreadEvent { 59 // Thread::Init() was called. 60 THREAD_EVENT_INIT, 61 62 // The MessageLoop for the thread was deleted. 63 THREAD_EVENT_MESSAGE_LOOP_DESTROYED, 64 65 // Thread::CleanUp() was called. 66 THREAD_EVENT_CLEANUP, 67 68 // Thread::CleanUpAfterMessageLoopDestruction() was called. 69 THREAD_EVENT_CLEANUP_AFTER_LOOP, 70}; 71 72typedef std::vector<ThreadEvent> EventList; 73 74class CaptureToEventList : public Thread { 75 public: 76 // This Thread pushes events into the vector |event_list| to show 77 // the order they occured in. |event_list| must remain valid for the 78 // lifetime of this thread. 79 explicit CaptureToEventList(EventList* event_list) 80 : Thread("none"), event_list_(event_list) { 81 } 82 83 virtual ~CaptureToEventList() { 84 // Must call Stop() manually to have our CleanUp() function called. 85 Stop(); 86 } 87 88 virtual void Init() { 89 event_list_->push_back(THREAD_EVENT_INIT); 90 } 91 92 virtual void CleanUp() { 93 event_list_->push_back(THREAD_EVENT_CLEANUP); 94 } 95 96 virtual void CleanUpAfterMessageLoopDestruction() { 97 event_list_->push_back(THREAD_EVENT_CLEANUP_AFTER_LOOP); 98 } 99 100 private: 101 EventList* event_list_; 102}; 103 104// Observer that writes a value into |event_list| when a message loop has been 105// destroyed. 106class CapturingDestructionObserver : public MessageLoop::DestructionObserver { 107 public: 108 // |event_list| must remain valid throughout the observer's lifetime. 109 explicit CapturingDestructionObserver(EventList* event_list) 110 : event_list_(event_list) { 111 } 112 113 // DestructionObserver implementation: 114 virtual void WillDestroyCurrentMessageLoop() { 115 event_list_->push_back(THREAD_EVENT_MESSAGE_LOOP_DESTROYED); 116 event_list_ = NULL; 117 } 118 119 private: 120 EventList* event_list_; 121}; 122 123// Task that adds a destruction observer to the current message loop. 124class RegisterDestructionObserver : public Task { 125 public: 126 explicit RegisterDestructionObserver( 127 MessageLoop::DestructionObserver* observer) 128 : observer_(observer) { 129 } 130 131 virtual void Run() { 132 MessageLoop::current()->AddDestructionObserver(observer_); 133 observer_ = NULL; 134 } 135 136 private: 137 MessageLoop::DestructionObserver* observer_; 138}; 139 140} // namespace 141 142TEST_F(ThreadTest, Restart) { 143 Thread a("Restart"); 144 a.Stop(); 145 EXPECT_FALSE(a.message_loop()); 146 EXPECT_FALSE(a.IsRunning()); 147 EXPECT_TRUE(a.Start()); 148 EXPECT_TRUE(a.message_loop()); 149 EXPECT_TRUE(a.IsRunning()); 150 a.Stop(); 151 EXPECT_FALSE(a.message_loop()); 152 EXPECT_FALSE(a.IsRunning()); 153 EXPECT_TRUE(a.Start()); 154 EXPECT_TRUE(a.message_loop()); 155 EXPECT_TRUE(a.IsRunning()); 156 a.Stop(); 157 EXPECT_FALSE(a.message_loop()); 158 EXPECT_FALSE(a.IsRunning()); 159 a.Stop(); 160 EXPECT_FALSE(a.message_loop()); 161 EXPECT_FALSE(a.IsRunning()); 162} 163 164TEST_F(ThreadTest, StartWithOptions_StackSize) { 165 Thread a("StartWithStackSize"); 166 // Ensure that the thread can work with only 12 kb and still process a 167 // message. 168 Thread::Options options; 169 options.stack_size = 12*1024; 170 EXPECT_TRUE(a.StartWithOptions(options)); 171 EXPECT_TRUE(a.message_loop()); 172 EXPECT_TRUE(a.IsRunning()); 173 174 bool was_invoked = false; 175 a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked)); 176 177 // wait for the task to run (we could use a kernel event here 178 // instead to avoid busy waiting, but this is sufficient for 179 // testing purposes). 180 for (int i = 100; i >= 0 && !was_invoked; --i) { 181 base::PlatformThread::Sleep(10); 182 } 183 EXPECT_TRUE(was_invoked); 184} 185 186TEST_F(ThreadTest, TwoTasks) { 187 bool was_invoked = false; 188 { 189 Thread a("TwoTasks"); 190 EXPECT_TRUE(a.Start()); 191 EXPECT_TRUE(a.message_loop()); 192 193 // Test that all events are dispatched before the Thread object is 194 // destroyed. We do this by dispatching a sleep event before the 195 // event that will toggle our sentinel value. 196 a.message_loop()->PostTask(FROM_HERE, new SleepSome(20)); 197 a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked)); 198 } 199 EXPECT_TRUE(was_invoked); 200} 201 202TEST_F(ThreadTest, StopSoon) { 203 Thread a("StopSoon"); 204 EXPECT_TRUE(a.Start()); 205 EXPECT_TRUE(a.message_loop()); 206 EXPECT_TRUE(a.IsRunning()); 207 a.StopSoon(); 208 a.StopSoon(); 209 a.Stop(); 210 EXPECT_FALSE(a.message_loop()); 211 EXPECT_FALSE(a.IsRunning()); 212} 213 214TEST_F(ThreadTest, ThreadName) { 215 Thread a("ThreadName"); 216 EXPECT_TRUE(a.Start()); 217 EXPECT_EQ("ThreadName", a.thread_name()); 218} 219 220// Make sure we can't use a thread between Start() and Init(). 221TEST_F(ThreadTest, SleepInsideInit) { 222 SleepInsideInitThread t; 223 EXPECT_FALSE(t.InitCalled()); 224 t.Start(); 225 EXPECT_TRUE(t.InitCalled()); 226} 227 228// Make sure that the destruction sequence is: 229// 230// (1) Thread::CleanUp() 231// (2) MessageLoop::~MessageLoop() 232// MessageLoop::DestructionObservers called. 233// (3) Thread::CleanUpAfterMessageLoopDestruction 234TEST_F(ThreadTest, CleanUp) { 235 EventList captured_events; 236 CapturingDestructionObserver loop_destruction_observer(&captured_events); 237 238 { 239 // Start a thread which writes its event into |captured_events|. 240 CaptureToEventList t(&captured_events); 241 EXPECT_TRUE(t.Start()); 242 EXPECT_TRUE(t.message_loop()); 243 EXPECT_TRUE(t.IsRunning()); 244 245 // Register an observer that writes into |captured_events| once the 246 // thread's message loop is destroyed. 247 t.message_loop()->PostTask( 248 FROM_HERE, 249 new RegisterDestructionObserver(&loop_destruction_observer)); 250 251 // Upon leaving this scope, the thread is deleted. 252 } 253 254 // Check the order of events during shutdown. 255 ASSERT_EQ(4u, captured_events.size()); 256 EXPECT_EQ(THREAD_EVENT_INIT, captured_events[0]); 257 EXPECT_EQ(THREAD_EVENT_CLEANUP, captured_events[1]); 258 EXPECT_EQ(THREAD_EVENT_MESSAGE_LOOP_DESTROYED, captured_events[2]); 259 EXPECT_EQ(THREAD_EVENT_CLEANUP_AFTER_LOOP, captured_events[3]); 260} 261