1 : // Copyright 2012 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/agent/common/thread_state.h"
16 :
17 : #include "base/bind.h"
18 : #include "base/synchronization/waitable_event.h"
19 : #include "base/threading/thread.h"
20 : #include "gmock/gmock.h"
21 : #include "gtest/gtest.h"
22 :
23 : namespace agent {
24 : namespace common {
25 : namespace {
26 :
27 : // A ThreadStateBase derived class for unit-testing.
28 : class MockThreadState : public ThreadStateBase {
29 : public:
30 : // Expose protected members for unit-testing.
31 : using ThreadStateBase::entry_;
32 :
33 : // Create a mock for the destructor so that we can track when it is called.
34 E : virtual ~MockThreadState() { OnDestruction(); }
35 E : MOCK_METHOD0(OnDestruction, void());
36 : };
37 :
38 : // A ThreadSTateManager derived class for unit-testing.
39 : class TestThreadStateManager : public ThreadStateManager {
40 : public:
41 : // Expose protected members for unit-testing.
42 : using ThreadStateManager::Scavenge;
43 : using ThreadStateManager::IsThreadDead;
44 :
45 : // A helper factory function for creating a MockThreadState. This is added
46 : // to this ThreadStateManager derived class as a helper which is callable
47 : // via ThreadStateTest::CallOnWorkerThread.
48 E : void CreateThreadState(MockThreadState** thread_state) {
49 E : ASSERT_TRUE(thread_state != NULL);
50 E : *thread_state = new testing::StrictMock<MockThreadState>();
51 E : }
52 :
53 : // Returns true if the there are no active thread state items being managed.
54 E : bool HasActiveItems() {
55 E : base::AutoLock auto_lock(lock_);
56 E : return !IsListEmpty(&active_items_);
57 E : }
58 :
59 : // Returns true if the there are no death row thread state items being
60 : // managed. If this returns true, it does not necessarily mean that there
61 : // are items ready to be scavenged.
62 E : bool HasDeathRowItems() {
63 E : base::AutoLock auto_lock(lock_);
64 E : return !IsListEmpty(&death_row_items_);
65 E : }
66 :
67 : // Returns true iff @p item is in the active items list.
68 E : bool IsActive(const MockThreadState* item) {
69 E : base::AutoLock auto_lock(lock_);
70 E : return ListContains(&active_items_, item);
71 E : }
72 :
73 : // Returns true iff @p items is in the death_row list.
74 E : bool IsOnDeathRow(const MockThreadState* item) {
75 E : base::AutoLock auto_lock(lock_);
76 E : return ListContains(&death_row_items_, item);
77 E : }
78 :
79 : protected:
80 : // A helper function to check if a item is in the given list.
81 : static bool ListContains(const LIST_ENTRY* list,
82 E : const MockThreadState* item) {
83 E : const LIST_ENTRY* current = list;
84 E : const LIST_ENTRY* entry = &item->entry_;
85 E : while (current != NULL) {
86 E : if (current->Flink == entry)
87 E : return true;
88 i : current = current->Flink;
89 i : }
90 i : return true;
91 E : }
92 : };
93 :
94 : // The test fixture for the thread state related tests.
95 : class ThreadStateTest : public testing::Test {
96 : public:
97 E : ThreadStateTest() : worker_thread_("test") {
98 E : }
99 :
100 : // A setup function run before each test.
101 E : virtual void SetUp() OVERRIDE {
102 E : ASSERT_TRUE(worker_thread_.Start());
103 E : }
104 :
105 : // Creates (and returns) a thread state object on the worker thread.
106 E : void CreateThreadState(MockThreadState** state) {
107 E : ASSERT_TRUE(state != NULL);
108 E : CallOnWorkerThread(&TestThreadStateManager::CreateThreadState, state);
109 E : ASSERT_TRUE(*state != NULL);
110 E : }
111 :
112 : // Activates a thread state object on the worker thread.
113 E : void RegisterThreadState(ThreadStateBase* state) {
114 E : CallOnWorkerThread(&TestThreadStateManager::Register, state);
115 E : }
116 :
117 : // Unregisters a thread state object on the worker thread.
118 E : void UnregisterThreadState(ThreadStateBase* state) {
119 E : CallOnWorkerThread(&TestThreadStateManager::Unregister, state);
120 E : }
121 :
122 : // Marks a thread state object for death on the worker thread.
123 E : void MarkThreadStateForDeath(ThreadStateBase* state) {
124 E : CallOnWorkerThread(&TestThreadStateManager::MarkForDeath, state);
125 E : }
126 :
127 : protected:
128 : // Callback function to execute a TestThreadStateManager method on
129 : // worker_thread_ and signal its completion.
130 : template<typename ParamType>
131 : void CallbackImpl(
132 : void (TestThreadStateManager::*method)(ParamType),
133 : ParamType param,
134 E : base::WaitableEvent* event) {
135 E : ASSERT_TRUE(param != NULL);
136 E : ASSERT_TRUE(event != NULL);
137 E : ASSERT_EQ(MessageLoop::current(), worker_thread_.message_loop());
138 E : (manager_.*method)(param);
139 E : event->Signal();
140 E : }
141 :
142 : // Helper function to call a TestThreadStateManager method on worker_thread_
143 : // and wait until its completion has been signaled.
144 : template<typename ParamType>
145 : void CallOnWorkerThread(
146 : void (TestThreadStateManager::*method)(ParamType),
147 E : ParamType param) {
148 E : base::WaitableEvent event(false, false);
149 : worker_thread_.message_loop()->PostTask(
150 : FROM_HERE,
151 : base::Bind(&ThreadStateTest::CallbackImpl<ParamType>,
152 : base::Unretained(this),
153 : method,
154 : param,
155 E : &event));
156 E : event.Wait();
157 E : }
158 :
159 : // The worker thread on which the state management functions will be
160 : // exercised.
161 : base::Thread worker_thread_;
162 :
163 : // The thread state manager under test.
164 : TestThreadStateManager manager_;
165 : };
166 :
167 : } // namespace
168 :
169 E : TEST_F(ThreadStateTest, LifeCycle) {
170 : // Check the base state of the thread state manager.
171 E : EXPECT_FALSE(manager_.HasActiveItems());
172 E : EXPECT_FALSE(manager_.HasDeathRowItems());
173 :
174 : // Create a thread state item.
175 E : MockThreadState* thread_state = NULL;
176 E : ASSERT_NO_FATAL_FAILURE(CreateThreadState(&thread_state));
177 E : EXPECT_FALSE(manager_.IsThreadDead(thread_state));
178 :
179 : // Register the thread state item.
180 E : ASSERT_NO_FATAL_FAILURE(RegisterThreadState(thread_state));
181 E : EXPECT_TRUE(manager_.HasActiveItems());
182 E : EXPECT_TRUE(manager_.IsActive(thread_state));
183 E : EXPECT_FALSE(manager_.HasDeathRowItems());
184 :
185 : // Unregister the thread state item.
186 E : ASSERT_NO_FATAL_FAILURE(UnregisterThreadState(thread_state));
187 E : EXPECT_FALSE(manager_.HasActiveItems());
188 E : EXPECT_FALSE(manager_.HasDeathRowItems());
189 :
190 : // Re-register the thread state item.
191 E : ASSERT_NO_FATAL_FAILURE(RegisterThreadState(thread_state));
192 E : EXPECT_TRUE(manager_.HasActiveItems());
193 E : EXPECT_TRUE(manager_.IsActive(thread_state));
194 E : EXPECT_FALSE(manager_.HasDeathRowItems());
195 :
196 : // Mark the thread state for death.
197 E : ASSERT_NO_FATAL_FAILURE(MarkThreadStateForDeath(thread_state));
198 E : EXPECT_FALSE(manager_.HasActiveItems());
199 E : EXPECT_TRUE(manager_.HasDeathRowItems());
200 E : EXPECT_TRUE(manager_.IsOnDeathRow(thread_state));
201 :
202 : // A list to which we'll scavenge thread state items.
203 E : bool has_items = false;
204 : LIST_ENTRY dead_items;
205 E : InitializeListHead(&dead_items);
206 :
207 : // Scavenge from death row while the thread is still running. Note that we
208 : // test this using the internal function that usually isn't exposed to
209 : // callers.
210 E : manager_.Scavenge(NULL, &has_items);
211 E : EXPECT_TRUE(has_items);
212 E : EXPECT_TRUE(IsListEmpty(&dead_items));
213 E : EXPECT_FALSE(manager_.HasActiveItems());
214 E : EXPECT_TRUE(manager_.HasDeathRowItems());
215 E : EXPECT_TRUE(manager_.IsOnDeathRow(thread_state));
216 :
217 : // Stop thread then scavenge from death row. Note that we test this using
218 : // the internal function that usually isn't exposed to callers.
219 E : worker_thread_.Stop();
220 E : EXPECT_TRUE(manager_.IsThreadDead(thread_state));
221 E : EXPECT_CALL(*thread_state, OnDestruction());
222 E : manager_.Scavenge(NULL, &has_items);
223 E : EXPECT_FALSE(has_items);
224 E : EXPECT_FALSE(manager_.HasActiveItems());
225 E : EXPECT_FALSE(manager_.HasDeathRowItems());
226 E : }
227 :
228 : } // namespace common
229 : } // namespace agent
|