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