• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
14 using base::Thread;
15 
16 typedef PlatformTest ThreadTest;
17 
18 namespace {
19 
20 class ToggleValue : public Task {
21  public:
ToggleValue(bool * value)22   explicit ToggleValue(bool* value) : value_(value) {
23     ANNOTATE_BENIGN_RACE(value, "Test-only data race on boolean "
24                          "in base/thread_unittest");
25   }
Run()26   virtual void Run() {
27     *value_ = !*value_;
28   }
29  private:
30   bool* value_;
31 };
32 
33 class SleepSome : public Task {
34  public:
SleepSome(int msec)35   explicit SleepSome(int msec) : msec_(msec) {
36   }
Run()37   virtual void Run() {
38     base::PlatformThread::Sleep(msec_);
39   }
40  private:
41   int msec_;
42 };
43 
44 class SleepInsideInitThread : public Thread {
45  public:
SleepInsideInitThread()46   SleepInsideInitThread() : Thread("none") { init_called_ = false; }
~SleepInsideInitThread()47   virtual ~SleepInsideInitThread() { }
48 
Init()49   virtual void Init() {
50     base::PlatformThread::Sleep(500);
51     init_called_ = true;
52   }
InitCalled()53   bool InitCalled() { return init_called_; }
54  private:
55   bool init_called_;
56 };
57 
58 enum 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 
72 typedef std::vector<ThreadEvent> EventList;
73 
74 class 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.
CaptureToEventList(EventList * event_list)79   explicit CaptureToEventList(EventList* event_list)
80       : Thread("none"), event_list_(event_list) {
81   }
82 
~CaptureToEventList()83   virtual ~CaptureToEventList() {
84     // Must call Stop() manually to have our CleanUp() function called.
85     Stop();
86   }
87 
Init()88   virtual void Init() {
89     event_list_->push_back(THREAD_EVENT_INIT);
90   }
91 
CleanUp()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.
102 class CapturingDestructionObserver : public MessageLoop::DestructionObserver {
103  public:
104   // |event_list| must remain valid throughout the observer's lifetime.
CapturingDestructionObserver(EventList * event_list)105   explicit CapturingDestructionObserver(EventList* event_list)
106       : event_list_(event_list) {
107   }
108 
109   // DestructionObserver implementation:
WillDestroyCurrentMessageLoop()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.
120 class RegisterDestructionObserver : public Task {
121  public:
RegisterDestructionObserver(MessageLoop::DestructionObserver * observer)122   explicit RegisterDestructionObserver(
123       MessageLoop::DestructionObserver* observer)
124       : observer_(observer) {
125   }
126 
Run()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 
TEST_F(ThreadTest,Restart)138 TEST_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 
TEST_F(ThreadTest,StartWithOptions_StackSize)160 TEST_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 
TEST_F(ThreadTest,TwoTasks)182 TEST_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 
TEST_F(ThreadTest,StopSoon)198 TEST_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 
TEST_F(ThreadTest,ThreadName)210 TEST_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().
TEST_F(ThreadTest,SleepInsideInit)217 TEST_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.
TEST_F(ThreadTest,CleanUp)229 TEST_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