thread_unittest.cc revision dc0f95d653279beabeb9817299e2902918ba123e
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 = 0,
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  // Keep at end of list.
69  THREAD_NUM_EVENTS
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 private:
97  EventList* event_list_;
98};
99
100// Observer that writes a value into |event_list| when a message loop has been
101// destroyed.
102class CapturingDestructionObserver : public MessageLoop::DestructionObserver {
103 public:
104  // |event_list| must remain valid throughout the observer's lifetime.
105  explicit CapturingDestructionObserver(EventList* event_list)
106      : event_list_(event_list) {
107  }
108
109  // DestructionObserver implementation:
110  virtual void WillDestroyCurrentMessageLoop() {
111    event_list_->push_back(THREAD_EVENT_MESSAGE_LOOP_DESTROYED);
112    event_list_ = NULL;
113  }
114
115 private:
116  EventList* event_list_;
117};
118
119// Task that adds a destruction observer to the current message loop.
120class RegisterDestructionObserver : public Task {
121 public:
122  explicit RegisterDestructionObserver(
123      MessageLoop::DestructionObserver* observer)
124      : observer_(observer) {
125  }
126
127  virtual void Run() {
128    MessageLoop::current()->AddDestructionObserver(observer_);
129    observer_ = NULL;
130  }
131
132 private:
133  MessageLoop::DestructionObserver* observer_;
134};
135
136}  // namespace
137
138TEST_F(ThreadTest, Restart) {
139  Thread a("Restart");
140  a.Stop();
141  EXPECT_FALSE(a.message_loop());
142  EXPECT_FALSE(a.IsRunning());
143  EXPECT_TRUE(a.Start());
144  EXPECT_TRUE(a.message_loop());
145  EXPECT_TRUE(a.IsRunning());
146  a.Stop();
147  EXPECT_FALSE(a.message_loop());
148  EXPECT_FALSE(a.IsRunning());
149  EXPECT_TRUE(a.Start());
150  EXPECT_TRUE(a.message_loop());
151  EXPECT_TRUE(a.IsRunning());
152  a.Stop();
153  EXPECT_FALSE(a.message_loop());
154  EXPECT_FALSE(a.IsRunning());
155  a.Stop();
156  EXPECT_FALSE(a.message_loop());
157  EXPECT_FALSE(a.IsRunning());
158}
159
160TEST_F(ThreadTest, StartWithOptions_StackSize) {
161  Thread a("StartWithStackSize");
162  // Ensure that the thread can work with only 12 kb and still process a
163  // message.
164  Thread::Options options;
165  options.stack_size = 12*1024;
166  EXPECT_TRUE(a.StartWithOptions(options));
167  EXPECT_TRUE(a.message_loop());
168  EXPECT_TRUE(a.IsRunning());
169
170  bool was_invoked = false;
171  a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked));
172
173  // wait for the task to run (we could use a kernel event here
174  // instead to avoid busy waiting, but this is sufficient for
175  // testing purposes).
176  for (int i = 100; i >= 0 && !was_invoked; --i) {
177    base::PlatformThread::Sleep(10);
178  }
179  EXPECT_TRUE(was_invoked);
180}
181
182TEST_F(ThreadTest, TwoTasks) {
183  bool was_invoked = false;
184  {
185    Thread a("TwoTasks");
186    EXPECT_TRUE(a.Start());
187    EXPECT_TRUE(a.message_loop());
188
189    // Test that all events are dispatched before the Thread object is
190    // destroyed.  We do this by dispatching a sleep event before the
191    // event that will toggle our sentinel value.
192    a.message_loop()->PostTask(FROM_HERE, new SleepSome(20));
193    a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked));
194  }
195  EXPECT_TRUE(was_invoked);
196}
197
198TEST_F(ThreadTest, StopSoon) {
199  Thread a("StopSoon");
200  EXPECT_TRUE(a.Start());
201  EXPECT_TRUE(a.message_loop());
202  EXPECT_TRUE(a.IsRunning());
203  a.StopSoon();
204  a.StopSoon();
205  a.Stop();
206  EXPECT_FALSE(a.message_loop());
207  EXPECT_FALSE(a.IsRunning());
208}
209
210TEST_F(ThreadTest, ThreadName) {
211  Thread a("ThreadName");
212  EXPECT_TRUE(a.Start());
213  EXPECT_EQ("ThreadName", a.thread_name());
214}
215
216// Make sure we can't use a thread between Start() and Init().
217TEST_F(ThreadTest, SleepInsideInit) {
218  SleepInsideInitThread t;
219  EXPECT_FALSE(t.InitCalled());
220  t.Start();
221  EXPECT_TRUE(t.InitCalled());
222}
223
224// Make sure that the destruction sequence is:
225//
226//  (1) Thread::CleanUp()
227//  (2) MessageLoop::~MessageLoop()
228//      MessageLoop::DestructionObservers called.
229TEST_F(ThreadTest, CleanUp) {
230  EventList captured_events;
231  CapturingDestructionObserver loop_destruction_observer(&captured_events);
232
233  {
234    // Start a thread which writes its event into |captured_events|.
235    CaptureToEventList t(&captured_events);
236    EXPECT_TRUE(t.Start());
237    EXPECT_TRUE(t.message_loop());
238    EXPECT_TRUE(t.IsRunning());
239
240    // Register an observer that writes into |captured_events| once the
241    // thread's message loop is destroyed.
242    t.message_loop()->PostTask(
243        FROM_HERE,
244        new RegisterDestructionObserver(&loop_destruction_observer));
245
246    // Upon leaving this scope, the thread is deleted.
247  }
248
249  // Check the order of events during shutdown.
250  ASSERT_EQ(static_cast<size_t>(THREAD_NUM_EVENTS), captured_events.size());
251  EXPECT_EQ(THREAD_EVENT_INIT, captured_events[0]);
252  EXPECT_EQ(THREAD_EVENT_CLEANUP, captured_events[1]);
253  EXPECT_EQ(THREAD_EVENT_MESSAGE_LOOP_DESTROYED, captured_events[2]);
254}
255