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/atomic_ref_count.h"
18 : #include "base/bind.h"
19 : #include "base/synchronization/waitable_event.h"
20 : #include "base/threading/thread.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 TestThreadState : public ThreadStateBase {
29 : public:
30 : // Expose protected members for unit-testing.
31 : using ThreadStateBase::entry_;
32 :
33 E : explicit TestThreadState(base::AtomicRefCount* ref) : ref_(ref) {
34 E : base::AtomicRefCountInc(ref_);
35 E : }
36 E : virtual ~TestThreadState() {
37 E : base::AtomicRefCountDec(ref_);
38 E : }
39 :
40 : private:
41 : base::AtomicRefCount* ref_;
42 : };
43 :
44 : // A ThreadStateManager derived class for unit-testing.
45 : class TestThreadStateManager : public ThreadStateManager {
46 : public:
47 : // Expose protected members for unit-testing.
48 : using ThreadStateManager::Scavenge;
49 : using ThreadStateManager::IsThreadDead;
50 :
51 : // Returns true if the there are no active thread state items being managed.
52 E : bool HasActiveItems() {
53 E : base::AutoLock auto_lock(lock_);
54 E : return !IsListEmpty(&active_items_);
55 E : }
56 :
57 : // Returns true if the there are no death row thread state items being
58 : // managed. If this returns true, it does not necessarily mean that there
59 : // are items ready to be scavenged.
60 E : bool HasDeathRowItems() {
61 E : base::AutoLock auto_lock(lock_);
62 E : return !IsListEmpty(&death_row_items_);
63 E : }
64 :
65 : // Returns true iff @p item is in the active items list.
66 E : bool IsActive(const TestThreadState* item) {
67 E : base::AutoLock auto_lock(lock_);
68 E : return ListContains(&active_items_, item);
69 E : }
70 :
71 : // Returns true iff @p items is in the death_row list.
72 E : bool IsOnDeathRow(const TestThreadState* item) {
73 E : base::AutoLock auto_lock(lock_);
74 E : return ListContains(&death_row_items_, item);
75 E : }
76 :
77 : protected:
78 : // A helper function to check if a item is in the given list.
79 : static bool ListContains(const LIST_ENTRY* list,
80 E : const TestThreadState* item) {
81 : return IsNodeOnList(const_cast<LIST_ENTRY*>(list),
82 E : const_cast<LIST_ENTRY*>(&item->entry_));
83 E : }
84 : };
85 :
86 : // The test fixture for the thread state related tests.
87 : class ThreadStateTest : public testing::Test {
88 : public:
89 : ThreadStateTest()
90 : : worker_thread_("test"),
91 : manager_(),
92 E : thread_states_(0) {
93 E : }
94 :
95 : // A setup function run before each test.
96 E : virtual void SetUp() override {
97 E : manager_.reset(new TestThreadStateManager);
98 E : ASSERT_TRUE(worker_thread_.Start());
99 E : }
100 :
101 : // A helper factory function for creating a TestThreadState.
102 E : void CreateThreadStateImpl(TestThreadState** thread_state) {
103 E : ASSERT_TRUE(thread_state != NULL);
104 E : *thread_state = new TestThreadState(&thread_states_);
105 E : }
106 :
107 : // Creates (and returns) a thread state object on the worker thread.
108 E : void CreateThreadState(TestThreadState** state) {
109 E : ASSERT_TRUE(state != NULL);
110 : CallOnWorkerThread(
111 : base::Bind(&ThreadStateTest::CreateThreadStateImpl,
112 : base::Unretained(this),
113 E : state));
114 E : ASSERT_TRUE(*state != NULL);
115 E : }
116 :
117 : // Activates a thread state object on the worker thread.
118 E : void RegisterThreadState(ThreadStateBase* state) {
119 : CallOnWorkerThread(
120 : base::Bind(&TestThreadStateManager::Register,
121 : base::Unretained(manager_.get()),
122 E : state));
123 E : }
124 :
125 : // Unregisters a thread state object on the worker thread.
126 E : void UnregisterThreadState(ThreadStateBase* state) {
127 : CallOnWorkerThread(
128 : base::Bind(&TestThreadStateManager::Unregister,
129 : base::Unretained(manager_.get()),
130 E : state));
131 E : }
132 :
133 : // Marks a thread state object for death on the worker thread.
134 E : void MarkThreadStateForDeath(ThreadStateBase* state) {
135 : CallOnWorkerThread(
136 : base::Bind(&TestThreadStateManager::MarkForDeath,
137 : base::Unretained(manager_.get()),
138 E : state));
139 E : }
140 :
141 : protected:
142 : // Callback function to execute a TestThreadStateManager method on
143 : // worker_thread_ and signal its completion.
144 : void CallbackImpl(
145 : base::Closure task,
146 E : base::WaitableEvent* event) {
147 E : ASSERT_TRUE(event != NULL);
148 E : ASSERT_EQ(base::MessageLoop::current(), worker_thread_.message_loop());
149 E : task.Run();
150 E : event->Signal();
151 E : }
152 :
153 : // Helper function to call a closure on worker_thread_
154 : // and wait until its completion has been signaled.
155 E : void CallOnWorkerThread(base::Closure task) {
156 E : base::WaitableEvent event(false, false);
157 : worker_thread_.message_loop()->PostTask(
158 : FROM_HERE,
159 : base::Bind(&ThreadStateTest::CallbackImpl,
160 : base::Unretained(this),
161 : task,
162 E : &event));
163 E : event.Wait();
164 E : }
165 :
166 : // A counter for the number of outstanding thread states.
167 : base::AtomicRefCount thread_states_;
168 :
169 : // The worker thread on which the state management functions will be
170 : // exercised.
171 : base::Thread worker_thread_;
172 :
173 : // The thread state manager under test.
174 : scoped_ptr<TestThreadStateManager> manager_;
175 : };
176 :
177 : } // namespace
178 :
179 E : TEST_F(ThreadStateTest, LifeCycle) {
180 : // Check the base state of the thread state manager_->
181 E : EXPECT_FALSE(manager_->HasActiveItems());
182 E : EXPECT_FALSE(manager_->HasDeathRowItems());
183 :
184 : // Create a thread state item.
185 E : TestThreadState* thread_state = NULL;
186 E : ASSERT_NO_FATAL_FAILURE(CreateThreadState(&thread_state));
187 E : EXPECT_FALSE(manager_->IsThreadDead(thread_state));
188 :
189 : // Register the thread state item.
190 E : ASSERT_NO_FATAL_FAILURE(RegisterThreadState(thread_state));
191 E : EXPECT_TRUE(manager_->HasActiveItems());
192 E : EXPECT_TRUE(manager_->IsActive(thread_state));
193 E : EXPECT_FALSE(manager_->HasDeathRowItems());
194 :
195 : // Unregister the thread state item.
196 E : ASSERT_NO_FATAL_FAILURE(UnregisterThreadState(thread_state));
197 E : EXPECT_FALSE(manager_->HasActiveItems());
198 E : EXPECT_FALSE(manager_->HasDeathRowItems());
199 :
200 : // Re-register the thread state item.
201 E : ASSERT_NO_FATAL_FAILURE(RegisterThreadState(thread_state));
202 E : EXPECT_TRUE(manager_->HasActiveItems());
203 E : EXPECT_TRUE(manager_->IsActive(thread_state));
204 E : EXPECT_FALSE(manager_->HasDeathRowItems());
205 :
206 : // Mark the thread state for death.
207 E : ASSERT_NO_FATAL_FAILURE(MarkThreadStateForDeath(thread_state));
208 E : EXPECT_FALSE(manager_->HasActiveItems());
209 E : EXPECT_TRUE(manager_->HasDeathRowItems());
210 E : EXPECT_TRUE(manager_->IsOnDeathRow(thread_state));
211 :
212 : // A list to which we'll scavenge thread state items.
213 E : bool has_items = false;
214 : LIST_ENTRY dead_items;
215 E : InitializeListHead(&dead_items);
216 :
217 : // Scavenge from death row while the thread is still running. Note that we
218 : // test this using the internal function that usually isn't exposed to
219 : // callers.
220 E : has_items = manager_->Scavenge();
221 E : EXPECT_TRUE(has_items);
222 E : EXPECT_TRUE(IsListEmpty(&dead_items));
223 E : EXPECT_FALSE(manager_->HasActiveItems());
224 E : EXPECT_TRUE(manager_->HasDeathRowItems());
225 E : EXPECT_TRUE(manager_->IsOnDeathRow(thread_state));
226 :
227 : // Stop thread then scavenge from death row. Note that we test this using
228 : // the internal function that usually isn't exposed to callers.
229 E : worker_thread_.Stop();
230 E : EXPECT_TRUE(manager_->IsThreadDead(thread_state));
231 E : EXPECT_TRUE(base::AtomicRefCountIsOne(&thread_states_));
232 E : has_items = manager_->Scavenge();
233 E : EXPECT_FALSE(has_items);
234 E : EXPECT_FALSE(manager_->HasActiveItems());
235 E : EXPECT_FALSE(manager_->HasDeathRowItems());
236 E : EXPECT_TRUE(base::AtomicRefCountIsZero(&thread_states_));
237 E : }
238 :
239 E : TEST_F(ThreadStateTest, DeletesAllThreadStatesOnDestruction) {
240 E : TestThreadState* thread_state = NULL;
241 E : ASSERT_NO_FATAL_FAILURE(CreateThreadState(&thread_state));
242 E : ASSERT_NO_FATAL_FAILURE(RegisterThreadState(thread_state));
243 :
244 : // We expect the thread state to be destroyed on deletion of the manager.
245 E : EXPECT_TRUE(base::AtomicRefCountIsOne(&thread_states_));
246 :
247 E : manager_.reset();
248 :
249 E : EXPECT_TRUE(base::AtomicRefCountIsZero(&thread_states_));
250 E : }
251 :
252 : } // namespace common
253 : } // namespace agent
|