[Tizen] Unify dnetmemoryenumlib terms to match the codebase (#291)
[platform/upstream/coreclr.git] / src / vm / comutilnative.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 //
5
6 //
7
8 /*============================================================
9 **
10 ** File:  COMUtilNative
11 **
12 **  
13 **
14 ** Purpose: A dumping ground for classes which aren't large
15 ** enough to get their own file in the EE.
16 **
17 **
18 **
19 ===========================================================*/
20 #include "common.h"
21 #include "object.h"
22 #include "excep.h"
23 #include "vars.hpp"
24 #include "comutilnative.h"
25
26 #include "utilcode.h"
27 #include "frames.h"
28 #include "field.h"
29 #include "winwrap.h"
30 #include "gcheaputilities.h"
31 #include "fcall.h"
32 #include "invokeutil.h"
33 #include "eeconfig.h"
34 #include "typestring.h"
35 #include "sha1.h"
36 #include "finalizerthread.h"
37 #include "threadsuspend.h"
38
39 #ifdef FEATURE_COMINTEROP
40     #include "comcallablewrapper.h"
41     #include "comcache.h"
42 #endif // FEATURE_COMINTEROP
43
44 #include "arraynative.inl"
45
46 /*===================================IsDigit====================================
47 **Returns a bool indicating whether the character passed in represents a   **
48 **digit.
49 ==============================================================================*/
50 bool IsDigit(WCHAR c, int radix, int *result)
51 {
52     CONTRACTL
53     {
54         NOTHROW;
55         GC_NOTRIGGER;
56         MODE_ANY;
57         PRECONDITION(CheckPointer(result));
58     }
59     CONTRACTL_END;
60
61     if (IS_DIGIT(c)) {
62         *result = DIGIT_TO_INT(c);
63     }
64     else if (c>='A' && c<='Z') {
65         //+10 is necessary because A is actually 10, etc.
66         *result = c-'A'+10;
67     }
68     else if (c>='a' && c<='z') {
69         //+10 is necessary because a is actually 10, etc.
70         *result = c-'a'+10;
71     }
72     else {
73         *result = -1;
74     }
75
76     if ((*result >=0) && (*result < radix))
77         return true;
78
79     return false;
80 }
81
82 INT32 wtoi(__in_ecount(length) WCHAR* wstr, DWORD length)
83 {  
84     CONTRACTL
85     {
86         NOTHROW;
87         GC_NOTRIGGER;
88         MODE_ANY;
89         PRECONDITION(CheckPointer(wstr));
90         PRECONDITION(length >= 0);
91     }
92     CONTRACTL_END;
93
94     DWORD i = 0;
95     int value;
96     INT32 result = 0;
97
98     while ( (i < length) && (IsDigit(wstr[i], 10 ,&value)) ) {
99         //Read all of the digits and convert to a number
100         result = result*10 + value;
101         i++;
102     }
103
104     return result;
105 }
106
107
108
109 //
110 //
111 // EXCEPTION NATIVE
112 //
113 //
114 FCIMPL1(FC_BOOL_RET, ExceptionNative::IsImmutableAgileException, Object* pExceptionUNSAFE)
115 {
116     FCALL_CONTRACT;
117
118     ASSERT(pExceptionUNSAFE != NULL);
119
120     OBJECTREF pException = (OBJECTREF) pExceptionUNSAFE;
121
122     // The preallocated exception objects may be used from multiple AppDomains
123     // and therefore must remain immutable from the application's perspective.
124     FC_RETURN_BOOL(CLRException::IsPreallocatedExceptionObject(pException));
125 }
126 FCIMPLEND
127
128 // This FCall sets a flag against the thread exception state to indicate to
129 // IL_Throw and the StackTraceInfo implementation to account for the fact
130 // that we have restored a foreign exception dispatch details.
131 //
132 // Refer to the respective methods for details on how they use this flag.
133 FCIMPL0(VOID, ExceptionNative::PrepareForForeignExceptionRaise)
134 {
135     FCALL_CONTRACT;
136
137     PTR_ThreadExceptionState pCurTES = GetThread()->GetExceptionState();
138
139         // Set a flag against the TES to indicate this is a foreign exception raise.
140         pCurTES->SetRaisingForeignException();
141 }
142 FCIMPLEND
143
144 // Given an exception object, this method will extract the stacktrace and dynamic method array and set them up for return to the caller.
145 FCIMPL3(VOID, ExceptionNative::GetStackTracesDeepCopy, Object* pExceptionObjectUnsafe, Object **pStackTraceUnsafe, Object **pDynamicMethodsUnsafe);
146 {
147     CONTRACTL
148     {
149         FCALL_CHECK;
150     }
151     CONTRACTL_END;
152
153     ASSERT(pExceptionObjectUnsafe != NULL);
154     ASSERT(pStackTraceUnsafe != NULL);
155     ASSERT(pDynamicMethodsUnsafe != NULL);
156
157     struct _gc
158     {
159         StackTraceArray stackTrace;
160         StackTraceArray stackTraceCopy;
161         EXCEPTIONREF refException;
162         PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
163         PTRARRAYREF dynamicMethodsArrayCopy; // Copy of the object array of Managed Resolvers
164     };
165     _gc gc;
166     ZeroMemory(&gc, sizeof(gc));
167
168     // GC protect the array reference
169     HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
170     
171     // Get the exception object reference
172     gc.refException = (EXCEPTIONREF)(ObjectToOBJECTREF(pExceptionObjectUnsafe));
173
174     // Fetch the stacktrace details from the exception under a lock
175     gc.refException->GetStackTrace(gc.stackTrace, &gc.dynamicMethodsArray);
176     
177     bool fHaveStackTrace = false;
178     bool fHaveDynamicMethodArray = false;
179
180     if ((unsigned)gc.stackTrace.Size() > 0)
181     {
182         // Deepcopy the array
183         gc.stackTraceCopy.CopyFrom(gc.stackTrace);
184         fHaveStackTrace = true;
185     }
186     
187     if (gc.dynamicMethodsArray != NULL)
188     {
189         // Get the number of elements in the dynamic methods array
190         unsigned   cOrigDynamic = gc.dynamicMethodsArray->GetNumComponents();
191     
192         // ..and allocate a new array. This can trigger GC or throw under OOM.
193         gc.dynamicMethodsArrayCopy = (PTRARRAYREF)AllocateObjectArray(cOrigDynamic, g_pObjectClass);
194     
195         // Deepcopy references to the new array we just allocated
196         memmoveGCRefs(gc.dynamicMethodsArrayCopy->GetDataPtr(), gc.dynamicMethodsArray->GetDataPtr(),
197                                                   cOrigDynamic * sizeof(Object *));
198
199         fHaveDynamicMethodArray = true;
200     }
201
202     // Prep to return
203     *pStackTraceUnsafe = fHaveStackTrace?OBJECTREFToObject(gc.stackTraceCopy.Get()):NULL;
204     *pDynamicMethodsUnsafe = fHaveDynamicMethodArray?OBJECTREFToObject(gc.dynamicMethodsArrayCopy):NULL;
205
206     HELPER_METHOD_FRAME_END();
207 }
208 FCIMPLEND
209
210 // 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.
211 FCIMPL3(VOID, ExceptionNative::SaveStackTracesFromDeepCopy, Object* pExceptionObjectUnsafe, Object *pStackTraceUnsafe, Object *pDynamicMethodsUnsafe);
212 {
213     CONTRACTL
214     {
215         FCALL_CHECK;
216     }
217     CONTRACTL_END;
218
219     ASSERT(pExceptionObjectUnsafe != NULL);
220
221     struct _gc
222     {
223         StackTraceArray stackTrace;
224         EXCEPTIONREF refException;
225         PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
226     };
227     _gc gc;
228     ZeroMemory(&gc, sizeof(gc));
229
230     // GC protect the array reference
231     HELPER_METHOD_FRAME_BEGIN_PROTECT(gc);
232     
233     // Get the exception object reference
234     gc.refException = (EXCEPTIONREF)(ObjectToOBJECTREF(pExceptionObjectUnsafe));
235
236     if (pStackTraceUnsafe != NULL)
237     {
238         // Copy the stacktrace
239         StackTraceArray stackTraceArray((I1ARRAYREF)ObjectToOBJECTREF(pStackTraceUnsafe));
240         gc.stackTrace.Swap(stackTraceArray);
241     }
242
243     gc.dynamicMethodsArray = NULL;
244     if (pDynamicMethodsUnsafe != NULL)
245     {
246         gc.dynamicMethodsArray = (PTRARRAYREF)ObjectToOBJECTREF(pDynamicMethodsUnsafe);
247     }
248
249     // If there is no stacktrace, then there cannot be any dynamic method array. Thus,
250     // save stacktrace only when we have it.
251     if (gc.stackTrace.Size() > 0)
252     {
253         // Save the stacktrace details in the exception under a lock
254         gc.refException->SetStackTrace(gc.stackTrace, gc.dynamicMethodsArray);
255     }
256     else
257     {
258         gc.refException->SetNullStackTrace();
259     }
260
261     HELPER_METHOD_FRAME_END();
262 }
263 FCIMPLEND
264
265 // This method performs a deep copy of the stack trace array.
266 FCIMPL1(Object*, ExceptionNative::CopyStackTrace, Object* pStackTraceUNSAFE)
267 {
268     FCALL_CONTRACT;
269
270     ASSERT(pStackTraceUNSAFE != NULL);
271
272     struct _gc
273     {
274         StackTraceArray stackTrace;
275         StackTraceArray stackTraceCopy;
276         _gc(I1ARRAYREF refStackTrace)
277             : stackTrace(refStackTrace)
278         {}
279     };
280     _gc gc((I1ARRAYREF)(ObjectToOBJECTREF(pStackTraceUNSAFE)));
281
282     // GC protect the array reference
283     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
284         
285     // Deepcopy the array
286     gc.stackTraceCopy.CopyFrom(gc.stackTrace);
287     
288     HELPER_METHOD_FRAME_END();
289
290     return OBJECTREFToObject(gc.stackTraceCopy.Get());
291 }
292 FCIMPLEND
293
294 // This method performs a deep copy of the dynamic method array.
295 FCIMPL1(Object*, ExceptionNative::CopyDynamicMethods, Object* pDynamicMethodsUNSAFE)
296 {
297     FCALL_CONTRACT;
298
299     ASSERT(pDynamicMethodsUNSAFE != NULL);
300
301     struct _gc
302     {
303         PTRARRAYREF dynamicMethodsArray; // Object array of Managed Resolvers
304         PTRARRAYREF dynamicMethodsArrayCopy; // Copy of the object array of Managed Resolvers
305         _gc()
306         {}
307     };
308     _gc gc;
309     ZeroMemory(&gc, sizeof(gc));
310     HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);
311     
312     gc.dynamicMethodsArray = (PTRARRAYREF)(ObjectToOBJECTREF(pDynamicMethodsUNSAFE));
313
314     // Get the number of elements in the array
315     unsigned   cOrigDynamic = gc.dynamicMethodsArray->GetNumComponents();
316     // ..and allocate a new array. This can trigger GC or throw under OOM.
317     gc.dynamicMethodsArrayCopy = (PTRARRAYREF)AllocateObjectArray(cOrigDynamic, g_pObjectClass);
318     
319     // Copy references to the new array we just allocated
320     memmoveGCRefs(gc.dynamicMethodsArrayCopy->GetDataPtr(), gc.dynamicMethodsArray->GetDataPtr(),
321                                               cOrigDynamic * sizeof(Object *));
322     HELPER_METHOD_FRAME_END();
323
324     return OBJECTREFToObject(gc.dynamicMethodsArrayCopy);
325 }
326 FCIMPLEND
327
328
329 BSTR BStrFromString(STRINGREF s)
330 {
331     CONTRACTL
332     {
333         THROWS;
334     }
335     CONTRACTL_END;
336
337     WCHAR *wz;
338     int cch;
339     BSTR bstr;
340
341     if (s == NULL)
342         return NULL;
343
344     s->RefInterpretGetStringValuesDangerousForGC(&wz, &cch);
345
346     bstr = SysAllocString(wz);
347     if (bstr == NULL)
348         COMPlusThrowOM();
349
350     return bstr;
351 }
352
353 static BSTR GetExceptionDescription(OBJECTREF objException)
354 {
355     CONTRACTL
356     {
357         THROWS;
358         GC_TRIGGERS;
359         MODE_COOPERATIVE;
360         PRECONDITION( IsException(objException->GetMethodTable()) );
361     }
362     CONTRACTL_END;
363
364     BSTR bstrDescription;
365
366     STRINGREF MessageString = NULL;
367     GCPROTECT_BEGIN(MessageString)
368     GCPROTECT_BEGIN(objException)
369     {
370         // read Exception.Message property
371         MethodDescCallSite getMessage(METHOD__EXCEPTION__GET_MESSAGE, &objException);
372
373         ARG_SLOT GetMessageArgs[] = { ObjToArgSlot(objException)};
374         MessageString = getMessage.Call_RetSTRINGREF(GetMessageArgs);
375
376         // if the message string is empty then use the exception classname.
377         if (MessageString == NULL || MessageString->GetStringLength() == 0) {
378             // call GetClassName
379             MethodDescCallSite getClassName(METHOD__EXCEPTION__GET_CLASS_NAME, &objException);
380             ARG_SLOT GetClassNameArgs[] = { ObjToArgSlot(objException)};
381             MessageString = getClassName.Call_RetSTRINGREF(GetClassNameArgs);
382             _ASSERTE(MessageString != NULL && MessageString->GetStringLength() != 0);
383         }
384
385         // Allocate the description BSTR.
386         int DescriptionLen = MessageString->GetStringLength();
387         bstrDescription = SysAllocStringLen(MessageString->GetBuffer(), DescriptionLen);
388     }
389     GCPROTECT_END();
390     GCPROTECT_END();
391
392     return bstrDescription;
393 }
394
395 static BSTR GetExceptionSource(OBJECTREF objException)
396 {
397     CONTRACTL
398     {
399         THROWS;
400         GC_TRIGGERS;
401         MODE_COOPERATIVE;
402         PRECONDITION( IsException(objException->GetMethodTable()) );
403     }
404     CONTRACTL_END;
405
406     STRINGREF refRetVal;
407     GCPROTECT_BEGIN(objException)
408
409     // read Exception.Source property
410     MethodDescCallSite getSource(METHOD__EXCEPTION__GET_SOURCE, &objException);
411
412     ARG_SLOT GetSourceArgs[] = { ObjToArgSlot(objException)};
413
414     refRetVal = getSource.Call_RetSTRINGREF(GetSourceArgs);
415
416     GCPROTECT_END();
417     return BStrFromString(refRetVal);
418 }
419
420 static void GetExceptionHelp(OBJECTREF objException, BSTR *pbstrHelpFile, DWORD *pdwHelpContext)
421 {
422     CONTRACTL
423     {
424         THROWS;
425         GC_TRIGGERS;
426         MODE_COOPERATIVE;
427         INJECT_FAULT(COMPlusThrowOM());
428         PRECONDITION(IsException(objException->GetMethodTable()));
429         PRECONDITION(CheckPointer(pbstrHelpFile));
430         PRECONDITION(CheckPointer(pdwHelpContext));
431     }
432     CONTRACTL_END;
433
434     *pdwHelpContext = 0;
435
436     GCPROTECT_BEGIN(objException);
437
438     // read Exception.HelpLink property
439     MethodDescCallSite getHelpLink(METHOD__EXCEPTION__GET_HELP_LINK, &objException);
440
441     ARG_SLOT GetHelpLinkArgs[] = { ObjToArgSlot(objException)};
442     *pbstrHelpFile = BStrFromString(getHelpLink.Call_RetSTRINGREF(GetHelpLinkArgs));
443
444     GCPROTECT_END();
445
446     // parse the help file to check for the presence of helpcontext
447     int len = SysStringLen(*pbstrHelpFile);
448     int pos = len;
449     WCHAR *pwstr = *pbstrHelpFile;
450     if (pwstr) {
451         BOOL fFoundPound = FALSE;
452
453         for (pos = len - 1; pos >= 0; pos--) {
454             if (pwstr[pos] == W('#')) {
455                 fFoundPound = TRUE;
456                 break;
457             }
458         }
459
460         if (fFoundPound) {
461             int PoundPos = pos;
462             int NumberStartPos = -1;
463             BOOL bNumberStarted = FALSE;
464             BOOL bNumberFinished = FALSE;
465             BOOL bInvalidDigitsFound = FALSE;
466
467             _ASSERTE(pwstr[pos] == W('#'));
468
469             // Check to see if the string to the right of the pound a valid number.
470             for (pos++; pos < len; pos++) {
471                 if (bNumberFinished) {
472                     if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
473                         bInvalidDigitsFound = TRUE;
474                         break;
475                     }
476                 }
477                 else if (bNumberStarted) {
478                     if (COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
479                         bNumberFinished = TRUE;
480                     }
481                     else if (!COMCharacter::nativeIsDigit(pwstr[pos])) {
482                         bInvalidDigitsFound = TRUE;
483                         break;
484                     }
485                 }
486                 else {
487                     if (COMCharacter::nativeIsDigit(pwstr[pos])) {
488                         NumberStartPos = pos;
489                         bNumberStarted = TRUE;
490                     }
491                     else if (!COMCharacter::nativeIsWhiteSpace(pwstr[pos])) {
492                         bInvalidDigitsFound = TRUE;
493                         break;
494                     }
495                 }
496             }
497
498             if (bNumberStarted && !bInvalidDigitsFound) {
499                 // Grab the help context and remove it from the help file.
500                 *pdwHelpContext = (DWORD)wtoi(&pwstr[NumberStartPos], len - NumberStartPos);
501
502                 // Allocate a new help file string of the right length.
503                 BSTR strOld = *pbstrHelpFile;
504                 *pbstrHelpFile = SysAllocStringLen(strOld, PoundPos);
505                 SysFreeString(strOld);
506                 if (!*pbstrHelpFile)
507                     COMPlusThrowOM();
508             }
509         }
510     }
511 }
512
513 // NOTE: caller cleans up any partially initialized BSTRs in pED
514 void ExceptionNative::GetExceptionData(OBJECTREF objException, ExceptionData *pED)
515 {
516     CONTRACTL
517     {
518         THROWS;
519         GC_TRIGGERS;
520         MODE_COOPERATIVE;
521         PRECONDITION(IsException(objException->GetMethodTable()));
522         PRECONDITION(CheckPointer(pED));
523     }
524     CONTRACTL_END;
525
526     ZeroMemory(pED, sizeof(ExceptionData));
527
528     GCPROTECT_BEGIN(objException);
529     pED->hr = GetExceptionHResult(objException);
530     pED->bstrDescription = GetExceptionDescription(objException);
531     pED->bstrSource = GetExceptionSource(objException);
532     GetExceptionHelp(objException, &pED->bstrHelpFile, &pED->dwHelpContext);
533     GCPROTECT_END();
534     return;
535 }
536
537 #ifdef FEATURE_COMINTEROP
538
539 HRESULT SimpleComCallWrapper::IErrorInfo_hr()
540 {
541     WRAPPER_NO_CONTRACT;
542     return GetExceptionHResult(this->GetObjectRef());
543 }
544
545 BSTR SimpleComCallWrapper::IErrorInfo_bstrDescription()
546 {
547     WRAPPER_NO_CONTRACT;
548     return GetExceptionDescription(this->GetObjectRef());
549 }
550
551 BSTR SimpleComCallWrapper::IErrorInfo_bstrSource()
552 {
553     WRAPPER_NO_CONTRACT;
554     return GetExceptionSource(this->GetObjectRef());
555 }
556
557 BSTR SimpleComCallWrapper::IErrorInfo_bstrHelpFile()
558 {
559     WRAPPER_NO_CONTRACT;
560     BSTR  bstrHelpFile;
561     DWORD dwHelpContext;
562     GetExceptionHelp(this->GetObjectRef(), &bstrHelpFile, &dwHelpContext);
563     return bstrHelpFile;
564 }
565
566 DWORD SimpleComCallWrapper::IErrorInfo_dwHelpContext()
567 {
568     WRAPPER_NO_CONTRACT;
569     BSTR  bstrHelpFile;
570     DWORD dwHelpContext;
571     GetExceptionHelp(this->GetObjectRef(), &bstrHelpFile, &dwHelpContext);
572     SysFreeString(bstrHelpFile);
573     return dwHelpContext;
574 }
575
576 GUID SimpleComCallWrapper::IErrorInfo_guid()
577 {
578     LIMITED_METHOD_CONTRACT;
579     return GUID_NULL;
580 }
581
582 #endif // FEATURE_COMINTEROP
583
584 FCIMPL0(EXCEPTION_POINTERS*, ExceptionNative::GetExceptionPointers)
585 {
586     FCALL_CONTRACT;
587
588     EXCEPTION_POINTERS* retVal = NULL;
589
590     Thread *pThread = GetThread();
591     _ASSERTE(pThread);
592
593     if (pThread->IsExceptionInProgress())
594     {
595         retVal = pThread->GetExceptionState()->GetExceptionPointers();
596     }
597
598     return retVal;
599 }
600 FCIMPLEND
601
602 FCIMPL0(INT32, ExceptionNative::GetExceptionCode)
603 {
604     FCALL_CONTRACT;
605
606     INT32 retVal = 0;
607
608     Thread *pThread = GetThread();
609     _ASSERTE(pThread);
610
611     if (pThread->IsExceptionInProgress())
612     {
613         retVal = pThread->GetExceptionState()->GetExceptionCode();
614     }
615
616     return retVal;
617 }
618 FCIMPLEND
619
620 extern uint32_t g_exceptionCount;
621 FCIMPL0(UINT32, ExceptionNative::GetExceptionCount)
622 {
623     FCALL_CONTRACT;
624     return g_exceptionCount;
625 }
626 FCIMPLEND
627
628
629 //
630 // This must be implemented as an FCALL because managed code cannot
631 // swallow a thread abort exception without resetting the abort,
632 // which we don't want to do.  Additionally, we can run into deadlocks
633 // if we use the ResourceManager to do resource lookups - it requires
634 // taking managed locks when initializing Globalization & Security,
635 // but a thread abort on a separate thread initializing those same
636 // systems would also do a resource lookup via the ResourceManager.
637 // We've deadlocked in CompareInfo.GetCompareInfo &
638 // Environment.GetResourceString.  It's not practical to take all of
639 // our locks within CER's to avoid this problem - just use the CLR's
640 // unmanaged resources.
641 //
642 void QCALLTYPE ExceptionNative::GetMessageFromNativeResources(ExceptionMessageKind kind, QCall::StringHandleOnStack retMesg)
643 {
644     QCALL_CONTRACT;
645
646     BEGIN_QCALL;
647
648     SString buffer;
649     HRESULT hr = S_OK;
650     const WCHAR * wszFallbackString = NULL;
651
652     switch(kind) {
653     case ThreadAbort:
654         hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_THREAD_ABORT);
655         if (FAILED(hr)) {
656             wszFallbackString = W("Thread was being aborted.");
657         }
658         break;
659
660     case ThreadInterrupted:
661         hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_THREAD_INTERRUPTED);
662         if (FAILED(hr)) {
663             wszFallbackString = W("Thread was interrupted from a waiting state.");
664         }
665         break;
666
667     case OutOfMemory:
668         hr = buffer.LoadResourceAndReturnHR(CCompRC::Error, IDS_EE_OUT_OF_MEMORY);
669         if (FAILED(hr)) {
670             wszFallbackString = W("Insufficient memory to continue the execution of the program.");
671         }
672         break;
673
674     default:
675         _ASSERTE(!"Unknown ExceptionMessageKind value!");
676     }
677     if (FAILED(hr)) {       
678         STRESS_LOG1(LF_BCL, LL_ALWAYS, "LoadResource error: %x", hr);
679         _ASSERTE(wszFallbackString != NULL);
680         retMesg.Set(wszFallbackString);
681     }
682     else {
683         retMesg.Set(buffer);
684     }
685
686     END_QCALL;
687 }
688
689 // BlockCopy
690 // This method from one primitive array to another based
691 //  upon an offset into each an a byte count.
692 FCIMPL5(VOID, Buffer::BlockCopy, ArrayBase *src, int srcOffset, ArrayBase *dst, int dstOffset, int count)
693 {
694     FCALL_CONTRACT;
695
696     // Verify that both the src and dst are Arrays of primitive
697     //  types.
698     // <TODO>@TODO: We need to check for booleans</TODO>
699     if (src==NULL || dst==NULL)
700         FCThrowArgumentNullVoid((src==NULL) ? W("src") : W("dst"));
701
702     SIZE_T srcLen, dstLen;
703
704     //
705     // Use specialized fast path for byte arrays because of it is what Buffer::BlockCopy is 
706     // typically used for.
707     //
708
709     MethodTable * pByteArrayMT = g_pByteArrayMT;
710     _ASSERTE(pByteArrayMT != NULL);
711     
712     // Optimization: If src is a byte array, we can
713     // simply set srcLen to GetNumComponents, without having
714     // to call GetComponentSize or verifying GetArrayElementType
715     if (src->GetMethodTable() == pByteArrayMT)
716     {
717         srcLen = src->GetNumComponents();
718     }
719     else
720     {
721         srcLen = src->GetNumComponents() * src->GetComponentSize();
722
723         // We only want to allow arrays of primitives, no Objects.
724         const CorElementType srcET = src->GetArrayElementType();
725         if (!CorTypeInfo::IsPrimitiveType_NoThrow(srcET))
726             FCThrowArgumentVoid(W("src"), W("Arg_MustBePrimArray"));
727     }
728     
729     // Optimization: If copying to/from the same array, then
730     // we know that dstLen and srcLen must be the same.
731     if (src == dst)
732     {
733         dstLen = srcLen;
734     }
735     else if (dst->GetMethodTable() == pByteArrayMT)
736     {
737         dstLen = dst->GetNumComponents();
738     }
739     else
740     {
741         dstLen = dst->GetNumComponents() * dst->GetComponentSize();
742         if (dst->GetMethodTable() != src->GetMethodTable())
743         {
744             const CorElementType dstET = dst->GetArrayElementType();
745             if (!CorTypeInfo::IsPrimitiveType_NoThrow(dstET))
746                 FCThrowArgumentVoid(W("dst"), W("Arg_MustBePrimArray"));
747         }
748     }
749
750     if (srcOffset < 0 || dstOffset < 0 || count < 0) {
751         const wchar_t* str = W("srcOffset");
752         if (dstOffset < 0) str = W("dstOffset");
753         if (count < 0) str = W("count");
754         FCThrowArgumentOutOfRangeVoid(str, W("ArgumentOutOfRange_NeedNonNegNum"));
755     }
756
757     if (srcLen < (SIZE_T)srcOffset + (SIZE_T)count || dstLen < (SIZE_T)dstOffset + (SIZE_T)count) {
758         FCThrowArgumentVoid(NULL, W("Argument_InvalidOffLen"));
759     }
760     
761     PTR_BYTE srcPtr = src->GetDataPtr() + srcOffset;
762     PTR_BYTE dstPtr = dst->GetDataPtr() + dstOffset;
763
764     if ((srcPtr != dstPtr) && (count > 0)) {
765         memmove(dstPtr, srcPtr, count);
766     }
767
768     FC_GC_POLL();
769 }
770 FCIMPLEND
771
772
773 void QCALLTYPE MemoryNative::Clear(void *dst, size_t length)
774 {
775     QCALL_CONTRACT;
776
777 #if defined(_X86_) || defined(_AMD64_)
778     if (length > 0x100)
779     {
780         // memset ends up calling rep stosb if the hardware claims to support it efficiently. rep stosb is up to 2x slower
781         // on misaligned blocks. Workaround this issue by aligning the blocks passed to memset upfront.
782
783         *(uint64_t*)dst = 0;
784         *((uint64_t*)dst + 1) = 0;
785         *((uint64_t*)dst + 2) = 0;
786         *((uint64_t*)dst + 3) = 0;
787
788         void* end = (uint8_t*)dst + length;
789         *((uint64_t*)end - 1) = 0;
790         *((uint64_t*)end - 2) = 0;
791         *((uint64_t*)end - 3) = 0;
792         *((uint64_t*)end - 4) = 0;
793
794         dst = ALIGN_UP((uint8_t*)dst + 1, 32);
795         length = ALIGN_DOWN((uint8_t*)end - 1, 32) - (uint8_t*)dst;
796     }
797 #endif
798
799     memset(dst, 0, length);
800 }
801
802 FCIMPL3(VOID, MemoryNative::BulkMoveWithWriteBarrier, void *dst, void *src, size_t byteCount)
803 {
804     FCALL_CONTRACT;
805
806     InlinedMemmoveGCRefsHelper(dst, src, byteCount);
807
808     FC_GC_POLL();
809 }
810 FCIMPLEND
811
812 void QCALLTYPE Buffer::MemMove(void *dst, void *src, size_t length)
813 {
814     QCALL_CONTRACT;
815
816     memmove(dst, src, length);
817 }
818
819 // Returns a bool to indicate if the array is of primitive types or not.
820 FCIMPL1(FC_BOOL_RET, Buffer::IsPrimitiveTypeArray, ArrayBase *arrayUNSAFE)
821 {
822     FCALL_CONTRACT;
823
824     _ASSERTE(arrayUNSAFE != NULL);
825
826     // Check the type from the contained element's handle
827     TypeHandle elementTH = arrayUNSAFE->GetArrayElementTypeHandle();
828     BOOL fIsPrimitiveTypeArray = CorTypeInfo::IsPrimitiveType_NoThrow(elementTH.GetVerifierCorElementType());
829
830     FC_RETURN_BOOL(fIsPrimitiveTypeArray);
831
832 }
833 FCIMPLEND
834
835 // Returns the length in bytes of an array containing
836 // primitive type elements
837 FCIMPL1(INT32, Buffer::ByteLength, ArrayBase* arrayUNSAFE)
838 {
839     FCALL_CONTRACT;
840
841     _ASSERTE(arrayUNSAFE != NULL);
842
843     SIZE_T iRetVal = arrayUNSAFE->GetNumComponents() * arrayUNSAFE->GetComponentSize();
844
845     // This API is explosed both as Buffer.ByteLength and also used indirectly in argument
846     // checks for Buffer.GetByte/SetByte.
847     //
848     // If somebody called Get/SetByte on 2GB+ arrays, there is a decent chance that 
849     // the computation of the index has overflowed. Thus we intentionally always 
850     // throw on 2GB+ arrays in Get/SetByte argument checks (even for indicies <2GB)
851     // to prevent people from running into a trap silently.
852     if (iRetVal > INT32_MAX)
853         FCThrow(kOverflowException);
854
855     return (INT32)iRetVal;
856 }
857 FCIMPLEND
858
859 //
860 // GCInterface
861 //
862 MethodDesc *GCInterface::m_pCacheMethod=NULL;
863
864 UINT64   GCInterface::m_ulMemPressure = 0;
865 UINT64   GCInterface::m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
866 INT32    GCInterface::m_gc_counts[3] = {0,0,0};
867 CrstStatic GCInterface::m_MemoryPressureLock;
868
869 UINT64   GCInterface::m_addPressure[NEW_PRESSURE_COUNT] = {0, 0, 0, 0};   // history of memory pressure additions
870 UINT64   GCInterface::m_remPressure[NEW_PRESSURE_COUNT] = {0, 0, 0, 0};   // history of memory pressure removals
871
872 // incremented after a gen2 GC has been detected,
873 // (m_iteration % NEW_PRESSURE_COUNT) is used as an index into m_addPressure and m_remPressure
874 UINT     GCInterface::m_iteration = 0;
875
876 FCIMPL6(void, GCInterface::GetMemoryInfo, UINT64* highMemLoadThreshold, UINT64* totalAvailableMemoryBytes, UINT64* lastRecordedMemLoadBytes, UINT32* lastRecordedMemLoadPct, size_t* lastRecordedHeapSizeBytes, size_t* lastRecordedFragmentationBytes)
877 {
878     FCALL_CONTRACT;
879
880     FC_GC_POLL_NOT_NEEDED();
881     
882     return GCHeapUtilities::GetGCHeap()->GetMemoryInfo(highMemLoadThreshold, totalAvailableMemoryBytes,
883                                                        lastRecordedMemLoadBytes, lastRecordedMemLoadPct, 
884                                                        lastRecordedHeapSizeBytes, lastRecordedFragmentationBytes);
885 }
886 FCIMPLEND
887
888 FCIMPL0(int, GCInterface::GetGcLatencyMode)
889 {
890     FCALL_CONTRACT;
891
892     FC_GC_POLL_NOT_NEEDED();
893
894     int result = (INT32)GCHeapUtilities::GetGCHeap()->GetGcLatencyMode();
895     return result;
896 }
897 FCIMPLEND
898
899 FCIMPL1(int, GCInterface::SetGcLatencyMode, int newLatencyMode)
900 {
901     FCALL_CONTRACT;
902
903     FC_GC_POLL_NOT_NEEDED();
904     
905     return GCHeapUtilities::GetGCHeap()->SetGcLatencyMode(newLatencyMode);
906 }
907 FCIMPLEND
908
909 FCIMPL0(int, GCInterface::GetLOHCompactionMode)
910 {
911     FCALL_CONTRACT;
912
913     FC_GC_POLL_NOT_NEEDED();
914
915     int result = (INT32)GCHeapUtilities::GetGCHeap()->GetLOHCompactionMode();
916     return result;
917 }
918 FCIMPLEND
919
920 FCIMPL1(void, GCInterface::SetLOHCompactionMode, int newLOHCompactionyMode)
921 {
922     FCALL_CONTRACT;
923
924     FC_GC_POLL_NOT_NEEDED();
925     
926     GCHeapUtilities::GetGCHeap()->SetLOHCompactionMode(newLOHCompactionyMode);
927 }
928 FCIMPLEND
929
930
931 FCIMPL2(FC_BOOL_RET, GCInterface::RegisterForFullGCNotification, UINT32 gen2Percentage, UINT32 lohPercentage)
932 {
933     FCALL_CONTRACT;
934
935     FC_GC_POLL_NOT_NEEDED();
936
937     FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->RegisterForFullGCNotification(gen2Percentage, lohPercentage));
938 }
939 FCIMPLEND
940
941 FCIMPL0(FC_BOOL_RET, GCInterface::CancelFullGCNotification)
942 {
943     FCALL_CONTRACT;
944
945     FC_GC_POLL_NOT_NEEDED();
946     FC_RETURN_BOOL(GCHeapUtilities::GetGCHeap()->CancelFullGCNotification());
947 }
948 FCIMPLEND
949
950 FCIMPL1(int, GCInterface::WaitForFullGCApproach, int millisecondsTimeout)
951 {
952     CONTRACTL
953     {
954         THROWS;
955         MODE_COOPERATIVE;
956         DISABLED(GC_TRIGGERS);  // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
957     }
958     CONTRACTL_END;
959
960     int result = 0; 
961
962     //We don't need to check the top end because the GC will take care of that.
963     HELPER_METHOD_FRAME_BEGIN_RET_0();
964
965     DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout);
966     result = GCHeapUtilities::GetGCHeap()->WaitForFullGCApproach(dwMilliseconds);
967
968     HELPER_METHOD_FRAME_END();
969
970     return result;
971 }
972 FCIMPLEND
973
974 FCIMPL1(int, GCInterface::WaitForFullGCComplete, int millisecondsTimeout)
975 {
976     CONTRACTL
977     {
978         THROWS;
979         MODE_COOPERATIVE;
980         DISABLED(GC_TRIGGERS);  // can't use this in an FCALL because we're in forbid gc mode until we setup a H_M_F.
981     }
982     CONTRACTL_END;
983
984     int result = 0; 
985
986     //We don't need to check the top end because the GC will take care of that.
987     HELPER_METHOD_FRAME_BEGIN_RET_0();
988
989     DWORD dwMilliseconds = ((millisecondsTimeout == -1) ? INFINITE : millisecondsTimeout);
990     result = GCHeapUtilities::GetGCHeap()->WaitForFullGCComplete(dwMilliseconds);
991
992     HELPER_METHOD_FRAME_END();
993
994     return result;
995 }
996 FCIMPLEND
997
998 /*================================GetGeneration=================================
999 **Action: Returns the generation in which args->obj is found.
1000 **Returns: The generation in which args->obj is found.
1001 **Arguments: args->obj -- The object to locate.
1002 **Exceptions: ArgumentException if args->obj is null.
1003 ==============================================================================*/
1004 FCIMPL1(int, GCInterface::GetGeneration, Object* objUNSAFE)
1005 {
1006     FCALL_CONTRACT;
1007
1008     if (objUNSAFE == NULL)
1009         FCThrowArgumentNull(W("obj"));
1010
1011     int result = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(objUNSAFE);
1012     FC_GC_POLL_RET();
1013     return result;
1014 }
1015 FCIMPLEND
1016
1017 /*================================GetSegmentSize========-=======================
1018 **Action: Returns the maximum GC heap segment size
1019 **Returns: The maximum segment size of either the normal heap or the large object heap, whichever is bigger
1020 ==============================================================================*/
1021 FCIMPL0(UINT64, GCInterface::GetSegmentSize)
1022 {
1023     FCALL_CONTRACT;
1024
1025     IGCHeap * pGC = GCHeapUtilities::GetGCHeap();
1026     size_t segment_size = pGC->GetValidSegmentSize(false);
1027     size_t large_segment_size = pGC->GetValidSegmentSize(true);
1028     _ASSERTE(segment_size < SIZE_T_MAX && large_segment_size < SIZE_T_MAX);
1029     if (segment_size < large_segment_size)
1030         segment_size = large_segment_size;
1031
1032     FC_GC_POLL_RET();
1033     return (UINT64) segment_size;
1034 }
1035 FCIMPLEND
1036
1037 /*================================CollectionCount=================================
1038 **Action: Returns the number of collections for this generation since the begining of the life of the process
1039 **Returns: The collection count.
1040 **Arguments: args->generation -- The generation
1041 **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration();
1042 ==============================================================================*/
1043 FCIMPL2(int, GCInterface::CollectionCount, INT32 generation, INT32 getSpecialGCCount)
1044 {
1045     FCALL_CONTRACT;
1046
1047     //We've already checked this in GC.cs, so we'll just assert it here.
1048     _ASSERTE(generation >= 0);
1049
1050     //We don't need to check the top end because the GC will take care of that.
1051     int result = (INT32)GCHeapUtilities::GetGCHeap()->CollectionCount(generation, getSpecialGCCount);
1052     FC_GC_POLL_RET();
1053     return result;
1054 }
1055 FCIMPLEND
1056
1057 int QCALLTYPE GCInterface::StartNoGCRegion(INT64 totalSize, BOOL lohSizeKnown, INT64 lohSize, BOOL disallowFullBlockingGC)
1058 {
1059     QCALL_CONTRACT;
1060
1061     int retVal = 0;
1062
1063     BEGIN_QCALL;
1064
1065     GCX_COOP();
1066
1067     retVal = GCHeapUtilities::GetGCHeap()->StartNoGCRegion((ULONGLONG)totalSize, 
1068                                                   !!lohSizeKnown,
1069                                                   (ULONGLONG)lohSize,
1070                                                   !!disallowFullBlockingGC);
1071
1072     END_QCALL;
1073
1074     return retVal;
1075 }
1076
1077 int QCALLTYPE GCInterface::EndNoGCRegion()
1078 {
1079     QCALL_CONTRACT;
1080
1081     int retVal = FALSE;
1082
1083     BEGIN_QCALL;
1084
1085     retVal = GCHeapUtilities::GetGCHeap()->EndNoGCRegion();
1086
1087     END_QCALL;
1088
1089     return retVal;
1090 }
1091
1092 /*===============================GetGenerationWR================================
1093 **Action: Returns the generation in which the object pointed to by a WeakReference is found.
1094 **Returns:
1095 **Arguments: args->handle -- the OBJECTHANDLE to the object which we're locating.
1096 **Exceptions: ArgumentException if handle points to an object which is not accessible.
1097 ==============================================================================*/
1098 FCIMPL1(int, GCInterface::GetGenerationWR, LPVOID handle)
1099 {
1100     FCALL_CONTRACT;
1101
1102     int iRetVal = 0;
1103
1104     HELPER_METHOD_FRAME_BEGIN_RET_0();
1105
1106     OBJECTREF temp;
1107     temp = ObjectFromHandle((OBJECTHANDLE) handle);
1108     if (temp == NULL)
1109         COMPlusThrowArgumentNull(W("weak handle"));
1110
1111     iRetVal = (INT32)GCHeapUtilities::GetGCHeap()->WhichGeneration(OBJECTREFToObject(temp));
1112
1113     HELPER_METHOD_FRAME_END();
1114
1115     return iRetVal;
1116 }
1117 FCIMPLEND
1118
1119 FCIMPL0(int, GCInterface::GetLastGCPercentTimeInGC)
1120 {
1121     FCALL_CONTRACT;
1122
1123     return GCHeapUtilities::GetGCHeap()->GetLastGCPercentTimeInGC();
1124 }
1125 FCIMPLEND
1126
1127 FCIMPL1(UINT64, GCInterface::GetGenerationSize, int gen)
1128 {
1129     FCALL_CONTRACT;
1130
1131     return (UINT64)(GCHeapUtilities::GetGCHeap()->GetLastGCGenerationSize(gen));
1132 }
1133 FCIMPLEND
1134
1135 /*================================GetTotalMemory================================
1136 **Action: Returns the total number of bytes in use
1137 **Returns: The total number of bytes in use
1138 **Arguments: None
1139 **Exceptions: None
1140 ==============================================================================*/
1141 INT64 QCALLTYPE GCInterface::GetTotalMemory()
1142 {
1143     QCALL_CONTRACT;
1144
1145     INT64 iRetVal = 0;
1146
1147     BEGIN_QCALL;
1148
1149     GCX_COOP();
1150     iRetVal = (INT64) GCHeapUtilities::GetGCHeap()->GetTotalBytesInUse();
1151
1152     END_QCALL;
1153
1154     return iRetVal;
1155 }
1156
1157 /*==============================Collect=========================================
1158 **Action: Collects all generations <= args->generation
1159 **Returns: void
1160 **Arguments: args->generation:  The maximum generation to collect
1161 **Exceptions: Argument exception if args->generation is < 0 or > GetMaxGeneration();
1162 ==============================================================================*/
1163 void QCALLTYPE GCInterface::Collect(INT32 generation, INT32 mode)
1164 {
1165     QCALL_CONTRACT;
1166
1167     BEGIN_QCALL;
1168
1169     //We've already checked this in GC.cs, so we'll just assert it here.
1170     _ASSERTE(generation >= -1);
1171
1172     //We don't need to check the top end because the GC will take care of that.
1173
1174     GCX_COOP();
1175     GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, false, mode);
1176
1177     END_QCALL;
1178 }
1179
1180
1181 /*==========================WaitForPendingFinalizers============================
1182 **Action: Run all Finalizers that haven't been run.
1183 **Arguments: None
1184 **Exceptions: None
1185 ==============================================================================*/
1186 void QCALLTYPE GCInterface::WaitForPendingFinalizers()
1187 {
1188     QCALL_CONTRACT;
1189
1190     BEGIN_QCALL;
1191
1192     FinalizerThread::FinalizerThreadWait();
1193
1194     END_QCALL;
1195 }
1196
1197
1198 /*===============================GetMaxGeneration===============================
1199 **Action: Returns the largest GC generation
1200 **Returns: The largest GC Generation
1201 **Arguments: None
1202 **Exceptions: None
1203 ==============================================================================*/
1204 FCIMPL0(int, GCInterface::GetMaxGeneration)
1205 {
1206     FCALL_CONTRACT;
1207
1208     return(INT32)GCHeapUtilities::GetGCHeap()->GetMaxGeneration();
1209 }
1210 FCIMPLEND
1211
1212 /*===============================GetAllocatedBytesForCurrentThread===============================
1213 **Action: Computes the allocated bytes so far on the current thread
1214 **Returns: The allocated bytes so far on the current thread
1215 **Arguments: None
1216 **Exceptions: None
1217 ==============================================================================*/
1218 FCIMPL0(INT64, GCInterface::GetAllocatedBytesForCurrentThread)
1219 {
1220     FCALL_CONTRACT;
1221
1222     INT64 currentAllocated = 0;
1223     Thread *pThread = GetThread();
1224     gc_alloc_context* ac = pThread->GetAllocContext();
1225     currentAllocated = ac->alloc_bytes + ac->alloc_bytes_loh - (ac->alloc_limit - ac->alloc_ptr);
1226
1227     return currentAllocated;
1228 }
1229 FCIMPLEND
1230
1231 /*===============================AllocateNewArray===============================
1232 **Action: Allocates a new array object. Allows passing extra flags
1233 **Returns: The allocated array.
1234 **Arguments: elementTypeHandle -> type of the element, 
1235 **           length -> number of elements, 
1236 **           zeroingOptional -> whether caller prefers to skip clearing the content of the array, if possible.
1237 **Exceptions: IDS_EE_ARRAY_DIMENSIONS_EXCEEDED when size is too large. OOM if can't allocate.
1238 ==============================================================================*/
1239 FCIMPL3(Object*, GCInterface::AllocateNewArray, void* arrayTypeHandle, INT32 length, CLR_BOOL zeroingOptional)
1240 {
1241     CONTRACTL {
1242         FCALL_CHECK;
1243         PRECONDITION(length >= 0);
1244     } CONTRACTL_END;
1245
1246     OBJECTREF pRet = NULL;
1247     TypeHandle arrayType = TypeHandle::FromPtr(arrayTypeHandle);
1248
1249     HELPER_METHOD_FRAME_BEGIN_RET_0();
1250
1251     pRet = AllocateSzArray(arrayType, length, zeroingOptional ? GC_ALLOC_ZEROING_OPTIONAL : GC_ALLOC_NO_FLAGS);
1252
1253     HELPER_METHOD_FRAME_END();
1254
1255     return OBJECTREFToObject(pRet);
1256 }
1257 FCIMPLEND
1258
1259
1260 FCIMPL1(INT64, GCInterface::GetTotalAllocatedBytes, CLR_BOOL precise)
1261 {
1262     FCALL_CONTRACT;
1263
1264     if (!precise)
1265     {
1266 #ifdef _TARGET_64BIT_
1267         uint64_t unused_bytes = Thread::dead_threads_non_alloc_bytes;
1268 #else
1269         // As it could be noticed we read 64bit values that may be concurrently updated.
1270         // Such reads are not guaranteed to be atomic on 32bit so extra care should be taken.
1271         uint64_t unused_bytes = FastInterlockCompareExchangeLong((LONG64*)& Thread::dead_threads_non_alloc_bytes, 0, 0);
1272 #endif
1273
1274         uint64_t allocated_bytes = GCHeapUtilities::GetGCHeap()->GetTotalAllocatedBytes() - unused_bytes;
1275
1276         // highest reported allocated_bytes. We do not want to report a value less than that even if unused_bytes has increased.
1277         static uint64_t high_watermark;
1278
1279         uint64_t current_high = high_watermark;
1280         while (allocated_bytes > current_high)
1281         {           
1282             uint64_t orig = FastInterlockCompareExchangeLong((LONG64*)& high_watermark, allocated_bytes, current_high);
1283             if (orig == current_high)
1284                 return allocated_bytes;
1285
1286             current_high = orig;
1287         }
1288
1289         return current_high;
1290     }
1291
1292     INT64 allocated;
1293
1294     HELPER_METHOD_FRAME_BEGIN_RET_0();
1295
1296     // We need to suspend/restart the EE to get each thread's
1297     // non-allocated memory from their allocation contexts
1298
1299     ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_OTHER);
1300
1301     allocated = GCHeapUtilities::GetGCHeap()->GetTotalAllocatedBytes() - Thread::dead_threads_non_alloc_bytes;
1302
1303     for (Thread *pThread = ThreadStore::GetThreadList(NULL); pThread; pThread = ThreadStore::GetThreadList(pThread))
1304     {
1305         gc_alloc_context* ac = pThread->GetAllocContext();
1306         allocated -= ac->alloc_limit - ac->alloc_ptr;
1307     }
1308
1309     ThreadSuspend::RestartEE(FALSE, TRUE);
1310
1311     HELPER_METHOD_FRAME_END();
1312
1313     return allocated;
1314 }
1315 FCIMPLEND;
1316
1317 #ifdef FEATURE_BASICFREEZE
1318
1319 /*===============================RegisterFrozenSegment===============================
1320 **Action: Registers the frozen segment
1321 **Returns: segment_handle
1322 **Arguments: args-> pointer to section, size of section
1323 **Exceptions: None
1324 ==============================================================================*/
1325 void* QCALLTYPE GCInterface::RegisterFrozenSegment(void* pSection, SIZE_T sizeSection)
1326 {
1327     QCALL_CONTRACT;
1328
1329     void* retVal = nullptr;
1330
1331     BEGIN_QCALL;
1332
1333     _ASSERTE(pSection != nullptr);
1334     _ASSERTE(sizeSection > 0);
1335
1336     GCX_COOP();
1337
1338     segment_info seginfo;
1339     seginfo.pvMem           = pSection;
1340     seginfo.ibFirstObject   = sizeof(ObjHeader);
1341     seginfo.ibAllocated     = sizeSection;
1342     seginfo.ibCommit        = seginfo.ibAllocated;
1343     seginfo.ibReserved      = seginfo.ibAllocated;
1344
1345     retVal = (void*)GCHeapUtilities::GetGCHeap()->RegisterFrozenSegment(&seginfo);
1346
1347     END_QCALL;
1348
1349     return retVal;
1350 }
1351
1352 /*===============================UnregisterFrozenSegment===============================
1353 **Action: Unregisters the frozen segment
1354 **Returns: void
1355 **Arguments: args-> segment handle
1356 **Exceptions: None
1357 ==============================================================================*/
1358 void QCALLTYPE GCInterface::UnregisterFrozenSegment(void* segment)
1359 {
1360     QCALL_CONTRACT;
1361
1362     BEGIN_QCALL;
1363
1364     _ASSERTE(segment != nullptr);
1365
1366     GCX_COOP();
1367
1368     GCHeapUtilities::GetGCHeap()->UnregisterFrozenSegment((segment_handle)segment);
1369
1370     END_QCALL;
1371 }
1372
1373 #endif // FEATURE_BASICFREEZE
1374
1375 /*==============================SuppressFinalize================================
1376 **Action: Indicate that an object's finalizer should not be run by the system
1377 **Arguments: Object of interest
1378 **Exceptions: None
1379 ==============================================================================*/
1380 FCIMPL1(void, GCInterface::SuppressFinalize, Object *obj)
1381 {
1382     FCALL_CONTRACT;
1383
1384     // Checked by the caller
1385     _ASSERTE(obj != NULL);
1386
1387     if (!obj->GetMethodTable ()->HasFinalizer())
1388         return;
1389
1390     GCHeapUtilities::GetGCHeap()->SetFinalizationRun(obj);
1391     FC_GC_POLL();
1392 }
1393 FCIMPLEND
1394
1395
1396 /*============================ReRegisterForFinalize==============================
1397 **Action: Indicate that an object's finalizer should be run by the system.
1398 **Arguments: Object of interest
1399 **Exceptions: None
1400 ==============================================================================*/
1401 FCIMPL1(void, GCInterface::ReRegisterForFinalize, Object *obj)
1402 {
1403     FCALL_CONTRACT;
1404
1405     // Checked by the caller
1406     _ASSERTE(obj != NULL);
1407
1408     if (obj->GetMethodTable()->HasFinalizer())
1409     {
1410         HELPER_METHOD_FRAME_BEGIN_1(obj);
1411         if (!GCHeapUtilities::GetGCHeap()->RegisterForFinalization(-1, obj))
1412         {
1413             ThrowOutOfMemory();
1414         }
1415         HELPER_METHOD_FRAME_END();
1416     }
1417 }
1418 FCIMPLEND
1419
1420 FORCEINLINE UINT64 GCInterface::InterlockedAdd (UINT64 *pAugend, UINT64 addend) {
1421     WRAPPER_NO_CONTRACT;
1422
1423     UINT64 oldMemValue;
1424     UINT64 newMemValue;
1425
1426     do {
1427         oldMemValue = *pAugend;
1428         newMemValue = oldMemValue + addend;
1429
1430         // check for overflow
1431         if (newMemValue < oldMemValue)
1432         {
1433             newMemValue = UINT64_MAX;
1434         }
1435     } while (InterlockedCompareExchange64((LONGLONG*) pAugend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue);
1436
1437     return newMemValue;
1438 }
1439
1440 FORCEINLINE UINT64 GCInterface::InterlockedSub(UINT64 *pMinuend, UINT64 subtrahend) {
1441     WRAPPER_NO_CONTRACT;
1442
1443     UINT64 oldMemValue;
1444     UINT64 newMemValue;
1445
1446     do {
1447         oldMemValue = *pMinuend;
1448         newMemValue = oldMemValue - subtrahend;
1449
1450         // check for underflow
1451         if (newMemValue > oldMemValue)
1452             newMemValue = 0;
1453         
1454     } while (InterlockedCompareExchange64((LONGLONG*) pMinuend, (LONGLONG) newMemValue, (LONGLONG) oldMemValue) != (LONGLONG) oldMemValue);
1455
1456     return newMemValue;
1457 }
1458
1459 void QCALLTYPE GCInterface::_AddMemoryPressure(UINT64 bytesAllocated) 
1460 {
1461     QCALL_CONTRACT;
1462
1463     // AddMemoryPressure could cause a GC, so we need a frame 
1464     BEGIN_QCALL;
1465     AddMemoryPressure(bytesAllocated);
1466     END_QCALL;
1467 }
1468
1469 void GCInterface::AddMemoryPressure(UINT64 bytesAllocated)
1470 {
1471     CONTRACTL
1472     {
1473         THROWS;
1474         GC_TRIGGERS;
1475         MODE_ANY;
1476     }
1477     CONTRACTL_END;
1478
1479     SendEtwAddMemoryPressureEvent(bytesAllocated);
1480
1481     UINT64 newMemValue = InterlockedAdd(&m_ulMemPressure, bytesAllocated);
1482
1483     if (newMemValue > m_ulThreshold)
1484     {
1485         INT32 gen_collect = 0;
1486         {
1487             GCX_PREEMP();
1488             CrstHolder holder(&m_MemoryPressureLock);
1489
1490             // to avoid collecting too often, take the max threshold of the linear and geometric growth 
1491             // heuristics.          
1492             UINT64 addMethod;
1493             UINT64 multMethod;
1494             UINT64 bytesAllocatedMax = (UINT64_MAX - m_ulThreshold) / 8;
1495
1496             if (bytesAllocated >= bytesAllocatedMax) // overflow check
1497             {
1498                 addMethod = UINT64_MAX;
1499             }
1500             else
1501             {
1502                 addMethod = m_ulThreshold + bytesAllocated * 8;
1503             }
1504
1505             multMethod = newMemValue + newMemValue / 10;
1506             if (multMethod < newMemValue) // overflow check
1507             {
1508                 multMethod = UINT64_MAX;
1509             }
1510
1511             m_ulThreshold = (addMethod > multMethod) ? addMethod : multMethod;
1512             for (int i = 0; i <= 1; i++)
1513             {
1514                 if ((GCHeapUtilities::GetGCHeap()->CollectionCount(i) / RELATIVE_GC_RATIO) > GCHeapUtilities::GetGCHeap()->CollectionCount(i + 1))
1515                 {
1516                     gen_collect = i + 1;
1517                     break;
1518                 }
1519             }
1520         }
1521
1522         PREFIX_ASSUME(gen_collect <= 2);
1523
1524         if ((gen_collect == 0) || (m_gc_counts[gen_collect] == GCHeapUtilities::GetGCHeap()->CollectionCount(gen_collect)))
1525         {
1526             GarbageCollectModeAny(gen_collect);
1527         }
1528
1529         for (int i = 0; i < 3; i++) 
1530         {
1531             m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i);
1532         }
1533     }
1534 }
1535
1536 #ifdef _WIN64
1537 const unsigned MIN_MEMORYPRESSURE_BUDGET = 4 * 1024 * 1024;        // 4 MB
1538 #else // _WIN64
1539 const unsigned MIN_MEMORYPRESSURE_BUDGET = 3 * 1024 * 1024;        // 3 MB
1540 #endif // _WIN64
1541
1542 const unsigned MAX_MEMORYPRESSURE_RATIO = 10;                      // 40 MB or 30 MB
1543
1544
1545 // Resets pressure accounting after a gen2 GC has occurred.
1546 void GCInterface::CheckCollectionCount()
1547 {
1548     LIMITED_METHOD_CONTRACT;
1549
1550     IGCHeap * pHeap = GCHeapUtilities::GetGCHeap();
1551     
1552     if (m_gc_counts[2] != pHeap->CollectionCount(2))
1553     {
1554         for (int i = 0; i < 3; i++) 
1555         {
1556             m_gc_counts[i] = pHeap->CollectionCount(i);
1557         }
1558
1559         m_iteration++;
1560
1561         UINT p = m_iteration % NEW_PRESSURE_COUNT;
1562
1563         m_addPressure[p] = 0;   // new pressure will be accumulated here
1564         m_remPressure[p] = 0; 
1565     }
1566 }
1567
1568
1569 // New AddMemoryPressure implementation (used by RCW and the CLRServicesImpl class)
1570 //
1571 //   1. Less sensitive than the original implementation (start budget 3 MB)
1572 //   2. Focuses more on newly added memory pressure
1573 //   3. Budget adjusted by effectiveness of last 3 triggered GC (add / remove ratio, max 10x)
1574 //   4. Budget maxed with 30% of current managed GC size
1575 //   5. If Gen2 GC is happening naturally, ignore past pressure
1576 //
1577 // Here's a brief description of the ideal algorithm for Add/Remove memory pressure:
1578 // Do a GC when (HeapStart < X * MemPressureGrowth) where
1579 // - HeapStart is GC Heap size after doing the last GC
1580 // - MemPressureGrowth is the net of Add and Remove since the last GC
1581 // - X is proportional to our guess of the ummanaged memory death rate per GC interval,
1582 //   and would be calculated based on historic data using standard exponential approximation:
1583 //   Xnew = UMDeath/UMTotal * 0.5 + Xprev
1584 //
1585 void GCInterface::NewAddMemoryPressure(UINT64 bytesAllocated)
1586 {
1587     CONTRACTL
1588     {
1589         THROWS;
1590         GC_TRIGGERS;
1591         MODE_ANY;
1592     }
1593     CONTRACTL_END;
1594
1595     CheckCollectionCount();
1596
1597     UINT p = m_iteration % NEW_PRESSURE_COUNT;
1598
1599     UINT64 newMemValue = InterlockedAdd(&m_addPressure[p], bytesAllocated);
1600
1601     static_assert(NEW_PRESSURE_COUNT == 4, "NewAddMemoryPressure contains unrolled loops which depend on NEW_PRESSURE_COUNT");
1602
1603     UINT64 add = m_addPressure[0] + m_addPressure[1] + m_addPressure[2] + m_addPressure[3] - m_addPressure[p];
1604     UINT64 rem = m_remPressure[0] + m_remPressure[1] + m_remPressure[2] + m_remPressure[3] - m_remPressure[p];
1605
1606     STRESS_LOG4(LF_GCINFO, LL_INFO10000, "AMP Add: %I64u => added=%I64u total_added=%I64u total_removed=%I64u",
1607         bytesAllocated, newMemValue, add, rem);
1608
1609     SendEtwAddMemoryPressureEvent(bytesAllocated); 
1610
1611     if (newMemValue >= MIN_MEMORYPRESSURE_BUDGET)
1612     {
1613         UINT64 budget = MIN_MEMORYPRESSURE_BUDGET;
1614
1615         if (m_iteration >= NEW_PRESSURE_COUNT) // wait until we have enough data points
1616         {
1617             // Adjust according to effectiveness of GC
1618             // Scale budget according to past m_addPressure / m_remPressure ratio
1619             if (add >= rem * MAX_MEMORYPRESSURE_RATIO)
1620             {
1621                 budget = MIN_MEMORYPRESSURE_BUDGET * MAX_MEMORYPRESSURE_RATIO;
1622             }
1623             else if (add > rem)
1624             {
1625                 CONSISTENCY_CHECK(rem != 0);
1626
1627                 // Avoid overflow by calculating addPressure / remPressure as fixed point (1 = 1024)
1628                 budget = (add * 1024 / rem) * budget / 1024;
1629             }
1630         }
1631
1632         // If still over budget, check current managed heap size
1633         if (newMemValue >= budget)
1634         {
1635             IGCHeap *pGCHeap = GCHeapUtilities::GetGCHeap();
1636             UINT64 heapOver3 = pGCHeap->GetCurrentObjSize() / 3;
1637
1638             if (budget < heapOver3) // Max
1639             {
1640                 budget = heapOver3;
1641             }
1642
1643             if (newMemValue >= budget)
1644             {
1645                 // last check - if we would exceed 20% of GC "duty cycle", do not trigger GC at this time
1646                 if ((pGCHeap->GetNow() - pGCHeap->GetLastGCStartTime(2)) > (pGCHeap->GetLastGCDuration(2) * 5))
1647                 {
1648                     STRESS_LOG6(LF_GCINFO, LL_INFO10000, "AMP Budget: pressure=%I64u ? budget=%I64u (total_added=%I64u, total_removed=%I64u, mng_heap=%I64u) pos=%d",
1649                         newMemValue, budget, add, rem, heapOver3 * 3, m_iteration);
1650
1651                     GarbageCollectModeAny(2);
1652
1653                     CheckCollectionCount();
1654                 }
1655             }
1656         }
1657     }
1658 }
1659
1660 void QCALLTYPE GCInterface::_RemoveMemoryPressure(UINT64 bytesAllocated)
1661 {
1662     QCALL_CONTRACT;
1663
1664     BEGIN_QCALL;
1665     RemoveMemoryPressure(bytesAllocated);
1666     END_QCALL;
1667 }
1668
1669 void GCInterface::RemoveMemoryPressure(UINT64 bytesAllocated)
1670 {
1671     CONTRACTL
1672     {
1673         NOTHROW;
1674         GC_TRIGGERS;
1675         MODE_ANY;
1676     }
1677     CONTRACTL_END;
1678
1679     SendEtwRemoveMemoryPressureEvent(bytesAllocated);
1680
1681     UINT64 newMemValue = InterlockedSub(&m_ulMemPressure, bytesAllocated);
1682     UINT64 new_th;  
1683     UINT64 bytesAllocatedMax = (m_ulThreshold / 4);
1684     UINT64 addMethod;
1685     UINT64 multMethod = (m_ulThreshold - m_ulThreshold / 20); // can never underflow
1686     if (bytesAllocated >= bytesAllocatedMax) // protect against underflow
1687     {
1688         m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
1689         return;
1690     }
1691     else
1692     {
1693         addMethod = m_ulThreshold - bytesAllocated * 4;
1694     }
1695
1696     new_th = (addMethod < multMethod) ? addMethod : multMethod;
1697
1698     if (newMemValue <= new_th)
1699     {
1700         GCX_PREEMP();
1701         CrstHolder holder(&m_MemoryPressureLock);
1702         if (new_th > MIN_GC_MEMORYPRESSURE_THRESHOLD)
1703             m_ulThreshold = new_th;
1704         else
1705             m_ulThreshold = MIN_GC_MEMORYPRESSURE_THRESHOLD;
1706
1707         for (int i = 0; i < 3; i++) 
1708         {
1709             m_gc_counts [i] = GCHeapUtilities::GetGCHeap()->CollectionCount(i);
1710         }
1711     }
1712 }
1713
1714 void GCInterface::NewRemoveMemoryPressure(UINT64 bytesAllocated)
1715 {
1716     CONTRACTL
1717     {
1718         NOTHROW;
1719         GC_TRIGGERS;
1720         MODE_ANY;
1721     }
1722     CONTRACTL_END;
1723
1724     CheckCollectionCount();
1725     
1726     UINT p = m_iteration % NEW_PRESSURE_COUNT;
1727
1728     SendEtwRemoveMemoryPressureEvent(bytesAllocated);
1729
1730     InterlockedAdd(&m_remPressure[p], bytesAllocated);
1731
1732     STRESS_LOG2(LF_GCINFO, LL_INFO10000, "AMP Remove: %I64u => removed=%I64u",
1733         bytesAllocated, m_remPressure[p]);
1734 }
1735
1736 inline void GCInterface::SendEtwAddMemoryPressureEvent(UINT64 bytesAllocated)
1737 {
1738     CONTRACTL
1739     {
1740         THROWS;
1741         GC_TRIGGERS;
1742         MODE_ANY;
1743     }
1744     CONTRACTL_END;
1745
1746     FireEtwIncreaseMemoryPressure(bytesAllocated, GetClrInstanceId());
1747 }
1748
1749 // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw.
1750 NOINLINE void GCInterface::SendEtwRemoveMemoryPressureEvent(UINT64 bytesAllocated)
1751 {
1752     CONTRACTL
1753     {
1754         NOTHROW;
1755         GC_TRIGGERS;
1756         MODE_ANY;
1757     }
1758     CONTRACTL_END;
1759
1760     EX_TRY
1761     {
1762         FireEtwDecreaseMemoryPressure(bytesAllocated, GetClrInstanceId());
1763     }
1764     EX_CATCH
1765     {
1766         // Ignore failures
1767     }
1768     EX_END_CATCH(SwallowAllExceptions)
1769 }
1770
1771 // Out-of-line helper to avoid EH prolog/epilog in functions that otherwise don't throw.
1772 NOINLINE void GCInterface::GarbageCollectModeAny(int generation)
1773 {
1774     CONTRACTL
1775     {
1776         THROWS;
1777         GC_TRIGGERS;
1778         MODE_ANY;
1779     }
1780     CONTRACTL_END;
1781
1782     GCX_COOP();
1783     GCHeapUtilities::GetGCHeap()->GarbageCollect(generation, false, collection_non_blocking);
1784 }
1785
1786 //
1787 // COMInterlocked
1788 //
1789
1790 #include <optsmallperfcritical.h>
1791
1792 FCIMPL2(INT32,COMInterlocked::Exchange, INT32 *location, INT32 value)
1793 {
1794     FCALL_CONTRACT;
1795
1796     if( NULL == location) {
1797         FCThrow(kNullReferenceException);
1798     }
1799
1800     return FastInterlockExchange((LONG *) location, value);
1801 }
1802 FCIMPLEND
1803
1804 FCIMPL2_IV(INT64,COMInterlocked::Exchange64, INT64 *location, INT64 value)
1805 {
1806     FCALL_CONTRACT;
1807
1808     if( NULL == location) {
1809         FCThrow(kNullReferenceException);
1810     }
1811
1812     return FastInterlockExchangeLong((INT64 *) location, value);
1813 }
1814 FCIMPLEND
1815
1816 FCIMPL2(LPVOID,COMInterlocked::ExchangePointer, LPVOID *location, LPVOID value)
1817 {
1818     FCALL_CONTRACT;
1819
1820     if( NULL == location) {
1821         FCThrow(kNullReferenceException);
1822     }
1823
1824     FCUnique(0x15);
1825     return FastInterlockExchangePointer(location, value);
1826 }
1827 FCIMPLEND
1828
1829 FCIMPL3(INT32, COMInterlocked::CompareExchange, INT32* location, INT32 value, INT32 comparand)
1830 {
1831     FCALL_CONTRACT;
1832
1833     if( NULL == location) {
1834         FCThrow(kNullReferenceException);
1835     }
1836
1837     return FastInterlockCompareExchange((LONG*)location, value, comparand);
1838 }
1839 FCIMPLEND
1840
1841 FCIMPL4(INT32, COMInterlocked::CompareExchangeReliableResult, INT32* location, INT32 value, INT32 comparand, CLR_BOOL* succeeded)
1842 {
1843     FCALL_CONTRACT;
1844
1845     if( NULL == location) {
1846         FCThrow(kNullReferenceException);
1847     }
1848
1849     INT32 result = FastInterlockCompareExchange((LONG*)location, value, comparand);
1850     if (result == comparand)
1851         *succeeded = true;
1852
1853     return result;
1854 }
1855 FCIMPLEND
1856
1857 FCIMPL3_IVV(INT64, COMInterlocked::CompareExchange64, INT64* location, INT64 value, INT64 comparand)
1858 {
1859     FCALL_CONTRACT;
1860
1861     if( NULL == location) {
1862         FCThrow(kNullReferenceException);
1863     }
1864
1865     return FastInterlockCompareExchangeLong((INT64*)location, value, comparand);
1866 }
1867 FCIMPLEND
1868
1869 FCIMPL3(LPVOID,COMInterlocked::CompareExchangePointer, LPVOID *location, LPVOID value, LPVOID comparand)
1870 {
1871     FCALL_CONTRACT;
1872
1873     if( NULL == location) {
1874         FCThrow(kNullReferenceException);
1875     }
1876
1877     FCUnique(0x59);
1878     return FastInterlockCompareExchangePointer(location, value, comparand);
1879 }
1880 FCIMPLEND
1881
1882 FCIMPL2_IV(float,COMInterlocked::ExchangeFloat, float *location, float value)
1883 {
1884     FCALL_CONTRACT;
1885
1886     if( NULL == location) {
1887         FCThrow(kNullReferenceException);
1888     }
1889
1890     LONG ret = FastInterlockExchange((LONG *) location, *(LONG*)&value);
1891     return *(float*)&ret;
1892 }
1893 FCIMPLEND
1894
1895 FCIMPL2_IV(double,COMInterlocked::ExchangeDouble, double *location, double value)
1896 {
1897     FCALL_CONTRACT;
1898
1899     if( NULL == location) {
1900         FCThrow(kNullReferenceException);
1901     }
1902
1903
1904     INT64 ret = FastInterlockExchangeLong((INT64 *) location, *(INT64*)&value);
1905     return *(double*)&ret;
1906 }
1907 FCIMPLEND
1908
1909 FCIMPL3_IVV(float,COMInterlocked::CompareExchangeFloat, float *location, float value, float comparand)
1910 {
1911     FCALL_CONTRACT;
1912
1913     if( NULL == location) {
1914         FCThrow(kNullReferenceException);
1915     }
1916
1917     LONG ret = (LONG)FastInterlockCompareExchange((LONG*) location, *(LONG*)&value, *(LONG*)&comparand);
1918     return *(float*)&ret;
1919 }
1920 FCIMPLEND
1921
1922 FCIMPL3_IVV(double,COMInterlocked::CompareExchangeDouble, double *location, double value, double comparand)
1923 {
1924     FCALL_CONTRACT;
1925
1926     if( NULL == location) {
1927         FCThrow(kNullReferenceException);
1928     }
1929
1930     INT64 ret = (INT64)FastInterlockCompareExchangeLong((INT64*) location, *(INT64*)&value, *(INT64*)&comparand);
1931     return *(double*)&ret;
1932 }
1933 FCIMPLEND
1934
1935 FCIMPL2(LPVOID,COMInterlocked::ExchangeObject, LPVOID*location, LPVOID value)
1936 {
1937     FCALL_CONTRACT;
1938
1939     if( NULL == location) {
1940         FCThrow(kNullReferenceException);
1941     }
1942
1943     LPVOID ret = FastInterlockExchangePointer(location, value);
1944 #ifdef _DEBUG
1945     Thread::ObjectRefAssign((OBJECTREF *)location);
1946 #endif
1947     ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value));
1948     return ret;
1949 }
1950 FCIMPLEND
1951
1952 FCIMPL3(LPVOID,COMInterlocked::CompareExchangeObject, LPVOID *location, LPVOID value, LPVOID comparand)
1953 {
1954     FCALL_CONTRACT;
1955
1956     if( NULL == location) {
1957         FCThrow(kNullReferenceException);
1958     }
1959
1960     // <TODO>@todo: only set ref if is updated</TODO>
1961     LPVOID ret = FastInterlockCompareExchangePointer(location, value, comparand);
1962     if (ret == comparand) {
1963 #ifdef _DEBUG
1964         Thread::ObjectRefAssign((OBJECTREF *)location);
1965 #endif
1966         ErectWriteBarrier((OBJECTREF*) location, ObjectToOBJECTREF((Object*) value));
1967     }
1968     return ret;
1969 }
1970 FCIMPLEND
1971
1972 FCIMPL2(INT32,COMInterlocked::ExchangeAdd32, INT32 *location, INT32 value)
1973 {
1974     FCALL_CONTRACT;
1975
1976     if( NULL == location) {
1977         FCThrow(kNullReferenceException);
1978     }
1979
1980     return FastInterlockExchangeAdd((LONG *) location, value);
1981 }
1982 FCIMPLEND
1983
1984 FCIMPL2_IV(INT64,COMInterlocked::ExchangeAdd64, INT64 *location, INT64 value)
1985 {
1986     FCALL_CONTRACT;
1987
1988     if( NULL == location) {
1989         FCThrow(kNullReferenceException);
1990     }
1991
1992     return FastInterlockExchangeAddLong((INT64 *) location, value);
1993 }
1994 FCIMPLEND
1995
1996 FCIMPL0(void, COMInterlocked::FCMemoryBarrier)
1997 {
1998     FCALL_CONTRACT;
1999
2000     MemoryBarrier();
2001     FC_GC_POLL();
2002 }
2003 FCIMPLEND
2004
2005 #include <optdefault.h>
2006
2007 void QCALLTYPE COMInterlocked::MemoryBarrierProcessWide()
2008 {
2009     QCALL_CONTRACT;
2010
2011     FlushProcessWriteBuffers();
2012 }
2013
2014 static BOOL HasOverriddenMethod(MethodTable* mt, MethodTable* classMT, WORD methodSlot)
2015 {
2016     CONTRACTL{
2017         NOTHROW;
2018         GC_NOTRIGGER;
2019         MODE_ANY;
2020     } CONTRACTL_END;
2021
2022     _ASSERTE(mt != NULL);
2023     _ASSERTE(classMT != NULL);
2024     _ASSERTE(methodSlot != 0);
2025
2026     PCODE actual = mt->GetRestoredSlot(methodSlot);
2027     PCODE base = classMT->GetRestoredSlot(methodSlot);
2028
2029     if (actual == base)
2030     {
2031         return FALSE;
2032     }
2033
2034     if (!classMT->IsZapped())
2035     {
2036         // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs
2037         // to detect match reliably
2038         if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
2039         {
2040             return FALSE;
2041         }
2042     }
2043
2044     return TRUE;
2045 }
2046
2047 static BOOL CanCompareBitsOrUseFastGetHashCode(MethodTable* mt)
2048 {
2049     CONTRACTL
2050     {
2051         THROWS;
2052         GC_TRIGGERS;
2053         MODE_COOPERATIVE;
2054     } CONTRACTL_END;
2055
2056     _ASSERTE(mt != NULL);
2057
2058     if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2059     {
2060         return mt->CanCompareBitsOrUseFastGetHashCode();
2061     }
2062
2063     if (mt->ContainsPointers()
2064         || mt->IsNotTightlyPacked())
2065     {
2066         mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
2067         return FALSE;
2068     }
2069
2070     MethodTable* valueTypeMT = MscorlibBinder::GetClass(CLASS__VALUE_TYPE);
2071     WORD slotEquals = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__EQUALS)->GetSlot();
2072     WORD slotGetHashCode = MscorlibBinder::GetMethod(METHOD__VALUE_TYPE__GET_HASH_CODE)->GetSlot();
2073
2074     // Check the input type.
2075     if (HasOverriddenMethod(mt, valueTypeMT, slotEquals)
2076         || HasOverriddenMethod(mt, valueTypeMT, slotGetHashCode))
2077     {
2078         mt->SetHasCheckedCanCompareBitsOrUseFastGetHashCode();
2079
2080         // If overridden Equals or GetHashCode found, stop searching further.
2081         return FALSE;
2082     }
2083
2084     BOOL canCompareBitsOrUseFastGetHashCode = TRUE;
2085
2086     // The type itself did not override Equals or GetHashCode, go for its fields.
2087     ApproxFieldDescIterator iter = ApproxFieldDescIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS);
2088     for (FieldDesc* pField = iter.Next(); pField != NULL; pField = iter.Next())
2089     {
2090         if (pField->GetFieldType() == ELEMENT_TYPE_VALUETYPE)
2091         {
2092             // Check current field type.
2093             MethodTable* fieldMethodTable = pField->GetApproxFieldTypeHandleThrowing().GetMethodTable();
2094             if (!CanCompareBitsOrUseFastGetHashCode(fieldMethodTable))
2095             {
2096                 canCompareBitsOrUseFastGetHashCode = FALSE;
2097                 break;
2098             }
2099         }
2100         else if (pField->GetFieldType() == ELEMENT_TYPE_R8
2101                 || pField->GetFieldType() == ELEMENT_TYPE_R4)
2102         {
2103             // We have double/single field, cannot compare in fast path.
2104             canCompareBitsOrUseFastGetHashCode = FALSE;
2105             break;
2106         }
2107     }
2108
2109     // We've gone through all instance fields. It's time to cache the result.
2110     // Note SetCanCompareBitsOrUseFastGetHashCode(BOOL) ensures the checked flag
2111     // and canCompare flag being set atomically to avoid race.
2112     mt->SetCanCompareBitsOrUseFastGetHashCode(canCompareBitsOrUseFastGetHashCode);
2113
2114     return canCompareBitsOrUseFastGetHashCode;
2115 }
2116
2117 NOINLINE static FC_BOOL_RET CanCompareBitsHelper(MethodTable* mt, OBJECTREF objRef)
2118 {
2119     FC_INNER_PROLOG(ValueTypeHelper::CanCompareBits);
2120
2121     _ASSERTE(mt != NULL);
2122     _ASSERTE(objRef != NULL);
2123
2124     BOOL ret = FALSE;
2125
2126     HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_1(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2, objRef);
2127
2128     ret = CanCompareBitsOrUseFastGetHashCode(mt);
2129
2130     HELPER_METHOD_FRAME_END();
2131     FC_INNER_EPILOG();
2132
2133     FC_RETURN_BOOL(ret);
2134 }
2135
2136 // Return true if the valuetype does not contain pointer, is tightly packed, 
2137 // does not have floating point number field and does not override Equals method.
2138 FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj)
2139 {
2140     FCALL_CONTRACT;
2141
2142     _ASSERTE(obj != NULL);
2143     MethodTable* mt = obj->GetMethodTable();
2144
2145     if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2146     {
2147         FC_RETURN_BOOL(mt->CanCompareBitsOrUseFastGetHashCode());
2148     }
2149
2150     OBJECTREF objRef(obj);
2151
2152     FC_INNER_RETURN(FC_BOOL_RET, CanCompareBitsHelper(mt, objRef));
2153 }
2154 FCIMPLEND
2155
2156 FCIMPL2(FC_BOOL_RET, ValueTypeHelper::FastEqualsCheck, Object* obj1, Object* obj2)
2157 {
2158     FCALL_CONTRACT;
2159
2160     _ASSERTE(obj1 != NULL);
2161     _ASSERTE(obj2 != NULL);
2162     _ASSERTE(!obj1->GetMethodTable()->ContainsPointers());
2163     _ASSERTE(obj1->GetSize() == obj2->GetSize());
2164
2165     TypeHandle pTh = obj1->GetTypeHandle();
2166
2167     FC_RETURN_BOOL(memcmp(obj1->GetData(),obj2->GetData(),pTh.GetSize()) == 0);
2168 }
2169 FCIMPLEND
2170
2171 static INT32 FastGetValueTypeHashCodeHelper(MethodTable *mt, void *pObjRef)
2172 {
2173     CONTRACTL
2174     {
2175         NOTHROW;
2176         GC_NOTRIGGER;
2177         MODE_COOPERATIVE;
2178     } CONTRACTL_END;
2179
2180     INT32 hashCode = 0;
2181     INT32 *pObj = (INT32*)pObjRef;
2182             
2183     // this is a struct with no refs and no "strange" offsets, just go through the obj and xor the bits
2184     INT32 size = mt->GetNumInstanceFieldBytes();
2185     for (INT32 i = 0; i < (INT32)(size / sizeof(INT32)); i++)
2186         hashCode ^= *pObj++;
2187
2188     return hashCode;
2189 }
2190
2191 static INT32 RegularGetValueTypeHashCode(MethodTable *mt, void *pObjRef)
2192 {
2193     CONTRACTL
2194     {
2195         THROWS;
2196         GC_TRIGGERS;
2197         MODE_COOPERATIVE;
2198     } CONTRACTL_END;
2199
2200     INT32 hashCode = 0;
2201
2202     GCPROTECT_BEGININTERIOR(pObjRef);
2203
2204     BOOL canUseFastGetHashCodeHelper = FALSE;
2205     if (mt->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2206     {
2207         canUseFastGetHashCodeHelper = mt->CanCompareBitsOrUseFastGetHashCode();
2208     }
2209     else
2210     {
2211         canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(mt);
2212     }
2213
2214     // While we shouln't get here directly from ValueTypeHelper::GetHashCode, if we recurse we need to 
2215     // be able to handle getting the hashcode for an embedded structure whose hashcode is computed by the fast path.
2216     if (canUseFastGetHashCodeHelper)
2217     {
2218         hashCode = FastGetValueTypeHashCodeHelper(mt, pObjRef);
2219     }
2220     else
2221     {
2222         // it's looking ugly so we'll use the old behavior in managed code. Grab the first non-null
2223         // field and return its hash code or 'it' as hash code
2224         // <TODO> Note that the old behavior has already been broken for value types
2225         //              that is qualified for CanUseFastGetHashCodeHelper. So maybe we should
2226         //              change the implementation here to use all fields instead of just the 1st one.
2227         // </TODO>
2228         //
2229         // <TODO> check this approximation - we may be losing exact type information </TODO>
2230         ApproxFieldDescIterator fdIterator(mt, ApproxFieldDescIterator::INSTANCE_FIELDS);
2231
2232         FieldDesc *field;
2233         while ((field = fdIterator.Next()) != NULL)
2234         {
2235             _ASSERTE(!field->IsRVA());
2236             if (field->IsObjRef())
2237             {
2238                 // if we get an object reference we get the hash code out of that
2239                 if (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()) != NULL)
2240                 {
2241                     PREPARE_SIMPLE_VIRTUAL_CALLSITE(METHOD__OBJECT__GET_HASH_CODE, (*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe())));
2242                     DECLARE_ARGHOLDER_ARRAY(args, 1);
2243                     args[ARGNUM_0] = PTR_TO_ARGHOLDER(*(Object**)((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2244                     CALL_MANAGED_METHOD(hashCode, INT32, args);
2245                 }
2246                 else
2247                 {
2248                     // null object reference, try next
2249                     continue;
2250                 }
2251             }
2252             else
2253             {
2254                 CorElementType fieldType = field->GetFieldType();
2255                 if (fieldType == ELEMENT_TYPE_R8)
2256                 {
2257                     PREPARE_NONVIRTUAL_CALLSITE(METHOD__DOUBLE__GET_HASH_CODE);
2258                     DECLARE_ARGHOLDER_ARRAY(args, 1);
2259                     args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2260                     CALL_MANAGED_METHOD(hashCode, INT32, args);
2261                 }
2262                 else if (fieldType == ELEMENT_TYPE_R4)
2263                 {
2264                     PREPARE_NONVIRTUAL_CALLSITE(METHOD__SINGLE__GET_HASH_CODE);
2265                     DECLARE_ARGHOLDER_ARRAY(args, 1);
2266                     args[ARGNUM_0] = PTR_TO_ARGHOLDER(((BYTE *)pObjRef + field->GetOffsetUnsafe()));
2267                     CALL_MANAGED_METHOD(hashCode, INT32, args);
2268                 }
2269                 else if (fieldType != ELEMENT_TYPE_VALUETYPE)
2270                 {
2271                     UINT fieldSize = field->LoadSize();
2272                     INT32 *pValue = (INT32*)((BYTE *)pObjRef + field->GetOffsetUnsafe());
2273                     for (INT32 j = 0; j < (INT32)(fieldSize / sizeof(INT32)); j++)
2274                         hashCode ^= *pValue++;
2275                 }
2276                 else
2277                 {
2278                     // got another value type. Get the type
2279                     TypeHandle fieldTH = field->GetFieldTypeHandleThrowing();
2280                     _ASSERTE(!fieldTH.IsNull());
2281                     hashCode = RegularGetValueTypeHashCode(fieldTH.GetMethodTable(), (BYTE *)pObjRef + field->GetOffsetUnsafe());
2282                 }
2283             }
2284             break;
2285         }
2286     }
2287
2288     GCPROTECT_END();
2289
2290     return hashCode;
2291 }
2292
2293 // The default implementation of GetHashCode() for all value types.
2294 // Note that this implementation reveals the value of the fields.
2295 // So if the value type contains any sensitive information it should
2296 // implement its own GetHashCode().
2297 FCIMPL1(INT32, ValueTypeHelper::GetHashCode, Object* objUNSAFE)
2298 {
2299     FCALL_CONTRACT;
2300
2301     if (objUNSAFE == NULL)
2302         FCThrow(kNullReferenceException);
2303
2304     OBJECTREF obj = ObjectToOBJECTREF(objUNSAFE);
2305     VALIDATEOBJECTREF(obj);
2306
2307     INT32 hashCode = 0;
2308     MethodTable *pMT = objUNSAFE->GetMethodTable();
2309
2310     // We don't want to expose the method table pointer in the hash code
2311     // Let's use the typeID instead.
2312     UINT32 typeID = pMT->LookupTypeID();
2313     if (typeID == TypeIDProvider::INVALID_TYPE_ID)
2314     {
2315         // If the typeID has yet to be generated, fall back to GetTypeID
2316         // This only needs to be done once per MethodTable
2317         HELPER_METHOD_FRAME_BEGIN_RET_1(obj);        
2318         typeID = pMT->GetTypeID();
2319         HELPER_METHOD_FRAME_END();
2320     }
2321
2322     // To get less colliding and more evenly distributed hash codes,
2323     // we munge the class index with two big prime numbers
2324     hashCode = typeID * 711650207 + 2506965631U;
2325
2326     BOOL canUseFastGetHashCodeHelper = FALSE;
2327     if (pMT->HasCheckedCanCompareBitsOrUseFastGetHashCode())
2328     {
2329         canUseFastGetHashCodeHelper = pMT->CanCompareBitsOrUseFastGetHashCode();
2330     }
2331     else
2332     {
2333         HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2334         canUseFastGetHashCodeHelper = CanCompareBitsOrUseFastGetHashCode(pMT);
2335         HELPER_METHOD_FRAME_END();
2336     }
2337
2338     if (canUseFastGetHashCodeHelper)
2339     {
2340         hashCode ^= FastGetValueTypeHashCodeHelper(pMT, obj->UnBox());
2341     }
2342     else
2343     {
2344         HELPER_METHOD_FRAME_BEGIN_RET_1(obj);
2345         hashCode ^= RegularGetValueTypeHashCode(pMT, obj->UnBox());
2346         HELPER_METHOD_FRAME_END();
2347     }
2348
2349     return hashCode;
2350 }
2351 FCIMPLEND
2352
2353 static LONG s_dwSeed;
2354
2355 FCIMPL1(INT32, ValueTypeHelper::GetHashCodeOfPtr, LPVOID ptr)
2356 {
2357     FCALL_CONTRACT;
2358
2359     INT32 hashCode = (INT32)((INT64)(ptr));
2360
2361     if (hashCode == 0)
2362     {
2363         return 0;
2364     }
2365
2366     DWORD dwSeed = s_dwSeed;
2367
2368     // Initialize s_dwSeed lazily
2369     if (dwSeed == 0)
2370     {
2371         // We use the first non-0 pointer as the seed, all hashcodes will be based off that.
2372         // This is to make sure that we only reveal relative memory addresses and never absolute ones.
2373         dwSeed = hashCode;
2374         InterlockedCompareExchange(&s_dwSeed, dwSeed, 0);
2375         dwSeed = s_dwSeed;
2376     }
2377     _ASSERTE(dwSeed != 0);
2378
2379     return hashCode - dwSeed;
2380 }
2381 FCIMPLEND
2382
2383 static MethodTable * g_pStreamMT;
2384 static WORD g_slotBeginRead, g_slotEndRead;
2385 static WORD g_slotBeginWrite, g_slotEndWrite;
2386
2387 static bool HasOverriddenStreamMethod(MethodTable * pMT, WORD slot)
2388 {
2389     CONTRACTL{
2390         NOTHROW;
2391         GC_NOTRIGGER;
2392         MODE_ANY;
2393     } CONTRACTL_END;
2394
2395     PCODE actual = pMT->GetRestoredSlot(slot);
2396     PCODE base = g_pStreamMT->GetRestoredSlot(slot);
2397     if (actual == base)
2398         return false;
2399
2400     if (!g_pStreamMT->IsZapped())
2401     {
2402         // If mscorlib is JITed, the slots can be patched and thus we need to compare the actual MethodDescs 
2403         // to detect match reliably
2404         if (MethodTable::GetMethodDescForSlotAddress(actual) == MethodTable::GetMethodDescForSlotAddress(base))
2405             return false;
2406     }
2407
2408     return true;
2409 }
2410
2411 FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndRead, Object *stream)
2412 {
2413     FCALL_CONTRACT;
2414
2415     if (stream == NULL)
2416         FC_RETURN_BOOL(TRUE);
2417
2418     if (g_pStreamMT == NULL || g_slotBeginRead == 0 || g_slotEndRead == 0)
2419     {
2420         HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
2421         g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM);
2422         g_slotBeginRead = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_READ)->GetSlot();
2423         g_slotEndRead = MscorlibBinder::GetMethod(METHOD__STREAM__END_READ)->GetSlot();
2424         HELPER_METHOD_FRAME_END();
2425     }
2426
2427     MethodTable * pMT = stream->GetMethodTable();
2428
2429     FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginRead) || HasOverriddenStreamMethod(pMT, g_slotEndRead));
2430 }
2431 FCIMPLEND
2432
2433 FCIMPL1(FC_BOOL_RET, StreamNative::HasOverriddenBeginEndWrite, Object *stream)
2434 {
2435     FCALL_CONTRACT;
2436
2437     if (stream == NULL) 
2438         FC_RETURN_BOOL(TRUE);
2439
2440     if (g_pStreamMT == NULL || g_slotBeginWrite == 0 || g_slotEndWrite == 0)
2441     {
2442         HELPER_METHOD_FRAME_BEGIN_RET_1(stream);
2443         g_pStreamMT = MscorlibBinder::GetClass(CLASS__STREAM);
2444         g_slotBeginWrite = MscorlibBinder::GetMethod(METHOD__STREAM__BEGIN_WRITE)->GetSlot();
2445         g_slotEndWrite = MscorlibBinder::GetMethod(METHOD__STREAM__END_WRITE)->GetSlot();
2446         HELPER_METHOD_FRAME_END();
2447     }
2448
2449     MethodTable * pMT = stream->GetMethodTable();
2450
2451     FC_RETURN_BOOL(HasOverriddenStreamMethod(pMT, g_slotBeginWrite) || HasOverriddenStreamMethod(pMT, g_slotEndWrite));
2452 }
2453 FCIMPLEND