Remove always defined FEATURE_CORECLR
[platform/upstream/coreclr.git] / src / vm / gcenv.os.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  * gcenv.os.cpp
7  *
8  * GCToOSInterface implementation
9  *
10
11  *
12  */
13
14 #include "common.h"
15 #include "gcenv.h"
16
17 #ifndef FEATURE_PAL
18 #include <Psapi.h>
19 #endif
20
21 #ifdef Sleep
22 #undef Sleep
23 #endif // Sleep
24
25 #include "env/gcenv.os.h"
26
27 #define MAX_PTR ((uint8_t*)(~(ptrdiff_t)0))
28
29 // Initialize the interface implementation
30 // Return:
31 //  true if it has succeeded, false if it has failed
32 bool GCToOSInterface::Initialize()
33 {
34     LIMITED_METHOD_CONTRACT;
35     return true;
36 }
37
38 // Shutdown the interface implementation
39 void GCToOSInterface::Shutdown()
40 {
41     LIMITED_METHOD_CONTRACT;
42 }
43
44 // Get numeric id of the current thread if possible on the
45 // current platform. It is indended for logging purposes only.
46 // Return:
47 //  Numeric id of the current thread or 0 if the 
48 uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
49 {
50     LIMITED_METHOD_CONTRACT;
51     return ::GetCurrentThreadId();
52 }
53
54 // Get id of the process
55 // Return:
56 //  Id of the current process
57 uint32_t GCToOSInterface::GetCurrentProcessId()
58 {
59     LIMITED_METHOD_CONTRACT;
60     return ::GetCurrentProcessId();
61 }
62
63 // Set ideal affinity for the current thread
64 // Parameters:
65 //  affinity - ideal processor affinity for the thread
66 // Return:
67 //  true if it has succeeded, false if it has failed
68 bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity)
69 {
70     LIMITED_METHOD_CONTRACT;
71
72     bool success = true;
73
74 #if !defined(FEATURE_PAL)
75     PROCESSOR_NUMBER proc;
76
77     if (affinity->Group != -1)
78     {
79         proc.Group = (WORD)affinity->Group;
80         proc.Number = (BYTE)affinity->Processor;
81         proc.Reserved = 0;
82         
83         success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
84     }
85     else
86     {
87         if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
88         {
89             proc.Number = (BYTE)affinity->Processor;
90             success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, &proc);
91         }        
92     }
93 #endif
94
95     return success;
96 }
97
98 // Get the number of the current processor
99 uint32_t GCToOSInterface::GetCurrentProcessorNumber()
100 {
101     LIMITED_METHOD_CONTRACT;
102
103     _ASSERTE(CanGetCurrentProcessorNumber());
104     return ::GetCurrentProcessorNumber();
105 }
106
107 // Check if the OS supports getting current processor number
108 bool GCToOSInterface::CanGetCurrentProcessorNumber()
109 {
110     LIMITED_METHOD_CONTRACT;
111
112 #ifdef FEATURE_PAL
113     return PAL_HasGetCurrentProcessorNumber();
114 #else
115     // on all Windows platforms we support this API exists
116     return true;
117 #endif
118 }
119
120 // Flush write buffers of processors that are executing threads of the current process
121 void GCToOSInterface::FlushProcessWriteBuffers()
122 {
123     LIMITED_METHOD_CONTRACT;
124     ::FlushProcessWriteBuffers();
125 }
126
127 // Break into a debugger
128 void GCToOSInterface::DebugBreak()
129 {
130     LIMITED_METHOD_CONTRACT;
131     ::DebugBreak();
132 }
133
134 // Get number of logical processors
135 uint32_t GCToOSInterface::GetLogicalCpuCount()
136 {
137     LIMITED_METHOD_CONTRACT;
138     return ::GetLogicalCpuCount();
139 }
140
141 // Causes the calling thread to sleep for the specified number of milliseconds
142 // Parameters:
143 //  sleepMSec   - time to sleep before switching to another thread
144 void GCToOSInterface::Sleep(uint32_t sleepMSec)
145 {
146     LIMITED_METHOD_CONTRACT;
147     __SwitchToThread(sleepMSec, 0);
148 }
149
150 // Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
151 // Parameters:
152 //  switchCount - number of times the YieldThread was called in a loop
153 void GCToOSInterface::YieldThread(uint32_t switchCount)
154 {
155     LIMITED_METHOD_CONTRACT;
156     __SwitchToThread(0, switchCount);
157 }
158
159 // Reserve virtual memory range.
160 // Parameters:
161 //  address   - starting virtual address, it can be NULL to let the function choose the starting address
162 //  size      - size of the virtual memory range
163 //  alignment - requested memory alignment
164 //  flags     - flags to control special settings like write watching
165 // Return:
166 //  Starting virtual address of the reserved range
167 void* GCToOSInterface::VirtualReserve(size_t size, size_t alignment, uint32_t flags)
168 {
169     LIMITED_METHOD_CONTRACT;
170
171     DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE;
172     if (alignment == 0)
173     {
174         return ::ClrVirtualAlloc(0, size, memFlags, PAGE_READWRITE);
175     }
176     else
177     {
178         return ::ClrVirtualAllocAligned(0, size, memFlags, PAGE_READWRITE, alignment);
179     }
180 }
181
182 // Release virtual memory range previously reserved using VirtualReserve
183 // Parameters:
184 //  address - starting virtual address
185 //  size    - size of the virtual memory range
186 // Return:
187 //  true if it has succeeded, false if it has failed
188 bool GCToOSInterface::VirtualRelease(void* address, size_t size)
189 {
190     LIMITED_METHOD_CONTRACT;
191
192     UNREFERENCED_PARAMETER(size);
193     return !!::ClrVirtualFree(address, 0, MEM_RELEASE);
194 }
195
196 // Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
197 // Parameters:
198 //  address - starting virtual address
199 //  size    - size of the virtual memory range
200 // Return:
201 //  true if it has succeeded, false if it has failed
202 bool GCToOSInterface::VirtualCommit(void* address, size_t size)
203 {
204     LIMITED_METHOD_CONTRACT;
205
206     return ::ClrVirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL;
207 }
208
209 // Decomit virtual memory range.
210 // Parameters:
211 //  address - starting virtual address
212 //  size    - size of the virtual memory range
213 // Return:
214 //  true if it has succeeded, false if it has failed
215 bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
216 {
217     LIMITED_METHOD_CONTRACT;
218
219     return !!::ClrVirtualFree(address, size, MEM_DECOMMIT);
220 }
221
222 // Reset virtual memory range. Indicates that data in the memory range specified by address and size is no 
223 // longer of interest, but it should not be decommitted.
224 // Parameters:
225 //  address - starting virtual address
226 //  size    - size of the virtual memory range
227 //  unlock  - true if the memory range should also be unlocked
228 // Return:
229 //  true if it has succeeded, false if it has failed
230 bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
231 {
232     LIMITED_METHOD_CONTRACT;
233
234     bool success = ::ClrVirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL;
235 #ifndef FEATURE_PAL
236     if (success && unlock)
237     {
238         // Remove the page range from the working set
239         ::VirtualUnlock(address, size);
240     }
241 #endif // FEATURE_PAL
242
243     return success;
244 }
245
246 // Check if the OS supports write watching
247 bool GCToOSInterface::SupportsWriteWatch()
248 {
249     LIMITED_METHOD_CONTRACT;
250
251     bool writeWatchSupported = false;
252
253     // check if the OS supports write-watch. 
254     // Drawbridge does not support write-watch so we still need to do the runtime detection for them.
255     // Otherwise, all currently supported OSes do support write-watch.
256     void* mem = VirtualReserve (g_SystemInfo.dwAllocationGranularity, 0, VirtualReserveFlags::WriteWatch);
257     if (mem != NULL)
258     {
259         VirtualRelease (mem, g_SystemInfo.dwAllocationGranularity);
260         writeWatchSupported = true;
261     }
262
263     return writeWatchSupported;
264 }
265
266 // Reset the write tracking state for the specified virtual memory range.
267 // Parameters:
268 //  address - starting virtual address
269 //  size    - size of the virtual memory range
270 void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
271 {
272     LIMITED_METHOD_CONTRACT;
273
274     ::ResetWriteWatch(address, size);
275 }
276
277 // Retrieve addresses of the pages that are written to in a region of virtual memory
278 // Parameters:
279 //  resetState         - true indicates to reset the write tracking state
280 //  address            - starting virtual address
281 //  size               - size of the virtual memory range
282 //  pageAddresses      - buffer that receives an array of page addresses in the memory region
283 //  pageAddressesCount - on input, size of the lpAddresses array, in array elements
284 //                       on output, the number of page addresses that are returned in the array.
285 // Return:
286 //  true if it has succeeded, false if it has failed
287 bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
288 {
289     LIMITED_METHOD_CONTRACT;
290
291     uint32_t flags = resetState ? 1 : 0;
292     ULONG granularity;
293
294     bool success = ::GetWriteWatch(flags, address, size, pageAddresses, (ULONG_PTR*)pageAddressesCount, &granularity) == 0;
295     _ASSERTE (granularity == OS_PAGE_SIZE);
296
297     return success;
298 }
299
300 // Get size of the largest cache on the processor die
301 // Parameters:
302 //  trueSize - true to return true cache size, false to return scaled up size based on
303 //             the processor architecture
304 // Return:
305 //  Size of the cache
306 size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize)
307 {
308     LIMITED_METHOD_CONTRACT;
309
310     return ::GetLargestOnDieCacheSize(trueSize);
311 }
312
313 // Get affinity mask of the current process
314 // Parameters:
315 //  processMask - affinity mask for the specified process
316 //  systemMask  - affinity mask for the system
317 // Return:
318 //  true if it has succeeded, false if it has failed
319 // Remarks:
320 //  A process affinity mask is a bit vector in which each bit represents the processors that
321 //  a process is allowed to run on. A system affinity mask is a bit vector in which each bit
322 //  represents the processors that are configured into a system.
323 //  A process affinity mask is a subset of the system affinity mask. A process is only allowed
324 //  to run on the processors configured into a system. Therefore, the process affinity mask cannot
325 //  specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor.
326 bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask)
327 {
328     LIMITED_METHOD_CONTRACT;
329
330 #ifndef FEATURE_PAL
331     return !!::GetProcessAffinityMask(GetCurrentProcess(), (PDWORD_PTR)processMask, (PDWORD_PTR)systemMask);
332 #else
333     return false;
334 #endif
335 }
336
337 // Get number of processors assigned to the current process
338 // Return:
339 //  The number of processors
340 uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
341 {
342     LIMITED_METHOD_CONTRACT;
343
344     return ::GetCurrentProcessCpuCount();
345 }
346
347 // Return the size of the user-mode portion of the virtual address space of this process.
348 // Return:
349 //  non zero if it has succeeded, 0 if it has failed
350 size_t GCToOSInterface::GetVirtualMemoryLimit()
351 {
352     LIMITED_METHOD_CONTRACT;
353
354     MEMORYSTATUSEX memStatus;
355     ::GetProcessMemoryLoad(&memStatus);
356
357     return (size_t)memStatus.ullTotalVirtual;
358 }
359
360
361 #ifndef FEATURE_PAL
362
363 typedef BOOL (WINAPI *PGET_PROCESS_MEMORY_INFO)(HANDLE handle, PROCESS_MEMORY_COUNTERS* memCounters, uint32_t cb);
364 static PGET_PROCESS_MEMORY_INFO GCGetProcessMemoryInfo = 0;
365
366 static size_t g_RestrictedPhysicalMemoryLimit = (size_t)MAX_PTR;
367
368 typedef BOOL (WINAPI *PIS_PROCESS_IN_JOB)(HANDLE processHandle, HANDLE jobHandle, BOOL* result);
369 typedef BOOL (WINAPI *PQUERY_INFORMATION_JOB_OBJECT)(HANDLE jobHandle, JOBOBJECTINFOCLASS jobObjectInfoClass, void* lpJobObjectInfo, DWORD cbJobObjectInfoLength, LPDWORD lpReturnLength);
370
371 static size_t GetRestrictedPhysicalMemoryLimit()
372 {
373     LIMITED_METHOD_CONTRACT;
374
375     // The limit was cached already
376     if (g_RestrictedPhysicalMemoryLimit != (size_t)MAX_PTR)
377         return g_RestrictedPhysicalMemoryLimit;
378
379     size_t job_physical_memory_limit = (size_t)MAX_PTR;
380     BOOL in_job_p = FALSE;
381     HINSTANCE hinstKernel32 = 0;
382
383     PIS_PROCESS_IN_JOB GCIsProcessInJob = 0;
384     PQUERY_INFORMATION_JOB_OBJECT GCQueryInformationJobObject = 0;
385
386     GCIsProcessInJob = &(::IsProcessInJob);
387
388     if (!GCIsProcessInJob(GetCurrentProcess(), NULL, &in_job_p))
389         goto exit;
390
391     if (in_job_p)
392     {
393         hinstKernel32 = WszLoadLibrary(L"kernel32.dll");
394         if (!hinstKernel32)
395             goto exit;
396
397         GCGetProcessMemoryInfo = (PGET_PROCESS_MEMORY_INFO)GetProcAddress(hinstKernel32, "K32GetProcessMemoryInfo");
398
399         if (!GCGetProcessMemoryInfo)
400             goto exit;
401
402         GCQueryInformationJobObject = &(::QueryInformationJobObject);
403
404         if (!GCQueryInformationJobObject)
405             goto exit;
406
407         JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit_info;
408         if (GCQueryInformationJobObject (NULL, JobObjectExtendedLimitInformation, &limit_info, 
409             sizeof(limit_info), NULL))
410         {
411             size_t job_memory_limit = (size_t)MAX_PTR;
412             size_t job_process_memory_limit = (size_t)MAX_PTR;
413             size_t job_workingset_limit = (size_t)MAX_PTR;
414
415             // Notes on the NT job object:
416             //
417             // You can specific a bigger process commit or working set limit than 
418             // job limit which is pointless so we use the smallest of all 3 as
419             // to calculate our "physical memory load" or "available physical memory"
420             // when running inside a job object, ie, we treat this as the amount of physical memory
421             // our process is allowed to use.
422             // 
423             // The commit limit is already reflected by default when you run in a 
424             // job but the physical memory load is not.
425             //
426             if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_JOB_MEMORY) != 0)
427                 job_memory_limit = limit_info.JobMemoryLimit;
428             if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_PROCESS_MEMORY) != 0)
429                 job_process_memory_limit = limit_info.ProcessMemoryLimit;
430             if ((limit_info.BasicLimitInformation.LimitFlags & JOB_OBJECT_LIMIT_WORKINGSET) != 0)
431                 job_workingset_limit = limit_info.BasicLimitInformation.MaximumWorkingSetSize;
432
433             job_physical_memory_limit = min (job_memory_limit, job_process_memory_limit);
434             job_physical_memory_limit = min (job_physical_memory_limit, job_workingset_limit);
435
436             MEMORYSTATUSEX ms;
437             ::GetProcessMemoryLoad(&ms);
438
439             // A sanity check in case someone set a larger limit than there is actual physical memory.
440             job_physical_memory_limit = (size_t) min (job_physical_memory_limit, ms.ullTotalPhys);
441         }
442     }
443
444 exit:
445     if (job_physical_memory_limit == (size_t)MAX_PTR)
446     {
447         job_physical_memory_limit = 0;
448
449         FreeLibrary(hinstKernel32);
450     }
451
452     VolatileStore(&g_RestrictedPhysicalMemoryLimit, job_physical_memory_limit);
453     return g_RestrictedPhysicalMemoryLimit;
454 }
455
456 #endif // FEATURE_PAL
457
458
459 // Get the physical memory that this process can use.
460 // Return:
461 //  non zero if it has succeeded, 0 if it has failed
462 uint64_t GCToOSInterface::GetPhysicalMemoryLimit()
463 {
464     LIMITED_METHOD_CONTRACT;
465
466 #ifndef FEATURE_PAL
467     size_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
468     if (restricted_limit != 0)
469         return restricted_limit;
470 #endif
471
472     MEMORYSTATUSEX memStatus;
473     ::GetProcessMemoryLoad(&memStatus);
474
475     return memStatus.ullTotalPhys;
476 }
477
478 // Get memory status
479 // Parameters:
480 //  memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
481 //      that is in use (0 indicates no memory use and 100 indicates full memory use).
482 //  available_physical - The amount of physical memory currently available, in bytes.
483 //  available_page_file - The maximum amount of memory the current process can commit, in bytes.
484 // Remarks:
485 //  Any parameter can be null.
486 void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
487 {
488     LIMITED_METHOD_CONTRACT;
489
490 #ifndef FEATURE_PAL
491     uint64_t restricted_limit = GetRestrictedPhysicalMemoryLimit();
492     if (restricted_limit != 0)
493     {
494         PROCESS_MEMORY_COUNTERS pmc;
495         if (GCGetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
496         {
497             if (memory_load)
498                 *memory_load = (uint32_t)((float)pmc.WorkingSetSize * 100.0 / (float)restricted_limit);
499             if (available_physical)
500                 *available_physical = restricted_limit - pmc.WorkingSetSize;
501             // Available page file doesn't mean much when physical memory is restricted since
502             // we don't know how much of it is available to this process so we are not going to 
503             // bother to make another OS call for it.
504             if (available_page_file)
505                 *available_page_file = 0;
506
507             return;
508         }
509     }
510 #endif
511
512     MEMORYSTATUSEX ms;
513     ::GetProcessMemoryLoad(&ms);
514
515     if (memory_load != NULL)
516         *memory_load = ms.dwMemoryLoad;
517     if (available_physical != NULL)
518         *available_physical = ms.ullAvailPhys;
519     if (available_page_file != NULL)
520         *available_page_file = ms.ullAvailPageFile;
521 }
522
523 // Get a high precision performance counter
524 // Return:
525 //  The counter value
526 int64_t GCToOSInterface::QueryPerformanceCounter()
527 {
528     LIMITED_METHOD_CONTRACT;
529
530     LARGE_INTEGER ts;
531     if (!::QueryPerformanceCounter(&ts))
532     {
533         DebugBreak();
534         _ASSERTE(!"Fatal Error - cannot query performance counter.");
535         EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);        // TODO: fatal error        
536     }
537
538     return ts.QuadPart;
539 }
540
541 // Get a frequency of the high precision performance counter
542 // Return:
543 //  The counter frequency
544 int64_t GCToOSInterface::QueryPerformanceFrequency()
545 {
546     LIMITED_METHOD_CONTRACT;
547
548     LARGE_INTEGER frequency;
549     if (!::QueryPerformanceFrequency(&frequency))
550     {
551         DebugBreak();
552         _ASSERTE(!"Fatal Error - cannot query performance counter.");
553         EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE);        // TODO: fatal error        
554     }
555
556     return frequency.QuadPart;
557 }
558
559 // Get a time stamp with a low precision
560 // Return:
561 //  Time stamp in milliseconds
562 uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
563 {
564     LIMITED_METHOD_CONTRACT;
565
566     return ::GetTickCount();
567 }
568
569 // Parameters of the GC thread stub
570 struct GCThreadStubParam
571 {
572     GCThreadFunction GCThreadFunction;
573     void* GCThreadParam;
574 };
575
576 // GC thread stub to convert GC thread function to an OS specific thread function
577 static DWORD __stdcall GCThreadStub(void* param)
578 {
579     WRAPPER_NO_CONTRACT;
580
581     GCThreadStubParam *stubParam = (GCThreadStubParam*)param;
582     GCThreadFunction function = stubParam->GCThreadFunction;
583     void* threadParam = stubParam->GCThreadParam;
584
585     delete stubParam;
586
587     function(threadParam);
588
589     return 0;
590 }
591
592 // Create a new thread
593 // Parameters:
594 //  function - the function to be executed by the thread
595 //  param    - parameters of the thread
596 //  affinity - processor affinity of the thread
597 // Return:
598 //  true if it has succeeded, false if it has failed
599 bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity)
600 {
601     LIMITED_METHOD_CONTRACT;
602
603     uint32_t thread_id;
604
605     NewHolder<GCThreadStubParam> stubParam = new (nothrow) GCThreadStubParam();
606     if (stubParam == NULL)
607     {
608         return false;
609     }
610
611     stubParam->GCThreadFunction = function;
612     stubParam->GCThreadParam = param;
613
614     HANDLE gc_thread = Thread::CreateUtilityThread(Thread::StackSize_Medium, GCThreadStub, stubParam, CREATE_SUSPENDED, (DWORD*)&thread_id);
615
616     if (!gc_thread)
617     {
618         return false;
619     }
620
621     stubParam.SuppressRelease();
622
623     SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST );
624
625 #ifndef FEATURE_PAL
626     if (affinity->Group != GCThreadAffinity::None)
627     {
628         _ASSERTE(affinity->Processor != GCThreadAffinity::None);
629         GROUP_AFFINITY ga;
630         ga.Group = (WORD)affinity->Group;
631         ga.Reserved[0] = 0; // reserve must be filled with zero
632         ga.Reserved[1] = 0; // otherwise call may fail
633         ga.Reserved[2] = 0;
634         ga.Mask = (size_t)1 << affinity->Processor;
635
636         CPUGroupInfo::SetThreadGroupAffinity(gc_thread, &ga, NULL);
637     }
638     else if (affinity->Processor != GCThreadAffinity::None)
639     {
640         SetThreadAffinityMask(gc_thread, (DWORD_PTR)1 << affinity->Processor);
641     }
642 #endif // !FEATURE_PAL
643
644     ResumeThread(gc_thread);
645     CloseHandle(gc_thread);
646
647     return true;
648 }
649
650 // Initialize the critical section
651 void CLRCriticalSection::Initialize()
652 {
653     WRAPPER_NO_CONTRACT;
654     UnsafeInitializeCriticalSection(&m_cs);
655 }
656
657 // Destroy the critical section
658 void CLRCriticalSection::Destroy()
659 {
660     WRAPPER_NO_CONTRACT;
661     UnsafeDeleteCriticalSection(&m_cs);
662 }
663
664 // Enter the critical section. Blocks until the section can be entered.
665 void CLRCriticalSection::Enter()
666 {
667     WRAPPER_NO_CONTRACT;
668     UnsafeEnterCriticalSection(&m_cs);
669 }
670
671 // Leave the critical section
672 void CLRCriticalSection::Leave()
673 {
674     WRAPPER_NO_CONTRACT;
675     UnsafeLeaveCriticalSection(&m_cs);
676 }