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 | ** Implementation: WeakReferenceNative.cpp |
8 | ** |
9 | ** |
10 | ===========================================================*/ |
11 | |
12 | #include "common.h" |
13 | |
14 | #include "gchandleutilities.h" |
15 | #include "weakreferencenative.h" |
16 | #include "typestring.h" |
17 | #include "typeparse.h" |
18 | |
19 | //************************************************************************ |
20 | |
21 | // We use several special values of the handle to track extra state without increasing the instance size. |
22 | const LPVOID specialWeakReferenceHandles[3] = { 0, 0, 0 }; |
23 | |
24 | // SPECIAL_HANDLE_SPINLOCK is used to implement spinlock that protects against races between setting the target and finalization |
25 | #define SPECIAL_HANDLE_SPINLOCK ((OBJECTHANDLE)(&specialWeakReferenceHandles[0])) |
26 | |
27 | // SPECIAL_HANDLE_FINALIZED is used to track the original type of the handle so that IsTrackResurrection keeps working on finalized |
28 | // objects for backward compatibility. |
29 | #define SPECIAL_HANDLE_FINALIZED_SHORT ((OBJECTHANDLE)(&specialWeakReferenceHandles[1])) |
30 | #define SPECIAL_HANDLE_FINALIZED_LONG ((OBJECTHANDLE)(&specialWeakReferenceHandles[2])) |
31 | |
32 | #define IS_SPECIAL_HANDLE(h) ((size_t)(h) - (size_t)(&specialWeakReferenceHandles) < sizeof(specialWeakReferenceHandles)) |
33 | |
34 | // |
35 | // A WeakReference instance can hold one of three types of handles - short or long weak handles, |
36 | // or a WinRT weak reference handle. The WinRT weak reference handle has the extra capability |
37 | // of recreating an RCW for a COM object which is still alive even though the previous RCW had |
38 | // been collected. In order to differentiate this type of handle from the standard weak handles, |
39 | // the bottom bit is stolen. |
40 | // |
41 | // Note that the bit is stolen only in the local copy of the object handle, held in the m_handle |
42 | // field of the weak reference object. The handle in the handle table itself does not have its |
43 | // bottom bit stolen, and requires using HandleFetchType to determine what type it is. The bit |
44 | // is strictly a performance optimization for the weak reference implementation, and it is |
45 | // responsible for setting up the bit as it needs and ensuring that it is cleared whenever an |
46 | // object handle leaves the weak reference code, for instance to interact with the handle table |
47 | // or diagnostics tools. |
48 | // |
49 | // The following functions are to set, test, and unset that bit before the handle is used. |
50 | // |
51 | |
52 | // Determine if an object handle is a WinRT weak reference handle |
53 | bool IsWinRTWeakReferenceHandle(OBJECTHANDLE handle) |
54 | { |
55 | STATIC_CONTRACT_LEAF; |
56 | return (reinterpret_cast<UINT_PTR>(handle) & 0x1) != 0x0; |
57 | } |
58 | |
59 | // Mark an object handle as being a WinRT weak reference handle |
60 | OBJECTHANDLE SetWinRTWeakReferenceHandle(OBJECTHANDLE handle) |
61 | { |
62 | STATIC_CONTRACT_LEAF; |
63 | |
64 | _ASSERTE(!IsWinRTWeakReferenceHandle(handle)); |
65 | return reinterpret_cast<OBJECTHANDLE>(reinterpret_cast<UINT_PTR>(handle) | 0x1); |
66 | } |
67 | |
68 | // Get the object handle value even if the object is a WinRT weak reference |
69 | OBJECTHANDLE GetHandleValue(OBJECTHANDLE handle) |
70 | { |
71 | STATIC_CONTRACT_LEAF; |
72 | UINT_PTR mask = ~(static_cast<UINT_PTR>(0x1)); |
73 | return reinterpret_cast<OBJECTHANDLE>(reinterpret_cast<UINT_PTR>(handle) & mask); |
74 | } |
75 | |
76 | FORCEINLINE OBJECTHANDLE AcquireWeakHandleSpinLock(WEAKREFERENCEREF pThis); |
77 | FORCEINLINE void ReleaseWeakHandleSpinLock(WEAKREFERENCEREF pThis, OBJECTHANDLE newHandle); |
78 | |
79 | struct WeakHandleSpinLockHolder |
80 | { |
81 | OBJECTHANDLE RawHandle; |
82 | OBJECTHANDLE Handle; |
83 | WEAKREFERENCEREF* pWeakReference; |
84 | |
85 | WeakHandleSpinLockHolder(OBJECTHANDLE rawHandle, WEAKREFERENCEREF* weakReference) |
86 | : RawHandle(rawHandle), Handle(GetHandleValue(rawHandle)), pWeakReference(weakReference) |
87 | { |
88 | STATIC_CONTRACT_LEAF; |
89 | } |
90 | |
91 | ~WeakHandleSpinLockHolder() |
92 | { |
93 | WRAPPER_NO_CONTRACT; |
94 | ReleaseWeakHandleSpinLock(*pWeakReference, RawHandle); |
95 | } |
96 | |
97 | private: |
98 | WeakHandleSpinLockHolder(const WeakHandleSpinLockHolder& other); |
99 | WeakHandleSpinLockHolder& operator=(const WeakHandleSpinLockHolder& other); |
100 | }; |
101 | |
102 | #ifdef FEATURE_COMINTEROP |
103 | |
104 | // Get a WinRT weak reference for the object underlying an RCW if applicable. If the incoming object cannot |
105 | // use a WinRT weak reference, nullptr is returned. Otherwise, an AddRef-ed IWeakReference* for the COM |
106 | // object underlying the RCW is returned. |
107 | // |
108 | // In order to qualify to be used with a HNDTYPE_WEAK_WINRT, the incoming object must: |
109 | // * be an RCW |
110 | // * respond to a QI for IWeakReferenceSource |
111 | // * succeed when asked for an IWeakReference* |
112 | // |
113 | // Note that *pObject should be GC protected on the way into this method |
114 | IWeakReference* GetWinRTWeakReference(OBJECTREF* pObject) |
115 | { |
116 | CONTRACTL |
117 | { |
118 | THROWS; |
119 | GC_TRIGGERS; |
120 | MODE_COOPERATIVE; |
121 | PRECONDITION(CheckPointer(pObject)); |
122 | } |
123 | CONTRACTL_END; |
124 | |
125 | if (*pObject == NULL) |
126 | { |
127 | return nullptr; |
128 | } |
129 | |
130 | ASSERT_PROTECTED(pObject); |
131 | |
132 | MethodTable* pMT = (*pObject)->GetMethodTable(); |
133 | |
134 | // If the object is not an RCW, then we do not want to use a WinRT weak reference to it |
135 | if (!pMT->IsComObjectType()) |
136 | { |
137 | return nullptr; |
138 | } |
139 | |
140 | // If the object is a managed type deriving from a COM type, then we also do not want to use a WinRT |
141 | // weak reference to it. (Otherwise, we'll wind up resolving IWeakReference-s back into the CLR |
142 | // when we don't want to have reentrancy). |
143 | if (pMT != g_pBaseCOMObject && pMT->IsExtensibleRCW()) |
144 | { |
145 | return nullptr; |
146 | } |
147 | |
148 | SafeComHolder<IWeakReferenceSource> pWeakReferenceSource(reinterpret_cast<IWeakReferenceSource*>(GetComIPFromObjectRef(pObject, IID_IWeakReferenceSource, false /* throwIfNoComIP */))); |
149 | if (pWeakReferenceSource == nullptr) |
150 | { |
151 | return nullptr; |
152 | } |
153 | |
154 | GCX_PREEMP(); |
155 | SafeComHolderPreemp<IWeakReference> pWeakReference; |
156 | if (FAILED(pWeakReferenceSource->GetWeakReference(&pWeakReference))) |
157 | { |
158 | return nullptr; |
159 | } |
160 | |
161 | return pWeakReference.Extract(); |
162 | } |
163 | |
164 | // Given an object handle that stores a WinRT weak reference, attempt to create an RCW |
165 | // and store it back in the handle, returning the RCW. If the underlying WinRT object |
166 | // is not alive, then the result is NULL. |
167 | // |
168 | // In order to create a new RCW, we must: |
169 | // * Have an m_handle of HNDTYPE_WEAK_WINRT (ie the bottom bit of m_handle is set) |
170 | // * Have stored an IWeakReference* in the handle extra info when setting up the handle |
171 | // (see GetWinRTWeakReference) |
172 | // * The IWeakReference* must respond to a Resolve request for IID_IInspectable |
173 | // * |
174 | NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, TypeHandle targetType, LPVOID __me) |
175 | { |
176 | CONTRACTL |
177 | { |
178 | FCALL_CHECK; |
179 | PRECONDITION(weakReference != NULL); |
180 | } |
181 | CONTRACTL_END; |
182 | |
183 | struct |
184 | { |
185 | WEAKREFERENCEREF weakReference; |
186 | OBJECTREF rcw; |
187 | OBJECTREF target; |
188 | } gc; |
189 | ZeroMemory(&gc, sizeof(gc)); |
190 | gc.weakReference = weakReference; |
191 | |
192 | FC_INNER_PROLOG_NO_ME_SETUP(); |
193 | HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_PROTECT(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, gc); |
194 | |
195 | // Acquire the spin lock to get the IWeakReference* associated with the weak reference. We will then need to |
196 | // release the lock while resolving the IWeakReference* since we need to enter preemptive mode while calling out |
197 | // to COM to resolve the object and we don't want to do that while holding the lock. If we wind up being able |
198 | // to geenrate a new RCW, we'll reacquire the lock to save the RCW in the handle. |
199 | // |
200 | // Since we're acquiring and releasing the lock multiple times, we need to check the handle state each time we |
201 | // reacquire the lock to make sure that another thread hasn't reassigned the target of the handle or finalized it |
202 | SafeComHolder<IWeakReference> pWinRTWeakReference = nullptr; |
203 | { |
204 | WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(gc.weakReference), &gc.weakReference); |
205 | GCX_NOTRIGGER(); |
206 | |
207 | // Make sure that while we were not holding the spin lock, another thread did not change the target of |
208 | // this weak reference. Only fetch the IWeakReference* if we still have a valid handle holding a NULL object |
209 | // and the handle is still a HNDTYPE_WEAK_WINRT type handle. |
210 | if ((handle.Handle != NULL) && !IS_SPECIAL_HANDLE(handle.Handle)) |
211 | { |
212 | if (*(Object **)(handle.Handle) != NULL) |
213 | { |
214 | // While we released the spin lock, another thread already set a new target for the weak reference. |
215 | // We don't want to replace it with an RCW that we fetch, so save it to return as the object the |
216 | // weak reference is targeting. |
217 | gc.target = ObjectToOBJECTREF(*(Object **)(handle.Handle)); |
218 | } |
219 | else if(IsWinRTWeakReferenceHandle(handle.RawHandle)) |
220 | { |
221 | _ASSERTE(GCHandleUtilities::GetGCHandleManager()->HandleFetchType(handle.Handle) == HNDTYPE_WEAK_WINRT); |
222 | |
223 | // Retrieve the associated IWeakReference* for this weak reference. Add a reference to it while we release |
224 | // the spin lock so that another thread doesn't release it out from underneath us. |
225 | // |
226 | // Setting pWinRTWeakReference will claim that it triggers a GC, however that's not true in this case because |
227 | // it's always set to NULL here and there's nothing for it to release. |
228 | _ASSERTE(pWinRTWeakReference.IsNull()); |
229 | CONTRACT_VIOLATION(GCViolation); |
230 | IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); |
231 | pWinRTWeakReference = reinterpret_cast<IWeakReference*>(mgr->GetExtraInfoFromHandle(handle.Handle)); |
232 | if (!pWinRTWeakReference.IsNull()) |
233 | { |
234 | pWinRTWeakReference->AddRef(); |
235 | } |
236 | } |
237 | } |
238 | } |
239 | |
240 | // If the weak reference was in a state that it had an IWeakReference* for us to use, then we need to find the IUnknown |
241 | // identity of the underlying COM object (assuming that object is still alive). This work is done without holding the |
242 | // spin lock since it will call out to arbitrary code and as such we need to switch to preemptive mode. |
243 | SafeComHolder<IUnknown> pTargetIdentity = nullptr; |
244 | if (pWinRTWeakReference != nullptr) |
245 | { |
246 | _ASSERTE(gc.target == NULL); |
247 | |
248 | GCX_PREEMP(); |
249 | |
250 | // Using the IWeakReference*, get ahold of the target WinRT object's IInspectable*. If this resolve fails, then we |
251 | // assume that the underlying WinRT object is no longer alive, and thus we cannot create a new RCW for it. |
252 | SafeComHolderPreemp<IInspectable> pTarget = nullptr; |
253 | if (SUCCEEDED(pWinRTWeakReference->Resolve(IID_IInspectable, &pTarget))) |
254 | { |
255 | if (!pTarget.IsNull()) |
256 | { |
257 | // Get the IUnknown identity for the underlying object |
258 | SafeQueryInterfacePreemp(pTarget, IID_IUnknown, &pTargetIdentity); |
259 | } |
260 | } |
261 | } |
262 | |
263 | // If we were able to get an IUnkown identity for the object, then we can find or create an associated RCW for it. |
264 | if (!pTargetIdentity.IsNull()) |
265 | { |
266 | GetObjectRefFromComIP(&gc.rcw, pTargetIdentity); |
267 | } |
268 | |
269 | // If we were able to get an RCW, then we need to reacquire the spin lock and store the RCW in the handle. Note that |
270 | // it's possible that another thread has acquired the spin lock and set the target of the weak reference while we were |
271 | // building the RCW. In that case, we will defer to the hadle that the other thread set, and let the RCW die. |
272 | if (gc.rcw != NULL) |
273 | { |
274 | // Make sure the type we got back from the WinRT object is compatible with the type the managed |
275 | // weak reference expects. (For instance, in the WeakReference<T> case, the returned type |
276 | // had better be compatible with T). |
277 | TypeHandle rcwType(gc.rcw->GetMethodTable()); |
278 | if (!rcwType.CanCastTo(targetType)) |
279 | { |
280 | SString weakReferenceTypeName; |
281 | TypeString::AppendType(weakReferenceTypeName, targetType, TypeString::FormatNamespace | TypeString::FormatFullInst | TypeString::FormatAssembly); |
282 | |
283 | SString resolvedTypeName; |
284 | TypeString::AppendType(resolvedTypeName, rcwType, TypeString::FormatNamespace | TypeString::FormatFullInst | TypeString::FormatAssembly); |
285 | |
286 | COMPlusThrow(kInvalidCastException, IDS_EE_WINRT_WEAKREF_BAD_TYPE, weakReferenceTypeName.GetUnicode(), resolvedTypeName.GetUnicode()); |
287 | } |
288 | |
289 | WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(gc.weakReference), &gc.weakReference); |
290 | GCX_NOTRIGGER(); |
291 | |
292 | |
293 | // Now that we've reacquired the lock, see if the handle is still empty. If so, then save the RCW as the new target of the handle. |
294 | if ((handle.Handle != NULL) && !IS_SPECIAL_HANDLE(handle.Handle)) |
295 | { |
296 | _ASSERTE(gc.target == NULL); |
297 | gc.target = ObjectToOBJECTREF(*(Object **)(handle.Handle)); |
298 | |
299 | if (gc.target == NULL) |
300 | { |
301 | StoreObjectInHandle(handle.Handle, gc.rcw); |
302 | gc.target = gc.rcw; |
303 | } |
304 | } |
305 | } |
306 | |
307 | HELPER_METHOD_FRAME_END(); |
308 | FC_INNER_EPILOG(); |
309 | |
310 | return OBJECTREFToObject(gc.target); |
311 | } |
312 | |
313 | #endif // FEATURE_COMINTEROP |
314 | |
315 | //************************************************************************ |
316 | |
317 | // |
318 | // Spinlock implemented by overloading the WeakReference::m_Handle field that protects against races between setting |
319 | // the target and finalization |
320 | // |
321 | |
322 | NOINLINE OBJECTHANDLE AcquireWeakHandleSpinLockSpin(WEAKREFERENCEREF pThis) |
323 | { |
324 | CONTRACTL |
325 | { |
326 | NOTHROW; |
327 | GC_NOTRIGGER; |
328 | SO_TOLERANT; |
329 | } |
330 | CONTRACTL_END; |
331 | |
332 | DWORD dwSwitchCount = 0; |
333 | |
334 | // |
335 | // Boilerplate spinning logic stolen from other locks |
336 | // |
337 | for (;;) |
338 | { |
339 | if (g_SystemInfo.dwNumberOfProcessors > 1) |
340 | { |
341 | DWORD spincount = g_SpinConstants.dwInitialDuration; |
342 | |
343 | for (;;) |
344 | { |
345 | for (DWORD i = 0; i < spincount; i++) |
346 | { |
347 | YieldProcessor(); |
348 | } |
349 | |
350 | OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); |
351 | if (handle != SPECIAL_HANDLE_SPINLOCK) |
352 | return handle; |
353 | |
354 | spincount *= g_SpinConstants.dwBackoffFactor; |
355 | if (spincount > g_SpinConstants.dwMaximumDuration) |
356 | { |
357 | break; |
358 | } |
359 | } |
360 | } |
361 | |
362 | __SwitchToThread(0, ++dwSwitchCount); |
363 | |
364 | OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); |
365 | if (handle != SPECIAL_HANDLE_SPINLOCK) |
366 | return handle; |
367 | } |
368 | } |
369 | |
370 | FORCEINLINE OBJECTHANDLE AcquireWeakHandleSpinLock(WEAKREFERENCEREF pThis) |
371 | { |
372 | CONTRACTL |
373 | { |
374 | NOTHROW; |
375 | GC_NOTRIGGER; |
376 | SO_TOLERANT; |
377 | } |
378 | CONTRACTL_END; |
379 | |
380 | OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); |
381 | if (handle != SPECIAL_HANDLE_SPINLOCK) |
382 | return handle; |
383 | return AcquireWeakHandleSpinLockSpin(pThis); |
384 | } |
385 | |
386 | FORCEINLINE void ReleaseWeakHandleSpinLock(WEAKREFERENCEREF pThis, OBJECTHANDLE newHandle) |
387 | { |
388 | LIMITED_METHOD_CONTRACT; |
389 | |
390 | _ASSERTE(newHandle != SPECIAL_HANDLE_SPINLOCK); |
391 | pThis->m_Handle = newHandle; |
392 | } |
393 | |
394 | //************************************************************************ |
395 | |
396 | MethodTable *pWeakReferenceMT = NULL; |
397 | MethodTable *pWeakReferenceOfTCanonMT = NULL; |
398 | |
399 | //************************************************************************ |
400 | |
401 | FCIMPL3(void, WeakReferenceNative::Create, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE, CLR_BOOL trackResurrection) |
402 | { |
403 | FCALL_CONTRACT; |
404 | |
405 | struct |
406 | { |
407 | WEAKREFERENCEREF pThis; |
408 | OBJECTREF pTarget; |
409 | } gc; |
410 | |
411 | gc.pThis = WEAKREFERENCEREF(pThisUNSAFE); |
412 | gc.pTarget = OBJECTREF(pTargetUNSAFE); |
413 | |
414 | HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); |
415 | |
416 | if (gc.pThis == NULL) |
417 | COMPlusThrow(kNullReferenceException); |
418 | |
419 | if (pWeakReferenceMT == NULL) |
420 | pWeakReferenceMT = MscorlibBinder::GetClass(CLASS__WEAKREFERENCE); |
421 | |
422 | _ASSERTE(gc.pThis->GetMethodTable()->CanCastToClass(pWeakReferenceMT)); |
423 | |
424 | // Create the handle. |
425 | #ifdef FEATURE_COMINTEROP |
426 | IWeakReference* pRawWinRTWeakReference = GetWinRTWeakReference(&gc.pTarget); |
427 | if (pRawWinRTWeakReference != nullptr) |
428 | { |
429 | SafeComHolder<IWeakReference> pWinRTWeakReferenceHolder(pRawWinRTWeakReference); |
430 | gc.pThis->m_Handle = SetWinRTWeakReferenceHandle(GetAppDomain()->CreateWinRTWeakHandle(gc.pTarget, pWinRTWeakReferenceHolder)); |
431 | pWinRTWeakReferenceHolder.SuppressRelease(); |
432 | } |
433 | else |
434 | #endif // FEATURE_COMINTEROP |
435 | { |
436 | gc.pThis->m_Handle = GetAppDomain()->CreateTypedHandle(gc.pTarget, |
437 | trackResurrection ? HNDTYPE_WEAK_LONG : HNDTYPE_WEAK_SHORT); |
438 | } |
439 | |
440 | HELPER_METHOD_FRAME_END(); |
441 | } |
442 | FCIMPLEND |
443 | |
444 | FCIMPL3(void, WeakReferenceOfTNative::Create, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE, CLR_BOOL trackResurrection) |
445 | { |
446 | FCALL_CONTRACT; |
447 | |
448 | struct |
449 | { |
450 | WEAKREFERENCEREF pThis; |
451 | OBJECTREF pTarget; |
452 | } gc; |
453 | |
454 | gc.pThis = WEAKREFERENCEREF(pThisUNSAFE); |
455 | gc.pTarget = OBJECTREF(pTargetUNSAFE); |
456 | |
457 | HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); |
458 | |
459 | if (gc.pThis == NULL) |
460 | COMPlusThrow(kNullReferenceException); |
461 | |
462 | if (pWeakReferenceOfTCanonMT == NULL) |
463 | pWeakReferenceOfTCanonMT = gc.pThis->GetMethodTable()->GetCanonicalMethodTable(); |
464 | |
465 | _ASSERTE(gc.pThis->GetMethodTable()->GetCanonicalMethodTable() == pWeakReferenceOfTCanonMT); |
466 | |
467 | // Create the handle. |
468 | #ifdef FEATURE_COMINTEROP |
469 | IWeakReference* pRawWinRTWeakReference = GetWinRTWeakReference(&gc.pTarget); |
470 | if (pRawWinRTWeakReference != nullptr) |
471 | { |
472 | SafeComHolder<IWeakReference> pWinRTWeakReferenceHolder(pRawWinRTWeakReference); |
473 | gc.pThis->m_Handle = SetWinRTWeakReferenceHandle(GetAppDomain()->CreateWinRTWeakHandle(gc.pTarget, pWinRTWeakReferenceHolder)); |
474 | pWinRTWeakReferenceHolder.SuppressRelease(); |
475 | } |
476 | else |
477 | #endif // FEATURE_COMINTEROP |
478 | { |
479 | gc.pThis->m_Handle = GetAppDomain()->CreateTypedHandle(gc.pTarget, |
480 | trackResurrection ? HNDTYPE_WEAK_LONG : HNDTYPE_WEAK_SHORT); |
481 | } |
482 | |
483 | HELPER_METHOD_FRAME_END(); |
484 | } |
485 | FCIMPLEND |
486 | |
487 | //************************************************************************ |
488 | |
489 | // This entrypoint is also used for direct finalization by the GC. Note that we cannot depend on the runtime being suspended |
490 | // when this is called because of background GC. Background GC is going to call this method while user managed code is running. |
491 | void FinalizeWeakReference(Object * obj) |
492 | { |
493 | CONTRACTL |
494 | { |
495 | NOTHROW; |
496 | GC_NOTRIGGER; |
497 | } |
498 | CONTRACTL_END; |
499 | |
500 | WEAKREFERENCEREF pThis((WeakReferenceObject *)(obj)); |
501 | |
502 | OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); |
503 | OBJECTHANDLE handleToDestroy = NULL; |
504 | bool isWeakWinRTHandle = false; |
505 | |
506 | // Check for not yet constructed or already finalized handle |
507 | if ((handle != NULL) && !IS_SPECIAL_HANDLE(handle)) |
508 | { |
509 | handleToDestroy = GetHandleValue(handle); |
510 | |
511 | // Cache the old handle value |
512 | HandleType handleType = GCHandleUtilities::GetGCHandleManager()->HandleFetchType(handleToDestroy); |
513 | #ifdef FEATURE_COMINTEROP |
514 | _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT || handleType == HNDTYPE_WEAK_WINRT); |
515 | isWeakWinRTHandle = handleType == HNDTYPE_WEAK_WINRT; |
516 | #else // !FEATURE_COMINTEROP |
517 | _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT); |
518 | #endif // FEATURE_COMINTEROP |
519 | |
520 | handle = (handleType == HNDTYPE_WEAK_LONG) ? |
521 | SPECIAL_HANDLE_FINALIZED_LONG : SPECIAL_HANDLE_FINALIZED_SHORT; |
522 | } |
523 | |
524 | // Release the spin lock |
525 | ReleaseWeakHandleSpinLock(pThis, handle); |
526 | |
527 | if (handleToDestroy != NULL) |
528 | { |
529 | #ifdef FEATURE_COMINTEROP |
530 | if (isWeakWinRTHandle) |
531 | { |
532 | DestroyWinRTWeakHandle(handleToDestroy); |
533 | } |
534 | else |
535 | #endif // FEATURE_COMINTEROP |
536 | { |
537 | DestroyTypedHandle(handleToDestroy); |
538 | } |
539 | } |
540 | } |
541 | |
542 | FCIMPL1(void, WeakReferenceNative::Finalize, WeakReferenceObject * pThis) |
543 | { |
544 | FCALL_CONTRACT; |
545 | |
546 | HELPER_METHOD_FRAME_BEGIN_NOPOLL(); |
547 | |
548 | if (pThis == NULL) |
549 | { |
550 | FCUnique(0x1); |
551 | COMPlusThrow(kNullReferenceException); |
552 | } |
553 | |
554 | FinalizeWeakReference(pThis); |
555 | |
556 | HELPER_METHOD_FRAME_END_POLL(); |
557 | } |
558 | FCIMPLEND |
559 | |
560 | FCIMPL1(void, WeakReferenceOfTNative::Finalize, WeakReferenceObject * pThis) |
561 | { |
562 | FCALL_CONTRACT; |
563 | |
564 | HELPER_METHOD_FRAME_BEGIN_NOPOLL(); |
565 | |
566 | if (pThis == NULL) |
567 | COMPlusThrow(kNullReferenceException); |
568 | |
569 | FinalizeWeakReference(pThis); |
570 | |
571 | HELPER_METHOD_FRAME_END_POLL(); |
572 | } |
573 | FCIMPLEND |
574 | |
575 | //************************************************************************ |
576 | |
577 | #include <optsmallperfcritical.h> |
578 | |
579 | static FORCEINLINE OBJECTREF GetWeakReferenceTarget(WEAKREFERENCEREF pThis) |
580 | { |
581 | CONTRACTL |
582 | { |
583 | NOTHROW; |
584 | GC_NOTRIGGER; |
585 | MODE_COOPERATIVE; |
586 | SO_TOLERANT; |
587 | } |
588 | CONTRACTL_END; |
589 | |
590 | OBJECTHANDLE rawHandle = pThis->m_Handle.LoadWithoutBarrier(); |
591 | OBJECTHANDLE handle = GetHandleValue(rawHandle); |
592 | |
593 | if (handle == NULL) |
594 | return NULL; |
595 | |
596 | // Try a speculative lock-free read first |
597 | if (rawHandle != SPECIAL_HANDLE_SPINLOCK) |
598 | { |
599 | // |
600 | // There is a theoretic chance that the speculative lock-free read may AV while reading the value |
601 | // of freed handle if the handle table decides to release the memory that the handle lives in. |
602 | // It is not exploitable security issue because of we will fail fast on the AV. It is denial of service only. |
603 | // Non-malicious user code will never hit. |
604 | // |
605 | // We had this theoretical bug in there since forever. Fixing it by always taking the lock would |
606 | // degrade the performance critical weak handle getter several times. The right fix may be |
607 | // to ensure that handle table memory is released only if the runtime is suspended. |
608 | // |
609 | Object * pSpeculativeTarget = VolatileLoad((Object **)(handle)); |
610 | |
611 | // |
612 | // We want to ensure that the handle was still alive when we fetched the target, |
613 | // so we double check m_handle here. Note that the reading of the handle |
614 | // value has to take memory barrier for this to work, but reading of m_handle does not. |
615 | // |
616 | if (rawHandle == pThis->m_Handle.LoadWithoutBarrier()) |
617 | { |
618 | return OBJECTREF(pSpeculativeTarget); |
619 | } |
620 | } |
621 | |
622 | |
623 | rawHandle = AcquireWeakHandleSpinLock(pThis); |
624 | GCX_NOTRIGGER(); |
625 | |
626 | handle = GetHandleValue(rawHandle); |
627 | OBJECTREF pTarget = OBJECTREF(*(Object **)(handle)); |
628 | |
629 | ReleaseWeakHandleSpinLock(pThis, rawHandle); |
630 | |
631 | return pTarget; |
632 | } |
633 | |
634 | FCIMPL1(Object *, WeakReferenceNative::GetTarget, WeakReferenceObject * pThisUNSAFE) |
635 | { |
636 | FCALL_CONTRACT; |
637 | |
638 | WEAKREFERENCEREF pThis(pThisUNSAFE); |
639 | if (pThis == NULL) |
640 | { |
641 | FCUnique(0x1); |
642 | FCThrow(kNullReferenceException); |
643 | } |
644 | |
645 | OBJECTREF pTarget = GetWeakReferenceTarget(pThis); |
646 | |
647 | #ifdef FEATURE_COMINTEROP |
648 | // If we found an object, or we're not a WinRT weak reference, then we're done. Othewrise |
649 | // we can try to create a new RCW to the underlying WinRT object if it's still alive. |
650 | if (pTarget != NULL || !IsWinRTWeakReferenceHandle(pThis->m_Handle)) |
651 | { |
652 | FC_GC_POLL_AND_RETURN_OBJREF(pTarget); |
653 | } |
654 | |
655 | FC_INNER_RETURN(Object*, LoadWinRTWeakReferenceTarget(pThis, g_pObjectClass, GetEEFuncEntryPointMacro(WeakReferenceNative::GetTarget))); |
656 | #else // !FEATURE_COMINTEROP |
657 | FC_GC_POLL_AND_RETURN_OBJREF(pTarget); |
658 | #endif // FEATURE_COMINTEROP |
659 | } |
660 | FCIMPLEND |
661 | |
662 | FCIMPL1(Object *, WeakReferenceOfTNative::GetTarget, WeakReferenceObject * pThisUNSAFE) |
663 | { |
664 | FCALL_CONTRACT; |
665 | |
666 | WEAKREFERENCEREF pThis(pThisUNSAFE); |
667 | if (pThis == NULL) |
668 | { |
669 | FCThrow(kNullReferenceException); |
670 | } |
671 | |
672 | OBJECTREF pTarget = GetWeakReferenceTarget(pThis); |
673 | |
674 | |
675 | #ifdef FEATURE_COMINTEROP |
676 | // If we found an object, or we're not a WinRT weak reference, then we're done. Othewrise |
677 | // we can try to create a new RCW to the underlying WinRT object if it's still alive. |
678 | if (pTarget != NULL || !IsWinRTWeakReferenceHandle(pThis->m_Handle)) |
679 | { |
680 | FC_GC_POLL_AND_RETURN_OBJREF(pTarget); |
681 | } |
682 | |
683 | FC_INNER_RETURN(Object*, LoadWinRTWeakReferenceTarget(pThis, pThis->GetMethodTable()->GetInstantiation()[0], GetEEFuncEntryPointMacro(WeakReferenceOfTNative::GetTarget))); |
684 | #else // !FEATURE_COMINTEROP |
685 | FC_GC_POLL_AND_RETURN_OBJREF(pTarget); |
686 | #endif // FEATURE_COMINTEROP |
687 | } |
688 | FCIMPLEND |
689 | |
690 | FCIMPL1(FC_BOOL_RET, WeakReferenceNative::IsAlive, WeakReferenceObject * pThisUNSAFE) |
691 | { |
692 | FCALL_CONTRACT; |
693 | |
694 | WEAKREFERENCEREF pThis(pThisUNSAFE); |
695 | if (pThis == NULL) |
696 | { |
697 | FCThrow(kNullReferenceException); |
698 | } |
699 | |
700 | BOOL fRet = GetWeakReferenceTarget(pThis) != NULL; |
701 | |
702 | FC_GC_POLL_RET(); |
703 | |
704 | FC_RETURN_BOOL(fRet); |
705 | } |
706 | FCIMPLEND |
707 | |
708 | #include <optdefault.h> |
709 | |
710 | //************************************************************************ |
711 | |
712 | #include <optsmallperfcritical.h> |
713 | |
714 | // Slow path helper for setting the target of a weak reference. This code is used if a WinRT weak reference might |
715 | // be required. |
716 | NOINLINE void SetWeakReferenceTarget(WEAKREFERENCEREF weakReference, OBJECTREF target, LPVOID __me) |
717 | { |
718 | FCALL_CONTRACT; |
719 | |
720 | FC_INNER_PROLOG_NO_ME_SETUP(); |
721 | HELPER_METHOD_FRAME_BEGIN_ATTRIB_2(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, target, weakReference); |
722 | |
723 | #ifdef FEATURE_COMINTEROP |
724 | SafeComHolder<IWeakReference> pTargetWeakReference(GetWinRTWeakReference(&target)); |
725 | #endif // FEATURE_COMINTEROP |
726 | |
727 | |
728 | WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(weakReference), &weakReference); |
729 | GCX_NOTRIGGER(); |
730 | |
731 | #ifdef FEATURE_COMINTEROP |
732 | // |
733 | // We have four combinations to handle here |
734 | // |
735 | // Existing target is a GC object, new target is a GC object: |
736 | // * Just store the new object in the handle |
737 | // |
738 | // Existing target is WinRT, new target is WinRT: |
739 | // * Release the existing IWeakReference* |
740 | // * Store the new IWeakReference* |
741 | // * Store the new object in the handle |
742 | // |
743 | // Existing target is WinRT, new target is GC: |
744 | // * Release the existing IWeakReference* |
745 | // * Store null to the IWeakReference* field |
746 | // * Store the new object in the handle |
747 | // |
748 | // Existing target is GC, new target is WinRT: |
749 | // * Destroy the existing handle |
750 | // * Allocate a new WinRT weak handle for the new target |
751 | // |
752 | |
753 | if (IsWinRTWeakReferenceHandle(handle.RawHandle)) |
754 | { |
755 | // If the existing reference is a WinRT weak reference, we need to release its IWeakReference pointer |
756 | // and update it with the new weak reference pointer. If the incoming object is not an RCW that can |
757 | // use IWeakReference, then pTargetWeakReference will be null. Therefore, no matter what the incoming |
758 | // object type is, we can unconditionally store pTargetWeakReference to the object handle's extra data. |
759 | IGCHandleManager *mgr = GCHandleUtilities::GetGCHandleManager(); |
760 | IWeakReference* pExistingWeakReference = reinterpret_cast<IWeakReference*>(mgr->GetExtraInfoFromHandle(handle.Handle)); |
761 | mgr->SetExtraInfoForHandle(handle.Handle, HNDTYPE_WEAK_WINRT, reinterpret_cast<void*>(pTargetWeakReference.GetValue())); |
762 | StoreObjectInHandle(handle.Handle, target); |
763 | |
764 | if (pExistingWeakReference != nullptr) |
765 | { |
766 | pExistingWeakReference->Release(); |
767 | } |
768 | } |
769 | else if (pTargetWeakReference != nullptr) |
770 | { |
771 | // The existing handle is not a WinRT weak reference, but we need to store the new object in |
772 | // a WinRT weak reference. Therefore we need to destroy the old handle and create a new WinRT |
773 | // handle. The new handle needs to be allocated first to prevent the weak reference from holding |
774 | // a destroyed handle if we fail to allocate the new one. |
775 | _ASSERTE(!IsWinRTWeakReferenceHandle(handle.RawHandle)); |
776 | OBJECTHANDLE previousHandle = handle.RawHandle; |
777 | |
778 | handle.Handle = GetAppDomain()->CreateWinRTWeakHandle(target, pTargetWeakReference); |
779 | handle.RawHandle = SetWinRTWeakReferenceHandle(handle.Handle); |
780 | |
781 | DestroyTypedHandle(previousHandle); |
782 | } |
783 | else |
784 | #endif // FEATURE_COMINTEROP |
785 | { |
786 | StoreObjectInHandle(handle.Handle, target); |
787 | } |
788 | |
789 | #ifdef FEATURE_COMINTEROP |
790 | pTargetWeakReference.SuppressRelease(); |
791 | #endif // FEATURE_COMINTEROP |
792 | |
793 | HELPER_METHOD_FRAME_END(); |
794 | FC_INNER_EPILOG(); |
795 | } |
796 | |
797 | FCIMPL2(void, WeakReferenceNative::SetTarget, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE) |
798 | { |
799 | FCALL_CONTRACT; |
800 | |
801 | WEAKREFERENCEREF pThis(pThisUNSAFE); |
802 | OBJECTREF pTarget(pTargetUNSAFE); |
803 | |
804 | if (pThis == NULL) |
805 | { |
806 | FCUnique(0x1); |
807 | FCThrowVoid(kNullReferenceException); |
808 | } |
809 | |
810 | bool storedObject = false; |
811 | |
812 | OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); |
813 | { |
814 | if (handle == NULL || IS_SPECIAL_HANDLE(handle)) |
815 | { |
816 | ReleaseWeakHandleSpinLock(pThis, handle); |
817 | FCThrowResVoid(kInvalidOperationException, W("InvalidOperation_HandleIsNotInitialized" )); |
818 | } |
819 | |
820 | // Switch to no-trigger after the handle was validate. FCThrow triggers. |
821 | GCX_NOTRIGGER(); |
822 | |
823 | // If the existing handle is a GC weak handle and the new target is not an RCW, then |
824 | // we can avoid setting up a helper method frame and just reset the handle directly. |
825 | if (!IsWinRTWeakReferenceHandle(handle)) |
826 | { |
827 | if (pTarget == NULL || !pTarget->GetMethodTable()->IsComObjectType()) |
828 | { |
829 | StoreObjectInHandle(handle, pTarget); |
830 | storedObject = true; |
831 | } |
832 | } |
833 | |
834 | // SetWeakReferenceTarget will reacquire the spinlock after setting up a helper method frame. This allows |
835 | // the frame setup to throw without worrying about leaking the spinlock, and allows the epilog to be cleanly |
836 | // walked by the epilog decoder. |
837 | ReleaseWeakHandleSpinLock(pThis, handle); |
838 | } |
839 | |
840 | // If we reset the handle directly, then early out before setting up a helper method frame |
841 | if (storedObject) |
842 | { |
843 | FC_GC_POLL(); |
844 | return; |
845 | } |
846 | |
847 | FC_INNER_RETURN_VOID(SetWeakReferenceTarget(pThis, pTarget, GetEEFuncEntryPointMacro(WeakReferenceNative::SetTarget))); |
848 | } |
849 | FCIMPLEND |
850 | |
851 | FCIMPL2(void, WeakReferenceOfTNative::SetTarget, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE) |
852 | { |
853 | FCALL_CONTRACT; |
854 | |
855 | WEAKREFERENCEREF pThis(pThisUNSAFE); |
856 | OBJECTREF pTarget(pTargetUNSAFE); |
857 | |
858 | if (pThis == NULL) |
859 | { |
860 | FCThrowVoid(kNullReferenceException); |
861 | } |
862 | |
863 | bool storedObject = false; |
864 | |
865 | OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); |
866 | { |
867 | if (handle == NULL || IS_SPECIAL_HANDLE(handle)) |
868 | { |
869 | ReleaseWeakHandleSpinLock(pThis, handle); |
870 | FCThrowResVoid(kInvalidOperationException, W("InvalidOperation_HandleIsNotInitialized" )); |
871 | } |
872 | |
873 | // Switch to no-trigger after the handle was validate. FCThrow triggers. |
874 | GCX_NOTRIGGER(); |
875 | |
876 | // If the existing handle is a GC weak handle and the new target is not an RCW, then |
877 | // we can avoid setting up a helper method frame and just reset the handle directly. |
878 | if (!IsWinRTWeakReferenceHandle(handle)) |
879 | { |
880 | if (pTarget == NULL || !pTarget->GetMethodTable()->IsComObjectType()) |
881 | { |
882 | StoreObjectInHandle(handle, pTarget); |
883 | storedObject = true; |
884 | } |
885 | } |
886 | |
887 | // SetWeakReferenceTarget will reacquire the spinlock after setting up a helper method frame. This allows |
888 | // the frame setup to throw without worrying about leaking the spinlock, and allows the epilog to be cleanly |
889 | // walked by the epilog decoder. |
890 | ReleaseWeakHandleSpinLock(pThis, handle); |
891 | } |
892 | |
893 | // If we reset the handle directly, then early out before setting up a helper method frame |
894 | if (storedObject) |
895 | { |
896 | FC_GC_POLL(); |
897 | return; |
898 | } |
899 | |
900 | FC_INNER_RETURN_VOID(SetWeakReferenceTarget(pThis, pTarget, GetEEFuncEntryPointMacro(WeakReferenceOfTNative::SetTarget))); |
901 | } |
902 | FCIMPLEND |
903 | |
904 | #include <optdefault.h> |
905 | |
906 | //************************************************************************ |
907 | |
908 | FCIMPL1(FC_BOOL_RET, WeakReferenceNative::IsTrackResurrection, WeakReferenceObject * pThisUNSAFE) |
909 | { |
910 | FCALL_CONTRACT; |
911 | |
912 | WEAKREFERENCEREF pThis(pThisUNSAFE); |
913 | |
914 | if (pThis == NULL) |
915 | { |
916 | FCUnique(0x1); |
917 | FCThrow(kNullReferenceException); |
918 | } |
919 | |
920 | BOOL trackResurrection = FALSE; |
921 | OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); |
922 | { |
923 | GCX_NOTRIGGER(); |
924 | |
925 | if (handle == NULL) |
926 | { |
927 | trackResurrection = FALSE; |
928 | } |
929 | else |
930 | if (IS_SPECIAL_HANDLE(handle)) |
931 | { |
932 | trackResurrection = (handle == SPECIAL_HANDLE_FINALIZED_LONG); |
933 | } |
934 | else |
935 | { |
936 | trackResurrection = GCHandleUtilities::GetGCHandleManager()->HandleFetchType(GetHandleValue(handle)) == HNDTYPE_WEAK_LONG; |
937 | } |
938 | |
939 | ReleaseWeakHandleSpinLock(pThis, handle); |
940 | } |
941 | |
942 | FC_GC_POLL_RET(); |
943 | FC_RETURN_BOOL(trackResurrection); |
944 | } |
945 | FCIMPLEND |
946 | |
947 | FCIMPL1(FC_BOOL_RET, WeakReferenceOfTNative::IsTrackResurrection, WeakReferenceObject * pThisUNSAFE) |
948 | { |
949 | FCALL_CONTRACT; |
950 | |
951 | WEAKREFERENCEREF pThis(pThisUNSAFE); |
952 | |
953 | if (pThis == NULL) |
954 | { |
955 | FCThrow(kNullReferenceException); |
956 | } |
957 | |
958 | BOOL trackResurrection = FALSE; |
959 | OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); |
960 | { |
961 | GCX_NOTRIGGER(); |
962 | |
963 | if (handle == NULL) |
964 | { |
965 | trackResurrection = FALSE; |
966 | } |
967 | else |
968 | if (IS_SPECIAL_HANDLE(handle)) |
969 | { |
970 | trackResurrection = (handle == SPECIAL_HANDLE_FINALIZED_LONG); |
971 | } |
972 | else |
973 | { |
974 | trackResurrection = GCHandleUtilities::GetGCHandleManager()->HandleFetchType(GetHandleValue(handle)) == HNDTYPE_WEAK_LONG; |
975 | } |
976 | |
977 | ReleaseWeakHandleSpinLock(pThis, handle); |
978 | } |
979 | |
980 | FC_GC_POLL_RET(); |
981 | FC_RETURN_BOOL(trackResurrection); |
982 | } |
983 | FCIMPLEND |
984 | |
985 | //************************************************************************ |
986 | |