1b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach// Use of this source code is governed by a BSD-style license that can be
3b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach// found in the LICENSE file.
4b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach
5b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach#include "base/basictypes.h"
6b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach#include "base/logging.h"
7b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach#include "base/memory/scoped_ptr.h"
8b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach#include "base/threading/thread_checker.h"
9b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach#include "base/threading/simple_thread.h"
10b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach#include "testing/gtest/include/gtest/gtest.h"
11b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach
12b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbachnamespace base {
13b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach
14b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach// Simple class to exercise the basics of ThreadChecker.
15b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach// Both the destructor and DoStuff should verify that they were
16b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach// called on the same thread as the constructor.
17b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbachclass ThreadCheckerClass : public ThreadChecker {
18b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach public:
19b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach  ThreadCheckerClass() {}
20b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach
21b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach  // Verifies that it was called on the same thread as the constructor.
22b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach  void DoStuff() {
23b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach    CHECK(CalledOnValidThread());
242d24e2a396a1d211baaeedf32148a3b657240170David Blaikie  }
2595a9d937728ca9cf2bf44f86ff1184df318b3bd7Benjamin Kramer
26b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach  void DetachFromThread() {
27b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach    ThreadChecker::DetachFromThread();
2895a9d937728ca9cf2bf44f86ff1184df318b3bd7Benjamin Kramer  }
2995a9d937728ca9cf2bf44f86ff1184df318b3bd7Benjamin Kramer
30b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach  static void MethodOnDifferentThreadImpl();
3161425c0a7f4e3608a85f7bbf254cd052a15b7446Jim Grosbach  static void DetachThenCallFromDifferentThreadImpl();
3261425c0a7f4e3608a85f7bbf254cd052a15b7446Jim Grosbach
33068c65b22d50c265b51886062b2b9c1cb696d67dDanil Malyshev private:
3461425c0a7f4e3608a85f7bbf254cd052a15b7446Jim Grosbach  DISALLOW_COPY_AND_ASSIGN(ThreadCheckerClass);
3561425c0a7f4e3608a85f7bbf254cd052a15b7446Jim Grosbach};
3661425c0a7f4e3608a85f7bbf254cd052a15b7446Jim Grosbach
3761425c0a7f4e3608a85f7bbf254cd052a15b7446Jim Grosbach// Calls ThreadCheckerClass::DoStuff on another thread.
38068c65b22d50c265b51886062b2b9c1cb696d67dDanil Malyshevclass CallDoStuffOnThread : public base::SimpleThread {
3961425c0a7f4e3608a85f7bbf254cd052a15b7446Jim Grosbach public:
4061425c0a7f4e3608a85f7bbf254cd052a15b7446Jim Grosbach  CallDoStuffOnThread(ThreadCheckerClass* thread_checker_class)
4130b9e322e159df8eaabb5b194cec6e11ba99c261Danil Malyshev      : SimpleThread("call_do_stuff_on_thread"),
4230b9e322e159df8eaabb5b194cec6e11ba99c261Danil Malyshev        thread_checker_class_(thread_checker_class) {
4330b9e322e159df8eaabb5b194cec6e11ba99c261Danil Malyshev  }
4430b9e322e159df8eaabb5b194cec6e11ba99c261Danil Malyshev
4530b9e322e159df8eaabb5b194cec6e11ba99c261Danil Malyshev  virtual void Run() {
46b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach    thread_checker_class_->DoStuff();
47b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach  }
48b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach
49b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach private:
50b572830a52faad2fffc7119de53aa96c18d9bf07Jim Grosbach  ThreadCheckerClass* thread_checker_class_;
51
52  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
53};
54
55// Deletes ThreadCheckerClass on a different thread.
56class DeleteThreadCheckerClassOnThread : public base::SimpleThread {
57 public:
58  DeleteThreadCheckerClassOnThread(ThreadCheckerClass* thread_checker_class)
59      : SimpleThread("delete_thread_checker_class_on_thread"),
60        thread_checker_class_(thread_checker_class) {
61  }
62
63  virtual void Run() {
64    thread_checker_class_.reset();
65  }
66
67 private:
68  scoped_ptr<ThreadCheckerClass> thread_checker_class_;
69
70  DISALLOW_COPY_AND_ASSIGN(DeleteThreadCheckerClassOnThread);
71};
72
73TEST(ThreadCheckerTest, CallsAllowedOnSameThread) {
74  scoped_ptr<ThreadCheckerClass> thread_checker_class(
75      new ThreadCheckerClass);
76
77  // Verify that DoStuff doesn't assert.
78  thread_checker_class->DoStuff();
79
80  // Verify that the destructor doesn't assert.
81  thread_checker_class.reset();
82}
83
84TEST(ThreadCheckerTest, DestructorAllowedOnDifferentThread) {
85  scoped_ptr<ThreadCheckerClass> thread_checker_class(
86      new ThreadCheckerClass);
87
88  // Verify that the destructor doesn't assert
89  // when called on a different thread.
90  DeleteThreadCheckerClassOnThread delete_on_thread(
91      thread_checker_class.release());
92
93  delete_on_thread.Start();
94  delete_on_thread.Join();
95}
96
97TEST(ThreadCheckerTest, DetachFromThread) {
98  scoped_ptr<ThreadCheckerClass> thread_checker_class(
99      new ThreadCheckerClass);
100
101  // Verify that DoStuff doesn't assert when called on a different thread after
102  // a call to DetachFromThread.
103  thread_checker_class->DetachFromThread();
104  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
105
106  call_on_thread.Start();
107  call_on_thread.Join();
108}
109
110#if GTEST_HAS_DEATH_TEST || NDEBUG
111
112void ThreadCheckerClass::MethodOnDifferentThreadImpl() {
113  scoped_ptr<ThreadCheckerClass> thread_checker_class(
114      new ThreadCheckerClass);
115
116  // DoStuff should assert in debug builds only when called on a
117  // different thread.
118  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
119
120  call_on_thread.Start();
121  call_on_thread.Join();
122}
123
124#ifndef NDEBUG
125TEST(ThreadCheckerDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
126  ASSERT_DEBUG_DEATH({
127      ThreadCheckerClass::MethodOnDifferentThreadImpl();
128    }, "");
129}
130#else
131TEST(ThreadCheckerTest, MethodAllowedOnDifferentThreadInRelease) {
132  ThreadCheckerClass::MethodOnDifferentThreadImpl();
133}
134#endif  // NDEBUG
135
136void ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl() {
137  scoped_ptr<ThreadCheckerClass> thread_checker_class(
138      new ThreadCheckerClass);
139
140  // DoStuff doesn't assert when called on a different thread
141  // after a call to DetachFromThread.
142  thread_checker_class->DetachFromThread();
143  CallDoStuffOnThread call_on_thread(thread_checker_class.get());
144
145  call_on_thread.Start();
146  call_on_thread.Join();
147
148  // DoStuff should assert in debug builds only after moving to
149  // another thread.
150  thread_checker_class->DoStuff();
151}
152
153#ifndef NDEBUG
154TEST(ThreadCheckerDeathTest, DetachFromThreadInDebug) {
155  ASSERT_DEBUG_DEATH({
156    ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
157    }, "");
158}
159#else
160TEST(ThreadCheckerTest, DetachFromThreadInRelease) {
161  ThreadCheckerClass::DetachThenCallFromDifferentThreadImpl();
162}
163#endif  // NDEBUG
164
165#endif  // GTEST_HAS_DEATH_TEST || NDEBUG
166
167}  // namespace base
168