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 E : : thread_handle_(::OpenThread(SYNCHRONIZE, FALSE, ::GetCurrentThreadId())) {
24 E : DCHECK(thread_handle_.IsValid());
25 E : InitializeListHead(&entry_);
26 E : }
27 :
28 E : ThreadStateBase::~ThreadStateBase() {
29 E : DCHECK(IsListEmpty(&entry_));
30 E : }
31 :
32 E : ThreadStateManager::ThreadStateManager() {
33 E : InitializeListHead(&active_items_);
34 E : InitializeListHead(&death_row_items_);
35 E : }
36 :
37 E : ThreadStateManager::~ThreadStateManager() {
38 E : bool has_leaked_items = false;
39 E : Scavenge(NULL, &has_leaked_items);
40 E : if (has_leaked_items)
41 E : LOG(WARNING) << "Leaking thread state items.";
42 E : }
43 :
44 E : void ThreadStateManager::Register(ThreadStateBase* item) {
45 E : DCHECK(item != NULL);
46 E : DCHECK(IsListEmpty(&item->entry_));
47 E : base::AutoLock auto_lock(lock_);
48 E : InsertTailList(&active_items_, &item->entry_);
49 E : }
50 :
51 E : void ThreadStateManager::Unregister(ThreadStateBase* item) {
52 E : DCHECK(item != NULL);
53 E : base::AutoLock auto_lock(lock_);
54 E : RemoveEntryList(&item->entry_);
55 E : InitializeListHead(&item->entry_);
56 E : }
57 :
58 E : void ThreadStateManager::MarkForDeath(ThreadStateBase* item) {
59 E : DCHECK(item != NULL);
60 E : Scavenge(item, NULL);
61 E : }
62 :
63 E : void ThreadStateManager::Scavenge(ThreadStateBase* item, bool* has_more_items) {
64 : // We'll store the list of scavenged items here.
65 : LIST_ENTRY dead_items;
66 E : InitializeListHead(&dead_items);
67 :
68 : // Acquire the lock when interacting with the internal data.
69 : {
70 E : base::AutoLock auto_lock(lock_);
71 :
72 : // Put all of the death row items belong to dead threads into dead_items.
73 E : GatherDeadItemsUnlocked(&dead_items);
74 :
75 : // If there's an item to mark for death, do so. We do this after gathering
76 : // the dead items because the item in question presumably belongs to the
77 : // current thread and so could never be gathered.
78 E : if (item != NULL) {
79 E : RemoveEntryList(&item->entry_);
80 E : InsertHeadList(&death_row_items_, &item->entry_);
81 : }
82 :
83 : // Return whether or not the thread state manager is no longer holding
84 : // any items.
85 E : if (has_more_items != NULL) {
86 : *has_more_items =
87 E : !IsListEmpty(&active_items_) || !IsListEmpty(&death_row_items_);
88 : }
89 E : }
90 :
91 : // We can delete any dead items we found outside of the lock.
92 E : DeleteDeadItems(&dead_items);
93 E : DCHECK(IsListEmpty(&dead_items));
94 E : }
95 :
96 E : void ThreadStateManager::GatherDeadItemsUnlocked(LIST_ENTRY* dead_items) {
97 E : DCHECK(dead_items != NULL);
98 E : DCHECK(IsListEmpty(dead_items));
99 E : lock_.AssertAcquired();
100 :
101 : // Return if the death row items list is empty.
102 E : if (IsListEmpty(&death_row_items_))
103 E : return;
104 :
105 : // Walk the death row items list, looking for items owned by dead threads.
106 : ThreadStateBase* item =
107 E : CONTAINING_RECORD(death_row_items_.Flink, ThreadStateBase, entry_);
108 E : while (item != NULL) {
109 E : ThreadStateBase* next_item = NULL;
110 E : if (item->entry_.Flink != &death_row_items_) {
111 : next_item =
112 i : CONTAINING_RECORD(item->entry_.Flink, ThreadStateBase, entry_);
113 : }
114 :
115 : // Move the item to the dead_items list if the associated thread is dead.
116 E : if (IsThreadDead(item)) {
117 E : RemoveEntryList(&item->entry_);
118 E : InsertTailList(dead_items, &item->entry_);
119 : }
120 :
121 E : item = next_item;
122 E : }
123 E : }
124 :
125 E : bool ThreadStateManager::IsThreadDead(ThreadStateBase* item) {
126 E : DCHECK(item != NULL);
127 E : return ::WaitForSingleObject(item->thread_handle_, 0) == WAIT_OBJECT_0;
128 E : }
129 :
130 E : void ThreadStateManager::DeleteDeadItems(LIST_ENTRY* dead_items) {
131 E : DCHECK(dead_items != NULL);
132 : // Ok, let's kill any entries we scavenged.
133 E : while (!IsListEmpty(dead_items)) {
134 : ThreadStateBase* item =
135 E : CONTAINING_RECORD(dead_items->Flink, ThreadStateBase, entry_);
136 E : RemoveHeadList(dead_items);
137 E : InitializeListHead(&item->entry_);
138 E : delete item;
139 E : }
140 E : }
141 :
142 : } // namespace common
143 : } // namespace agent
|