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.
22const 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
53bool 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
60OBJECTHANDLE 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
69OBJECTHANDLE 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
76FORCEINLINE OBJECTHANDLE AcquireWeakHandleSpinLock(WEAKREFERENCEREF pThis);
77FORCEINLINE void ReleaseWeakHandleSpinLock(WEAKREFERENCEREF pThis, OBJECTHANDLE newHandle);
78
79struct 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
97private:
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
114IWeakReference* 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// *
174NOINLINE 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
322NOINLINE 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
370FORCEINLINE 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
386FORCEINLINE 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
396MethodTable *pWeakReferenceMT = NULL;
397MethodTable *pWeakReferenceOfTCanonMT = NULL;
398
399//************************************************************************
400
401FCIMPL3(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}
442FCIMPLEND
443
444FCIMPL3(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}
485FCIMPLEND
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.
491void 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
542FCIMPL1(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}
558FCIMPLEND
559
560FCIMPL1(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}
573FCIMPLEND
574
575//************************************************************************
576
577#include <optsmallperfcritical.h>
578
579static 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
634FCIMPL1(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}
660FCIMPLEND
661
662FCIMPL1(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}
688FCIMPLEND
689
690FCIMPL1(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}
706FCIMPLEND
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.
716NOINLINE 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
797FCIMPL2(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}
849FCIMPLEND
850
851FCIMPL2(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}
902FCIMPLEND
903
904#include <optdefault.h>
905
906//************************************************************************
907
908FCIMPL1(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}
945FCIMPLEND
946
947FCIMPL1(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}
983FCIMPLEND
984
985//************************************************************************
986