1// Copyright (c) 2011 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/non_thread_safe.h"
9#include "base/threading/simple_thread.h"
10#include "testing/gtest/include/gtest/gtest.h"
11
12namespace base {
13
14// Simple class to exersice the basics of NonThreadSafe.
15// Both the destructor and DoStuff should verify that they were
16// called on the same thread as the constructor.
17class NonThreadSafeClass : public NonThreadSafe {
18 public:
19  NonThreadSafeClass() {}
20
21  // Verifies that it was called on the same thread as the constructor.
22  void DoStuff() {
23    CHECK(CalledOnValidThread());
24  }
25
26  void DetachFromThread() {
27    NonThreadSafe::DetachFromThread();
28  }
29
30  static void MethodOnDifferentThreadImpl();
31  static void DestructorOnDifferentThreadImpl();
32
33 private:
34  DISALLOW_COPY_AND_ASSIGN(NonThreadSafeClass);
35};
36
37// Calls NonThreadSafeClass::DoStuff on another thread.
38class CallDoStuffOnThread : public SimpleThread {
39 public:
40  CallDoStuffOnThread(NonThreadSafeClass* non_thread_safe_class)
41      : SimpleThread("call_do_stuff_on_thread"),
42        non_thread_safe_class_(non_thread_safe_class) {
43  }
44
45  virtual void Run() {
46    non_thread_safe_class_->DoStuff();
47  }
48
49 private:
50  NonThreadSafeClass* non_thread_safe_class_;
51
52  DISALLOW_COPY_AND_ASSIGN(CallDoStuffOnThread);
53};
54
55// Deletes NonThreadSafeClass on a different thread.
56class DeleteNonThreadSafeClassOnThread : public SimpleThread {
57 public:
58  DeleteNonThreadSafeClassOnThread(NonThreadSafeClass* non_thread_safe_class)
59      : SimpleThread("delete_non_thread_safe_class_on_thread"),
60        non_thread_safe_class_(non_thread_safe_class) {
61  }
62
63  virtual void Run() {
64    non_thread_safe_class_.reset();
65  }
66
67 private:
68  scoped_ptr<NonThreadSafeClass> non_thread_safe_class_;
69
70  DISALLOW_COPY_AND_ASSIGN(DeleteNonThreadSafeClassOnThread);
71};
72
73TEST(NonThreadSafeTest, CallsAllowedOnSameThread) {
74  scoped_ptr<NonThreadSafeClass> non_thread_safe_class(
75      new NonThreadSafeClass);
76
77  // Verify that DoStuff doesn't assert.
78  non_thread_safe_class->DoStuff();
79
80  // Verify that the destructor doesn't assert.
81  non_thread_safe_class.reset();
82}
83
84TEST(NonThreadSafeTest, DetachThenDestructOnDifferentThread) {
85  scoped_ptr<NonThreadSafeClass> non_thread_safe_class(
86      new NonThreadSafeClass);
87
88  // Verify that the destructor doesn't assert when called on a different thread
89  // after a detach.
90  non_thread_safe_class->DetachFromThread();
91  DeleteNonThreadSafeClassOnThread delete_on_thread(
92      non_thread_safe_class.release());
93
94  delete_on_thread.Start();
95  delete_on_thread.Join();
96}
97
98#if GTEST_HAS_DEATH_TEST || NDEBUG
99
100void NonThreadSafeClass::MethodOnDifferentThreadImpl() {
101  scoped_ptr<NonThreadSafeClass> non_thread_safe_class(
102      new NonThreadSafeClass);
103
104  // Verify that DoStuff asserts in debug builds only when called
105  // on a different thread.
106  CallDoStuffOnThread call_on_thread(non_thread_safe_class.get());
107
108  call_on_thread.Start();
109  call_on_thread.Join();
110}
111
112#ifndef NDEBUG
113TEST(NonThreadSafeDeathTest, MethodNotAllowedOnDifferentThreadInDebug) {
114  ASSERT_DEBUG_DEATH({
115      NonThreadSafeClass::MethodOnDifferentThreadImpl();
116    }, "");
117}
118#else
119TEST(NonThreadSafeTest, MethodAllowedOnDifferentThreadInRelease) {
120  NonThreadSafeClass::MethodOnDifferentThreadImpl();
121}
122#endif  // NDEBUG
123
124void NonThreadSafeClass::DestructorOnDifferentThreadImpl() {
125  scoped_ptr<NonThreadSafeClass> non_thread_safe_class(
126      new NonThreadSafeClass);
127
128  // Verify that the destructor asserts in debug builds only
129  // when called on a different thread.
130  DeleteNonThreadSafeClassOnThread delete_on_thread(
131      non_thread_safe_class.release());
132
133  delete_on_thread.Start();
134  delete_on_thread.Join();
135}
136
137#ifndef NDEBUG
138TEST(NonThreadSafeDeathTest, DestructorNotAllowedOnDifferentThreadInDebug) {
139  ASSERT_DEBUG_DEATH({
140      NonThreadSafeClass::DestructorOnDifferentThreadImpl();
141    }, "");
142}
143#else
144TEST(NonThreadSafeTest, DestructorAllowedOnDifferentThreadInRelease) {
145  NonThreadSafeClass::DestructorOnDifferentThreadImpl();
146}
147#endif  // NDEBUG
148
149#endif  // GTEST_HAS_DEATH_TEST || NDEBUG
150
151}  // namespace base
152