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: Canary.h
6//
7
8//
9// Header file Debugger Canary
10//
11//*****************************************************************************
12
13#ifndef CANARY_H
14#define CANARY_H
15
16//-----------------------------------------------------------------------------
17// Canary.
18//
19// The helper thread needs to be very careful about what locks it takes. If it takes a lock
20// held by a suspended thread, then the whole process deadlocks (Since the suspended thread
21// is waiting for the helper to resume it).
22// In general, we try to avoid having the helper take such locks, but the problem is unsolvable
23// because:
24// - we don't know what that set of locks are (eg, OS apis may take new locks between versions)
25// - the helper may call into the EE and that takes unsafe locks.
26// The most prominent dangerous lock is the heap lock, which is why we have the "InteropSafe" heap.
27// Since we don't even know what locks are bad (eg, we can't actually find the Heaplock), we can't
28// explicitly check if the lock is safe to take.
29// So we spin up an auxiallary "Canary" thread which can sniff for locks that the helper thread will
30// need to take. Thus the helper thread can find out if the locks are available without actually taking them.
31// The "Canary" can call APIs that take the locks (such as regular "new" for the process heap lock).
32// The helper will wait on the canary with timeout. If the canary returns, the helper knows it's
33// safe to take the locks. If the canary times out, then the helper assumes it's blocked on the
34// locks and thus not safe for the helper to take them.
35//-----------------------------------------------------------------------------
36class HelperCanary
37{
38public:
39 HelperCanary();
40 ~HelperCanary();
41
42 void Init();
43 bool AreLocksAvailable();
44 void ClearCache();
45
46protected:
47 static DWORD WINAPI ThreadProc(LPVOID param);
48 void ThreadProc();
49 void TakeLocks();
50 bool AreLocksAvailableWorker();
51
52 // Flag to tell Canary thread to exit.
53 bool m_fStop;
54
55 // Flag to indicate Init has been run
56 bool m_initialized;
57
58 // Cache the answers between stops so that we don't have to ping the canary every time.
59 bool m_fCachedValid;
60 bool m_fCachedAnswer;
61
62 HANDLE m_hCanaryThread; // handle for canary thread
63 DWORD m_CanaryThreadId; // canary thread OS Thread ID
64
65 // These counters are read + written by both helper and canary thread.
66 // These need to be volatile because of how they're being accessed from different threads.
67 // However, since each is only read from 1 thread, and written by another, and the WFSO/SetEvent
68 // will give us a memory barrier, and we have a flexible polling operation, volatile is
69 // sufficient to deal with memory barrier issues.
70 Volatile<DWORD> m_RequestCounter;
71 Volatile<DWORD> m_AnswerCounter;
72 HandleHolder m_hPingEvent;
73
74 // We use a Manual wait event to replace Sleep.
75 HandleHolder m_hWaitEvent;
76};
77
78
79#endif // CANARY_H
80
81