1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | //***************************************************************************** |
5 | // File: ShimEvents.cpp |
6 | // |
7 | |
8 | // |
9 | // The V3 ICD debugging APIs have a lower abstraction level than V2. |
10 | // This provides V2 ICD debugging functionality on top of the V3 debugger object. |
11 | //***************************************************************************** |
12 | |
13 | #include "stdafx.h" |
14 | |
15 | #include "safewrap.h" |
16 | #include "check.h" |
17 | |
18 | #include <limits.h> |
19 | #include "shimpriv.h" |
20 | |
21 | //--------------------------------------------------------------------------------------- |
22 | // Need virtual dtor since this is a base class. |
23 | // Derived classes will do real work |
24 | //--------------------------------------------------------------------------------------- |
25 | ManagedEvent::~ManagedEvent() |
26 | { |
27 | } |
28 | |
29 | #ifdef _DEBUG |
30 | //--------------------------------------------------------------------------------------- |
31 | // For debugging, get a pointer value that can identify the type of this event. |
32 | // |
33 | // Returns: |
34 | // persistent pointer value that can be used as cookie to identify this event type. |
35 | //--------------------------------------------------------------------------------------- |
36 | void * ManagedEvent::GetDebugCookie() |
37 | { |
38 | // Return vtable, first void* in the structure. |
39 | return *(reinterpret_cast<void**> (this)); |
40 | } |
41 | #endif |
42 | |
43 | //--------------------------------------------------------------------------------------- |
44 | // Ctor for DispatchArgs |
45 | // |
46 | // Arguments: |
47 | // pCallback1 - 1st callback, for debug events in V1.0, V1.1 |
48 | // pCallback2 - 2nd callback, for debug events added in V2 |
49 | // |
50 | // Notes: |
51 | // We'll have a lot of derived classes of ManagedEvent, and so encapsulating the arguments |
52 | // for the Dispatch() function lets us juggle them around easily without hitting every signature. |
53 | //--------------------------------------------------------------------------------------- |
54 | ManagedEvent::DispatchArgs::DispatchArgs(ICorDebugManagedCallback * pCallback1, ICorDebugManagedCallback2 * pCallback2, ICorDebugManagedCallback3 * pCallback3, ICorDebugManagedCallback4 * pCallback4) |
55 | { |
56 | m_pCallback1 = pCallback1; |
57 | m_pCallback2 = pCallback2; |
58 | m_pCallback3 = pCallback3; |
59 | m_pCallback4 = pCallback4; |
60 | } |
61 | |
62 | |
63 | // trivial accessor to get Callback 1 |
64 | ICorDebugManagedCallback * ManagedEvent::DispatchArgs::GetCallback1() |
65 | { |
66 | return m_pCallback1; |
67 | } |
68 | |
69 | // trivial accessor to get callback 2 |
70 | ICorDebugManagedCallback2 * ManagedEvent::DispatchArgs::GetCallback2() |
71 | { |
72 | return m_pCallback2; |
73 | } |
74 | |
75 | // trivial accessor to get callback 3 |
76 | ICorDebugManagedCallback3 * ManagedEvent::DispatchArgs::GetCallback3() |
77 | { |
78 | return m_pCallback3; |
79 | } |
80 | |
81 | // trivial accessor to get callback 4 |
82 | ICorDebugManagedCallback4 * ManagedEvent::DispatchArgs::GetCallback4() |
83 | { |
84 | return m_pCallback4; |
85 | } |
86 | |
87 | // Returns OS Thread Id that this event occurred on, 0 if no thread affinity. |
88 | DWORD ManagedEvent::GetOSTid() |
89 | { |
90 | return m_dwThreadId; |
91 | } |
92 | |
93 | //--------------------------------------------------------------------------------------- |
94 | // Constructore for events with thread affinity |
95 | // |
96 | // Arguments: |
97 | // pThread - thread that this event is associated with. |
98 | // |
99 | // Notes: |
100 | // Thread affinity is used with code:ManagedEventQueue::HasQueuedCallbacks |
101 | // This includes event callbacks that have a thread parameter |
102 | //--------------------------------------------------------------------------------------- |
103 | ManagedEvent::ManagedEvent(ICorDebugThread * pThread) |
104 | { |
105 | m_dwThreadId = 0; |
106 | if (pThread != NULL) |
107 | { |
108 | pThread->GetID(&m_dwThreadId); |
109 | } |
110 | |
111 | m_pNext = NULL; |
112 | } |
113 | |
114 | //--------------------------------------------------------------------------------------- |
115 | // Constructor for events with no thread affinity |
116 | //--------------------------------------------------------------------------------------- |
117 | ManagedEvent::ManagedEvent() |
118 | { |
119 | m_dwThreadId = 0; |
120 | m_pNext = NULL; |
121 | } |
122 | |
123 | |
124 | |
125 | |
126 | |
127 | |
128 | // Ctor |
129 | ManagedEventQueue::ManagedEventQueue() |
130 | { |
131 | m_pFirstEvent = NULL; |
132 | m_pLastEvent = NULL; |
133 | m_pLock = NULL; |
134 | } |
135 | |
136 | //--------------------------------------------------------------------------------------- |
137 | // Initialize |
138 | // |
139 | // Arguments: |
140 | // pLock - lock that protects this event queue. This takes a weak ref to the lock, |
141 | // so caller ensures lock stays alive for lifespan of this object |
142 | // |
143 | // Notes: |
144 | // Event queue locks itself using this lock. |
145 | // Only call this once. |
146 | //--------------------------------------------------------------------------------------- |
147 | void ManagedEventQueue::Init(RSLock * pLock) |
148 | { |
149 | _ASSERTE(m_pLock == NULL); |
150 | m_pLock = pLock; |
151 | } |
152 | |
153 | //--------------------------------------------------------------------------------------- |
154 | // Remove event from the top. |
155 | // |
156 | // Returns: |
157 | // Event that was just dequeued. |
158 | // |
159 | // Notes: |
160 | // Caller then takes ownership of Event and will call Delete on it. |
161 | // If IsEmpty() function returns NULL. |
162 | // |
163 | // It is an error to call Dequeue when the only elements in the queue are suspended. |
164 | // Suspending the queue implies there are going to be new events added which should come before |
165 | // the elements that are suspended. Trying to deqeue when there are only suspended elements |
166 | // left is error-prone - if it were allowed, the order may be non-deterministic. |
167 | // In practice we could probably ban calling Dequeue at all when any elements are suspended, |
168 | // but this seems overly restrictive - there is nothing wrong with allowing these "new" |
169 | // events to be dequeued since we know they come first (you can't nest suspensions). |
170 | //--------------------------------------------------------------------------------------- |
171 | ManagedEvent * ManagedEventQueue::Dequeue() |
172 | { |
173 | RSLockHolder lockHolder(m_pLock); |
174 | if (m_pFirstEvent == NULL) |
175 | { |
176 | return NULL; |
177 | } |
178 | |
179 | ManagedEvent * pEvent = m_pFirstEvent; |
180 | m_pFirstEvent = m_pFirstEvent->m_pNext; |
181 | if (m_pFirstEvent == NULL) |
182 | { |
183 | m_pLastEvent = NULL; |
184 | } |
185 | |
186 | pEvent->m_pNext = NULL; |
187 | return pEvent; |
188 | } |
189 | |
190 | //--------------------------------------------------------------------------------------- |
191 | // Append the event to the end of the queue. |
192 | // Queue owns the event and will delete it (unless it's dequeued first). |
193 | // |
194 | // Note that this can be called when a suspended queue is active. Events are pushed onto |
195 | // the currently active queue (ahead of the suspended queue). |
196 | // |
197 | // Arguments: |
198 | // pEvent - event to queue. |
199 | // |
200 | //--------------------------------------------------------------------------------------- |
201 | void ManagedEventQueue::QueueEvent(ManagedEvent * pEvent) |
202 | { |
203 | RSLockHolder lockHolder(m_pLock); |
204 | _ASSERTE(pEvent != NULL); |
205 | _ASSERTE(pEvent->m_pNext == NULL); |
206 | |
207 | if (m_pLastEvent == NULL) |
208 | { |
209 | _ASSERTE(m_pFirstEvent == NULL); |
210 | m_pFirstEvent = m_pLastEvent = pEvent; |
211 | } |
212 | else |
213 | { |
214 | m_pLastEvent->m_pNext = pEvent; |
215 | m_pLastEvent = pEvent; |
216 | } |
217 | } |
218 | |
219 | |
220 | //--------------------------------------------------------------------------------------- |
221 | // Returns true iff the event queue is empty (including any suspended queue elements) |
222 | //--------------------------------------------------------------------------------------- |
223 | bool ManagedEventQueue::IsEmpty() |
224 | { |
225 | RSLockHolder lockHolder(m_pLock); |
226 | if (m_pFirstEvent != NULL) |
227 | { |
228 | _ASSERTE(m_pLastEvent != NULL); |
229 | return false; |
230 | } |
231 | |
232 | _ASSERTE(m_pLastEvent == NULL); |
233 | return true; |
234 | } |
235 | |
236 | |
237 | //--------------------------------------------------------------------------------------- |
238 | // Delete all events and empty the queue (including any suspended queue elements) |
239 | // |
240 | // Notes: |
241 | // This is like calling { while(!IsEmpty()) delete Dequeue(); } |
242 | //--------------------------------------------------------------------------------------- |
243 | void ManagedEventQueue::DeleteAll() |
244 | { |
245 | RSLockHolder lockHolder(m_pLock); |
246 | |
247 | while (m_pFirstEvent != NULL) |
248 | { |
249 | // verify that the last event in the queue is actually the one stored as the last event |
250 | _ASSERTE( m_pFirstEvent->m_pNext != NULL || m_pFirstEvent == m_pLastEvent ); |
251 | |
252 | ManagedEvent * pNext = m_pFirstEvent->m_pNext; |
253 | delete m_pFirstEvent; |
254 | m_pFirstEvent = pNext; |
255 | } |
256 | m_pLastEvent = NULL; |
257 | |
258 | _ASSERTE(IsEmpty()); |
259 | }; |
260 | |
261 | //--------------------------------------------------------------------------------------- |
262 | // Worker to implement ICorDebugProcess::HasQueuedCallbacks for shim |
263 | //--------------------------------------------------------------------------------------- |
264 | BOOL ManagedEventQueue::HasQueuedCallbacks(ICorDebugThread * pThread) |
265 | { |
266 | // This is from the public paths of ICorDebugProcess::HasQueuedCallbacks. |
267 | // In V2, this would fail in cases, notably including if the process is not synchronized. |
268 | // In arrowhead, it always succeeds. |
269 | |
270 | // No thread - look process wide. |
271 | if (pThread == NULL) |
272 | { |
273 | return !IsEmpty(); |
274 | } |
275 | |
276 | // If we have a thread, look for events with thread affinity. |
277 | DWORD dwThreadID = 0; |
278 | HRESULT hr = pThread->GetID(&dwThreadID); |
279 | (void)hr; //prevent "unused variable" error from GCC |
280 | SIMPLIFYING_ASSUMPTION(SUCCEEDED(hr)); |
281 | |
282 | // Don't take lock until after we don't call any ICorDebug APIs. |
283 | RSLockHolder lockHolder(m_pLock); |
284 | |
285 | ManagedEvent * pCurrent = m_pFirstEvent; |
286 | while (pCurrent != NULL) |
287 | { |
288 | if (pCurrent->GetOSTid() == dwThreadID) |
289 | { |
290 | return true; |
291 | } |
292 | pCurrent = pCurrent->m_pNext; |
293 | } |
294 | return false; |
295 | } |
296 | |
297 | |
298 | |
299 | |
300 | |