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 : // Defines the members of the ThreadStateBase and ThreadStateManager classes
16 :
17 : #include "syzygy/agent/common/thread_state.h"
18 :
19 : namespace agent {
20 : namespace common {
21 :
22 : ThreadStateBase::ThreadStateBase()
23 : : thread_handle_(
24 E : ::OpenThread(SYNCHRONIZE, FALSE, ::GetCurrentThreadId())) {
25 E : DCHECK(thread_handle_.IsValid());
26 E : InitializeListHead(&entry_);
27 E : }
28 :
29 E : ThreadStateBase::~ThreadStateBase() {
30 E : DCHECK(IsListEmpty(&entry_));
31 E : }
32 :
33 E : ThreadStateManager::ThreadStateManager() {
34 E : InitializeListHead(&active_items_);
35 E : InitializeListHead(&death_row_items_);
36 E : }
37 :
38 E : ThreadStateManager::~ThreadStateManager() {
39 : // Destroy all active and death row thread states here. Note that this is
40 : // racy as hell if other threads are active, but it's the caller's
41 : // responsibility to ensure that's not the case.
42 :
43 : // Attempt an orderly deletion of items of the death row.
44 E : Scavenge();
45 :
46 : // Note that we don't hold lock_ for these operations, as the destructor
47 : // has to be the only member of the party at this point.
48 E : if (!IsListEmpty(&death_row_items_)) {
49 : // This will happen if the items have been marked for death, but their
50 : // threads are still active.
51 E : LOG(WARNING) << "Active death row items at manager destruction.";
52 :
53 E : DeleteItems(&death_row_items_);
54 : }
55 :
56 E : if (!IsListEmpty(&active_items_)) {
57 : // This can and will happen if other threads in the process have been
58 : // terminated, as that'll orphan their thread states.
59 E : LOG(WARNING) << "Active thread states at manager destruction.";
60 :
61 E : DeleteItems(&active_items_);
62 : }
63 :
64 : // If either of these asserts fire, then there are active threads in the
65 : // process that are still interacting with the manager. This is obviously
66 : // very bad, as the manager is about to wink out of existence.
67 E : DCHECK(IsListEmpty(&active_items_));
68 E : DCHECK(IsListEmpty(&death_row_items_));
69 E : }
70 :
71 E : void ThreadStateManager::Register(ThreadStateBase* item) {
72 E : DCHECK(item != NULL);
73 E : DCHECK(IsListEmpty(&item->entry_));
74 E : base::AutoLock auto_lock(lock_);
75 E : InsertTailList(&active_items_, &item->entry_);
76 E : }
77 :
78 E : void ThreadStateManager::Unregister(ThreadStateBase* item) {
79 E : DCHECK(item != NULL);
80 E : base::AutoLock auto_lock(lock_);
81 E : RemoveEntryList(&item->entry_);
82 E : InitializeListHead(&item->entry_);
83 E : }
84 :
85 E : void ThreadStateManager::MarkForDeath(ThreadStateBase* item) {
86 E : DCHECK(item != NULL);
87 :
88 : {
89 E : base::AutoLock auto_lock(lock_);
90 :
91 : // Make sure the item we're marking is on the active or death row lists.
92 : DCHECK(IsNodeOnList(&active_items_, &item->entry_) ||
93 E : IsNodeOnList(&death_row_items_, &item->entry_));
94 :
95 : // Pull it out of the list it's on, this'll preserve it over the scavenge
96 : // below, in the unlikely case that the item is being marked from another
97 : // thread than it's own.
98 E : RemoveEntryList(&item->entry_);
99 E : }
100 :
101 : // Use this opportunity to scavenge existing thread states on death row.
102 E : Scavenge();
103 :
104 : // Mark item for death, for later scavenging.
105 : {
106 E : base::AutoLock auto_lock(lock_);
107 :
108 E : InsertHeadList(&death_row_items_, &item->entry_);
109 E : }
110 E : }
111 :
112 E : bool ThreadStateManager::Scavenge() {
113 : // We'll store the list of scavenged items here.
114 : LIST_ENTRY dead_items;
115 E : InitializeListHead(&dead_items);
116 E : bool has_more_items = false;
117 :
118 : // Acquire the lock when interacting with the internal data.
119 : {
120 E : base::AutoLock auto_lock(lock_);
121 :
122 : // Put all of the death row items belonging
123 : // to dead threads into dead_items.
124 E : GatherDeadItemsUnlocked(&dead_items);
125 :
126 : // Return whether or not the thread state manager is no longer holding
127 : // any items.
128 : has_more_items =
129 E : !IsListEmpty(&active_items_) || !IsListEmpty(&death_row_items_);
130 E : }
131 :
132 : // We can delete any dead items we found outside of the lock.
133 E : DeleteItems(&dead_items);
134 E : DCHECK(IsListEmpty(&dead_items));
135 :
136 E : return has_more_items;
137 E : }
138 :
139 E : void ThreadStateManager::GatherDeadItemsUnlocked(LIST_ENTRY* dead_items) {
140 E : DCHECK(dead_items != NULL);
141 E : DCHECK(IsListEmpty(dead_items));
142 E : lock_.AssertAcquired();
143 :
144 : // Return if the death row items list is empty.
145 E : if (IsListEmpty(&death_row_items_))
146 E : return;
147 :
148 : // Walk the death row items list, looking for items owned by dead threads.
149 : ThreadStateBase* item =
150 E : CONTAINING_RECORD(death_row_items_.Flink, ThreadStateBase, entry_);
151 E : while (item != NULL) {
152 E : ThreadStateBase* next_item = NULL;
153 E : if (item->entry_.Flink != &death_row_items_) {
154 : next_item =
155 E : CONTAINING_RECORD(item->entry_.Flink, ThreadStateBase, entry_);
156 : }
157 :
158 : // Move the item to the dead_items list if the associated thread is dead.
159 E : if (IsThreadDead(item)) {
160 E : RemoveEntryList(&item->entry_);
161 E : InsertTailList(dead_items, &item->entry_);
162 : }
163 :
164 E : item = next_item;
165 E : }
166 E : }
167 :
168 E : bool ThreadStateManager::IsThreadDead(ThreadStateBase* item) {
169 E : DCHECK(item != NULL);
170 E : return ::WaitForSingleObject(item->thread_handle_.Get(), 0) == WAIT_OBJECT_0;
171 E : }
172 :
173 E : void ThreadStateManager::DeleteItems(LIST_ENTRY* items) {
174 E : DCHECK(items != NULL);
175 : // Let's delete all entries in items.
176 E : while (!IsListEmpty(items)) {
177 : ThreadStateBase* item =
178 E : CONTAINING_RECORD(items->Flink, ThreadStateBase, entry_);
179 E : RemoveHeadList(items);
180 E : InitializeListHead(&item->entry_);
181 E : delete item;
182 E : }
183 E : }
184 :
185 : } // namespace common
186 : } // namespace agent
|