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