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.
8 /*============================================================
10 ** File: COMUtilNative
14 ** Purpose: A dumping ground for classes which aren't large
15 ** enough to get their own file in the EE.
19 ===========================================================*/
24 #include "comutilnative.h"
30 #include "gcheaputilities.h"
32 #include "invokeutil.h"
34 #include "typestring.h"
36 #include "finalizerthread.h"
38 #ifdef FEATURE_COMINTEROP
39 #include "comcallablewrapper.h"
41 #endif // FEATURE_COMINTEROP
43 #include "arraynative.inl"
45 #define STACK_OVERFLOW_MESSAGE W("StackOverflowException")
47 /*===================================IsDigit====================================
48 **Returns a bool indicating whether the character passed in represents a **
50 ==============================================================================*/
51 bool IsDigit(WCHAR c, int radix, int *result)
58 PRECONDITION(CheckPointer(result));
63 *result = DIGIT_TO_INT(c);
65 else if (c>='A' && c<='Z') {
66 //+10 is necessary because A is actually 10, etc.
69 else if (c>='a' && c<='z') {
70 //+10 is necessary because a is actually 10, etc.
77 if ((*result >=0) && (*result < radix))
83 INT32 wtoi(__in_ecount(length) WCHAR* wstr, DWORD length)
90 PRECONDITION(CheckPointer(wstr));
91 PRECONDITION(length >= 0);
99 while ( (i < length) && (IsDigit(wstr[i], 10 ,&value)) ) {
100 //Read all of the digits and convert to a number
101 result = result*10 + value;
115 FCIMPL1(FC_BOOL_RET, ExceptionNative::IsImmutableAgileException, Object* pExceptionUNSAFE)
119 ASSERT(pExceptionUNSAFE != NULL);
121 OBJECTREF pException = (OBJECTREF) pExceptionUNSAFE;
123 // The preallocated exception objects may be used from multiple AppDomains
124 // and therefore must remain immutable from the application's perspective.
125 FC_RETURN_BOOL(CLRException::IsPreallocatedExceptionObject(pException));
129 FCIMPL1(FC_BOOL_RET, ExceptionNative::IsTransient, INT32 hresult)
133 FC_RETURN_BOOL(Exception::IsTransient(hresult));
138 // This FCall sets a flag against the thread exception state to indicate to
139 // IL_Throw and the StackTraceInfo implementation to account for the fact
140 // that we have restored a foreign exception dispatch details.
142 // Refer to the respective methods for details on how they use this flag.
143 FCIMPL0(VOID, ExceptionNative::PrepareForForeignExceptionRaise)
147 PTR_ThreadExceptionState pCurTES = GetThread()->GetExceptionState();
149 // Set a flag against the TES to indicate this is a foreign exception raise.
150 pCurTES->SetRaisingForeignException();
154 // Given an exception object, this method will extract the stacktrace and dynamic method array and set them up for return to the caller.
155 FCIMPL3(VOID, ExceptionNative::GetStackTracesDeepCopy, Object* pExceptionObjectUnsafe, Object **pStackTraceUnsafe, Object **pDynamicMethodsUnsafe);
163 ASSERT(pExceptionObjectUnsafe != NULL);
164 ASSERT(pStackTraceUnsafe != NULL);
165 ASSERT(pDynamicMethodsUnsafe != NULL);
169 StackTraceArray stackTrace;
170 StackTraceArray stackTraceCopy;
171 EXCEPTIONREF refException;
172 PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
173 PTRARRAYREF dynamicMethodsArrayCopy; // Copy of the object array of Managed Resolvers
176 ZeroMemory(&gc, sizeof(gc));
178 // GC protect the array reference
179 HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
181 // Get the exception object reference
182 gc.refException = (EXCEPTIONREF)(ObjectToOBJECTREF(pExceptionObjectUnsafe));
184 // Fetch the stacktrace details from the exception under a lock
185 gc.refException->GetStackTrace(gc.stackTrace, &gc.dynamicMethodsArray);
187 bool fHaveStackTrace = false;
188 bool fHaveDynamicMethodArray = false;
190 if ((unsigned)gc.stackTrace.Size() > 0)
192 // Deepcopy the array
193 gc.stackTraceCopy.CopyFrom(gc.stackTrace);
194 fHaveStackTrace = true;
197 if (gc.dynamicMethodsArray != NULL)
199 // Get the number of elements in the dynamic methods array
200 unsigned cOrigDynamic = gc.dynamicMethodsArray->GetNumComponents();
202 // ..and allocate a new array. This can trigger GC or throw under OOM.
203 gc.dynamicMethodsArrayCopy = (PTRARRAYREF)AllocateObjectArray(cOrigDynamic, g_pObjectClass);
205 // Deepcopy references to the new array we just allocated
206 memmoveGCRefs(gc.dynamicMethodsArrayCopy->GetDataPtr(), gc.dynamicMethodsArray->GetDataPtr(),
207 cOrigDynamic * sizeof(Object *));
209 fHaveDynamicMethodArray = true;
213 *pStackTraceUnsafe = fHaveStackTrace?OBJECTREFToObject(gc.stackTraceCopy.Get()):NULL;
214 *pDynamicMethodsUnsafe = fHaveDynamicMethodArray?OBJECTREFToObject(gc.dynamicMethodsArrayCopy):NULL;
216 HELPER_METHOD_FRAME_END();
220 // Given an exception object and deep copied instances of a stacktrace and/or dynamic method array, this method will set the latter in the exception object instance.
221 FCIMPL3(VOID, ExceptionNative::SaveStackTracesFromDeepCopy, Object* pExceptionObjectUnsafe, Object *pStackTraceUnsafe, Object *pDynamicMethodsUnsafe);
229 ASSERT(pExceptionObjectUnsafe != NULL);
233 StackTraceArray stackTrace;
234 EXCEPTIONREF refException;
235 PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
238 ZeroMemory(&gc, sizeof(gc));
240 // GC protect the array reference
241 HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
243 // Get the exception object reference
244 gc.refException = (EXCEPTIONREF)(ObjectToOBJECTREF(pExceptionObjectUnsafe));
246 if (pStackTraceUnsafe != NULL)
248 // Copy the stacktrace
249 StackTraceArray stackTraceArray((I1ARRAYREF)ObjectToOBJECTREF(pStackTraceUnsafe));
250 gc.stackTrace.Swap(stackTraceArray);
253 gc.dynamicMethodsArray = NULL;
254 if (pDynamicMethodsUnsafe != NULL)
256 gc.dynamicMethodsArray = (PTRARRAYREF)ObjectToOBJECTREF(pDynamicMethodsUnsafe);
259 // If there is no stacktrace, then there cannot be any dynamic method array. Thus,
260 // save stacktrace only when we have it.
261 if (gc.stackTrace.Size() > 0)
263 // Save the stacktrace details in the exception under a lock
264 gc.refException->SetStackTrace(gc.stackTrace, gc.dynamicMethodsArray);
268 gc.refException->SetNullStackTrace();
271 HELPER_METHOD_FRAME_END();
275 // This method performs a deep copy of the stack trace array.
276 FCIMPL1(Object*, ExceptionNative::CopyStackTrace, Object* pStackTraceUNSAFE)
280 ASSERT(pStackTraceUNSAFE != NULL);
284 StackTraceArray stackTrace;
285 StackTraceArray stackTraceCopy;
286 _gc(I1ARRAYREF refStackTrace)
287 : stackTrace(refStackTrace)
290 _gc gc((I1ARRAYREF)(ObjectToOBJECTREF(pStackTraceUNSAFE)));
292 // GC protect the array reference
293 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
295 // Deepcopy the array
296 gc.stackTraceCopy.CopyFrom(gc.stackTrace);
298 HELPER_METHOD_FRAME_END();
300 return OBJECTREFToObject(gc.stackTraceCopy.Get());
304 // This method performs a deep copy of the dynamic method array.
305 FCIMPL1(Object*, ExceptionNative::CopyDynamicMethods, Object* pDynamicMethodsUNSAFE)
309 ASSERT(pDynamicMethodsUNSAFE != NULL);
313 PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
314 PTRARRAYREF dynamicMethodsArrayCopy; // Copy of the object array of Managed Resolvers
319 ZeroMemory(&gc, sizeof(gc));
320 HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
322 gc.dynamicMethodsArray = (PTRARRAYREF)(ObjectToOBJECTREF(pDynamicMethodsUNSAFE));
324 // Get the number of elements in the array
325 unsigned cOrigDynamic = gc.dynamicMethodsArray->GetNumComponents();
326 // ..and allocate a new array. This can trigger GC or throw under OOM.
327 gc.dynamicMethodsArrayCopy = (PTRARRAYREF)AllocateObjectArray(cOrigDynamic, g_pObjectClass);
329 // Copy references to the new array we just allocated
330 memmoveGCRefs(gc.dynamicMethodsArrayCopy->GetDataPtr(), gc.dynamicMethodsArray->GetDataPtr(),
331 cOrigDynamic * sizeof(Object *));
332 HELPER_METHOD_FRAME_END();
334 return OBJECTREFToObject(gc.dynamicMethodsArrayCopy);
339 BSTR BStrFromString(STRINGREF s)
354 s->RefInterpretGetStringValuesDangerousForGC(&wz, &cch);
356 bstr = SysAllocString(wz);
363 static BSTR GetExceptionDescription(OBJECTREF objException)
370 PRECONDITION( IsException(objException->GetMethodTable()) );
374 BSTR bstrDescription;
376 STRINGREF MessageString = NULL;
377 GCPROTECT_BEGIN(MessageString)
378 GCPROTECT_BEGIN(objException)
381 if (AppX::IsAppXProcess())
383 // In AppX, call Exception.ToString(false, false) which returns a string containing the exception class
384 // name and callstack without file paths/names. This is used for unhandled exception bucketing/analysis.
385 MethodDescCallSite getMessage(METHOD__EXCEPTION__TO_STRING, &objException);
387 ARG_SLOT GetMessageArgs[] =
389 ObjToArgSlot(objException),
390 BoolToArgSlot(false), // needFileLineInfo
391 BoolToArgSlot(false) // needMessage
393 MessageString = getMessage.Call_RetSTRINGREF(GetMessageArgs);
396 #endif // FEATURE_APPX
398 // read Exception.Message property
399 MethodDescCallSite getMessage(METHOD__EXCEPTION__GET_MESSAGE, &objException);
401 ARG_SLOT GetMessageArgs[] = { ObjToArgSlot(objException)};
402 MessageString = getMessage.Call_RetSTRINGREF(GetMessageArgs);
404 // if the message string is empty then use the exception classname.
405 if (MessageString == NULL || MessageString->GetStringLength() == 0) {
407 MethodDescCallSite getClassName(METHOD__EXCEPTION__GET_CLASS_NAME, &objException);
408 ARG_SLOT GetClassNameArgs[] = { ObjToArgSlot(objException)};
409 MessageString = getClassName.Call_RetSTRINGREF(GetClassNameArgs);
410 _ASSERTE(MessageString != NULL && MessageString->GetStringLength() != 0);
414 // Allocate the description BSTR.
415 int DescriptionLen = MessageString->GetStringLength();
416 bstrDescription = SysAllocStringLen(MessageString->GetBuffer(), DescriptionLen);
421 return bstrDescription;
424 static BSTR GetExceptionSource(OBJECTREF objException)
431 PRECONDITION( IsException(objException->GetMethodTable()) );
436 GCPROTECT_BEGIN(objException)
438 // read Exception.Source property
439 MethodDescCallSite getSource(METHOD__EXCEPTION__GET_SOURCE, &objException);
441 ARG_SLOT GetSourceArgs[] = { ObjToArgSlot(objException)};
443 refRetVal = getSource.Call_RetSTRINGREF(GetSourceArgs);
446 return BStrFromString(refRetVal);
449 static void GetExceptionHelp(OBJECTREF objException, BSTR *pbstrHelpFile, DWORD *pdwHelpContext)
456 INJECT_FAULT(COMPlusThrowOM());
457 PRECONDITION(IsException(objException->GetMethodTable()));
458 PRECONDITION(CheckPointer(pbstrHelpFile));
459 PRECONDITION(CheckPointer(pdwHelpContext));
465 GCPROTECT_BEGIN(objException);
467 // read Exception.HelpLink property
468 MethodDescCallSite getHelpLink(METHOD__EXCEPTION__GET_HELP_LINK, &objException);
470 ARG_SLOT GetHelpLinkArgs[] = { ObjToArgSlot(objException)};
471 *pbstrHelpFile = BStrFromString(getHelpLink.Call_RetSTRINGREF(GetHelpLinkArgs));
475 // parse the help file to check for the presence of helpcontext
476 int len = SysStringLen(*pbstrHelpFile);
478 WCHAR *pwstr = *pbstrHelpFile;
480 BOOL fFoundPound = FALSE;
482 for (pos = len - 1; pos >= 0; pos--) {
483 if (pwstr[pos] == W('#')) {
491 int NumberStartPos = -1;
492 BOOL bNumberStarted = FALSE;
493 BOOL bNumberFinished = FALSE;
494 BOOL bInvalidDigitsFound = FALSE;
496 _ASSERTE(pwstr[pos] == W('#'));
498 // Check to see if the string to the right of the pound a valid number.
499 for (pos++; pos < len; pos++) {
500 if (bNumberFinished) {
501 if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
502 bInvalidDigitsFound = TRUE;
506 else if (bNumberStarted) {
507 if (COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
508 bNumberFinished = TRUE;
510 else if (!COMCharacter::nativeIsDigit(pwstr[pos])) {
511 bInvalidDigitsFound = TRUE;
516 if (COMCharacter::nativeIsDigit(pwstr[pos])) {
517 NumberStartPos = pos;
518 bNumberStarted = TRUE;
520 else if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
521 bInvalidDigitsFound = TRUE;
527 if (bNumberStarted && !bInvalidDigitsFound) {
528 // Grab the help context and remove it from the help file.
529 *pdwHelpContext = (DWORD)wtoi(&pwstr[NumberStartPos], len - NumberStartPos);
531 // Allocate a new help file string of the right length.
532 BSTR strOld = *pbstrHelpFile;
533 *pbstrHelpFile = SysAllocStringLen(strOld, PoundPos);
534 SysFreeString(strOld);
542 // NOTE: caller cleans up any partially initialized BSTRs in pED
543 void ExceptionNative::GetExceptionData(OBJECTREF objException, ExceptionData *pED)
550 PRECONDITION(IsException(objException->GetMethodTable()));
551 PRECONDITION(CheckPointer(pED));
555 ZeroMemory(pED, sizeof(ExceptionData));
557 if (objException->GetMethodTable() == g_pStackOverflowExceptionClass) {
558 // In a low stack situation, most everything else in here will fail.
559 // <TODO>@TODO: We're not turning the guard page back on here, yet.</TODO>
560 pED->hr = COR_E_STACKOVERFLOW;
561 pED->bstrDescription = SysAllocString(STACK_OVERFLOW_MESSAGE);
565 GCPROTECT_BEGIN(objException);
566 pED->hr = GetExceptionHResult(objException);
567 pED->bstrDescription = GetExceptionDescription(objException);
568 pED->bstrSource = GetExceptionSource(objException);
569 GetExceptionHelp(objException, &pED->bstrHelpFile, &pED->dwHelpContext);
574 #ifdef FEATURE_COMINTEROP
576 HRESULT SimpleComCallWrapper::IErrorInfo_hr()
579 return GetExceptionHResult(this->GetObjectRef());
582 BSTR SimpleComCallWrapper::IErrorInfo_bstrDescription()
585 return GetExceptionDescription(this->GetObjectRef());
588 BSTR SimpleComCallWrapper::IErrorInfo_bstrSource()
591 return GetExceptionSource(this->GetObjectRef());
594 BSTR SimpleComCallWrapper::IErrorInfo_bstrHelpFile()
599 GetExceptionHelp(this->GetObjectRef(), &bstrHelpFile, &dwHelpContext);
603 DWORD SimpleComCallWrapper::IErrorInfo_dwHelpContext()
608 GetExceptionHelp(this->GetObjectRef(), &bstrHelpFile, &dwHelpContext);
609 SysFreeString(bstrHelpFile);
610 return dwHelpContext;
613 GUID SimpleComCallWrapper::IErrorInfo_guid()
615 LIMITED_METHOD_CONTRACT;
619 #endif // FEATURE_COMINTEROP
621 FCIMPL0(EXCEPTION_POINTERS*, ExceptionNative::GetExceptionPointers)
625 EXCEPTION_POINTERS* retVal = NULL;
627 Thread *pThread = GetThread();
630 if (pThread->IsExceptionInProgress())
632 retVal = pThread->GetExceptionState()->GetExceptionPointers();
639 FCIMPL0(INT32, ExceptionNative::GetExceptionCode)
645 Thread *pThread = GetThread();
648 if (pThread->IsExceptionInProgress())
650 retVal = pThread->GetExceptionState()->GetExceptionCode();
659 // This must be implemented as an FCALL because managed code cannot
660 // swallow a thread abort exception without resetting the abort,
661 // which we don't want to do. Additionally, we can run into deadlocks
662 // if we use the ResourceManager to do resource lookups - it requires
663 // taking managed locks when initializing Globalization & Security,
664 // but a thread abort on a separate thread initializing those same
665 // systems would also do a resource lookup via the ResourceManager.
666 // We've deadlocked in CompareInfo.GetCompareInfo &
667 // Environment.GetResourceString. It's not practical to take all of
668 // our locks within CER's to avoid this problem - just use the CLR's
669 // unmanaged resources.
671 void QCALLTYPE ExceptionNative::GetMessageFromNativeResources(ExceptionMessageKind kind, QCall::StringHandleOnStack retMesg)
679 const WCHAR * wszFallbackString = NULL;
683 hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_THREAD_ABORT);
685 wszFallbackString = W("Thread was being aborted.");
689 case ThreadInterrupted:
690 hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_THREAD_INTERRUPTED);
692 wszFallbackString = W("Thread was interrupted from a waiting state.");
697 hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_OUT_OF_MEMORY);
699 wszFallbackString = W("Insufficient memory to continue the execution of the program.");
704 _ASSERTE(!"Unknown ExceptionMessageKind value!");
707 STRESS_LOG1(LF_BCL, LL_ALWAYS, "LoadResource error: %x", hr);
708 _ASSERTE(wszFallbackString != NULL);
709 retMesg.Set(wszFallbackString);
719 // This method from one primitive array to another based
720 // upon an offset into each an a byte count.
721 FCIMPL5(VOID, Buffer::BlockCopy, ArrayBase *src, int srcOffset, ArrayBase *dst, int dstOffset, int count)
725 // Verify that both the src and dst are Arrays of primitive
727 // <TODO>@TODO: We need to check for booleans</TODO>
728 if (src==NULL || dst==NULL)
729 FCThrowArgumentNullVoid((src==NULL) ? W("src") : W("dst"));
731 SIZE_T srcLen, dstLen;
734 // Use specialized fast path for byte arrays because of it is what Buffer::BlockCopy is
735 // typically used for.
738 MethodTable * pByteArrayMT = g_pByteArrayMT;
739 _ASSERTE(pByteArrayMT != NULL);
741 // Optimization: If src is a byte array, we can
742 // simply set srcLen to GetNumComponents, without having
743 // to call GetComponentSize or verifying GetArrayElementType
744 if (src->GetMethodTable() == pByteArrayMT)
746 srcLen = src->GetNumComponents();
750 srcLen = src->GetNumComponents() * src->GetComponentSize();
752 // We only want to allow arrays of primitives, no Objects.
753 const CorElementType srcET = src->GetArrayElementType();
754 if (!CorTypeInfo::IsPrimitiveType_NoThrow(srcET))
755 FCThrowArgumentVoid(W("src"), W("Arg_MustBePrimArray"));
758 // Optimization: If copying to/from the same array, then
759 // we know that dstLen and srcLen must be the same.
764 else if (dst->GetMethodTable() == pByteArrayMT)
766 dstLen = dst->GetNumComponents();
770 dstLen = dst->GetNumComponents() * dst->GetComponentSize();
771 if (dst->GetMethodTable() != src->GetMethodTable())
773 const CorElementType dstET = dst->GetArrayElementType();
774 if (!CorTypeInfo::IsPrimitiveType_NoThrow(dstET))
775 FCThrowArgumentVoid(W("dest"), W("Arg_MustBePrimArray"));
779 if (srcOffset < 0 || dstOffset < 0 || count < 0) {
780 const wchar_t* str = W("srcOffset");
781 if (dstOffset < 0) str = W("dstOffset");
782 if (count < 0) str = W("count");
783 FCThrowArgumentOutOfRangeVoid(str, W("ArgumentOutOfRange_NeedNonNegNum"));
786 if (srcLen < (SIZE_T)srcOffset + (SIZE_T)count || dstLen < (SIZE_T)dstOffset + (SIZE_T)count) {
787 FCThrowArgumentVoid(NULL, W("Argument_InvalidOffLen"));
790 PTR_BYTE srcPtr = src->GetDataPtr() + srcOffset;
791 PTR_BYTE dstPtr = dst->GetDataPtr() + dstOffset;
793 if ((srcPtr != dstPtr) && (count > 0)) {
794 #if defined(_AMD64_) && !defined(PLATFORM_UNIX)
795 JIT_MemCpy(dstPtr, srcPtr, count);
797 memmove(dstPtr, srcPtr, count);
806 void QCALLTYPE MemoryNative::Clear(void *dst, size_t length)
810 memset(dst, 0, length);
813 FCIMPL3(VOID, MemoryNative::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount)
817 InlinedMemmoveGCRefsHelper(dst, src, byteCount);
823 void QCALLTYPE Buffer::MemMove(void *dst, void *src, size_t length)
827 #if !defined(FEATURE_CORESYSTEM)
828 // Callers of memcpy do expect and handle access violations in some scenarios.
829 // Access violations in the runtime dll are turned into fail fast by the vector exception handler by default.
830 // We need to supress this behavior for CoreCLR using AVInRuntimeImplOkayHolder because of memcpy is statically linked in.
831 AVInRuntimeImplOkayHolder avOk;
834 memmove(dst, src, length);
837 // Returns a bool to indicate if the array is of primitive types or not.
838 FCIMPL1(FC_BOOL_RET, Buffer::IsPrimitiveTypeArray, ArrayBase *arrayUNSAFE)
842 _ASSERTE(arrayUNSAFE != NULL);
844 // Check the type from the contained element's handle
845 TypeHandle elementTH = arrayUNSAFE->GetArrayElementTypeHandle();
846 BOOL fIsPrimitiveTypeArray = CorTypeInfo::IsPrimitiveType_NoThrow(elementTH.GetVerifierCorElementType());
848 FC_RETURN_BOOL(fIsPrimitiveTypeArray);
853 // Gets a particular byte out of the array. The array can't be an array of Objects - it
854 // must be a primitive array.
855 FCIMPL2(FC_UINT8_RET, Buffer::GetByte, ArrayBase *arrayUNSAFE, INT32 index)
859 _ASSERTE(arrayUNSAFE != NULL);
860 _ASSERTE(index >=0 && index < ((INT32)(arrayUNSAFE->GetComponentSize() * arrayUNSAFE->GetNumComponents())));
862 UINT8 bData = *((BYTE*)arrayUNSAFE->GetDataPtr() + index);
867 // Sets a particular byte in an array. The array can't be an array of Objects - it
868 // must be a primitive array.
870 // Semantically the bData argment is of type BYTE but FCallCheckSignature expects the
871 // type to be UINT8 and raises an error if this isn't this case when
872 // COMPlus_ConsistencyCheck is set.
873 FCIMPL3(VOID, Buffer::SetByte, ArrayBase *arrayUNSAFE, INT32 index, UINT8 bData)
877 _ASSERTE(arrayUNSAFE != NULL);
878 _ASSERTE(index >=0 && index < ((INT32)(arrayUNSAFE->GetComponentSize() * arrayUNSAFE->GetNumComponents())));
880 *((BYTE*)arrayUNSAFE->GetDataPtr() + index) = (BYTE) bData;
884 // Returns the length in bytes of an array containing
885 // primitive type elements
886 FCIMPL1(INT32, Buffer::ByteLength, ArrayBase* arrayUNSAFE)
890 _ASSERTE(arrayUNSAFE != NULL);
892 SIZE_T iRetVal = arrayUNSAFE->GetNumComponents() * arrayUNSAFE->GetComponentSize();
894 // This API is explosed both as Buffer.ByteLength and also used indirectly in argument
895 // checks for Buffer.GetByte/SetByte.
897 // If somebody called Get/SetByte on 2GB+ arrays, there is a decent chance that
898 // the computation of the index has overflowed. Thus we intentionally always
899 // throw on 2GB+ arrays in Get/SetByte argument checks (even for indicies <2GB)
900 // to prevent people from running into a trap silently.
901 if (iRetVal > INT32_MAX)
902 FCThrow(kOverflowException);
904 return (INT32)iRetVal;
911 MethodDesc *GCInterface::m_pCacheMethod=NULL;
913 UINT64 GCInterface::m_ulMemPressure = 0;
914 UINT64 GCInterface::m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
915 INT32 GCInterface::m_gc_counts[3] = {0,0,0};
916 CrstStatic GCInterface::m_MemoryPressureLock;
918 UINT64 GCInterface::m_addPressure[NEW_PRESSURE_COUNT] = {0, 0, 0, 0}; // history of memory pressure additions
919 UINT64 GCInterface::m_remPressure[NEW_PRESSURE_COUNT] = {0, 0, 0, 0}; // history of memory pressure removals
921 // incremented after a gen2 GC has been detected,
922 // (m_iteration % NEW_PRESSURE_COUNT) is used as an index into m_addPressure and m_remPressure
923 UINT GCInterface::m_iteration = 0;
925 FCIMPL0(int, GCInterface::GetGcLatencyMode)
929 FC_GC_POLL_NOT_NEEDED();
931 int result = (INT32)GCHeapUtilities::GetGCHeap()->GetGcLatencyMode();
936 FCIMPL1(int, GCInterface::SetGcLatencyMode, int newLatencyMode)
940 FC_GC_POLL_NOT_NEEDED();
942 return GCHeapUtilities::GetGCHeap()->SetGcLatencyMode(newLatencyMode);
946 FCIMPL0(int, GCInterface::GetLOHCompactionMode)
950 FC_GC_POLL_NOT_NEEDED();
952 int result = (INT32)GCHeapUtilities::GetGCHeap()->GetLOHCompactionMode();
957 FCIMPL1(void, GCInterface::SetLOHCompactionMode, int newLOHCompactionyMode)
961 FC_GC_POLL_NOT_NEEDED();
963 GCHeapUtilities::GetGCHeap()->SetLOHCompactionMode(newLOHCompactionyMode);
968 FCIMPL2(FC_BOOL_RET, GCInterface::RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage)
972 FC_GC_POLL_NOT_NEEDED();
974 FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->RegisterForFullGCNotification(gen2Percentage, lohPercentage));
978 FCIMPL0(FC_BOOL_RET, GCInterface::CancelFullGCNotification)
982 FC_GC_POLL_NOT_NEEDED();
983 FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->CancelFullGCNotification());
987 FCIMPL1(int, GCInterface::WaitForFullGCApproach, int millisecondsTimeout)
993 DISABLED(GC_TRIGGERS); // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
1000 //We don't need to check the top end because the GC will take care of that.
1001 HELPER_METHOD_FRAME_BEGIN_RET_0();
1003 DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout);
1004 result = GCHeapUtilities::GetGCHeap()->WaitForFullGCApproach(dwMilliseconds);
1006 HELPER_METHOD_FRAME_END();
1012 FCIMPL1(int, GCInterface::WaitForFullGCComplete, int millisecondsTimeout)
1018 DISABLED(GC_TRIGGERS); // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
1025 //We don't need to check the top end because the GC will take care of that.
1026 HELPER_METHOD_FRAME_BEGIN_RET_0();
1028 DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout);
1029 result = GCHeapUtilities::GetGCHeap()->WaitForFullGCComplete(dwMilliseconds);
1031 HELPER_METHOD_FRAME_END();
1037 /*================================GetGeneration=================================
1038 **Action: Returns the generation in which args->obj is found.
1039 **Returns: The generation in which args->obj is found.
1040 **Arguments: args->obj -- The object to locate.
1041 **Exceptions: ArgumentException if args->obj is null.
1042 ==============================================================================*/
1043 FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE)
1047 if (objUNSAFE == NULL)
1048 FCThrowArgumentNull(W("obj"));
1050 int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE);
1056 /*================================CollectionCount=================================
1057 **Action: Returns the number of collections for this generation since the begining of the life of the process
1058 **Returns: The collection count.
1059 **Arguments: args->generation -- The generation
1060 **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration();
1061 ==============================================================================*/
1062 FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCCount)
1066 //We've already checked this in GC.cs, so we'll just assert it here.
1067 _ASSERTE(generation >= 0);
1069 //We don't need to check the top end because the GC will take care of that.
1070 int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount);
1076 int QCALLTYPE GCInterface::StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC)
1086 retVal = GCHeapUtilities::GetGCHeap()->StartNoGCRegion((ULONGLONG)totalSize,
1089 !!disallowFullBlockingGC);
1096 int QCALLTYPE GCInterface::EndNoGCRegion()
1104 retVal = GCHeapUtilities::GetGCHeap()->EndNoGCRegion();
1111 /*===============================GetGenerationWR================================
1112 **Action: Returns the generation in which the object pointed to by a WeakReference is found.
1114 **Arguments: args->handle -- the OBJECTHANDLE to the object which we're locating.
1115 **Exceptions: ArgumentException if handle points to an object which is not accessible.
1116 ==============================================================================*/
1117 FCIMPL1(int, GCInterface::GetGenerationWR, LPVOID handle)
1123 HELPER_METHOD_FRAME_BEGIN_RET_0();
1126 temp = ObjectFromHandle((OBJECTHANDLE) handle);
1128 COMPlusThrowArgumentNull(W("weak handle"));
1130 iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp));
1132 HELPER_METHOD_FRAME_END();
1138 /*================================GetTotalMemory================================
1139 **Action: Returns the total number of bytes in use
1140 **Returns: The total number of bytes in use
1143 ==============================================================================*/
1144 INT64 QCALLTYPE GCInterface::GetTotalMemory()
1153 iRetVal = (INT64) GCHeapUtilities::GetGCHeap()->GetTotalBytesInUse();
1160 /*==============================Collect=========================================
1161 **Action: Collects all generations <= args->generation
1163 **Arguments: args->generation: The maximum generation to collect
1164 **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration();
1165 ==============================================================================*/
1166 void QCALLTYPE GCInterface::Collect(INT32 generation, INT32 mode)
1172 //We've already checked this in GC.cs, so we'll just assert it here.
1173 _ASSERTE(generation >= -1);
1175 //We don't need to check the top end because the GC will take care of that.
1178 GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, false, mode);
1184 /*==========================WaitForPendingFinalizers============================
1185 **Action: Run all Finalizers that haven't been run.
1188 ==============================================================================*/
1189 void QCALLTYPE GCInterface::WaitForPendingFinalizers()
1195 FinalizerThread::FinalizerThreadWait();
1201 /*===============================GetMaxGeneration===============================
1202 **Action: Returns the largest GC generation
1203 **Returns: The largest GC Generation
1206 ==============================================================================*/
1207 FCIMPL0(int, GCInterface::GetMaxGeneration)
1211 return(INT32)GCHeapUtilities::GetGCHeap()->GetMaxGeneration();
1215 /*===============================GetAllocatedBytesForCurrentThread===============================
1216 **Action: Computes the allocated bytes so far on the current thread
1217 **Returns: The allocated bytes so far on the current thread
1220 ==============================================================================*/
1221 FCIMPL0(INT64, GCInterface::GetAllocatedBytesForCurrentThread)
1225 INT64 currentAllocated = 0;
1226 Thread *pThread = GetThread();
1227 gc_alloc_context* ac = pThread->GetAllocContext();
1228 currentAllocated = ac->alloc_bytes + ac->alloc_bytes_loh - (ac->alloc_limit - ac->alloc_ptr);
1230 return currentAllocated;
1234 /*==============================SuppressFinalize================================
1235 **Action: Indicate that an object's finalizer should not be run by the system
1236 **Arguments: Object of interest
1238 ==============================================================================*/
1239 FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj)
1243 // Checked by the caller
1244 _ASSERTE(obj != NULL);
1246 if (!obj->GetMethodTable ()->HasFinalizer())
1249 GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj);
1255 /*============================ReRegisterForFinalize==============================
1256 **Action: Indicate that an object's finalizer should be run by the system.
1257 **Arguments: Object of interest
1259 ==============================================================================*/
1260 FCIMPL1(void, GCInterface::ReRegisterForFinalize, Object *obj)
1264 // Checked by the caller
1265 _ASSERTE(obj != NULL);
1267 if (obj->GetMethodTable()->HasFinalizer())
1269 HELPER_METHOD_FRAME_BEGIN_1(obj);
1270 GCHeapUtilities::GetGCHeap()->RegisterForFinalization(-1, obj);
1271 HELPER_METHOD_FRAME_END();
1276 FORCEINLINE UINT64 GCInterface::InterlockedAdd (UINT64 *pAugend, UINT64 addend) {
1277 WRAPPER_NO_CONTRACT;
1278 STATIC_CONTRACT_SO_TOLERANT;
1284 oldMemValue = *pAugend;
1285 newMemValue = oldMemValue + addend;
1287 // check for overflow
1288 if (newMemValue < oldMemValue)
1290 newMemValue = UINT64_MAX;
1292 } while (InterlockedCompareExchange64((LONGLONG*) pAugend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue);
1297 FORCEINLINE UINT64 GCInterface::InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend) {
1298 WRAPPER_NO_CONTRACT;
1299 STATIC_CONTRACT_SO_TOLERANT;
1305 oldMemValue = *pMinuend;
1306 newMemValue = oldMemValue - subtrahend;
1308 // check for underflow
1309 if (newMemValue > oldMemValue)
1312 } while (InterlockedCompareExchange64((LONGLONG*) pMinuend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue);
1317 void QCALLTYPE GCInterface::_AddMemoryPressure(UINT64 bytesAllocated)
1321 // AddMemoryPressure could cause a GC, so we need a frame
1323 AddMemoryPressure(bytesAllocated);
1327 void GCInterface::AddMemoryPressure(UINT64 bytesAllocated)
1337 SendEtwAddMemoryPressureEvent(bytesAllocated);
1339 UINT64 newMemValue = InterlockedAdd(&m_ulMemPressure, bytesAllocated);
1341 if (newMemValue > m_ulThreshold)
1343 INT32 gen_collect = 0;
1346 CrstHolder holder(&m_MemoryPressureLock);
1348 // to avoid collecting too often, take the max threshold of the linear and geometric growth
1352 UINT64 bytesAllocatedMax = (UINT64_MAX - m_ulThreshold) / 8;
1354 if (bytesAllocated >= bytesAllocatedMax) // overflow check
1356 addMethod = UINT64_MAX;
1360 addMethod = m_ulThreshold + bytesAllocated * 8;
1363 multMethod = newMemValue + newMemValue / 10;
1364 if (multMethod < newMemValue) // overflow check
1366 multMethod = UINT64_MAX;
1369 m_ulThreshold = (addMethod > multMethod) ? addMethod : multMethod;
1370 for (int i = 0; i <= 1; i++)
1372 if ((GCHeapUtilities::GetGCHeap()->CollectionCount(i) / RELATIVE_GC_RATIO) > GCHeapUtilities::GetGCHeap()->CollectionCount(i + 1))
1374 gen_collect = i + 1;
1380 PREFIX_ASSUME(gen_collect <= 2);
1382 if ((gen_collect == 0) || (m_gc_counts[gen_collect] == GCHeapUtilities::GetGCHeap()->CollectionCount(gen_collect)))
1384 GarbageCollectModeAny(gen_collect);
1387 for (int i = 0; i < 3; i++)
1389 m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i);
1395 const unsigned MIN_MEMORYPRESSURE_BUDGET = 4 * 1024 * 1024; // 4 MB
1397 const unsigned MIN_MEMORYPRESSURE_BUDGET = 3 * 1024 * 1024; // 3 MB
1400 const unsigned MAX_MEMORYPRESSURE_RATIO = 10; // 40 MB or 30 MB
1403 // Resets pressure accounting after a gen2 GC has occurred.
1404 void GCInterface::CheckCollectionCount()
1406 LIMITED_METHOD_CONTRACT;
1408 IGCHeap * pHeap = GCHeapUtilities::GetGCHeap();
1410 if (m_gc_counts[2] != pHeap->CollectionCount(2))
1412 for (int i = 0; i < 3; i++)
1414 m_gc_counts[i] = pHeap->CollectionCount(i);
1419 UINT p = m_iteration % NEW_PRESSURE_COUNT;
1421 m_addPressure[p] = 0; // new pressure will be accumulated here
1422 m_remPressure[p] = 0;
1427 // New AddMemoryPressure implementation (used by RCW and the CLRServicesImpl class)
1429 // 1. Less sensitive than the original implementation (start budget 3 MB)
1430 // 2. Focuses more on newly added memory pressure
1431 // 3. Budget adjusted by effectiveness of last 3 triggered GC (add / remove ratio, max 10x)
1432 // 4. Budget maxed with 30% of current managed GC size
1433 // 5. If Gen2 GC is happening naturally, ignore past pressure
1435 // Here's a brief description of the ideal algorithm for Add/Remove memory pressure:
1436 // Do a GC when (HeapStart < X * MemPressureGrowth) where
1437 // - HeapStart is GC Heap size after doing the last GC
1438 // - MemPressureGrowth is the net of Add and Remove since the last GC
1439 // - X is proportional to our guess of the ummanaged memory death rate per GC interval,
1440 // and would be calculated based on historic data using standard exponential approximation:
1441 // Xnew = UMDeath/UMTotal * 0.5 + Xprev
1443 void GCInterface::NewAddMemoryPressure(UINT64 bytesAllocated)
1453 CheckCollectionCount();
1455 UINT p = m_iteration % NEW_PRESSURE_COUNT;
1457 UINT64 newMemValue = InterlockedAdd(&m_addPressure[p], bytesAllocated);
1459 static_assert(NEW_PRESSURE_COUNT == 4, "NewAddMemoryPressure contains unrolled loops which depend on NEW_PRESSURE_COUNT");
1461 UINT64 add = m_addPressure[0] + m_addPressure[1] + m_addPressure[2] + m_addPressure[3] - m_addPressure[p];
1462 UINT64 rem = m_remPressure[0] + m_remPressure[1] + m_remPressure[2] + m_remPressure[3] - m_remPressure[p];
1464 STRESS_LOG4(LF_GCINFO, LL_INFO10000, "AMP Add: %I64u => added=%I64u total_added=%I64u total_removed=%I64u",
1465 bytesAllocated, newMemValue, add, rem);
1467 SendEtwAddMemoryPressureEvent(bytesAllocated);
1469 if (newMemValue >= MIN_MEMORYPRESSURE_BUDGET)
1471 UINT64 budget = MIN_MEMORYPRESSURE_BUDGET;
1473 if (m_iteration >= NEW_PRESSURE_COUNT) // wait until we have enough data points
1475 // Adjust according to effectiveness of GC
1476 // Scale budget according to past m_addPressure / m_remPressure ratio
1477 if (add >= rem * MAX_MEMORYPRESSURE_RATIO)
1479 budget = MIN_MEMORYPRESSURE_BUDGET * MAX_MEMORYPRESSURE_RATIO;
1483 CONSISTENCY_CHECK(rem != 0);
1485 // Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024)
1486 budget = (add * 1024 / rem) * budget / 1024;
1490 // If still over budget, check current managed heap size
1491 if (newMemValue >= budget)
1493 IGCHeap *pGCHeap = GCHeapUtilities::GetGCHeap();
1494 UINT64 heapOver3 = pGCHeap->GetCurrentObjSize() / 3;
1496 if (budget < heapOver3) // Max
1501 if (newMemValue >= budget)
1503 // last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time
1504 if ((pGCHeap->GetNow() - pGCHeap->GetLastGCStartTime(2)) > (pGCHeap->GetLastGCDuration(2) * 5))
1506 STRESS_LOG6(LF_GCINFO, LL_INFO10000, "AMP Budget: pressure=%I64u ? budget=%I64u (total_added=%I64u, total_removed=%I64u, mng_heap=%I64u) pos=%d",
1507 newMemValue, budget, add, rem, heapOver3 * 3, m_iteration);
1509 GarbageCollectModeAny(2);
1511 CheckCollectionCount();
1518 void QCALLTYPE GCInterface::_RemoveMemoryPressure(UINT64 bytesAllocated)
1523 RemoveMemoryPressure(bytesAllocated);
1527 void GCInterface::RemoveMemoryPressure(UINT64 bytesAllocated)
1537 SendEtwRemoveMemoryPressureEvent(bytesAllocated);
1539 UINT64 newMemValue = InterlockedSub(&m_ulMemPressure, bytesAllocated);
1541 UINT64 bytesAllocatedMax = (m_ulThreshold / 4);
1543 UINT64 multMethod = (m_ulThreshold - m_ulThreshold / 20); // can never underflow
1544 if (bytesAllocated >= bytesAllocatedMax) // protect against underflow
1546 m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
1551 addMethod = m_ulThreshold - bytesAllocated * 4;
1554 new_th = (addMethod < multMethod) ? addMethod : multMethod;
1556 if (newMemValue <= new_th)
1559 CrstHolder holder(&m_MemoryPressureLock);
1560 if (new_th > MIN_GC_MEMORYPRESSURE_THRESHOLD)
1561 m_ulThreshold = new_th;
1563 m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
1565 for (int i = 0; i < 3; i++)
1567 m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i);
1572 void GCInterface::NewRemoveMemoryPressure(UINT64 bytesAllocated)
1582 CheckCollectionCount();
1584 UINT p = m_iteration % NEW_PRESSURE_COUNT;
1586 SendEtwRemoveMemoryPressureEvent(bytesAllocated);
1588 InterlockedAdd(&m_remPressure[p], bytesAllocated);
1590 STRESS_LOG2(LF_GCINFO, LL_INFO10000, "AMP Remove: %I64u => removed=%I64u",
1591 bytesAllocated, m_remPressure[p]);
1594 inline void GCInterface::SendEtwAddMemoryPressureEvent(UINT64 bytesAllocated)
1604 FireEtwIncreaseMemoryPressure(bytesAllocated, GetClrInstanceId());
1607 // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw.
1608 NOINLINE void GCInterface::SendEtwRemoveMemoryPressureEvent(UINT64 bytesAllocated)
1620 FireEtwDecreaseMemoryPressure(bytesAllocated, GetClrInstanceId());
1626 EX_END_CATCH(SwallowAllExceptions)
1629 // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw.
1630 NOINLINE void GCInterface::GarbageCollectModeAny(int generation)
1641 GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, false, collection_non_blocking);
1648 #include <optsmallperfcritical.h>
1650 FCIMPL2(INT32,COMInterlocked::Exchange, INT32 *location, INT32 value)
1654 if( NULL == location) {
1655 FCThrow(kNullReferenceException);
1658 return FastInterlockExchange((LONG *) location, value);
1662 FCIMPL2_IV(INT64,COMInterlocked::Exchange64, INT64 *location, INT64 value)
1666 if( NULL == location) {
1667 FCThrow(kNullReferenceException);
1670 return FastInterlockExchangeLong((INT64 *) location, value);
1674 FCIMPL2(LPVOID,COMInterlocked::ExchangePointer, LPVOID *location, LPVOID value)
1678 if( NULL == location) {
1679 FCThrow(kNullReferenceException);
1683 return FastInterlockExchangePointer(location, value);
1687 FCIMPL3(INT32, COMInterlocked::CompareExchange, INT32* location, INT32 value, INT32 comparand)
1691 if( NULL == location) {
1692 FCThrow(kNullReferenceException);
1695 return FastInterlockCompareExchange((LONG*)location, value, comparand);
1699 FCIMPL4(INT32, COMInterlocked::CompareExchangeReliableResult, INT32* location, INT32 value, INT32 comparand, CLR_BOOL* succeeded)
1703 if( NULL == location) {
1704 FCThrow(kNullReferenceException);
1707 INT32 result = FastInterlockCompareExchange((LONG*)location, value, comparand);
1708 if (result == comparand)
1715 FCIMPL3_IVV(INT64, COMInterlocked::CompareExchange64, INT64* location, INT64 value, INT64 comparand)
1719 if( NULL == location) {
1720 FCThrow(kNullReferenceException);
1723 return FastInterlockCompareExchangeLong((INT64*)location, value, comparand);
1727 FCIMPL3(LPVOID,COMInterlocked::CompareExchangePointer, LPVOID *location, LPVOID value, LPVOID comparand)
1731 if( NULL == location) {
1732 FCThrow(kNullReferenceException);
1736 return FastInterlockCompareExchangePointer(location, value, comparand);
1740 FCIMPL2_IV(float,COMInterlocked::ExchangeFloat, float *location, float value)
1744 if( NULL == location) {
1745 FCThrow(kNullReferenceException);
1748 LONG ret = FastInterlockExchange((LONG *) location, *(LONG*)&value);
1749 return *(float*)&ret;
1753 FCIMPL2_IV(double,COMInterlocked::ExchangeDouble, double *location, double value)
1757 if( NULL == location) {
1758 FCThrow(kNullReferenceException);
1762 INT64 ret = FastInterlockExchangeLong((INT64 *) location, *(INT64*)&value);
1763 return *(double*)&ret;
1767 FCIMPL3_IVV(float,COMInterlocked::CompareExchangeFloat, float *location, float value, float comparand)
1771 if( NULL == location) {
1772 FCThrow(kNullReferenceException);
1775 LONG ret = (LONG)FastInterlockCompareExchange((LONG*) location, *(LONG*)&value, *(LONG*)&comparand);
1776 return *(float*)&ret;
1780 FCIMPL3_IVV(double,COMInterlocked::CompareExchangeDouble, double *location, double value, double comparand)
1784 if( NULL == location) {
1785 FCThrow(kNullReferenceException);
1788 INT64 ret = (INT64)FastInterlockCompareExchangeLong((INT64*) location, *(INT64*)&value, *(INT64*)&comparand);
1789 return *(double*)&ret;
1793 FCIMPL2(LPVOID,COMInterlocked::ExchangeObject, LPVOID*location, LPVOID value)
1797 if( NULL == location) {
1798 FCThrow(kNullReferenceException);
1801 LPVOID ret = FastInterlockExchangePointer(location, value);
1803 Thread::ObjectRefAssign((OBJECTREF *)location);
1805 ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value));
1810 FCIMPL3(LPVOID,COMInterlocked::CompareExchangeObject, LPVOID *location, LPVOID value, LPVOID comparand)
1814 if( NULL == location) {
1815 FCThrow(kNullReferenceException);
1818 // <TODO>@todo: only set ref if is updated</TODO>
1819 LPVOID ret = FastInterlockCompareExchangePointer(location, value, comparand);
1820 if (ret == comparand) {
1822 Thread::ObjectRefAssign((OBJECTREF *)location);
1824 ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value));
1830 FCIMPL2(INT32,COMInterlocked::ExchangeAdd32, INT32 *location, INT32 value)
1834 if( NULL == location) {
1835 FCThrow(kNullReferenceException);
1838 return FastInterlockExchangeAdd((LONG *) location, value);
1842 FCIMPL2_IV(INT64,COMInterlocked::ExchangeAdd64, INT64 *location, INT64 value)
1846 if( NULL == location) {
1847 FCThrow(kNullReferenceException);
1850 return FastInterlockExchangeAddLong((INT64 *) location, value);
1854 FCIMPL0(void, COMInterlocked::FCMemoryBarrier)
1863 #include <optdefault.h>
1865 void QCALLTYPE COMInterlocked::MemoryBarrierProcessWide()
1869 FlushProcessWriteBuffers();
1872 static BOOL HasOverriddenMethod(MethodTable* mt, MethodTable* classMT, WORD methodSlot)
1881 _ASSERTE(mt != NULL);
1882 _ASSERTE(classMT != NULL);
1883 _ASSERTE(methodSlot != 0);
1885 PCODE actual = mt->GetRestoredSlot(methodSlot);
1886 PCODE base = classMT->GetRestoredSlot(methodSlot);
1893 if (!classMT->IsZapped())
1895 // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs
1896 // to detect match reliably
1897 if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
1906 static BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
1915 _ASSERTE(mt != NULL);
1917 if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
1919 return mt->CanCompareBitsOrUseFastGetHashCode();
1922 if (mt->ContainsPointers()
1923 || mt->IsNotTightlyPacked())
1925 mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
1929 MethodTable* valueTypeMT = MscorlibBinder::GetClass(CLASS__VALUE_TYPE);
1930 WORD slotEquals = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__EQUALS)->GetSlot();
1931 WORD slotGetHashCode = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__GET_HASH_CODE)->GetSlot();
1933 // Check the input type.
1934 if (HasOverriddenMethod(mt, valueTypeMT, slotEquals)
1935 || HasOverriddenMethod(mt, valueTypeMT, slotGetHashCode))
1937 mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
1939 // If overridden Equals or GetHashCode found, stop searching further.
1943 BOOL canCompareBitsOrUseFastGetHashCode = TRUE;
1945 // The type itself did not override Equals or GetHashCode, go for its fields.
1946 ApproxFieldDescIterator iter = ApproxFieldDescIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS);
1947 for (FieldDesc* pField = iter.Next(); pField != NULL; pField = iter.Next())
1949 if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
1951 // Check current field type.
1952 MethodTable* fieldMethodTable = pField->GetApproxFieldTypeHandleThrowing().GetMethodTable();
1953 if (!CanCompareBitsOrUseFastGetHashCode(fieldMethodTable))
1955 canCompareBitsOrUseFastGetHashCode = FALSE;
1959 else if (pField->GetFieldType() == ELEMENT_TYPE_R8
1960 || pField->GetFieldType() == ELEMENT_TYPE_R4)
1962 // We have double/single field, cannot compare in fast path.
1963 canCompareBitsOrUseFastGetHashCode = FALSE;
1968 // We've gone through all instance fields. It's time to cache the result.
1969 // Note SetCanCompareBitsOrUseFastGetHashCode(BOOL) ensures the checked flag
1970 // and canCompare flag being set atomically to avoid race.
1971 mt->SetCanCompareBitsOrUseFastGetHashCode(canCompareBitsOrUseFastGetHashCode);
1973 return canCompareBitsOrUseFastGetHashCode;
1976 NOINLINE static FC_BOOL_RET CanCompareBitsHelper(MethodTable* mt, OBJECTREF objRef)
1978 FC_INNER_PROLOG(ValueTypeHelper::CanCompareBits);
1980 _ASSERTE(mt != NULL);
1981 _ASSERTE(objRef != NULL);
1985 HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
1987 ret = CanCompareBitsOrUseFastGetHashCode(mt);
1989 HELPER_METHOD_FRAME_END();
1992 FC_RETURN_BOOL(ret);
1995 // Return true if the valuetype does not contain pointer, is tightly packed,
1996 // does not have floating point number field and does not override Equals method.
1997 FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
2001 _ASSERTE(obj != NULL);
2002 MethodTable* mt = obj->GetMethodTable();
2004 if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2006 FC_RETURN_BOOL(mt->CanCompareBitsOrUseFastGetHashCode());
2009 OBJECTREF objRef(obj);
2011 FC_INNER_RETURN(FC_BOOL_RET, CanCompareBitsHelper(mt, objRef));
2015 FCIMPL2(FC_BOOL_RET, ValueTypeHelper::FastEqualsCheck, Object* obj1, Object* obj2)
2019 _ASSERTE(obj1 != NULL);
2020 _ASSERTE(obj2 != NULL);
2021 _ASSERTE(!obj1->GetMethodTable()->ContainsPointers());
2022 _ASSERTE(obj1->GetSize() == obj2->GetSize());
2024 TypeHandle pTh = obj1->GetTypeHandle();
2026 FC_RETURN_BOOL(memcmp(obj1->GetData(),obj2->GetData(),pTh.GetSize()) == 0);
2030 static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef)
2041 INT32 *pObj = (INT32*)pObjRef;
2043 // this is a struct with no refs and no "strange" offsets, just go through the obj and xor the bits
2044 INT32 size = mt->GetNumInstanceFieldBytes();
2045 for (INT32 i = 0; i < (INT32)(size / sizeof(INT32)); i++)
2046 hashCode ^= *pObj++;
2051 static INT32 RegularGetValueTypeHashCode(MethodTable *mt, void *pObjRef)
2062 GCPROTECT_BEGININTERIOR(pObjRef);
2064 BOOL canUseFastGetHashCodeHelper = FALSE;
2065 if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2067 canUseFastGetHashCodeHelper = mt->CanCompareBitsOrUseFastGetHashCode();
2071 canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(mt);
2074 // While we shouln't get here directly from ValueTypeHelper::GetHashCode, if we recurse we need to
2075 // be able to handle getting the hashcode for an embedded structure whose hashcode is computed by the fast path.
2076 if (canUseFastGetHashCodeHelper)
2078 hashCode = FastGetValueTypeHashCodeHelper(mt, pObjRef);
2082 // it's looking ugly so we'll use the old behavior in managed code. Grab the first non-null
2083 // field and return its hash code or 'it' as hash code
2084 // <TODO> Note that the old behavior has already been broken for value types
2085 // that is qualified for CanUseFastGetHashCodeHelper. So maybe we should
2086 // change the implementation here to use all fields instead of just the 1st one.
2089 // <TODO> check this approximation - we may be losing exact type information </TODO>
2090 ApproxFieldDescIterator fdIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS);
2093 while ((field = fdIterator.Next()) != NULL)
2095 _ASSERTE(!field->IsRVA());
2096 if (field->IsObjRef())
2098 // if we get an object reference we get the hash code out of that
2099 if (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()) != NULL)
2101 PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__OBJECT__GET_HASH_CODE, (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe())));
2102 DECLARE_ARGHOLDER_ARRAY(args, 1);
2103 args[ARGNUM_0] = PTR_TO_ARGHOLDER(*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2104 CALL_MANAGED_METHOD(hashCode, INT32, args);
2108 // null object reference, try next
2114 CorElementType fieldType = field->GetFieldType();
2115 if (fieldType == ELEMENT_TYPE_R8)
2117 PREPARE_NONVIRTUAL_CALLSITE(METHOD__DOUBLE__GET_HASH_CODE);
2118 DECLARE_ARGHOLDER_ARRAY(args, 1);
2119 args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2120 CALL_MANAGED_METHOD(hashCode, INT32, args);
2122 else if (fieldType == ELEMENT_TYPE_R4)
2124 PREPARE_NONVIRTUAL_CALLSITE(METHOD__SINGLE__GET_HASH_CODE);
2125 DECLARE_ARGHOLDER_ARRAY(args, 1);
2126 args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2127 CALL_MANAGED_METHOD(hashCode, INT32, args);
2129 else if (fieldType != ELEMENT_TYPE_VALUETYPE)
2131 UINT fieldSize = field->LoadSize();
2132 INT32 *pValue = (INT32*)((BYTE *)pObjRef + field->GetOffsetUnsafe());
2133 for (INT32 j = 0; j < (INT32)(fieldSize / sizeof(INT32)); j++)
2134 hashCode ^= *pValue++;
2138 // got another value type. Get the type
2139 TypeHandle fieldTH = field->GetFieldTypeHandleThrowing();
2140 _ASSERTE(!fieldTH.IsNull());
2141 hashCode = RegularGetValueTypeHashCode(fieldTH.GetMethodTable(), (BYTE *)pObjRef + field->GetOffsetUnsafe());
2153 // The default implementation of GetHashCode() for all value types.
2154 // Note that this implementation reveals the value of the fields.
2155 // So if the value type contains any sensitive information it should
2156 // implement its own GetHashCode().
2157 FCIMPL1(INT32, ValueTypeHelper::GetHashCode, Object* objUNSAFE)
2161 if (objUNSAFE == NULL)
2162 FCThrow(kNullReferenceException);
2164 OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE);
2165 VALIDATEOBJECTREF(obj);
2168 MethodTable *pMT = objUNSAFE->GetMethodTable();
2170 // We don't want to expose the method table pointer in the hash code
2171 // Let's use the typeID instead.
2172 UINT32 typeID = pMT->LookupTypeID();
2173 if (typeID == TypeIDProvider::INVALID_TYPE_ID)
2175 // If the typeID has yet to be generated, fall back to GetTypeID
2176 // This only needs to be done once per MethodTable
2177 HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2178 typeID = pMT->GetTypeID();
2179 HELPER_METHOD_FRAME_END();
2182 // To get less colliding and more evenly distributed hash codes,
2183 // we munge the class index with two big prime numbers
2184 hashCode = typeID * 711650207 + 2506965631U;
2186 BOOL canUseFastGetHashCodeHelper = FALSE;
2187 if (pMT->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2189 canUseFastGetHashCodeHelper = pMT->CanCompareBitsOrUseFastGetHashCode();
2193 HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2194 canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(pMT);
2195 HELPER_METHOD_FRAME_END();
2198 if (canUseFastGetHashCodeHelper)
2200 hashCode ^= FastGetValueTypeHashCodeHelper(pMT, obj->UnBox());
2204 HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2205 hashCode ^= RegularGetValueTypeHashCode(pMT, obj->UnBox());
2206 HELPER_METHOD_FRAME_END();
2213 static LONG s_dwSeed;
2215 FCIMPL1(INT32, ValueTypeHelper::GetHashCodeOfPtr, LPVOID ptr)
2219 INT32 hashCode = (INT32)((INT64)(ptr));
2226 DWORD dwSeed = s_dwSeed;
2228 // Initialize s_dwSeed lazily
2231 // We use the first non-0 pointer as the seed, all hashcodes will be based off that.
2232 // This is to make sure that we only reveal relative memory addresses and never absolute ones.
2234 InterlockedCompareExchange(&s_dwSeed, dwSeed, 0);
2237 _ASSERTE(dwSeed != 0);
2239 return hashCode - dwSeed;
2243 static MethodTable * g_pStreamMT;
2244 static WORD g_slotBeginRead, g_slotEndRead;
2245 static WORD g_slotBeginWrite, g_slotEndWrite;
2247 static bool HasOverriddenStreamMethod(MethodTable * pMT, WORD slot)
2256 PCODE actual = pMT->GetRestoredSlot(slot);
2257 PCODE base = g_pStreamMT->GetRestoredSlot(slot);
2261 if (!g_pStreamMT->IsZapped())
2263 // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs
2264 // to detect match reliably
2265 if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
2272 FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndRead, Object *stream)
2277 FC_RETURN_BOOL(TRUE);
2279 if (g_pStreamMT == NULL || g_slotBeginRead == 0 || g_slotEndRead == 0)
2281 HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
2282 g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM);
2283 g_slotBeginRead = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_READ)->GetSlot();
2284 g_slotEndRead = MscorlibBinder::GetMethod(METHOD__STREAM__END_READ)->GetSlot();
2285 HELPER_METHOD_FRAME_END();
2288 MethodTable * pMT = stream->GetMethodTable();
2290 FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginRead) || HasOverriddenStreamMethod(pMT, g_slotEndRead));
2294 FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndWrite, Object *stream)
2299 FC_RETURN_BOOL(TRUE);
2301 if (g_pStreamMT == NULL || g_slotBeginWrite == 0 || g_slotEndWrite == 0)
2303 HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
2304 g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM);
2305 g_slotBeginWrite = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_WRITE)->GetSlot();
2306 g_slotEndWrite = MscorlibBinder::GetMethod(METHOD__STREAM__END_WRITE)->GetSlot();
2307 HELPER_METHOD_FRAME_END();
2310 MethodTable * pMT = stream->GetMethodTable();
2312 FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginWrite) || HasOverriddenStreamMethod(pMT, g_slotEndWrite));