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/logging.h"
6#include "base/macros.h"
7#include "base/memory/scoped_ptr.h"
8#include "base/threading/simple_thread.h"
9#include "base/threading/thread_checker.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  void Run() override { thread_checker_class_->DoStuff(); }
56
57 private:
58  ThreadCheckerClass* thread_checker_class_;
59
60  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
61};
62
63// Deletes ThreadCheckerClass on a different thread.
64class DeleteThreadCheckerClassOnThread : public base::SimpleThread {
65 public:
66  explicit DeleteThreadCheckerClassOnThread(
67      ThreadCheckerClass* thread_checker_class)
68      : SimpleThread("delete_thread_checker_class_on_thread"),
69        thread_checker_class_(thread_checker_class) {
70  }
71
72  void Run() override { thread_checker_class_.reset(); }
73
74 private:
75  scoped_ptr<ThreadCheckerClass> thread_checker_class_;
76
77  DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
78};
79
80}  // namespace
81
82TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
83  scoped_ptr<ThreadCheckerClass> thread_checker_class(
84      new ThreadCheckerClass);
85
86  // Verify that DoStuff doesn't assert.
87  thread_checker_class->DoStuff();
88
89  // Verify that the destructor doesn't assert.
90  thread_checker_class.reset();
91}
92
93TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
94  scoped_ptr<ThreadCheckerClass> thread_checker_class(
95      new ThreadCheckerClass);
96
97  // Verify that the destructor doesn't assert
98  // when called on a different thread.
99  DeleteThreadCheckerClassOnThread delete_on_thread(
100      thread_checker_class.release());
101
102  delete_on_thread.Start();
103  delete_on_thread.Join();
104}
105
106TEST(ThreadCheckerTest, DetachFromThread) {
107  scoped_ptr<ThreadCheckerClass> thread_checker_class(
108      new ThreadCheckerClass);
109
110  // Verify that DoStuff doesn't assert when called on a different thread after
111  // a call to DetachFromThread.
112  thread_checker_class->DetachFromThread();
113  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
114
115  call_on_thread.Start();
116  call_on_thread.Join();
117}
118
119#if GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
120
121void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
122  scoped_ptr<ThreadCheckerClass> thread_checker_class(
123      new ThreadCheckerClass);
124
125  // DoStuff should assert in debug builds only when called on a
126  // different thread.
127  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
128
129  call_on_thread.Start();
130  call_on_thread.Join();
131}
132
133#if ENABLE_THREAD_CHECKER
134TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
135  ASSERT_DEATH({
136      ThreadCheckerClass::MethodOnDifferentThreadImpl();
137    }, "");
138}
139#else
140TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
141  ThreadCheckerClass::MethodOnDifferentThreadImpl();
142}
143#endif  // ENABLE_THREAD_CHECKER
144
145void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
146  scoped_ptr<ThreadCheckerClass> thread_checker_class(
147      new ThreadCheckerClass);
148
149  // DoStuff doesn't assert when called on a different thread
150  // after a call to DetachFromThread.
151  thread_checker_class->DetachFromThread();
152  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
153
154  call_on_thread.Start();
155  call_on_thread.Join();
156
157  // DoStuff should assert in debug builds only after moving to
158  // another thread.
159  thread_checker_class->DoStuff();
160}
161
162#if ENABLE_THREAD_CHECKER
163TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
164  ASSERT_DEATH({
165    ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
166    }, "");
167}
168#else
169TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
170  ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
171}
172#endif  // ENABLE_THREAD_CHECKER
173
174#endif  // GTEST_HAS_DEATH_TEST || !ENABLE_THREAD_CHECKER
175
176// Just in case we ever get lumped together with other compilation units.
177#undef ENABLE_THREAD_CHECKER
178
179}  // namespace base
180