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 | |
6 | // |
7 | |
8 | /*============================================================ |
9 | ** |
10 | ** Header: RCWWalker.h |
11 | ** |
12 | ** |
13 | ** Purpose: Solve native/manage cyclic reference issue by |
14 | ** walking RCW objects |
15 | ** |
16 | ==============================================================*/ |
17 | |
18 | #ifndef _H_RCWWALKER_ |
19 | #define _H_RCWWALKER_ |
20 | |
21 | #ifdef FEATURE_COMINTEROP |
22 | |
23 | #include "internalunknownimpl.h" |
24 | #include "utilcode.h" |
25 | #include "runtimecallablewrapper.h" |
26 | |
27 | |
28 | // |
29 | // RCW Walker |
30 | // Walks jupiter RCW objects and create references from RCW to referenced CCW (in native side) |
31 | // |
32 | class RCWWalker |
33 | { |
34 | friend struct _DacGlobals; |
35 | |
36 | private : |
37 | static VolatilePtr<IJupiterGCManager> s_pGCManager; // The one and only GCManager instance |
38 | static BOOL s_bGCStarted; // Has GC started? |
39 | SVAL_DECL(BOOL, s_bIsGlobalPeggingOn); // Do we need to peg every CCW? |
40 | |
41 | public : |
42 | #ifndef DACCESS_COMPILE |
43 | static void OnJupiterRCWCreated(RCW *pRCW, IJupiterObject *pJupiterObject); |
44 | static void AfterJupiterRCWCreated(RCW *pRCW); |
45 | static void BeforeJupiterRCWDestroyed(RCW *pRCW); |
46 | static void OnEEShutdown(); |
47 | |
48 | // |
49 | // Send out a AfterAddRef callback to notify Jupiter we've done a AddRef |
50 | // We should do this *after* we made a AddRef because we should never |
51 | // be in a state where reported refs > actual refs |
52 | // |
53 | FORCEINLINE static void AfterInterfaceAddRef(RCW *pRCW) |
54 | { |
55 | |
56 | CONTRACTL { |
57 | NOTHROW; |
58 | GC_NOTRIGGER; |
59 | MODE_ANY; |
60 | } |
61 | CONTRACTL_END; |
62 | |
63 | IJupiterObject *pJupiterObject = pRCW->GetJupiterObject(); |
64 | if (pJupiterObject) |
65 | { |
66 | STRESS_LOG2(LF_INTEROP, LL_INFO100, "[RCW Walker] Calling IJupiterObject::AfterAddRef (IJupiterObject = 0x%p, RCW = 0x%p)\n" , pJupiterObject, pRCW); |
67 | pJupiterObject->AfterAddRef(); |
68 | } |
69 | } |
70 | |
71 | // |
72 | // Send out BeforeRelease callback for every cached interface pointer |
73 | // This needs to be made before call Release because we should never be in a |
74 | // state that reported refs > actual refs |
75 | // |
76 | FORCEINLINE static void BeforeInterfaceRelease(RCW *pRCW) |
77 | { |
78 | CONTRACTL { |
79 | NOTHROW; |
80 | GC_NOTRIGGER; |
81 | MODE_ANY; |
82 | } |
83 | CONTRACTL_END; |
84 | |
85 | IJupiterObject *pJupiterObject = pRCW->GetJupiterObject(); |
86 | if (pJupiterObject) |
87 | { |
88 | STRESS_LOG2(LF_INTEROP, LL_INFO100, "[RCW Walker] Calling IJupiterObject::BeforeRelease before Release (IJupiterObject = 0x%p, RCW = 0x%p)\n" , pJupiterObject, pRCW); |
89 | pJupiterObject->BeforeRelease(); |
90 | } |
91 | } |
92 | |
93 | |
94 | #endif // !DACCESS_COMPILE |
95 | |
96 | |
97 | public : |
98 | // |
99 | // Called in ComCallableWrapper::IsWrapperActive |
100 | // Used to override the individual pegging flag on CCWs and force pegging every jupiter referenced CCW |
101 | // See IsWrapperActive for more details |
102 | // |
103 | static FORCEINLINE BOOL IsGlobalPeggingOn() |
104 | { |
105 | // We need this weird cast because s_bIsGlobalPeggingOn is used in DAC and defined as |
106 | // __GlobalVal in DAC build |
107 | // C++'s operator magic didn't work if two levels of operator overloading are involved... |
108 | return VolatileLoad((BOOL *)&s_bIsGlobalPeggingOn); |
109 | } |
110 | |
111 | #ifndef DACCESS_COMPILE |
112 | // |
113 | // Tells GC whether walking all the Jupiter RCW is necessary, which only should happen |
114 | // if we have seen jupiter RCWs |
115 | // |
116 | static FORCEINLINE BOOL NeedToWalkRCWs() |
117 | { |
118 | LIMITED_METHOD_CONTRACT; |
119 | |
120 | return (((IJupiterGCManager *)s_pGCManager) != NULL); |
121 | } |
122 | |
123 | // |
124 | // Whether a GC has been started and we need to RCW walk |
125 | // |
126 | static FORCEINLINE BOOL HasGCStarted() |
127 | { |
128 | return s_bGCStarted; |
129 | } |
130 | |
131 | // |
132 | // Called when GC started |
133 | // We do most of our work here |
134 | // |
135 | static void OnGCStarted(int nCondemnedGeneration); |
136 | |
137 | // |
138 | // Called when GC finished |
139 | // |
140 | static void OnGCFinished(int nCondemnedGeneration); |
141 | |
142 | private : |
143 | static void OnGCStartedWorker(); |
144 | static void OnGCFinishedWorker(); |
145 | static void WalkRCWs(); |
146 | static HRESULT WalkOneRCW(RCW *pRCW, RCWRefCache *pRCWRefCache); |
147 | #endif // DACCESS_COMPILE |
148 | }; |
149 | |
150 | #endif // FEATURE_COMINTEROP |
151 | |
152 | #endif // _H_RCWWALKER_ |
153 | |