diff options
Diffstat (limited to 'src/vm/weakreferencenative.cpp')
-rw-r--r-- | src/vm/weakreferencenative.cpp | 981 |
1 files changed, 981 insertions, 0 deletions
diff --git a/src/vm/weakreferencenative.cpp b/src/vm/weakreferencenative.cpp new file mode 100644 index 0000000000..f6badd5321 --- /dev/null +++ b/src/vm/weakreferencenative.cpp @@ -0,0 +1,981 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +/*============================================================ +** +** Implementation: WeakReferenceNative.cpp +** +** +===========================================================*/ + +#include "common.h" + +#include "weakreferencenative.h" +#include "handletablepriv.h" + +//************************************************************************ + +// We use several special values of the handle to track extra state without increasing the instance size. +const LPVOID specialWeakReferenceHandles[3] = { 0, 0, 0 }; + +// SPECIAL_HANDLE_SPINLOCK is used to implement spinlock that protects against races between setting the target and finalization +#define SPECIAL_HANDLE_SPINLOCK ((OBJECTHANDLE)(&specialWeakReferenceHandles[0])) + +// SPECIAL_HANDLE_FINALIZED is used to track the original type of the handle so that IsTrackResurrection keeps working on finalized +// objects for backward compatibility. +#define SPECIAL_HANDLE_FINALIZED_SHORT ((OBJECTHANDLE)(&specialWeakReferenceHandles[1])) +#define SPECIAL_HANDLE_FINALIZED_LONG ((OBJECTHANDLE)(&specialWeakReferenceHandles[2])) + +#define IS_SPECIAL_HANDLE(h) ((size_t)(h) - (size_t)(&specialWeakReferenceHandles) < sizeof(specialWeakReferenceHandles)) + +// +// A WeakReference instance can hold one of three types of handles - short or long weak handles, +// or a WinRT weak reference handle. The WinRT weak reference handle has the extra capability +// of recreating an RCW for a COM object which is still alive even though the previous RCW had +// been collected. In order to differentiate this type of handle from the standard weak handles, +// the bottom bit is stolen. +// +// Note that the bit is stolen only in the local copy of the object handle, held in the m_handle +// field of the weak reference object. The handle in the handle table itself does not have its +// bottom bit stolen, and requires using HandleFetchType to determine what type it is. The bit +// is strictly a performance optimization for the weak reference implementation, and it is +// responsible for setting up the bit as it needs and ensuring that it is cleared whenever an +// object handle leaves the weak reference code, for instance to interact with the handle table +// or diagnostics tools. +// +// The following functions are to set, test, and unset that bit before the handle is used. +// + +// Determine if an object handle is a WinRT weak reference handle +bool IsWinRTWeakReferenceHandle(OBJECTHANDLE handle) +{ + STATIC_CONTRACT_LEAF; + return (reinterpret_cast<UINT_PTR>(handle) & 0x1) != 0x0; +} + +// Mark an object handle as being a WinRT weak reference handle +OBJECTHANDLE SetWinRTWeakReferenceHandle(OBJECTHANDLE handle) +{ + STATIC_CONTRACT_LEAF; + + _ASSERTE(!IsWinRTWeakReferenceHandle(handle)); + return reinterpret_cast<OBJECTHANDLE>(reinterpret_cast<UINT_PTR>(handle) | 0x1); +} + +// Get the object handle value even if the object is a WinRT weak reference +OBJECTHANDLE GetHandleValue(OBJECTHANDLE handle) +{ + STATIC_CONTRACT_LEAF; + UINT_PTR mask = ~(static_cast<UINT_PTR>(0x1)); + return reinterpret_cast<OBJECTHANDLE>(reinterpret_cast<UINT_PTR>(handle) & mask); +} + +FORCEINLINE OBJECTHANDLE AcquireWeakHandleSpinLock(WEAKREFERENCEREF pThis); +FORCEINLINE void ReleaseWeakHandleSpinLock(WEAKREFERENCEREF pThis, OBJECTHANDLE newHandle); + +struct WeakHandleSpinLockHolder +{ + OBJECTHANDLE RawHandle; + OBJECTHANDLE Handle; + WEAKREFERENCEREF* pWeakReference; + + WeakHandleSpinLockHolder(OBJECTHANDLE rawHandle, WEAKREFERENCEREF* weakReference) + : RawHandle(rawHandle), Handle(GetHandleValue(rawHandle)), pWeakReference(weakReference) + { + STATIC_CONTRACT_LEAF; + } + + ~WeakHandleSpinLockHolder() + { + WRAPPER_NO_CONTRACT; + ReleaseWeakHandleSpinLock(*pWeakReference, RawHandle); + } + +private: + WeakHandleSpinLockHolder(const WeakHandleSpinLockHolder& other); + WeakHandleSpinLockHolder& operator=(const WeakHandleSpinLockHolder& other); +}; + +#ifdef FEATURE_COMINTEROP + +// Get a WinRT weak reference for the object underlying an RCW if applicable. If the incoming object cannot +// use a WinRT weak reference, nullptr is returned. Otherwise, an AddRef-ed IWeakReference* for the COM +// object underlying the RCW is returned. +// +// In order to qualify to be used with a HNDTYPE_WEAK_WINRT, the incoming object must: +// * be an RCW +// * respond to a QI for IWeakReferenceSource +// * succeed when asked for an IWeakReference* +// +// Note that *pObject should be GC protected on the way into this method +IWeakReference* GetWinRTWeakReference(OBJECTREF* pObject) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + PRECONDITION(CheckPointer(pObject)); + } + CONTRACTL_END; + + if (*pObject == NULL) + { + return nullptr; + } + + ASSERT_PROTECTED(pObject); + + MethodTable* pMT = (*pObject)->GetMethodTable(); + + // If the object is not an RCW, then we do not want to use a WinRT weak reference to it + if (!pMT->IsComObjectType()) + { + return nullptr; + } + + // If the object is a managed type deriving from a COM type, then we also do not want to use a WinRT + // weak reference to it. (Otherwise, we'll wind up resolving IWeakReference-s back into the CLR + // when we don't want to have reentrancy). + if (pMT != g_pBaseCOMObject && pMT->IsExtensibleRCW()) + { + return nullptr; + } + + SafeComHolder<IWeakReferenceSource> pWeakReferenceSource(reinterpret_cast<IWeakReferenceSource*>(GetComIPFromObjectRef(pObject, IID_IWeakReferenceSource, false /* throwIfNoComIP */))); + if (pWeakReferenceSource == nullptr) + { + return nullptr; + } + + GCX_PREEMP(); + SafeComHolderPreemp<IWeakReference> pWeakReference; + if (FAILED(pWeakReferenceSource->GetWeakReference(&pWeakReference))) + { + return nullptr; + } + + return pWeakReference.Extract(); +} + +// Given an object handle that stores a WinRT weak reference, attempt to create an RCW +// and store it back in the handle, returning the RCW. If the underlying WinRT object +// is not alive, then the result is NULL. +// +// In order to create a new RCW, we must: +// * Have an m_handle of HNDTYPE_WEAK_WINRT (ie the bottom bit of m_handle is set) +// * Have stored an IWeakReference* in the handle extra info when setting up the handle +// (see GetWinRTWeakReference) +// * The IWeakReference* must respond to a Resolve request for IID_IInspectable +// * +NOINLINE Object* LoadWinRTWeakReferenceTarget(WEAKREFERENCEREF weakReference, TypeHandle targetType, LPVOID __me) +{ + CONTRACTL + { + FCALL_CHECK; + PRECONDITION(weakReference != NULL); + } + CONTRACTL_END; + + struct + { + WEAKREFERENCEREF weakReference; + OBJECTREF rcw; + OBJECTREF target; + } gc; + ZeroMemory(&gc, sizeof(gc)); + gc.weakReference = weakReference; + + FC_INNER_PROLOG_NO_ME_SETUP(); + HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_PROTECT(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, gc); + + // Acquire the spin lock to get the IWeakReference* associated with the weak reference. We will then need to + // release the lock while resolving the IWeakReference* since we need to enter preemptive mode while calling out + // to COM to resolve the object and we don't want to do that while holding the lock. If we wind up being able + // to geenrate a new RCW, we'll reacquire the lock to save the RCW in the handle. + // + // Since we're acquiring and releasing the lock multiple times, we need to check the handle state each time we + // reacquire the lock to make sure that another thread hasn't reassigned the target of the handle or finalized it + SafeComHolder<IWeakReference> pWinRTWeakReference = nullptr; + { + WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(gc.weakReference), &gc.weakReference); + GCX_NOTRIGGER(); + + // Make sure that while we were not holding the spin lock, another thread did not change the target of + // this weak reference. Only fetch the IWeakReference* if we still have a valid handle holding a NULL object + // and the handle is still a HNDTYPE_WEAK_WINRT type handle. + if ((handle.Handle != NULL) && !IS_SPECIAL_HANDLE(handle.Handle)) + { + if (*(Object **)(handle.Handle) != NULL) + { + // While we released the spin lock, another thread already set a new target for the weak reference. + // We don't want to replace it with an RCW that we fetch, so save it to return as the object the + // weak reference is targeting. + gc.target = ObjectToOBJECTREF(*(Object **)(handle.Handle)); + } + else if(IsWinRTWeakReferenceHandle(handle.RawHandle)) + { + _ASSERTE(HandleFetchType(handle.Handle) == HNDTYPE_WEAK_WINRT); + + // Retrieve the associated IWeakReference* for this weak reference. Add a reference to it while we release + // the spin lock so that another thread doesn't release it out from underneath us. + // + // Setting pWinRTWeakReference will claim that it triggers a GC, however that's not true in this case because + // it's always set to NULL here and there's nothing for it to release. + _ASSERTE(pWinRTWeakReference.IsNull()); + CONTRACT_VIOLATION(GCViolation); + pWinRTWeakReference = reinterpret_cast<IWeakReference*>(HndGetHandleExtraInfo(handle.Handle)); + if (!pWinRTWeakReference.IsNull()) + { + pWinRTWeakReference->AddRef(); + } + } + } + } + + // If the weak reference was in a state that it had an IWeakReference* for us to use, then we need to find the IUnknown + // identity of the underlying COM object (assuming that object is still alive). This work is done without holding the + // spin lock since it will call out to arbitrary code and as such we need to switch to preemptive mode. + SafeComHolder<IUnknown> pTargetIdentity = nullptr; + if (pWinRTWeakReference != nullptr) + { + _ASSERTE(gc.target == NULL); + + GCX_PREEMP(); + + // Using the IWeakReference*, get ahold of the target WinRT object's IInspectable*. If this resolve fails, then we + // assume that the underlying WinRT object is no longer alive, and thus we cannot create a new RCW for it. + SafeComHolderPreemp<IInspectable> pTarget = nullptr; + if (SUCCEEDED(pWinRTWeakReference->Resolve(IID_IInspectable, &pTarget))) + { + if (!pTarget.IsNull()) + { + // Get the IUnknown identity for the underlying object + SafeQueryInterfacePreemp(pTarget, IID_IUnknown, &pTargetIdentity); + } + } + } + + // If we were able to get an IUnkown identity for the object, then we can find or create an associated RCW for it. + if (!pTargetIdentity.IsNull()) + { + GetObjectRefFromComIP(&gc.rcw, pTargetIdentity); + } + + // 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 + // it's possible that another thread has acquired the spin lock and set the target of the weak reference while we were + // building the RCW. In that case, we will defer to the hadle that the other thread set, and let the RCW die. + if (gc.rcw != NULL) + { + // Make sure the type we got back from the WinRT object is compatible with the type the managed + // weak reference expects. (For instance, in the WeakReference<T> case, the returned type + // had better be compatible with T). + TypeHandle rcwType(gc.rcw->GetMethodTable()); + if (!rcwType.CanCastTo(targetType)) + { + SString weakReferenceTypeName; + TypeString::AppendType(weakReferenceTypeName, targetType, TypeString::FormatNamespace | TypeString::FormatFullInst | TypeString::FormatAssembly); + + SString resolvedTypeName; + TypeString::AppendType(resolvedTypeName, rcwType, TypeString::FormatNamespace | TypeString::FormatFullInst | TypeString::FormatAssembly); + + COMPlusThrow(kInvalidCastException, IDS_EE_WINRT_WEAKREF_BAD_TYPE, weakReferenceTypeName.GetUnicode(), resolvedTypeName.GetUnicode()); + } + + WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(gc.weakReference), &gc.weakReference); + GCX_NOTRIGGER(); + + + // 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. + if ((handle.Handle != NULL) && !IS_SPECIAL_HANDLE(handle.Handle)) + { + _ASSERTE(gc.target == NULL); + gc.target = ObjectToOBJECTREF(*(Object **)(handle.Handle)); + + if (gc.target == NULL) + { + StoreObjectInHandle(handle.Handle, gc.rcw); + gc.target = gc.rcw; + } + } + } + + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); + + return OBJECTREFToObject(gc.target); +} + +#endif // FEATURE_COMINTEROP + +//************************************************************************ + +// +// Spinlock implemented by overloading the WeakReference::m_Handle field that protects against races between setting +// the target and finalization +// + +NOINLINE OBJECTHANDLE AcquireWeakHandleSpinLockSpin(WEAKREFERENCEREF pThis) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + DWORD dwSwitchCount = 0; + + // + // Boilerplate spinning logic stolen from other locks + // + for (;;) + { + if (g_SystemInfo.dwNumberOfProcessors > 1) + { + DWORD spincount = g_SpinConstants.dwInitialDuration; + + for (;;) + { + for (DWORD i = 0; i < spincount; i++) + { + YieldProcessor(); + } + + OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); + if (handle != SPECIAL_HANDLE_SPINLOCK) + return handle; + + spincount *= g_SpinConstants.dwBackoffFactor; + if (spincount > g_SpinConstants.dwMaximumDuration) + { + break; + } + } + } + + __SwitchToThread(0, ++dwSwitchCount); + + OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); + if (handle != SPECIAL_HANDLE_SPINLOCK) + return handle; + } +} + +FORCEINLINE OBJECTHANDLE AcquireWeakHandleSpinLock(WEAKREFERENCEREF pThis) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + OBJECTHANDLE handle = InterlockedExchangeT(&pThis->m_Handle, SPECIAL_HANDLE_SPINLOCK); + if (handle != SPECIAL_HANDLE_SPINLOCK) + return handle; + return AcquireWeakHandleSpinLockSpin(pThis); +} + +FORCEINLINE void ReleaseWeakHandleSpinLock(WEAKREFERENCEREF pThis, OBJECTHANDLE newHandle) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE(newHandle != SPECIAL_HANDLE_SPINLOCK); + pThis->m_Handle = newHandle; +} + +//************************************************************************ + +MethodTable *pWeakReferenceMT = NULL; +MethodTable *pWeakReferenceOfTCanonMT = NULL; + +//************************************************************************ + +FCIMPL3(void, WeakReferenceNative::Create, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE, CLR_BOOL trackResurrection) +{ + FCALL_CONTRACT; + + struct + { + WEAKREFERENCEREF pThis; + OBJECTREF pTarget; + } gc; + + gc.pThis = WEAKREFERENCEREF(pThisUNSAFE); + gc.pTarget = OBJECTREF(pTargetUNSAFE); + + HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); + + if (gc.pThis == NULL) + COMPlusThrow(kNullReferenceException); + + if (pWeakReferenceMT == NULL) + pWeakReferenceMT = MscorlibBinder::GetClass(CLASS__WEAKREFERENCE); + + _ASSERTE(gc.pThis->GetMethodTable()->CanCastToClass(pWeakReferenceMT)); + + // Create the handle. +#ifdef FEATURE_COMINTEROP + IWeakReference* pRawWinRTWeakReference = GetWinRTWeakReference(&gc.pTarget); + if (pRawWinRTWeakReference != nullptr) + { + SafeComHolder<IWeakReference> pWinRTWeakReferenceHolder(pRawWinRTWeakReference); + gc.pThis->m_Handle = SetWinRTWeakReferenceHandle(GetAppDomain()->CreateWinRTWeakHandle(gc.pTarget, pWinRTWeakReferenceHolder)); + pWinRTWeakReferenceHolder.SuppressRelease(); + } + else +#endif // FEATURE_COMINTEROP + { + gc.pThis->m_Handle = GetAppDomain()->CreateTypedHandle(gc.pTarget, + trackResurrection ? HNDTYPE_WEAK_LONG : HNDTYPE_WEAK_SHORT); + } + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +FCIMPL3(void, WeakReferenceOfTNative::Create, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE, CLR_BOOL trackResurrection) +{ + FCALL_CONTRACT; + + struct + { + WEAKREFERENCEREF pThis; + OBJECTREF pTarget; + } gc; + + gc.pThis = WEAKREFERENCEREF(pThisUNSAFE); + gc.pTarget = OBJECTREF(pTargetUNSAFE); + + HELPER_METHOD_FRAME_BEGIN_PROTECT(gc); + + if (gc.pThis == NULL) + COMPlusThrow(kNullReferenceException); + + if (pWeakReferenceOfTCanonMT == NULL) + pWeakReferenceOfTCanonMT = gc.pThis->GetMethodTable()->GetCanonicalMethodTable(); + + _ASSERTE(gc.pThis->GetMethodTable()->GetCanonicalMethodTable() == pWeakReferenceOfTCanonMT); + + // Create the handle. +#ifdef FEATURE_COMINTEROP + IWeakReference* pRawWinRTWeakReference = GetWinRTWeakReference(&gc.pTarget); + if (pRawWinRTWeakReference != nullptr) + { + SafeComHolder<IWeakReference> pWinRTWeakReferenceHolder(pRawWinRTWeakReference); + gc.pThis->m_Handle = SetWinRTWeakReferenceHandle(GetAppDomain()->CreateWinRTWeakHandle(gc.pTarget, pWinRTWeakReferenceHolder)); + pWinRTWeakReferenceHolder.SuppressRelease(); + } + else +#endif // FEATURE_COMINTEROP + { + gc.pThis->m_Handle = GetAppDomain()->CreateTypedHandle(gc.pTarget, + trackResurrection ? HNDTYPE_WEAK_LONG : HNDTYPE_WEAK_SHORT); + } + + HELPER_METHOD_FRAME_END(); +} +FCIMPLEND + +//************************************************************************ + +// This entrypoint is also used for direct finalization by the GC. Note that we cannot depend on the runtime being suspended +// when this is called because of background GC. Background GC is going to call this method while user managed code is running. +void FinalizeWeakReference(Object * obj) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + WEAKREFERENCEREF pThis((WeakReferenceObject *)(obj)); + + OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); + OBJECTHANDLE handleToDestroy = NULL; + bool isWeakWinRTHandle = false; + + // Check for not yet constructed or already finalized handle + if ((handle != NULL) && !IS_SPECIAL_HANDLE(handle)) + { + handleToDestroy = GetHandleValue(handle); + + // Cache the old handle value + UINT handleType = HandleFetchType(handleToDestroy); +#ifdef FEATURE_COMINTEROP + _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT || handleType == HNDTYPE_WEAK_WINRT); + isWeakWinRTHandle = handleType == HNDTYPE_WEAK_WINRT; +#else // !FEATURE_COMINTEROP + _ASSERTE(handleType == HNDTYPE_WEAK_LONG || handleType == HNDTYPE_WEAK_SHORT); +#endif // FEATURE_COMINTEROP + + handle = (handleType == HNDTYPE_WEAK_LONG) ? + SPECIAL_HANDLE_FINALIZED_LONG : SPECIAL_HANDLE_FINALIZED_SHORT; + } + + // Release the spin lock + ReleaseWeakHandleSpinLock(pThis, handle); + + if (handleToDestroy != NULL) + { +#ifdef FEATURE_COMINTEROP + if (isWeakWinRTHandle) + { + DestroyWinRTWeakHandle(handleToDestroy); + } + else +#endif // FEATURE_COMINTEROP + { + DestroyTypedHandle(handleToDestroy); + } + } +} + +FCIMPL1(void, WeakReferenceNative::Finalize, WeakReferenceObject * pThis) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_NOPOLL(); + + if (pThis == NULL) + { + FCUnique(0x1); + COMPlusThrow(kNullReferenceException); + } + + FinalizeWeakReference(pThis); + + HELPER_METHOD_FRAME_END_POLL(); +} +FCIMPLEND + +FCIMPL1(void, WeakReferenceOfTNative::Finalize, WeakReferenceObject * pThis) +{ + FCALL_CONTRACT; + + HELPER_METHOD_FRAME_BEGIN_NOPOLL(); + + if (pThis == NULL) + COMPlusThrow(kNullReferenceException); + + FinalizeWeakReference(pThis); + + HELPER_METHOD_FRAME_END_POLL(); +} +FCIMPLEND + +//************************************************************************ + +#include <optsmallperfcritical.h> + +static FORCEINLINE OBJECTREF GetWeakReferenceTarget(WEAKREFERENCEREF pThis) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_COOPERATIVE; + SO_TOLERANT; + } + CONTRACTL_END; + + OBJECTHANDLE rawHandle = pThis->m_Handle.LoadWithoutBarrier(); + OBJECTHANDLE handle = GetHandleValue(rawHandle); + + if (handle == NULL) + return NULL; + + // Try a speculative lock-free read first + if (rawHandle != SPECIAL_HANDLE_SPINLOCK) + { + // + // There is a theoretic chance that the speculative lock-free read may AV while reading the value + // of freed handle if the handle table decides to release the memory that the handle lives in. + // It is not exploitable security issue because of we will fail fast on the AV. It is denial of service only. + // Non-malicious user code will never hit. + // + // We had this theoretical bug in there since forever. Fixing it by always taking the lock would + // degrade the performance critical weak handle getter several times. The right fix may be + // to ensure that handle table memory is released only if the runtime is suspended. + // + Object * pSpeculativeTarget = VolatileLoad((Object **)(handle)); + + // + // We want to ensure that the handle was still alive when we fetched the target, + // so we double check m_handle here. Note that the reading of the handle + // value has to take memory barrier for this to work, but reading of m_handle does not. + // + if (rawHandle == pThis->m_Handle.LoadWithoutBarrier()) + { + return OBJECTREF(pSpeculativeTarget); + } + } + + + rawHandle = AcquireWeakHandleSpinLock(pThis); + GCX_NOTRIGGER(); + + handle = GetHandleValue(rawHandle); + OBJECTREF pTarget = OBJECTREF(*(Object **)(handle)); + + ReleaseWeakHandleSpinLock(pThis, rawHandle); + + return pTarget; +} + +FCIMPL1(Object *, WeakReferenceNative::GetTarget, WeakReferenceObject * pThisUNSAFE) +{ + FCALL_CONTRACT; + + WEAKREFERENCEREF pThis(pThisUNSAFE); + if (pThis == NULL) + { + FCUnique(0x1); + FCThrow(kNullReferenceException); + } + + OBJECTREF pTarget = GetWeakReferenceTarget(pThis); + +#ifdef FEATURE_COMINTEROP + // If we found an object, or we're not a WinRT weak reference, then we're done. Othewrise + // we can try to create a new RCW to the underlying WinRT object if it's still alive. + if (pTarget != NULL || !IsWinRTWeakReferenceHandle(pThis->m_Handle)) + { + FC_GC_POLL_AND_RETURN_OBJREF(pTarget); + } + + FC_INNER_RETURN(Object*, LoadWinRTWeakReferenceTarget(pThis, g_pObjectClass, GetEEFuncEntryPointMacro(WeakReferenceNative::GetTarget))); +#else // !FEATURE_COMINTEROP + FC_GC_POLL_AND_RETURN_OBJREF(pTarget); +#endif // FEATURE_COMINTEROP +} +FCIMPLEND + +FCIMPL1(Object *, WeakReferenceOfTNative::GetTarget, WeakReferenceObject * pThisUNSAFE) +{ + FCALL_CONTRACT; + + WEAKREFERENCEREF pThis(pThisUNSAFE); + if (pThis == NULL) + { + FCThrow(kNullReferenceException); + } + + OBJECTREF pTarget = GetWeakReferenceTarget(pThis); + + +#ifdef FEATURE_COMINTEROP + // If we found an object, or we're not a WinRT weak reference, then we're done. Othewrise + // we can try to create a new RCW to the underlying WinRT object if it's still alive. + if (pTarget != NULL || !IsWinRTWeakReferenceHandle(pThis->m_Handle)) + { + FC_GC_POLL_AND_RETURN_OBJREF(pTarget); + } + + FC_INNER_RETURN(Object*, LoadWinRTWeakReferenceTarget(pThis, pThis->GetMethodTable()->GetInstantiation()[0], GetEEFuncEntryPointMacro(WeakReferenceOfTNative::GetTarget))); +#else // !FEATURE_COMINTEROP + FC_GC_POLL_AND_RETURN_OBJREF(pTarget); +#endif // FEATURE_COMINTEROP +} +FCIMPLEND + +FCIMPL1(FC_BOOL_RET, WeakReferenceNative::IsAlive, WeakReferenceObject * pThisUNSAFE) +{ + FCALL_CONTRACT; + + WEAKREFERENCEREF pThis(pThisUNSAFE); + if (pThis == NULL) + { + FCThrow(kNullReferenceException); + } + + BOOL fRet = GetWeakReferenceTarget(pThis) != NULL; + + FC_GC_POLL_RET(); + + FC_RETURN_BOOL(fRet); +} +FCIMPLEND + +#include <optdefault.h> + +//************************************************************************ + +#include <optsmallperfcritical.h> + +// Slow path helper for setting the target of a weak reference. This code is used if a WinRT weak reference might +// be required. +NOINLINE void SetWeakReferenceTarget(WEAKREFERENCEREF weakReference, OBJECTREF target, LPVOID __me) +{ + FCALL_CONTRACT; + + FC_INNER_PROLOG_NO_ME_SETUP(); + HELPER_METHOD_FRAME_BEGIN_ATTRIB_2(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, target, weakReference); + +#ifdef FEATURE_COMINTEROP + SafeComHolder<IWeakReference> pTargetWeakReference(GetWinRTWeakReference(&target)); +#endif // FEATURE_COMINTEROP + + + WeakHandleSpinLockHolder handle(AcquireWeakHandleSpinLock(weakReference), &weakReference); + GCX_NOTRIGGER(); + +#ifdef FEATURE_COMINTEROP + // + // We have four combinations to handle here + // + // Existing target is a GC object, new target is a GC object: + // * Just store the new object in the handle + // + // Existing target is WinRT, new target is WinRT: + // * Release the existing IWeakReference* + // * Store the new IWeakReference* + // * Store the new object in the handle + // + // Existing target is WinRT, new target is GC: + // * Release the existing IWeakReference* + // * Store null to the IWeakReference* field + // * Store the new object in the handle + // + // Existing target is GC, new target is WinRT: + // * Destroy the existing handle + // * Allocate a new WinRT weak handle for the new target + // + + if (IsWinRTWeakReferenceHandle(handle.RawHandle)) + { + // If the existing reference is a WinRT weak reference, we need to release its IWeakReference pointer + // and update it with the new weak reference pointer. If the incoming object is not an RCW that can + // use IWeakReference, then pTargetWeakReference will be null. Therefore, no matter what the incoming + // object type is, we can unconditionally store pTargetWeakReference to the object handle's extra data. + IWeakReference* pExistingWeakReference = reinterpret_cast<IWeakReference*>(HndGetHandleExtraInfo(handle.Handle)); + HndSetHandleExtraInfo(handle.Handle, HNDTYPE_WEAK_WINRT, reinterpret_cast<LPARAM>(pTargetWeakReference.GetValue())); + StoreObjectInHandle(handle.Handle, target); + + if (pExistingWeakReference != nullptr) + { + pExistingWeakReference->Release(); + } + } + else if (pTargetWeakReference != nullptr) + { + // The existing handle is not a WinRT weak reference, but we need to store the new object in + // a WinRT weak reference. Therefore we need to destroy the old handle and create a new WinRT + // handle. The new handle needs to be allocated first to prevent the weak reference from holding + // a destroyed handle if we fail to allocate the new one. + _ASSERTE(!IsWinRTWeakReferenceHandle(handle.RawHandle)); + OBJECTHANDLE previousHandle = handle.RawHandle; + + handle.Handle = GetAppDomain()->CreateWinRTWeakHandle(target, pTargetWeakReference); + handle.RawHandle = SetWinRTWeakReferenceHandle(handle.Handle); + + DestroyTypedHandle(previousHandle); + } + else +#endif // FEATURE_COMINTEROP + { + StoreObjectInHandle(handle.Handle, target); + } + +#ifdef FEATURE_COMINTEROP + pTargetWeakReference.SuppressRelease(); +#endif // FEATURE_COMINTEROP + + HELPER_METHOD_FRAME_END(); + FC_INNER_EPILOG(); +} + +FCIMPL2(void, WeakReferenceNative::SetTarget, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE) +{ + FCALL_CONTRACT; + + WEAKREFERENCEREF pThis(pThisUNSAFE); + OBJECTREF pTarget(pTargetUNSAFE); + + if (pThis == NULL) + { + FCUnique(0x1); + FCThrowVoid(kNullReferenceException); + } + + bool storedObject = false; + + OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); + { + if (handle == NULL || IS_SPECIAL_HANDLE(handle)) + { + ReleaseWeakHandleSpinLock(pThis, handle); + FCThrowResVoid(kInvalidOperationException, W("InvalidOperation_HandleIsNotInitialized")); + } + + // Switch to no-trigger after the handle was validate. FCThrow triggers. + GCX_NOTRIGGER(); + + // If the existing handle is a GC weak handle and the new target is not an RCW, then + // we can avoid setting up a helper method frame and just reset the handle directly. + if (!IsWinRTWeakReferenceHandle(handle)) + { + if (pTarget == NULL || !pTarget->GetMethodTable()->IsComObjectType()) + { + StoreObjectInHandle(handle, pTarget); + storedObject = true; + } + } + + // SetWeakReferenceTarget will reacquire the spinlock after setting up a helper method frame. This allows + // the frame setup to throw without worrying about leaking the spinlock, and allows the epilog to be cleanly + // walked by the epilog decoder. + ReleaseWeakHandleSpinLock(pThis, handle); + } + + // If we reset the handle directly, then early out before setting up a helper method frame + if (storedObject) + { + FC_GC_POLL(); + return; + } + + FC_INNER_RETURN_VOID(SetWeakReferenceTarget(pThis, pTarget, GetEEFuncEntryPointMacro(WeakReferenceNative::SetTarget))); +} +FCIMPLEND + +FCIMPL2(void, WeakReferenceOfTNative::SetTarget, WeakReferenceObject * pThisUNSAFE, Object * pTargetUNSAFE) +{ + FCALL_CONTRACT; + + WEAKREFERENCEREF pThis(pThisUNSAFE); + OBJECTREF pTarget(pTargetUNSAFE); + + if (pThis == NULL) + { + FCThrowVoid(kNullReferenceException); + } + + bool storedObject = false; + + OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); + { + if (handle == NULL || IS_SPECIAL_HANDLE(handle)) + { + ReleaseWeakHandleSpinLock(pThis, handle); + FCThrowResVoid(kInvalidOperationException, W("InvalidOperation_HandleIsNotInitialized")); + } + + // Switch to no-trigger after the handle was validate. FCThrow triggers. + GCX_NOTRIGGER(); + + // If the existing handle is a GC weak handle and the new target is not an RCW, then + // we can avoid setting up a helper method frame and just reset the handle directly. + if (!IsWinRTWeakReferenceHandle(handle)) + { + if (pTarget == NULL || !pTarget->GetMethodTable()->IsComObjectType()) + { + StoreObjectInHandle(handle, pTarget); + storedObject = true; + } + } + + // SetWeakReferenceTarget will reacquire the spinlock after setting up a helper method frame. This allows + // the frame setup to throw without worrying about leaking the spinlock, and allows the epilog to be cleanly + // walked by the epilog decoder. + ReleaseWeakHandleSpinLock(pThis, handle); + } + + // If we reset the handle directly, then early out before setting up a helper method frame + if (storedObject) + { + FC_GC_POLL(); + return; + } + + FC_INNER_RETURN_VOID(SetWeakReferenceTarget(pThis, pTarget, GetEEFuncEntryPointMacro(WeakReferenceOfTNative::SetTarget))); +} +FCIMPLEND + +#include <optdefault.h> + +//************************************************************************ + +FCIMPL1(FC_BOOL_RET, WeakReferenceNative::IsTrackResurrection, WeakReferenceObject * pThisUNSAFE) +{ + FCALL_CONTRACT; + + WEAKREFERENCEREF pThis(pThisUNSAFE); + + if (pThis == NULL) + { + FCUnique(0x1); + FCThrow(kNullReferenceException); + } + + BOOL trackResurrection = FALSE; + OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); + { + GCX_NOTRIGGER(); + + if (handle == NULL) + { + trackResurrection = FALSE; + } + else + if (IS_SPECIAL_HANDLE(handle)) + { + trackResurrection = (handle == SPECIAL_HANDLE_FINALIZED_LONG); + } + else + { + trackResurrection = HandleFetchType(GetHandleValue(handle)) == HNDTYPE_WEAK_LONG; + } + + ReleaseWeakHandleSpinLock(pThis, handle); + } + + FC_GC_POLL_RET(); + FC_RETURN_BOOL(trackResurrection); +} +FCIMPLEND + +FCIMPL1(FC_BOOL_RET, WeakReferenceOfTNative::IsTrackResurrection, WeakReferenceObject * pThisUNSAFE) +{ + FCALL_CONTRACT; + + WEAKREFERENCEREF pThis(pThisUNSAFE); + + if (pThis == NULL) + { + FCThrow(kNullReferenceException); + } + + BOOL trackResurrection = FALSE; + OBJECTHANDLE handle = AcquireWeakHandleSpinLock(pThis); + { + GCX_NOTRIGGER(); + + if (handle == NULL) + { + trackResurrection = FALSE; + } + else + if (IS_SPECIAL_HANDLE(handle)) + { + trackResurrection = (handle == SPECIAL_HANDLE_FINALIZED_LONG); + } + else + { + trackResurrection = HandleFetchType(GetHandleValue(handle)) == HNDTYPE_WEAK_LONG; + } + + ReleaseWeakHandleSpinLock(pThis, handle); + } + + FC_GC_POLL_RET(); + FC_RETURN_BOOL(trackResurrection); +} +FCIMPLEND + +//************************************************************************ |