text: // Licensed to the .NET Foundation under one or more agreements.
  // The .NET Foundation licenses this file to you under the MIT license.
  // See the LICENSE file in the project root for more information.
  //
  
  //
  
  
  #include "common.h"
  
  #include "hosting.h"
  #include "mscoree.h"
  #include "mscoreepriv.h"
  #include "corhost.h"
  #include "threads.h"
  
  
  #define countof(x) (sizeof(x) / sizeof(x[0]))
  
  //Copied from winbase.h
  #ifndef STARTF_TITLEISAPPID
  #define STARTF_TITLEISAPPID     	  0x00001000
  #endif
  #ifndef STARTF_PREVENTPINNING
  #define STARTF_PREVENTPINNING    0x00002000
  #endif
  
  //Flags encoded in the first parameter of CorLaunchApplication.
  #define MASK_NOTPINNABLE 	0x80000000
  #define MASK_HOSTTYPE 		0x00000003
  #define MASK_DONT_SHOW_INSTALL_DIALOG 	0x00000100
  
  #ifdef _DEBUG
  // This function adds a static annotation read by SCAN to indicate HOST_CALLS. Its
  // purpose is to be called from the BEGIN_SO_TOLERANT_CODE_CALLING_HOST macro, to
  // effectively mark all functions that use BEGIN_SO_TOLERANT_CODE_CALLING_HOST as being
  // HOST_CALLS. If you hit a SCAN violation that references AddHostCallsStaticMarker, then
  // you have a function marked as HOST_NOCALLS that eventually calls into a function that
  // uses BEGIN_SO_TOLERANT_CODE_CALLING_HOST.
  DEBUG_NOINLINE void AddHostCallsStaticMarker()
  {
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_CANNOT_TAKE_LOCK;
      STATIC_CONTRACT_GC_NOTRIGGER;
      STATIC_CONTRACT_SO_TOLERANT;
      STATIC_CONTRACT_HOST_CALLS;
  
      METHOD_CANNOT_BE_FOLDED_DEBUG;
  }
  #endif  //_DEBUG
  
  //
  // memory management functions
  //
  
  // global debug only tracking utilities
  #ifdef _DEBUG
  
  static const LONG MaxGlobalAllocCount = 8;
  
  class GlobalAllocStore {
  public:
      static void AddAlloc (LPVOID p)
      {
          LIMITED_METHOD_CONTRACT;
  
          if (!p) {
              return;
          }
          if (m_Disabled) {
              return;
          }
  
          //InterlockedIncrement (&numMemWriter);
          //if (CheckMemFree) {
          //    goto Return;
          //}
  
          //m_Count is number of allocation we've ever tried, it's OK to be bigger than
          //size of m_Alloc[]
          InterlockedIncrement (&m_Count);
  
          //this is by no means an accurate record of heap allocation.
          //the algorithm used here can't guarantee an allocation is saved in
          //m_Alloc[] even there's enough free space. However this is only used
          //for debugging purpose and most importantly, m_Count is accurate.
          for (size_t n = 0; n < countof(m_Alloc); n ++) {
              if (m_Alloc[n] == 0) {
                  if (InterlockedCompareExchangeT(&m_Alloc[n],p,0) == 0) {
                      return;
                  }
              }
          }
          
          //InterlockedDecrement (&numMemWriter);
      }
  
      //this is called in non-host case where we don't care the free after
      //alloc store is disabled
      static BOOL RemoveAlloc (LPVOID p)
      {
          LIMITED_METHOD_CONTRACT;
  
          if (m_Disabled)
          {
              return TRUE;
          }
          //decrement the counter even we might not find the allocation
          //in m_Alloc. Because it's possible for an allocation not to be saved
          //in the array
          InterlockedDecrement (&m_Count);
          // Binary search        
          for (size_t n = 0; n < countof(m_Alloc); n ++) {
              if (m_Alloc[n] == p) {
                  m_Alloc[n] = 0;
                  return TRUE;
              }
          }
          return FALSE;
      }
  
      //this is called in host case where if the store is disabled, we want to 
      //guarantee we don't try to free anything the host doesn't know about
      static void ValidateFree(LPVOID p)
      {
          LIMITED_METHOD_CONTRACT;
  
          if (p == 0) {
              return;
          }
          if (m_Disabled) {
              for (size_t n = 0; n < countof(m_Alloc); n ++) {
                  //there could be miss, because an allocation might not be saved
                  //in the array
                  if (m_Alloc[n] == p) {
                      _ASSERTE (!"Free a memory that host interface does not know");
                      return;
                  }
              }
          }
      }
  
      static void Validate()
      {
          LIMITED_METHOD_CONTRACT;
  
          if (m_Count > MaxGlobalAllocCount) {
              _ASSERTE (!"Using too many memory allocator before Host Interface is set up");
          }       
          
          //while (numMemWriter != 0) {
          //    Sleep(5);
          //}
          //qsort (GlobalMemAddr, (MemAllocCount>MaxAllocCount)?MaxAllocCount:MemAllocCount, sizeof(LPVOID), MemAddrCompare);
      }
  
      static void Disable ()
      {
          LIMITED_METHOD_CONTRACT;
          if (!m_Disabled) 
          {
              // Let all threads know
              InterlockedIncrement((LONG*)&m_Disabled);
          }
      }
  
  private:
      static BOOL m_Disabled;    
      static LPVOID m_Alloc[MaxGlobalAllocCount];
      //m_Count is number of allocation we tried, it's legal to be bigger than
      //size of m_Alloc[]
      static LONG m_Count;
      // static LONG numMemWriter = 0;
  };
  
  // used from corhost.cpp
  void ValidateHostInterface()
  {
      WRAPPER_NO_CONTRACT;
  
      GlobalAllocStore::Validate();
      GlobalAllocStore::Disable();    
  }
  
  void DisableGlobalAllocStore ()
  {
      WRAPPER_NO_CONTRACT;
      GlobalAllocStore::Disable();
  }
  LPVOID GlobalAllocStore::m_Alloc[MaxGlobalAllocCount];
  LONG GlobalAllocStore::m_Count = 0;
  BOOL GlobalAllocStore::m_Disabled = FALSE;
  
  #endif
  
  
  
  HANDLE g_ExecutableHeapHandle = NULL;
  
  #undef VirtualAlloc
  LPVOID EEVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          SO_TOLERANT;
      }
      CONTRACTL_END;
  
  #ifdef FAILPOINTS_ENABLED
          if (RFS_HashStack ())
              return NULL;
  #endif
  
  
  #ifdef _DEBUG
          if (g_fEEStarted) {
              _ASSERTE (!EEAllocationDisallowed());
          }
          _ASSERTE (lpAddress || (dwSize % g_SystemInfo.dwAllocationGranularity) == 0);
  #endif
  
      {
  
          LPVOID p = NULL;
  
  #ifdef _DEBUG
          {
              DEBUG_ONLY_REGION();
  
          if (lpAddress == NULL && (flAllocationType & MEM_RESERVE) != 0 && PEDecoder::GetForceRelocs())
          {
  #ifdef _WIN64
              // Try to allocate memory all over the place when we are stressing relocations on _WIN64.
              // This will make sure that we generate jump stubs correctly among other things.
              static BYTE* ptr = (BYTE*)0x234560000;
              ptr += 0x123450000;
              // Wrap around
              if (ptr < (BYTE *)BOT_MEMORY || ptr > (BYTE *)TOP_MEMORY) 
              {
                  // Make sure to keep the alignment of the ptr so that we are not 
                  // trying the same places over and over again
                  ptr = (BYTE*)BOT_MEMORY + (((SIZE_T)ptr) & 0xFFFFFFFF);
              }
              p = ::VirtualAlloc(ptr, dwSize, flAllocationType, flProtect);
  #else
              // Allocate memory top to bottom to stress ngen fixups with LARGEADDRESSAWARE support.
              p = ::VirtualAlloc(lpAddress, dwSize, flAllocationType | MEM_TOP_DOWN, flProtect);
  #endif // _WIN64
          }
          }
  #endif // _DEBUG
  
          // Fall back to the default method if the forced relocation failed
          if (p == NULL)
          {
              p = ::VirtualAlloc (lpAddress, dwSize, flAllocationType, flProtect);
          }
  
  #ifdef _DEBUG
          GlobalAllocStore::AddAlloc (p);
  #endif
  
          if(p == NULL){
               STRESS_LOG_OOM_STACK(dwSize);
          }
  
          return p;
      }
  
  }
  #define VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect) Dont_Use_VirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect)
  
  #undef VirtualFree
  BOOL EEVirtualFree(LPVOID lpAddress, SIZE_T dwSize, DWORD dwFreeType) {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          SO_TOLERANT;
      }
      CONTRACTL_END;
  
      BOOL retVal = FALSE;
  
      {
  #ifdef _DEBUG
          GlobalAllocStore::RemoveAlloc (lpAddress);
  #endif
  
          retVal = (BOOL)(BYTE)::VirtualFree (lpAddress, dwSize, dwFreeType);
      }
  
      return retVal;
  }
  #define VirtualFree(lpAddress, dwSize, dwFreeType) Dont_Use_VirtualFree(lpAddress, dwSize, dwFreeType)
  
  #undef VirtualQuery
  SIZE_T EEVirtualQuery(LPCVOID lpAddress, PMEMORY_BASIC_INFORMATION lpBuffer, SIZE_T dwLength) 
  {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          SO_TOLERANT;
      }
      CONTRACTL_END;
  
      {
          return ::VirtualQuery(lpAddress, lpBuffer, dwLength);
      }
  }
  #define VirtualQuery(lpAddress, lpBuffer, dwLength) Dont_Use_VirtualQuery(lpAddress, lpBuffer, dwLength)
  
  #undef VirtualProtect
  BOOL EEVirtualProtect(LPVOID lpAddress, SIZE_T dwSize, DWORD flNewProtect, PDWORD lpflOldProtect) 
  {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          SO_TOLERANT;
      }
      CONTRACTL_END;
  
      {
          return ::VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect);
      }
  }
  #define VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect) Dont_Use_VirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect)
  
  #undef GetProcessHeap
  HANDLE EEGetProcessHeap() 
  {
      // Note: this can be called a little early for real contracts, so we use static contracts instead.
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_GC_NOTRIGGER;
      STATIC_CONTRACT_SO_TOLERANT;
  
      {
          return GetProcessHeap();
      }
  }
  #define GetProcessHeap() Dont_Use_GetProcessHeap()
  
  #undef HeapCreate
  HANDLE EEHeapCreate(DWORD flOptions, SIZE_T dwInitialSize, SIZE_T dwMaximumSize) 
  {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          SO_TOLERANT;
      }
      CONTRACTL_END;
  
  #ifndef FEATURE_PAL
      
      {
          return ::HeapCreate(flOptions, dwInitialSize, dwMaximumSize);
      }
  #else // !FEATURE_PAL
      return NULL;
  #endif // !FEATURE_PAL
  }
  #define HeapCreate(flOptions, dwInitialSize, dwMaximumSize) Dont_Use_HeapCreate(flOptions, dwInitialSize, dwMaximumSize)
  
  #undef HeapDestroy
  BOOL EEHeapDestroy(HANDLE hHeap) 
  {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          SO_TOLERANT;
      }
      CONTRACTL_END;
  
  #ifndef FEATURE_PAL
  
      {
          return ::HeapDestroy(hHeap);
      }
  #else // !FEATURE_PAL
      UNREACHABLE();
  #endif // !FEATURE_PAL
  }
  #define HeapDestroy(hHeap) Dont_Use_HeapDestroy(hHeap)
  
  #ifdef _DEBUG
  #ifdef _TARGET_X86_
  #define OS_HEAP_ALIGN 8
  #else
  #define OS_HEAP_ALIGN 16
  #endif
  #endif
  
  
  #undef HeapAlloc
  LPVOID EEHeapAlloc(HANDLE hHeap, DWORD dwFlags, SIZE_T dwBytes) 
  {
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_SO_INTOLERANT;
  
  #ifdef FAILPOINTS_ENABLED
      if (RFS_HashStack ())
          return NULL;
  #endif
  
  
      {
  
          LPVOID p = NULL;
  #ifdef _DEBUG
          // Store the heap handle to detect heap contamination
          p = ::HeapAlloc (hHeap, dwFlags, dwBytes + OS_HEAP_ALIGN);
          if(p)
          {
              *((HANDLE*)p) = hHeap;
              p = (BYTE*)p + OS_HEAP_ALIGN;
          }
          GlobalAllocStore::AddAlloc (p);
  #else
          p = ::HeapAlloc (hHeap, dwFlags, dwBytes);
  #endif
  
          if(p == NULL
              //under OOM, we might not be able to get Execution Engine and can't access stress log
              && GetExecutionEngine ()
             // If we have not created StressLog ring buffer, we should not try to use it.
             // StressLog is going to do a memory allocation.  We may enter an endless loop.
             && ClrFlsGetValue(TlsIdx_StressLog) != NULL )
          {
              STRESS_LOG_OOM_STACK(dwBytes);
          }
  
          return p;
      }
  }
  #define HeapAlloc(hHeap, dwFlags, dwBytes) Dont_Use_HeapAlloc(hHeap, dwFlags, dwBytes)
  
  LPVOID EEHeapAllocInProcessHeap(DWORD dwFlags, SIZE_T dwBytes)
  {
      WRAPPER_NO_CONTRACT;
      STATIC_CONTRACT_SO_TOLERANT;
  
  #ifdef _DEBUG
      // Check whether (indispensable) implicit casting in ClrAllocInProcessHeapBootstrap is safe.
      static FastAllocInProcessHeapFunc pFunc = EEHeapAllocInProcessHeap;
  #endif
  
      static HANDLE ProcessHeap = NULL;
  
      // We need to guarentee a very small stack consumption in allocating.  And we can't allow
      // an SO to happen while calling into the host.  This will force a hard SO which is OK because
      // we shouldn't ever get this close inside the EE in SO-intolerant code, so this should
      // only fail if we call directly in from outside the EE, such as the JIT.
      MINIMAL_STACK_PROBE_CHECK_THREAD(GetThread());
  
      if (ProcessHeap == NULL)
          ProcessHeap = EEGetProcessHeap();
  
      return EEHeapAlloc(ProcessHeap,dwFlags,dwBytes);
  }
  
  #undef HeapFree
  BOOL EEHeapFree(HANDLE hHeap, DWORD dwFlags, LPVOID lpMem)
  {
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_GC_NOTRIGGER;
      STATIC_CONTRACT_SO_TOLERANT;
  
      // @todo -  Need a backout validation here.
      CONTRACT_VIOLATION(SOToleranceViolation);
  
  
      BOOL retVal = FALSE;
  
      {
  #ifdef _DEBUG
          GlobalAllocStore::RemoveAlloc (lpMem);
  
          // Check the heap handle to detect heap contamination
          lpMem = (BYTE*)lpMem - OS_HEAP_ALIGN;
          HANDLE storedHeapHandle = *((HANDLE*)lpMem);
          if(storedHeapHandle != hHeap)
              _ASSERTE(!"Heap contamination detected! HeapFree was called on a heap other than the one that memory was allocated from.\n"
                        "Possible cause: you used new (executable) to allocate the memory, but didn't use DeleteExecutable() to free it.");
  #endif
          // DON'T REMOVE THIS SEEMINGLY USELESS CAST
          //
          // On AMD64 the OS HeapFree calls RtlFreeHeap which returns a 1byte
          // BOOLEAN, HeapFree then doesn't correctly clean the return value
          // so the other 3 bytes which come back can be junk and in that case
          // this return value can never be false.
          retVal =  (BOOL)(BYTE)::HeapFree (hHeap, dwFlags, lpMem);
      }
  
      return retVal;
  }
  #define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem)
  
  BOOL EEHeapFreeInProcessHeap(DWORD dwFlags, LPVOID lpMem)
  {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          SO_TOLERANT;
          MODE_ANY;
      }
      CONTRACTL_END;
  
  #ifdef _DEBUG
      // Check whether (indispensable) implicit casting in ClrFreeInProcessHeapBootstrap is safe.
      static FastFreeInProcessHeapFunc pFunc = EEHeapFreeInProcessHeap;
  #endif
  
      // Take a look at comment in EEHeapFree and EEHeapAllocInProcessHeap, obviously someone
      // needs to take a little time to think more about this code.
      //CONTRACT_VIOLATION(SOToleranceViolation);
  
      static HANDLE ProcessHeap = NULL;
  
      if (ProcessHeap == NULL)
          ProcessHeap = EEGetProcessHeap();
  
      return EEHeapFree(ProcessHeap,dwFlags,lpMem);
  }
  
  
  #undef HeapValidate
  BOOL EEHeapValidate(HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem) {
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_GC_NOTRIGGER;
  
  #ifndef FEATURE_PAL
  
      {
          return ::HeapValidate(hHeap, dwFlags, lpMem);
      }
  #else // !FEATURE_PAL
      return TRUE;
  #endif // !FEATURE_PAL
  }
  #define HeapValidate(hHeap, dwFlags, lpMem) Dont_Use_HeapValidate(hHeap, dwFlags, lpMem)
  
  HANDLE EEGetProcessExecutableHeap() {
      // Note: this can be called a little early for real contracts, so we use static contracts instead.
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_GC_NOTRIGGER;
  
  
  #ifndef FEATURE_PAL
  
      //
      // Create the executable heap lazily
      //
  #undef HeapCreate
  #undef HeapDestroy
      if (g_ExecutableHeapHandle == NULL)
      {
  
          HANDLE ExecutableHeapHandle = HeapCreate(
                                      HEAP_CREATE_ENABLE_EXECUTE,                 // heap allocation attributes
                                      0,                                          // initial heap size
                                      0                                           // maximum heap size; 0 == growable
                                      );
  
          if (ExecutableHeapHandle == NULL)
              return NULL;
  
          HANDLE ExistingValue = InterlockedCompareExchangeT(&g_ExecutableHeapHandle, ExecutableHeapHandle, NULL);
          if (ExistingValue != NULL)
          {
              HeapDestroy(ExecutableHeapHandle);
          }
      }
  
  #define HeapCreate(flOptions, dwInitialSize, dwMaximumSize) Dont_Use_HeapCreate(flOptions, dwInitialSize, dwMaximumSize)
  #define HeapDestroy(hHeap) Dont_Use_HeapDestroy(hHeap)
  
  #else // !FEATURE_PAL
      UNREACHABLE();
  #endif // !FEATURE_PAL
  
  
      // TODO: implement hosted executable heap
      return g_ExecutableHeapHandle;
  }
  
  
  #undef SleepEx
  #undef Sleep
  DWORD EESleepEx(DWORD dwMilliseconds, BOOL bAlertable)
  {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          MODE_ANY;
          SO_TOLERANT;
      }
      CONTRACTL_END;
  
      DWORD res;
  
      {
          res = ::SleepEx(dwMilliseconds, bAlertable);
      }
  
      return res;
  }
  #define SleepEx(dwMilliseconds,bAlertable) \
          Dont_Use_SleepEx(dwMilliseconds,bAlertable)
  #define Sleep(a) Dont_Use_Sleep(a)
  
  // non-zero return value if this function causes the OS to switch to another thread
  // See file:spinlock.h#SwitchToThreadSpinning for an explanation of dwSwitchCount
  BOOL __SwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount)
  {
    CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          MODE_ANY;
          SO_TOLERANT;
      }
      CONTRACTL_END;
  	
      return  __DangerousSwitchToThread(dwSleepMSec, dwSwitchCount, FALSE);
  }
  
  #undef SleepEx
  BOOL __DangerousSwitchToThread (DWORD dwSleepMSec, DWORD dwSwitchCount, BOOL goThroughOS)
  {
      // If you sleep for a long time, the thread should be in Preemptive GC mode.
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          MODE_ANY;
          SO_TOLERANT;
          PRECONDITION(dwSleepMSec < 10000 || GetThread() == NULL || !GetThread()->PreemptiveGCDisabled());
      }
      CONTRACTL_END;
  
      if (dwSleepMSec > 0)
      {
          // when called with goThroughOS make sure to not call into the host. This function
          // may be called from GetRuntimeFunctionCallback() which is called by the OS to determine
          // the personality routine when it needs to unwind managed code off the stack. when this
          // happens in the context of an SO we want to avoid calling into the host
          if (goThroughOS)
              ::SleepEx(dwSleepMSec, FALSE);
          else
              ClrSleepEx(dwSleepMSec,FALSE);
          return TRUE;
      }
  
      // In deciding when to insert sleeps, we wait until we have been spinning
      // for a long time and then always sleep.  The former is to let short perf-critical
      // __SwitchToThread loops avoid context switches.  The latter is to ensure
      // that if many threads are spinning waiting for a lower-priority thread
      // to run that they will eventually all be asleep at the same time.
      // 
      // The specific values are derived from the NDP 2.0 SP1 fix: it waits for 
      // 8 million cycles of __SwitchToThread calls where each takes ~300-500,
      // which means we should wait in the neighborhood of 25000 calls.
      // 
      // As of early 2011, ARM CPUs are much slower, so we need a lower threshold.
      // The following two values appear to yield roughly equivalent spin times
      // on their respective platforms.
      //
  #ifdef _TARGET_ARM_
      #define SLEEP_START_THRESHOLD (5 * 1024)
  #else
      #define SLEEP_START_THRESHOLD (32 * 1024)
  #endif
  
      _ASSERTE(CALLER_LIMITS_SPINNING < SLEEP_START_THRESHOLD);
      if (dwSwitchCount >= SLEEP_START_THRESHOLD)
      {
          if (goThroughOS)
              ::SleepEx(1, FALSE);
          else
              ClrSleepEx(1, FALSE);
      }
  
      {
          return SwitchToThread();
      }
  }
  #define SleepEx(dwMilliseconds,bAlertable) \
          Dont_Use_SleepEx(dwMilliseconds,bAlertable)
  
  // Locking routines supplied by the EE to the other DLLs of the CLR.  In a _DEBUG
  // build of the EE, we poison the Crst as a poor man's attempt to do some argument
  // validation.
  #define POISON_BITS 3
  
  static inline CRITSEC_COOKIE CrstToCookie(Crst * pCrst) {
      LIMITED_METHOD_CONTRACT;
  
      _ASSERTE((((uintptr_t) pCrst) & POISON_BITS) == 0);
  #ifdef _DEBUG
      if (pCrst)
      {
      pCrst = (Crst *) (((uintptr_t) pCrst) | POISON_BITS);
      }
  #endif
      return (CRITSEC_COOKIE) pCrst;
  }
  
  static inline Crst *CookieToCrst(CRITSEC_COOKIE cookie) {
      LIMITED_METHOD_CONTRACT;
  
      _ASSERTE((((uintptr_t) cookie) & POISON_BITS) == POISON_BITS);
  #ifdef _DEBUG
      cookie = (CRITSEC_COOKIE) (((uintptr_t) cookie) & ~POISON_BITS);
  #endif
      return (Crst *) cookie;
  }
  
  CRITSEC_COOKIE EECreateCriticalSection(CrstType crstType, CrstFlags flags) {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
      }
      CONTRACTL_END;
  
      CRITSEC_COOKIE ret = NULL;
  
      EX_TRY
      {
      // This may be controversial, but seems like the correct discipline.  If the
      // EE has called out to any other DLL of the CLR in cooperative mode, we
      // arbitrarily force lock acquisition to occur in preemptive mode.  See our
      // treatment of AcquireLock below.
      //_ASSERTE((flags & (CRST_UNSAFE_COOPGC | CRST_UNSAFE_ANYMODE)) == 0);
          ret = CrstToCookie(new Crst(crstType, flags));
      }
      EX_CATCH
      {
      }
      EX_END_CATCH(SwallowAllExceptions);
  
      // Note: we'll return NULL if the create fails. That's a true NULL, not a poisoned NULL.
      return ret;
  }
  
  void EEDeleteCriticalSection(CRITSEC_COOKIE cookie)
  {
      CONTRACTL
      {
          NOTHROW;
          WRAPPER(GC_NOTRIGGER);
          SO_TOLERANT;
      }
      CONTRACTL_END;
  
      VALIDATE_BACKOUT_STACK_CONSUMPTION;
  
      Crst *pCrst = CookieToCrst(cookie);
      _ASSERTE(pCrst);
  
      delete pCrst;
  }
  
  DEBUG_NOINLINE void EEEnterCriticalSection(CRITSEC_COOKIE cookie) {
  
      // Entering a critical section has many different contracts
      // depending on the flags used to initialize the critical section.
      // See CrstBase::Enter() for the actual contract. It's much too
      // complex to repeat here.
  
      CONTRACTL
      {
          WRAPPER(THROWS);
          WRAPPER(GC_TRIGGERS);
          SO_INTOLERANT;
      }
      CONTRACTL_END;
  
      ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
  
      Crst *pCrst = CookieToCrst(cookie);
      _ASSERTE(pCrst);
  
      pCrst->Enter();
  }
  
  DEBUG_NOINLINE void EELeaveCriticalSection(CRITSEC_COOKIE cookie)
  {
      CONTRACTL
      {
          NOTHROW;
          GC_NOTRIGGER;
          SO_INTOLERANT;
      }
      CONTRACTL_END;
  
      ANNOTATION_SPECIAL_HOLDER_CALLER_NEEDS_DYNAMIC_CONTRACT;
  
      Crst *pCrst = CookieToCrst(cookie);
      _ASSERTE(pCrst);
  
      pCrst->Leave();
  }
  
  LPVOID EETlsGetValue(DWORD slot)
  {
      STATIC_CONTRACT_GC_NOTRIGGER;
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_MODE_ANY;
      STATIC_CONTRACT_CANNOT_TAKE_LOCK;
      STATIC_CONTRACT_SO_TOLERANT;
  
      //
      // @todo: we don't want TlsGetValue to throw, but CheckThreadState throws right now. Either modify
      // CheckThreadState to not throw, or catch any exception and just return NULL.
      //
      //CONTRACT_VIOLATION(ThrowsViolation);
      SCAN_IGNORE_THROW;
  
      void **pTlsData = CExecutionEngine::CheckThreadState(slot, FALSE);
  
      if (pTlsData)
          return pTlsData[slot];
      else
          return NULL;
  }
  
  BOOL EETlsCheckValue(DWORD slot, LPVOID * pValue)
  {
      STATIC_CONTRACT_GC_NOTRIGGER;
      STATIC_CONTRACT_NOTHROW;
      STATIC_CONTRACT_MODE_ANY;
      STATIC_CONTRACT_SO_TOLERANT;
  
      //
      // @todo: we don't want TlsGetValue to throw, but CheckThreadState throws right now. Either modify
      // CheckThreadState to not throw, or catch any exception and just return NULL.
      //
      //CONTRACT_VIOLATION(ThrowsViolation);
      SCAN_IGNORE_THROW;
  
      void **pTlsData = CExecutionEngine::CheckThreadState(slot, FALSE);
  
      if (pTlsData)
      {
          *pValue = pTlsData[slot];
          return TRUE;
      }
  
      return FALSE;
  }
  
  VOID EETlsSetValue(DWORD slot, LPVOID pData)
  {
      STATIC_CONTRACT_GC_NOTRIGGER;
      STATIC_CONTRACT_THROWS;
      STATIC_CONTRACT_MODE_ANY;
      STATIC_CONTRACT_SO_TOLERANT;
  
      void **pTlsData = CExecutionEngine::CheckThreadState(slot);
  
      if (pTlsData)  // Yes, CheckThreadState(slot, TRUE) can return NULL now.
      {
          pTlsData[slot] = pData;
      }
  }
  
  BOOL EEAllocationDisallowed()
  {
      WRAPPER_NO_CONTRACT;
  
  #ifdef _DEBUG
      // On Debug build we make sure that a thread is not going to do memory allocation
      // after it suspends another thread, since the another thread may be suspended while
      // having OS Heap lock.
      return !Thread::Debug_AllowCallout();
  #else
      return FALSE;
  #endif
  }
  
  