1// Copyright (c) 2012 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/basictypes.h"
6#include "base/logging.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/threading/thread_checker.h"
9#include "base/threading/simple_thread.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12// Duplicated from base/threading/thread_checker.h so that we can be
13// good citizens there and undef the macro.
14#if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
15#define ENABLE_THREAD_CHECKER 1
16#else
17#define ENABLE_THREAD_CHECKER 0
18#endif
19
20namespace base {
21
22namespace {
23
24// Simple class to exercise the basics of ThreadChecker.
25// Both the destructor and DoStuff should verify that they were
26// called on the same thread as the constructor.
27class ThreadCheckerClass : public ThreadChecker {
28 public:
29  ThreadCheckerClass() {}
30
31  // Verifies that it was called on the same thread as the constructor.
32  void DoStuff() {
33    DCHECK(CalledOnValidThread());
34  }
35
36  void DetachFromThread() {
37    ThreadChecker::DetachFromThread();
38  }
39
40  static void MethodOnDifferentThreadImpl();
41  static void DetachThenCallFromDifferentThreadImpl();
42
43 private:
44  DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
45};
46
47// Calls ThreadCheckerClass::DoStuff on another thread.
48class CallDoStuffOnThread : public base::SimpleThread {
49 public:
50  explicit CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
51      : SimpleThread("call_do_stuff_on_thread"),
52        thread_checker_class_(thread_checker_class) {
53  }
54
55  virtual void Run() OVERRIDE {
56    thread_checker_class_->DoStuff();
57  }
58
59 private:
60  ThreadCheckerClass* thread_checker_class_;
61
62  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
63};
64
65// Deletes ThreadCheckerClass on a different thread.
66class DeleteThreadCheckerClassOnThread : public base::SimpleThread {
67 public:
68  explicit DeleteThreadCheckerClassOnThread(
69      ThreadCheckerClass* thread_checker_class)
70      : SimpleThread("delete_thread_checker_class_on_thread"),
71        thread_checker_class_(thread_checker_class) {
72  }
73
74  virtual void Run() OVERRIDE {
75    thread_checker_class_.reset();
76  }
77
78 private:
79  scoped_ptr<ThreadCheckerClass> thread_checker_class_;
80
81  DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
82};
83
84}  // namespace
85
86TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
87  scoped_ptr<ThreadCheckerClass> thread_checker_class(
88      new ThreadCheckerClass);
89
90  // Verify that DoStuff doesn't assert.
91  thread_checker_class->DoStuff();
92
93  // Verify that the destructor doesn't assert.
94  thread_checker_class.reset();
95}
96
97TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
98  scoped_ptr<ThreadCheckerClass> thread_checker_class(
99      new ThreadCheckerClass);
100
101  // Verify that the destructor doesn't assert
102  // when called on a different thread.
103  DeleteThreadCheckerClassOnThread delete_on_thread(
104      thread_checker_class.release());
105
106  delete_on_thread.Start();
107  delete_on_thread.Join();
108}
109
110TEST(ThreadCheckerTest, DetachFromThread) {
111  scoped_ptr<ThreadCheckerClass> thread_checker_class(
112      new ThreadCheckerClass);
113
114  // Verify that DoStuff doesn't assert when called on a different thread after
115  // a call to DetachFromThread.
116  thread_checker_class->DetachFromThread();
117  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
118
119  call_on_thread.Start();
120  call_on_thread.Join();
121}
122
123#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
124
125void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
126  scoped_ptr<ThreadCheckerClass> thread_checker_class(
127      new ThreadCheckerClass);
128
129  // DoStuff should assert in debug builds only when called on a
130  // different thread.
131  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
132
133  call_on_thread.Start();
134  call_on_thread.Join();
135}
136
137#if ENABLE_THREAD_CHECKER
138TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
139  ASSERT_DEATH({
140      ThreadCheckerClass::MethodOnDifferentThreadImpl();
141    }, "");
142}
143#else
144TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
145  ThreadCheckerClass::MethodOnDifferentThreadImpl();
146}
147#endif  // ENABLE_THREAD_CHECKER
148
149void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
150  scoped_ptr<ThreadCheckerClass> thread_checker_class(
151      new ThreadCheckerClass);
152
153  // DoStuff doesn't assert when called on a different thread
154  // after a call to DetachFromThread.
155  thread_checker_class->DetachFromThread();
156  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
157
158  call_on_thread.Start();
159  call_on_thread.Join();
160
161  // DoStuff should assert in debug builds only after moving to
162  // another thread.
163  thread_checker_class->DoStuff();
164}
165
166#if ENABLE_THREAD_CHECKER
167TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
168  ASSERT_DEATH({
169    ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
170    }, "");
171}
172#else
173TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
174  ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
175}
176#endif  // ENABLE_THREAD_CHECKER
177
178#endif  // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
179
180// Just in case we ever get lumped together with other compilation units.
181#undef ENABLE_THREAD_CHECKER
182
183}  // namespace base
184