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//---------------------------------------------------------------------------------------
25ManagedEvent::~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//---------------------------------------------------------------------------------------
36void * 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//---------------------------------------------------------------------------------------
54ManagedEvent::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
64ICorDebugManagedCallback * ManagedEvent::DispatchArgs::GetCallback1()
65{
66 return m_pCallback1;
67}
68
69// trivial accessor to get callback 2
70ICorDebugManagedCallback2 * ManagedEvent::DispatchArgs::GetCallback2()
71{
72 return m_pCallback2;
73}
74
75// trivial accessor to get callback 3
76ICorDebugManagedCallback3 * ManagedEvent::DispatchArgs::GetCallback3()
77{
78 return m_pCallback3;
79}
80
81// trivial accessor to get callback 4
82ICorDebugManagedCallback4 * 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.
88DWORD 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//---------------------------------------------------------------------------------------
103ManagedEvent::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//---------------------------------------------------------------------------------------
117ManagedEvent::ManagedEvent()
118{
119 m_dwThreadId = 0;
120 m_pNext = NULL;
121}
122
123
124
125
126
127
128// Ctor
129ManagedEventQueue::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//---------------------------------------------------------------------------------------
147void 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//---------------------------------------------------------------------------------------
171ManagedEvent * 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//---------------------------------------------------------------------------------------
201void 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//---------------------------------------------------------------------------------------
223bool 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//---------------------------------------------------------------------------------------
243void 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//---------------------------------------------------------------------------------------
264BOOL 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