From 5b975f8233e8c8d17b215372f89ca713b45d6a0b Mon Sep 17 00:00:00 2001 From: Jiyoung Yun Date: Thu, 27 Apr 2017 16:54:50 +0900 Subject: Imported Upstream version 2.0.0.11599 --- src/gc/env/gcenv.os.h | 80 ++++++++++ src/gc/gc.cpp | 52 +++---- src/gc/gc.h | 6 +- src/gc/gccommon.cpp | 12 +- src/gc/gcee.cpp | 11 +- src/gc/gchandletable.cpp | 128 ++++++++++------ src/gc/gchandletableimpl.h | 43 ++++-- src/gc/gcimpl.h | 7 +- src/gc/gcinterface.h | 50 +++--- src/gc/gcpriv.h | 28 ++-- src/gc/handletable.cpp | 12 +- src/gc/handletable.inl | 21 --- src/gc/handletablecore.cpp | 38 +++-- src/gc/handletablescan.cpp | 4 +- src/gc/objecthandle.cpp | 138 ++++++++++------- src/gc/objecthandle.h | 24 +-- src/gc/sample/GCSample.cpp | 8 +- src/gc/unix/CMakeLists.txt | 1 + src/gc/unix/config.h.in | 4 +- src/gc/unix/configure.cmake | 17 ++- src/gc/unix/events.cpp | 323 +++++++++++++++++++++++++++++++++++++++ src/gc/unix/gcenv.unix.cpp | 41 +++-- src/gc/unix/globals.h | 30 ++++ src/gc/windows/gcenv.windows.cpp | 142 +++++++++++++++++ 24 files changed, 938 insertions(+), 282 deletions(-) create mode 100644 src/gc/unix/events.cpp create mode 100644 src/gc/unix/globals.h (limited to 'src/gc') diff --git a/src/gc/env/gcenv.os.h b/src/gc/env/gcenv.os.h index 6a126f29ed..d3e40ac4ff 100644 --- a/src/gc/env/gcenv.os.h +++ b/src/gc/env/gcenv.os.h @@ -47,6 +47,86 @@ struct GCThreadAffinity int Processor; }; +// An event is a synchronization object whose state can be set and reset +// indicating that an event has occured. It is used pervasively throughout +// the GC. +// +// Note that GCEvent deliberately leaks its contents by not having a non-trivial destructor. +// This is by design; since all uses of GCEvent have static lifetime, their destructors +// are run on process exit, potentially concurrently with other threads that may still be +// operating on the static event. To avoid these sorts of unsafety, GCEvent chooses to +// not have a destructor at all. The cost of this is leaking a small amount of memory, but +// this is not a problem since a majority of the uses of GCEvent are static. See CoreCLR#11111 +// for more details on the hazards of static destructors. +class GCEvent { +private: + class Impl; + Impl *m_impl; + +public: + // Constructs a new uninitialized event. + GCEvent(); + + // Closes the event. Attempting to use the event past calling CloseEvent + // is a logic error. + void CloseEvent(); + + // "Sets" the event, indicating that a particular event has occured. May + // wake up other threads waiting on this event. Depending on whether or + // not this event is an auto-reset event, the state of the event may + // or may not be automatically reset after Set is called. + void Set(); + + // Resets the event, resetting it back to a non-signalled state. Auto-reset + // events automatically reset once the event is set, while manual-reset + // events do not reset until Reset is called. It is a no-op to call Reset + // on an auto-reset event. + void Reset(); + + // Waits for some period of time for this event to be signalled. The + // period of time may be infinite (if the timeout argument is INFINITE) or + // it may be a specified period of time, in milliseconds. + // Returns: + // One of three values, depending on how why this thread was awoken: + // WAIT_OBJECT_0 - This event was signalled and woke up this thread. + // WAIT_TIMEOUT - The timeout interval expired without this event being signalled. + // WAIT_FAILED - The wait failed. + uint32_t Wait(uint32_t timeout, bool alertable); + + // Determines whether or not this event is valid. + // Returns: + // true if this event is invalid (i.e. it has not yet been initialized or + // has already been closed), false otherwise + bool IsValid() const + { + return m_impl != nullptr; + } + + // Initializes this event to be a host-aware manual reset event with the + // given initial state. + // Returns: + // true if the initialization succeeded, false if it did not + bool CreateManualEventNoThrow(bool initialState); + + // Initializes this event to be a host-aware auto-resetting event with the + // given initial state. + // Returns: + // true if the initialization succeeded, false if it did not + bool CreateAutoEventNoThrow(bool initialState); + + // Initializes this event to be a host-unaware manual reset event with the + // given initial state. + // Returns: + // true if the initialization succeeded, false if it did not + bool CreateOSManualEventNoThrow(bool initialState); + + // Initializes this event to be a host-unaware auto-resetting event with the + // given initial state. + // Returns: + // true if the initialization succeeded, false if it did not + bool CreateOSAutoEventNoThrow(bool initialState); +}; + // GC thread function prototype typedef void (*GCThreadFunction)(void* param); diff --git a/src/gc/gc.cpp b/src/gc/gc.cpp index ecc13e38fd..08de1facb2 100644 --- a/src/gc/gc.cpp +++ b/src/gc/gc.cpp @@ -621,7 +621,7 @@ enum gc_join_flavor #define first_thread_arrived 2 struct join_structure { - CLREvent joined_event[3]; // the last event in the array is only used for first_thread_arrived. + GCEvent joined_event[3]; // the last event in the array is only used for first_thread_arrived. VOLATILE(int32_t) join_lock; VOLATILE(int32_t) r_join_lock; VOLATILE(int32_t) join_restart; @@ -1201,8 +1201,8 @@ class recursive_gc_sync static VOLATILE(BOOL) gc_background_running; //initial state FALSE static VOLATILE(int32_t) foreground_count; // initial state 0; static VOLATILE(uint32_t) foreground_gate; // initial state FALSE; - static CLREvent foreground_complete;//Auto Reset - static CLREvent foreground_allowed;//Auto Reset + static GCEvent foreground_complete;//Auto Reset + static GCEvent foreground_allowed;//Auto Reset public: static void begin_background(); static void end_background(); @@ -1218,8 +1218,8 @@ VOLATILE(int32_t) recursive_gc_sync::foreground_request_count = 0;//initial stat VOLATILE(int32_t) recursive_gc_sync::foreground_count = 0; // initial state 0; VOLATILE(BOOL) recursive_gc_sync::gc_background_running = FALSE; //initial state FALSE VOLATILE(uint32_t) recursive_gc_sync::foreground_gate = 0; -CLREvent recursive_gc_sync::foreground_complete;//Auto Reset -CLREvent recursive_gc_sync::foreground_allowed;//Manual Reset +GCEvent recursive_gc_sync::foreground_complete;//Auto Reset +GCEvent recursive_gc_sync::foreground_allowed;//Manual Reset BOOL recursive_gc_sync::init () { @@ -2308,7 +2308,7 @@ sorted_table* gc_heap::seg_table; #endif //!SEG_MAPPING_TABLE || FEATURE_BASICFREEZE #ifdef MULTIPLE_HEAPS -CLREvent gc_heap::ee_suspend_event; +GCEvent gc_heap::ee_suspend_event; size_t gc_heap::min_balance_threshold = 0; #endif //MULTIPLE_HEAPS @@ -2316,7 +2316,7 @@ VOLATILE(BOOL) gc_heap::gc_started; #ifdef MULTIPLE_HEAPS -CLREvent gc_heap::gc_start_event; +GCEvent gc_heap::gc_start_event; bool gc_heap::gc_thread_no_affinitize_p = false; @@ -2385,13 +2385,13 @@ uint64_t gc_heap::total_physical_mem; uint64_t gc_heap::entry_available_physical_mem; #ifdef BACKGROUND_GC -CLREvent gc_heap::bgc_start_event; +GCEvent gc_heap::bgc_start_event; gc_mechanisms gc_heap::saved_bgc_settings; -CLREvent gc_heap::background_gc_done_event; +GCEvent gc_heap::background_gc_done_event; -CLREvent gc_heap::ee_proceed_event; +GCEvent gc_heap::ee_proceed_event; bool gc_heap::gc_can_use_concurrent = false; @@ -2403,7 +2403,7 @@ BOOL gc_heap::dont_restart_ee_p = FALSE; BOOL gc_heap::keep_bgc_threads_p = FALSE; -CLREvent gc_heap::bgc_threads_sync_event; +GCEvent gc_heap::bgc_threads_sync_event; BOOL gc_heap::do_ephemeral_gc_p = FALSE; @@ -2589,7 +2589,7 @@ BOOL gc_heap::bgc_thread_running; CLRCriticalSection gc_heap::bgc_threads_timeout_cs; -CLREvent gc_heap::gc_lh_block_event; +GCEvent gc_heap::gc_lh_block_event; #endif //BACKGROUND_GC @@ -2685,9 +2685,9 @@ int gc_heap::loh_pinned_queue_decay = LOH_PIN_DECAY; #endif //FEATURE_LOH_COMPACTION -CLREvent gc_heap::full_gc_approach_event; +GCEvent gc_heap::full_gc_approach_event; -CLREvent gc_heap::full_gc_end_event; +GCEvent gc_heap::full_gc_end_event; uint32_t gc_heap::fgn_maxgen_percent = 0; @@ -5151,7 +5151,6 @@ void gc_heap::destroy_thread_support () } } -#if !defined(FEATURE_PAL) void set_thread_group_affinity_for_heap(int heap_number, GCThreadAffinity* affinity) { affinity->Group = GCThreadAffinity::None; @@ -5231,7 +5230,6 @@ void set_thread_affinity_mask_for_heap(int heap_number, GCThreadAffinity* affini } } } -#endif // !FEATURE_PAL bool gc_heap::create_gc_thread () { @@ -5241,7 +5239,6 @@ bool gc_heap::create_gc_thread () affinity.Group = GCThreadAffinity::None; affinity.Processor = GCThreadAffinity::None; -#if !defined(FEATURE_PAL) if (!gc_thread_no_affinitize_p) { // We are about to set affinity for GC threads. It is a good place to set up NUMA and @@ -5252,7 +5249,6 @@ bool gc_heap::create_gc_thread () else set_thread_affinity_mask_for_heap(heap_number, &affinity); } -#endif // !FEATURE_PAL return GCToOSInterface::CreateThread(gc_thread_stub, this, &affinity); } @@ -9266,12 +9262,10 @@ void gc_heap::delete_heap_segment (heap_segment* seg, BOOL consider_hoarding) void gc_heap::reset_heap_segment_pages (heap_segment* seg) { -#ifndef FEATURE_PAL // No MEM_RESET support in PAL VirtualAlloc size_t page_start = align_on_page ((size_t)heap_segment_allocated (seg)); size_t size = (size_t)heap_segment_committed (seg) - page_start; if (size != 0) GCToOSInterface::VirtualReset((void*)page_start, size, false /* unlock */); -#endif //!FEATURE_PAL } void gc_heap::decommit_heap_segment_pages (heap_segment* seg, @@ -10312,7 +10306,7 @@ gc_heap::loh_state_info gc_heap::last_loh_states[max_saved_loh_states]; VOLATILE(int32_t) gc_heap::gc_done_event_lock; VOLATILE(bool) gc_heap::gc_done_event_set; -CLREvent gc_heap::gc_done_event; +GCEvent gc_heap::gc_done_event; #endif //!MULTIPLE_HEAPS VOLATILE(bool) gc_heap::internal_gc_done; @@ -11741,7 +11735,7 @@ void gc_heap::send_full_gc_notification (int gen_num, BOOL due_to_alloc_p) } } -wait_full_gc_status gc_heap::full_gc_wait (CLREvent *event, int time_out_ms) +wait_full_gc_status gc_heap::full_gc_wait (GCEvent *event, int time_out_ms) { if (fgn_maxgen_percent == 0) { @@ -20362,7 +20356,7 @@ size_t gc_heap::update_brick_table (uint8_t* tree, size_t current_brick, dprintf (3, ("tree: %Ix, current b: %Ix, x: %Ix, plug_end: %Ix", tree, current_brick, x, plug_end)); - if (tree > 0) + if (tree != NULL) { dprintf (3, ("b- %Ix->%Ix pointing to tree %Ix", current_brick, (size_t)(tree - brick_address (current_brick)), tree)); @@ -30809,7 +30803,6 @@ CObjectHeader* gc_heap::allocate_large_object (size_t jsize, int64_t& alloc_byte void reset_memory (uint8_t* o, size_t sizeo) { -#ifndef FEATURE_PAL if (sizeo > 128 * 1024) { // We cannot reset the memory for the useful part of a free object. @@ -30824,7 +30817,6 @@ void reset_memory (uint8_t* o, size_t sizeo) reset_mm_p = GCToOSInterface::VirtualReset((void*)page_start, size, true /* unlock */); } } -#endif //!FEATURE_PAL } void gc_heap::reset_large_object (uint8_t* o) @@ -32417,7 +32409,7 @@ void gc_heap::descr_generations (BOOL begin_gc_p) VOLATILE(BOOL) GCHeap::GcInProgress = FALSE; //GCTODO //CMCSafeLock* GCHeap::fGcLock; -CLREvent *GCHeap::WaitForGCEvent = NULL; +GCEvent *GCHeap::WaitForGCEvent = NULL; //GCTODO #ifdef TRACE_GC unsigned int GCHeap::GcDuration; @@ -33691,7 +33683,7 @@ HRESULT GCHeap::Initialize () gc_heap::youngest_gen_desired_th = gc_heap::mem_one_percent; #endif // BIT64 - WaitForGCEvent = new (nothrow) CLREvent; + WaitForGCEvent = new (nothrow) GCEvent; if (!WaitForGCEvent) { @@ -33891,7 +33883,7 @@ bool GCHeap::IsHeapPointer (void* vpObject, bool small_heap_only) STATIC_CONTRACT_SO_TOLERANT; // removed STATIC_CONTRACT_CAN_TAKE_LOCK here because find_segment - // no longer calls CLREvent::Wait which eventually takes a lock. + // no longer calls GCEvent::Wait which eventually takes a lock. uint8_t* object = (uint8_t*) vpObject; #ifndef FEATURE_BASICFREEZE @@ -34226,7 +34218,7 @@ bool GCHeap::StressHeap(gc_alloc_context * context) if (g_pConfig->AppDomainLeaks() && str->SetAppDomainNoThrow()) { #endif - StoreObjectInHandle(m_StressObjs[i], ObjectToOBJECTREF(str)); + HndAssignHandle(m_StressObjs[i], ObjectToOBJECTREF(str)); #if CHECK_APP_DOMAIN_LEAKS } #endif @@ -34259,7 +34251,7 @@ bool GCHeap::StressHeap(gc_alloc_context * context) { // Let the string itself become garbage. // will be realloced next time around - StoreObjectInHandle(m_StressObjs[m_CurStressObj], 0); + HndAssignHandle(m_StressObjs[m_CurStressObj], 0); } } } diff --git a/src/gc/gc.h b/src/gc/gc.h index a661c311ab..07ae6c916c 100644 --- a/src/gc/gc.h +++ b/src/gc/gc.h @@ -115,7 +115,7 @@ extern "C" GCHeapType g_gc_heap_type; extern "C" uint32_t g_max_generation; extern "C" MethodTable* g_gc_pFreeObjectMethodTable; -::IGCHandleTable* CreateGCHandleTable(); +::IGCHandleManager* CreateGCHandleManager(); namespace WKS { ::IGCHeapInternal* CreateGCHeap(); @@ -260,8 +260,8 @@ void updateGCShadow(Object** ptr, Object* val); // The single GC heap instance, shared with the VM. extern IGCHeapInternal* g_theGCHeap; -// The single GC handle table instance, shared with the VM. -extern IGCHandleTable* g_theGCHandleTable; +// The single GC handle manager instance, shared with the VM. +extern IGCHandleManager* g_theGCHandleManager; #ifndef DACCESS_COMPILE inline bool IsGCInProgress(bool bConsiderGCStart = false) diff --git a/src/gc/gccommon.cpp b/src/gc/gccommon.cpp index f931597667..4950809cda 100644 --- a/src/gc/gccommon.cpp +++ b/src/gc/gccommon.cpp @@ -15,7 +15,7 @@ #include "gc.h" IGCHeapInternal* g_theGCHeap; -IGCHandleTable* g_theGCHandleTable; +IGCHandleManager* g_theGCHandleManager; #ifdef FEATURE_STANDALONE_GC IGCToCLR* g_theGCToCLR; @@ -143,7 +143,7 @@ namespace SVR extern void PopulateDacVars(GcDacVars* dacVars); } -bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleTable** gcHandleTable, GcDacVars* gcDacVars) +bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleManager** gcHandleManager, GcDacVars* gcDacVars) { LIMITED_METHOD_CONTRACT; @@ -151,10 +151,10 @@ bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleTa assert(gcDacVars != nullptr); assert(gcHeap != nullptr); - assert(gcHandleTable != nullptr); + assert(gcHandleManager != nullptr); - IGCHandleTable* handleTable = CreateGCHandleTable(); - if (handleTable == nullptr) + IGCHandleManager* handleManager = CreateGCHandleManager(); + if (handleManager == nullptr) { return false; } @@ -192,7 +192,7 @@ bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleTa assert(clrToGC == nullptr); #endif - *gcHandleTable = handleTable; + *gcHandleManager = handleManager; *gcHeap = heap; return true; } diff --git a/src/gc/gcee.cpp b/src/gc/gcee.cpp index 889f940973..0404058cde 100644 --- a/src/gc/gcee.cpp +++ b/src/gc/gcee.cpp @@ -428,9 +428,14 @@ void GCHeap::SetGCInProgress(bool fInProgress) GcInProgress = fInProgress; } -CLREvent * GCHeap::GetWaitForGCEvent() +void GCHeap::SetWaitForGCEvent() { - return WaitForGCEvent; + WaitForGCEvent->Set(); +} + +void GCHeap::ResetWaitForGCEvent() +{ + WaitForGCEvent->Reset(); } void GCHeap::WaitUntilConcurrentGCComplete() @@ -520,7 +525,7 @@ void gc_heap::fire_etw_pin_object_event (uint8_t* object, uint8_t** ppObject) } #endif // FEATURE_EVENT_TRACE -uint32_t gc_heap::user_thread_wait (CLREvent *event, BOOL no_mode_change, int time_out_ms) +uint32_t gc_heap::user_thread_wait (GCEvent *event, BOOL no_mode_change, int time_out_ms) { Thread* pCurThread = NULL; bool mode = false; diff --git a/src/gc/gchandletable.cpp b/src/gc/gchandletable.cpp index 82ab269861..52fede6299 100644 --- a/src/gc/gchandletable.cpp +++ b/src/gc/gchandletable.cpp @@ -8,104 +8,146 @@ #include "gchandletableimpl.h" #include "objecthandle.h" -IGCHandleTable* CreateGCHandleTable() +GCHandleStore* g_gcGlobalHandleStore; + +IGCHandleManager* CreateGCHandleManager() { - return new(nothrow) GCHandleTable(); + return new (nothrow) GCHandleManager(); } -bool GCHandleTable::Initialize() +void GCHandleStore::Uproot() { - return Ref_Initialize(); + Ref_RemoveHandleTableBucket(&_underlyingBucket); } -void GCHandleTable::Shutdown() +bool GCHandleStore::ContainsHandle(OBJECTHANDLE handle) { - Ref_Shutdown(); + return _underlyingBucket.Contains(handle); } -void* GCHandleTable::GetGlobalHandleStore() +OBJECTHANDLE GCHandleStore::CreateHandleOfType(Object* object, int type) { - return (void*)g_HandleTableMap.pBuckets[0]; + HHANDLETABLE handletable = _underlyingBucket.pTable[GetCurrentThreadHomeHeapNumber()]; + return ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(object)); } -void* GCHandleTable::CreateHandleStore(void* context) +OBJECTHANDLE GCHandleStore::CreateHandleOfType(Object* object, int type, int heapToAffinitizeTo) { -#ifndef FEATURE_REDHAWK - return (void*)::Ref_CreateHandleTableBucket(ADIndex((DWORD)(uintptr_t)context)); -#else - assert("CreateHandleStore is not implemented when FEATURE_REDHAWK is defined!"); - return nullptr; -#endif + HHANDLETABLE handletable = _underlyingBucket.pTable[heapToAffinitizeTo]; + return ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(object)); } -void* GCHandleTable::GetHandleContext(OBJECTHANDLE handle) +OBJECTHANDLE GCHandleStore::CreateHandleWithExtraInfo(Object* object, int type, void* pExtraInfo) { - return (void*)((uintptr_t)::HndGetHandleTableADIndex(::HndGetHandleTable(handle)).m_dwIndex); + HHANDLETABLE handletable = _underlyingBucket.pTable[GetCurrentThreadHomeHeapNumber()]; + return ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(object), reinterpret_cast(pExtraInfo)); } -void GCHandleTable::DestroyHandleStore(void* store) +OBJECTHANDLE GCHandleStore::CreateDependentHandle(Object* primary, Object* secondary) { - Ref_DestroyHandleTableBucket((HandleTableBucket*) store); + HHANDLETABLE handletable = _underlyingBucket.pTable[GetCurrentThreadHomeHeapNumber()]; + OBJECTHANDLE handle = ::HndCreateHandle(handletable, HNDTYPE_DEPENDENT, ObjectToOBJECTREF(primary)); + if (!handle) + { + return nullptr; + } + + ::SetDependentHandleSecondary(handle, ObjectToOBJECTREF(secondary)); + return handle; } -void GCHandleTable::UprootHandleStore(void* store) +GCHandleStore::~GCHandleStore() { - Ref_RemoveHandleTableBucket((HandleTableBucket*) store); + ::Ref_DestroyHandleTableBucket(&_underlyingBucket); } -bool GCHandleTable::ContainsHandle(void* store, OBJECTHANDLE handle) +bool GCHandleManager::Initialize() { - return ((HandleTableBucket*)store)->Contains(handle); + return Ref_Initialize(); } -OBJECTHANDLE GCHandleTable::CreateHandleOfType(void* store, Object* object, int type) +void GCHandleManager::Shutdown() { - HHANDLETABLE handletable = ((HandleTableBucket*)store)->pTable[GetCurrentThreadHomeHeapNumber()]; - return ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(object)); + if (g_gcGlobalHandleStore != nullptr) + { + DestroyHandleStore(g_gcGlobalHandleStore); + } + + ::Ref_Shutdown(); } -OBJECTHANDLE GCHandleTable::CreateHandleOfType(void* store, Object* object, int type, int heapToAffinitizeTo) +IGCHandleStore* GCHandleManager::GetGlobalHandleStore() { - HHANDLETABLE handletable = ((HandleTableBucket*)store)->pTable[heapToAffinitizeTo]; - return ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(object)); + return g_gcGlobalHandleStore; } -OBJECTHANDLE GCHandleTable::CreateGlobalHandleOfType(Object* object, int type) +IGCHandleStore* GCHandleManager::CreateHandleStore(void* context) { - return ::HndCreateHandle(g_HandleTableMap.pBuckets[0]->pTable[GetCurrentThreadHomeHeapNumber()], type, ObjectToOBJECTREF(object)); +#ifndef FEATURE_REDHAWK + GCHandleStore* store = new (nothrow) GCHandleStore(); + if (store == nullptr) + return nullptr; + + bool success = ::Ref_InitializeHandleTableBucket(&store->_underlyingBucket, context); + if (!success) + { + delete store; + return nullptr; + } + + return store; +#else + assert("CreateHandleStore is not implemented when FEATURE_REDHAWK is defined!"); + return nullptr; +#endif } -OBJECTHANDLE GCHandleTable::CreateHandleWithExtraInfo(void* store, Object* object, int type, void* pExtraInfo) +void GCHandleManager::DestroyHandleStore(IGCHandleStore* store) { - HHANDLETABLE handletable = ((HandleTableBucket*)store)->pTable[GetCurrentThreadHomeHeapNumber()]; - return ::HndCreateHandle(handletable, type, ObjectToOBJECTREF(object), reinterpret_cast(pExtraInfo)); + delete store; } -OBJECTHANDLE GCHandleTable::CreateDependentHandle(void* store, Object* primary, Object* secondary) +void* GCHandleManager::GetHandleContext(OBJECTHANDLE handle) { - HHANDLETABLE handletable = ((HandleTableBucket*)store)->pTable[GetCurrentThreadHomeHeapNumber()]; - OBJECTHANDLE handle = ::HndCreateHandle(handletable, HNDTYPE_DEPENDENT, ObjectToOBJECTREF(primary)); - ::SetDependentHandleSecondary(handle, ObjectToOBJECTREF(secondary)); + return (void*)((uintptr_t)::HndGetHandleTableADIndex(::HndGetHandleTable(handle)).m_dwIndex); +} - return handle; +OBJECTHANDLE GCHandleManager::CreateGlobalHandleOfType(Object* object, int type) +{ + return ::HndCreateHandle(g_HandleTableMap.pBuckets[0]->pTable[GetCurrentThreadHomeHeapNumber()], type, ObjectToOBJECTREF(object)); } -OBJECTHANDLE GCHandleTable::CreateDuplicateHandle(OBJECTHANDLE handle) +OBJECTHANDLE GCHandleManager::CreateDuplicateHandle(OBJECTHANDLE handle) { return ::HndCreateHandle(HndGetHandleTable(handle), HNDTYPE_DEFAULT, ::HndFetchHandle(handle)); } -void GCHandleTable::DestroyHandleOfType(OBJECTHANDLE handle, int type) +void GCHandleManager::DestroyHandleOfType(OBJECTHANDLE handle, int type) { ::HndDestroyHandle(::HndGetHandleTable(handle), type, handle); } -void GCHandleTable::DestroyHandleOfUnknownType(OBJECTHANDLE handle) +void GCHandleManager::DestroyHandleOfUnknownType(OBJECTHANDLE handle) { ::HndDestroyHandleOfUnknownType(::HndGetHandleTable(handle), handle); } -void* GCHandleTable::GetExtraInfoFromHandle(OBJECTHANDLE handle) +void* GCHandleManager::GetExtraInfoFromHandle(OBJECTHANDLE handle) { return (void*)::HndGetHandleExtraInfo(handle); } + +void GCHandleManager::StoreObjectInHandle(OBJECTHANDLE handle, Object* object) +{ + ::HndAssignHandle(handle, ObjectToOBJECTREF(object)); +} + +bool GCHandleManager::StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object) +{ + return !!::HndFirstAssignHandle(handle, ObjectToOBJECTREF(object)); +} + +Object* GCHandleManager::InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE handle, Object* object, Object* comparandObject) +{ + return (Object*)::HndInterlockedCompareExchangeHandle(handle, ObjectToOBJECTREF(object), ObjectToOBJECTREF(comparandObject)); +} diff --git a/src/gc/gchandletableimpl.h b/src/gc/gchandletableimpl.h index af20f52e54..01c1c130ed 100644 --- a/src/gc/gchandletableimpl.h +++ b/src/gc/gchandletableimpl.h @@ -6,33 +6,44 @@ #define GCHANDLETABLE_H_ #include "gcinterface.h" +#include "objecthandle.h" -class GCHandleTable : public IGCHandleTable +class GCHandleStore : public IGCHandleStore { public: - virtual bool Initialize(); + virtual void Uproot(); - virtual void Shutdown(); + virtual bool ContainsHandle(OBJECTHANDLE handle); - virtual void* GetGlobalHandleStore(); + virtual OBJECTHANDLE CreateHandleOfType(Object* object, int type); - virtual void* CreateHandleStore(void* context); + virtual OBJECTHANDLE CreateHandleOfType(Object* object, int type, int heapToAffinitizeTo); - virtual void* GetHandleContext(OBJECTHANDLE handle); + virtual OBJECTHANDLE CreateHandleWithExtraInfo(Object* object, int type, void* pExtraInfo); - virtual void DestroyHandleStore(void* store); + virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary); - virtual void UprootHandleStore(void* store); + virtual ~GCHandleStore(); - virtual bool ContainsHandle(void* store, OBJECTHANDLE handle); + HandleTableBucket _underlyingBucket; +}; - virtual OBJECTHANDLE CreateHandleOfType(void* store, Object* object, int type); +extern GCHandleStore* g_gcGlobalHandleStore; - virtual OBJECTHANDLE CreateHandleOfType(void* store, Object* object, int type, int heapToAffinitizeTo); +class GCHandleManager : public IGCHandleManager +{ +public: + virtual bool Initialize(); - virtual OBJECTHANDLE CreateHandleWithExtraInfo(void* store, Object* object, int type, void* pExtraInfo); + virtual void Shutdown(); - virtual OBJECTHANDLE CreateDependentHandle(void* store, Object* primary, Object* secondary); + virtual void* GetHandleContext(OBJECTHANDLE handle); + + virtual IGCHandleStore* GetGlobalHandleStore(); + + virtual IGCHandleStore* CreateHandleStore(void* context); + + virtual void DestroyHandleStore(IGCHandleStore* store); virtual OBJECTHANDLE CreateGlobalHandleOfType(Object* object, int type); @@ -43,6 +54,12 @@ public: virtual void DestroyHandleOfUnknownType(OBJECTHANDLE handle); virtual void* GetExtraInfoFromHandle(OBJECTHANDLE handle); + + virtual void StoreObjectInHandle(OBJECTHANDLE handle, Object* object); + + virtual bool StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object); + + virtual Object* InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE handle, Object* object, Object* comparandObject); }; #endif // GCHANDLETABLE_H_ diff --git a/src/gc/gcimpl.h b/src/gc/gcimpl.h index 2a51d477b0..8ac16c5107 100644 --- a/src/gc/gcimpl.h +++ b/src/gc/gcimpl.h @@ -6,8 +6,6 @@ #ifndef GCIMPL_H_ #define GCIMPL_H_ -#define CLREvent CLREventStatic - #ifdef SERVER_GC #define MULTIPLE_HEAPS 1 #endif // SERVER_GC @@ -93,7 +91,8 @@ public: bool RuntimeStructuresValid(); - CLREvent * GetWaitForGCEvent(); + void SetWaitForGCEvent(); + void ResetWaitForGCEvent(); HRESULT Initialize (); @@ -242,7 +241,7 @@ public: // FIX void TemporaryDisableConcurrentGC(); bool IsConcurrentGCEnabled(); - PER_HEAP_ISOLATED CLREvent *WaitForGCEvent; // used for syncing w/GC + PER_HEAP_ISOLATED GCEvent *WaitForGCEvent; // used for syncing w/GC PER_HEAP_ISOLATED CFinalize* m_Finalize; diff --git a/src/gc/gcinterface.h b/src/gc/gcinterface.h index cac2ba7114..552a8caec8 100644 --- a/src/gc/gcinterface.h +++ b/src/gc/gcinterface.h @@ -169,12 +169,12 @@ struct segment_info class Object; class IGCHeap; -class IGCHandleTable; +class IGCHandleManager; // Initializes the garbage collector. Should only be called // once, during EE startup. Returns true if the initialization // was successful, false otherwise. -bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleTable** gcHandleTable, GcDacVars* gcDacVars); +bool InitializeGarbageCollector(IGCToCLR* clrToGC, IGCHeap** gcHeap, IGCHandleManager** gcHandleTable, GcDacVars* gcDacVars); // The runtime needs to know whether we're using workstation or server GC // long before the GCHeap is created. This function sets the type of @@ -402,32 +402,38 @@ typedef struct OBJECTHANDLE__* OBJECTHANDLE; typedef uintptr_t OBJECTHANDLE; #endif -class IGCHandleTable { +class IGCHandleStore { public: - virtual bool Initialize() = 0; + virtual void Uproot() = 0; - virtual void Shutdown() = 0; + virtual bool ContainsHandle(OBJECTHANDLE handle) = 0; - virtual void* GetHandleContext(OBJECTHANDLE handle) = 0; + virtual OBJECTHANDLE CreateHandleOfType(Object* object, int type) = 0; + + virtual OBJECTHANDLE CreateHandleOfType(Object* object, int type, int heapToAffinitizeTo) = 0; - virtual void* GetGlobalHandleStore() = 0; + virtual OBJECTHANDLE CreateHandleWithExtraInfo(Object* object, int type, void* pExtraInfo) = 0; - virtual void* CreateHandleStore(void* context) = 0; + virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary) = 0; - virtual void DestroyHandleStore(void* store) = 0; + virtual ~IGCHandleStore() {}; +}; + +class IGCHandleManager { +public: - virtual void UprootHandleStore(void* store) = 0; + virtual bool Initialize() = 0; - virtual bool ContainsHandle(void* store, OBJECTHANDLE handle) = 0; + virtual void Shutdown() = 0; - virtual OBJECTHANDLE CreateHandleOfType(void* store, Object* object, int type) = 0; + virtual void* GetHandleContext(OBJECTHANDLE handle) = 0; - virtual OBJECTHANDLE CreateHandleOfType(void* store, Object* object, int type, int heapToAffinitizeTo) = 0; + virtual IGCHandleStore* GetGlobalHandleStore() = 0; - virtual OBJECTHANDLE CreateHandleWithExtraInfo(void* store, Object* object, int type, void* pExtraInfo) = 0; + virtual IGCHandleStore* CreateHandleStore(void* context) = 0; - virtual OBJECTHANDLE CreateDependentHandle(void* store, Object* primary, Object* secondary) = 0; + virtual void DestroyHandleStore(IGCHandleStore* store) = 0; virtual OBJECTHANDLE CreateGlobalHandleOfType(Object* object, int type) = 0; @@ -438,6 +444,12 @@ public: virtual void DestroyHandleOfUnknownType(OBJECTHANDLE handle) = 0; virtual void* GetExtraInfoFromHandle(OBJECTHANDLE handle) = 0; + + virtual void StoreObjectInHandle(OBJECTHANDLE handle, Object* object) = 0; + + virtual bool StoreObjectInHandleIfNull(OBJECTHANDLE handle, Object* object) = 0; + + virtual Object* InterlockedCompareExchangeObjectInHandle(OBJECTHANDLE handle, Object* object, Object* comparandObject) = 0; }; // IGCHeap is the interface that the VM will use when interacting with the GC. @@ -687,9 +699,11 @@ public: // background GC as the BGC threads also need to walk LOH. virtual void PublishObject(uint8_t* obj) = 0; - // Gets the event that suspended threads will use to wait for the - // end of a GC. - virtual CLREventStatic* GetWaitForGCEvent() = 0; + // Signals the WaitForGCEvent event, indicating that a GC has completed. + virtual void SetWaitForGCEvent() = 0; + + // Resets the state of the WaitForGCEvent back to an unsignalled state. + virtual void ResetWaitForGCEvent() = 0; /* =========================================================================== diff --git a/src/gc/gcpriv.h b/src/gc/gcpriv.h index 108045cd37..a2ec64b614 100644 --- a/src/gc/gcpriv.h +++ b/src/gc/gcpriv.h @@ -197,8 +197,6 @@ void GCLogConfig (const char *fmt, ... ); //Please leave these definitions intact. -#define CLREvent CLREventStatic - // hosted api #ifdef memcpy #undef memcpy @@ -2766,7 +2764,7 @@ public: BOOL dont_restart_ee_p; PER_HEAP_ISOLATED - CLREvent bgc_start_event; + GCEvent bgc_start_event; #endif //BACKGROUND_GC // The variables in this block are known to the DAC and must come first @@ -2833,9 +2831,9 @@ public: PER_HEAP #ifndef MULTIPLE_HEAPS - CLREvent gc_done_event; + GCEvent gc_done_event; #else // MULTIPLE_HEAPS - CLREvent gc_done_event; + GCEvent gc_done_event; #endif // MULTIPLE_HEAPS PER_HEAP @@ -2890,10 +2888,10 @@ public: // notification feature which is only enabled if concurrent // GC is disabled. PER_HEAP_ISOLATED - CLREvent full_gc_approach_event; + GCEvent full_gc_approach_event; PER_HEAP_ISOLATED - CLREvent full_gc_end_event; + GCEvent full_gc_end_event; // Full GC Notification percentages. PER_HEAP_ISOLATED @@ -2913,9 +2911,9 @@ public: PER_HEAP size_t fgn_last_alloc; - static uint32_t user_thread_wait (CLREvent *event, BOOL no_mode_change, int time_out_ms=INFINITE); + static uint32_t user_thread_wait (GCEvent *event, BOOL no_mode_change, int time_out_ms=INFINITE); - static wait_full_gc_status full_gc_wait (CLREvent *event, int time_out_ms); + static wait_full_gc_status full_gc_wait (GCEvent *event, int time_out_ms); PER_HEAP uint8_t* demotion_low; @@ -2943,10 +2941,10 @@ public: bool gc_thread_no_affinitize_p; PER_HEAP_ISOLATED - CLREvent gc_start_event; + GCEvent gc_start_event; PER_HEAP_ISOLATED - CLREvent ee_suspend_event; + GCEvent ee_suspend_event; PER_HEAP heap_segment* new_heap_segment; @@ -3133,7 +3131,7 @@ protected: // we need to create them on the thread that called // SuspendEE which is heap 0. PER_HEAP_ISOLATED - CLREvent bgc_threads_sync_event; + GCEvent bgc_threads_sync_event; PER_HEAP Thread* bgc_thread; @@ -3142,13 +3140,13 @@ protected: CLRCriticalSection bgc_threads_timeout_cs; PER_HEAP_ISOLATED - CLREvent background_gc_done_event; + GCEvent background_gc_done_event; PER_HEAP_ISOLATED - CLREvent ee_proceed_event; + GCEvent ee_proceed_event; PER_HEAP - CLREvent gc_lh_block_event; + GCEvent gc_lh_block_event; PER_HEAP_ISOLATED bool gc_can_use_concurrent; diff --git a/src/gc/handletable.cpp b/src/gc/handletable.cpp index eee181959f..05137e4d68 100644 --- a/src/gc/handletable.cpp +++ b/src/gc/handletable.cpp @@ -285,12 +285,7 @@ OBJECTHANDLE HndCreateHandle(HHANDLETABLE hTable, uint32_t uType, OBJECTREF obje { CONTRACTL { -#ifdef FEATURE_REDHAWK - // Redhawk returns NULL on failure. NOTHROW; -#else - THROWS; -#endif GC_NOTRIGGER; if (object != NULL) { @@ -308,8 +303,7 @@ OBJECTHANDLE HndCreateHandle(HHANDLETABLE hTable, uint32_t uType, OBJECTREF obje if (g_pConfig->ShouldInjectFault(INJECTFAULT_HANDLETABLE)) { FAULT_NOT_FATAL(); - char *a = new char; - delete a; + return NULL; } #endif // _DEBUG && !FEATURE_REDHAWK @@ -331,11 +325,7 @@ OBJECTHANDLE HndCreateHandle(HHANDLETABLE hTable, uint32_t uType, OBJECTREF obje // did the allocation succeed? if (!handle) { -#ifdef FEATURE_REDHAWK return NULL; -#else - ThrowOutOfMemory(); -#endif } #ifdef DEBUG_DestroyedHandleValue diff --git a/src/gc/handletable.inl b/src/gc/handletable.inl index ae815c129b..752a7b01ae 100644 --- a/src/gc/handletable.inl +++ b/src/gc/handletable.inl @@ -22,13 +22,6 @@ inline void HndAssignHandle(OBJECTHANDLE handle, OBJECTREF objref) // sanity _ASSERTE(handle); -#ifdef _DEBUG_IMPL - // handle should not be in unloaded domain - ValidateAppDomainForHandle(handle); - - // Make sure the objref is valid before it is assigned to a handle - ValidateAssignObjrefForHandle(objref, HndGetHandleTableADIndex(HndGetHandleTable(handle))); -#endif // unwrap the objectref we were given _UNCHECKED_OBJECTREF value = OBJECTREF_TO_UNCHECKED_OBJECTREF(objref); @@ -49,13 +42,6 @@ inline void* HndInterlockedCompareExchangeHandle(OBJECTHANDLE handle, OBJECTREF // sanity _ASSERTE(handle); -#ifdef _DEBUG_IMPL - // handle should not be in unloaded domain - ValidateAppDomainForHandle(handle); - - // Make sure the objref is valid before it is assigned to a handle - ValidateAssignObjrefForHandle(objref, HndGetHandleTableADIndex(HndGetHandleTable(handle))); -#endif // unwrap the objectref we were given _UNCHECKED_OBJECTREF value = OBJECTREF_TO_UNCHECKED_OBJECTREF(objref); _UNCHECKED_OBJECTREF oldValue = OBJECTREF_TO_UNCHECKED_OBJECTREF(oldObjref); @@ -88,13 +74,6 @@ inline BOOL HndFirstAssignHandle(OBJECTHANDLE handle, OBJECTREF objref) // sanity _ASSERTE(handle); -#ifdef _DEBUG_IMPL - // handle should not be in unloaded domain - ValidateAppDomainForHandle(handle); - - // Make sure the objref is valid before it is assigned to a handle - ValidateAssignObjrefForHandle(objref, HndGetHandleTableADIndex(HndGetHandleTable(handle))); -#endif // unwrap the objectref we were given _UNCHECKED_OBJECTREF value = OBJECTREF_TO_UNCHECKED_OBJECTREF(objref); _UNCHECKED_OBJECTREF null = NULL; diff --git a/src/gc/handletablecore.cpp b/src/gc/handletablecore.cpp index 5776c26ace..00ab6a24b9 100644 --- a/src/gc/handletablecore.cpp +++ b/src/gc/handletablecore.cpp @@ -961,12 +961,12 @@ BOOL SegmentHandleAsyncPinHandles (TableSegment *pSegment) } // Replace an async pin handle with one from default domain -void SegmentRelocateAsyncPinHandles (TableSegment *pSegment, HandleTable *pTargetTable) +bool SegmentRelocateAsyncPinHandles (TableSegment *pSegment, HandleTable *pTargetTable) { CONTRACTL { GC_NOTRIGGER; - THROWS; + NOTHROW; MODE_COOPERATIVE; } CONTRACTL_END; @@ -975,7 +975,7 @@ void SegmentRelocateAsyncPinHandles (TableSegment *pSegment, HandleTable *pTarge if (uBlock == BLOCK_INVALID) { // There is no pinning handles. - return; + return true; } for (uBlock = 0; uBlock < pSegment->bEmptyLine; uBlock ++) { @@ -1003,12 +1003,21 @@ void SegmentRelocateAsyncPinHandles (TableSegment *pSegment, HandleTable *pTarge overlapped->m_userObject = NULL; } BashMTForPinnedObject(ObjectToOBJECTREF(value)); - overlapped->m_pinSelf = CreateAsyncPinningHandle((HHANDLETABLE)pTargetTable,ObjectToOBJECTREF(value)); + + overlapped->m_pinSelf = HndCreateHandle((HHANDLETABLE)pTargetTable, HNDTYPE_ASYNCPINNED, ObjectToOBJECTREF(value)); + if (!overlapped->m_pinSelf) + { + // failed to allocate a new handle - callers have to handle this. + return false; + } + *pValue = NULL; } pValue ++; } while (pValue != pLast); } + + return true; } // Mark all non-pending AsyncPinHandle ready for cleanup. @@ -1067,6 +1076,7 @@ void TableRelocateAsyncPinHandles(HandleTable *pTable, HandleTable *pTargetTable BOOL fGotException = FALSE; TableSegment *pSegment = pTable->pSegmentList; + bool wasSuccessful = true; #ifdef _DEBUG // on debug builds, execute the OOM path 10% of the time. @@ -1075,21 +1085,18 @@ void TableRelocateAsyncPinHandles(HandleTable *pTable, HandleTable *pTargetTable #endif // Step 1: replace pinning handles with ones from default domain - EX_TRY + while (pSegment) { - while (pSegment) + wasSuccessful = wasSuccessful && SegmentRelocateAsyncPinHandles (pSegment, pTargetTable); + if (!wasSuccessful) { - SegmentRelocateAsyncPinHandles (pSegment, pTargetTable); - pSegment = pSegment->pNextSegment; + break; } + + pSegment = pSegment->pNextSegment; } - EX_CATCH - { - fGotException = TRUE; - } - EX_END_CATCH(SwallowAllExceptions); - if (!fGotException) + if (wasSuccessful) { return; } @@ -2719,9 +2726,8 @@ void TableFreeBulkUnpreparedHandles(HandleTable *pTable, uint32_t uType, const O { CONTRACTL { - THROWS; + NOTHROW; WRAPPER(GC_TRIGGERS); - INJECT_FAULT(COMPlusThrowOM()); } CONTRACTL_END; diff --git a/src/gc/handletablescan.cpp b/src/gc/handletablescan.cpp index 86ce62d5b1..967aca5095 100644 --- a/src/gc/handletablescan.cpp +++ b/src/gc/handletablescan.cpp @@ -949,7 +949,7 @@ static void VerifyObjectAndAge(_UNCHECKED_OBJECTREF *pValue, _UNCHECKED_OBJECTRE if (minAge >= GEN_MAX_AGE || (minAge > thisAge && thisAge < static_cast(g_theGCHeap->GetMaxGeneration()))) { _ASSERTE(!"Fatal Error in HandleTable."); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + GCToEEInterface::HandleFatalError(COR_E_EXECUTIONENGINE); } } @@ -1423,7 +1423,7 @@ PTR_TableSegment CALLBACK StandardSegmentIterator(PTR_HandleTable pTable, PTR_Ta { CONTRACTL { - WRAPPER(THROWS); + WRAPPER(NOTHROW); WRAPPER(GC_TRIGGERS); FORBID_FAULT; SUPPORTS_DAC; diff --git a/src/gc/objecthandle.cpp b/src/gc/objecthandle.cpp index 5df53baad5..dd43ec23d5 100644 --- a/src/gc/objecthandle.cpp +++ b/src/gc/objecthandle.cpp @@ -19,6 +19,8 @@ #include "objecthandle.h" #include "handletablepriv.h" +#include "gchandletableimpl.h" + #ifdef FEATURE_COMINTEROP #include "comcallablewrapper.h" #endif // FEATURE_COMINTEROP @@ -627,54 +629,62 @@ bool Ref_Initialize() if (pBuckets == NULL) return false; - ZeroMemory(pBuckets, - INITIAL_HANDLE_TABLE_ARRAY_SIZE * sizeof (HandleTableBucket *)); + ZeroMemory(pBuckets, INITIAL_HANDLE_TABLE_ARRAY_SIZE * sizeof (HandleTableBucket *)); - // Crate the first bucket - HandleTableBucket * pBucket = new (nothrow) HandleTableBucket; - if (pBucket != NULL) + g_gcGlobalHandleStore = new (nothrow) GCHandleStore(); + if (g_gcGlobalHandleStore == NULL) { - pBucket->HandleTableIndex = 0; - - int n_slots = getNumberOfSlots(); - - HandleTableBucketHolder bucketHolder(pBucket, n_slots); + delete[] pBuckets; + return false; + } - // create the handle table set for the first bucket - pBucket->pTable = new (nothrow) HHANDLETABLE[n_slots]; - if (pBucket->pTable == NULL) - goto CleanupAndFail; + // Initialize the bucket in the global handle store + HandleTableBucket* pBucket = &g_gcGlobalHandleStore->_underlyingBucket; - ZeroMemory(pBucket->pTable, - n_slots * sizeof(HHANDLETABLE)); - for (int uCPUindex = 0; uCPUindex < n_slots; uCPUindex++) - { - pBucket->pTable[uCPUindex] = HndCreateHandleTable(s_rgTypeFlags, _countof(s_rgTypeFlags), ADIndex(1)); - if (pBucket->pTable[uCPUindex] == NULL) - goto CleanupAndFail; + pBucket->HandleTableIndex = 0; - HndSetHandleTableIndex(pBucket->pTable[uCPUindex], 0); - } + int n_slots = getNumberOfSlots(); - pBuckets[0] = pBucket; - bucketHolder.SuppressRelease(); + HandleTableBucketHolder bucketHolder(pBucket, n_slots); - g_HandleTableMap.pBuckets = pBuckets; - g_HandleTableMap.dwMaxIndex = INITIAL_HANDLE_TABLE_ARRAY_SIZE; - g_HandleTableMap.pNext = NULL; + // create the handle table set for the first bucket + pBucket->pTable = new (nothrow) HHANDLETABLE[n_slots]; + if (pBucket->pTable == NULL) + goto CleanupAndFail; - // Allocate contexts used during dependent handle promotion scanning. There's one of these for every GC - // heap since they're scanned in parallel. - g_pDependentHandleContexts = new (nothrow) DhContext[n_slots]; - if (g_pDependentHandleContexts == NULL) + ZeroMemory(pBucket->pTable, + n_slots * sizeof(HHANDLETABLE)); + for (int uCPUindex = 0; uCPUindex < n_slots; uCPUindex++) + { + pBucket->pTable[uCPUindex] = HndCreateHandleTable(s_rgTypeFlags, _countof(s_rgTypeFlags), ADIndex(1)); + if (pBucket->pTable[uCPUindex] == NULL) goto CleanupAndFail; - return true; + HndSetHandleTableIndex(pBucket->pTable[uCPUindex], 0); } + pBuckets[0] = pBucket; + bucketHolder.SuppressRelease(); + + g_HandleTableMap.pBuckets = pBuckets; + g_HandleTableMap.dwMaxIndex = INITIAL_HANDLE_TABLE_ARRAY_SIZE; + g_HandleTableMap.pNext = NULL; + + // Allocate contexts used during dependent handle promotion scanning. There's one of these for every GC + // heap since they're scanned in parallel. + g_pDependentHandleContexts = new (nothrow) DhContext[n_slots]; + if (g_pDependentHandleContexts == NULL) + goto CleanupAndFail; + + return true; + CleanupAndFail: if (pBuckets != NULL) delete[] pBuckets; + + if (g_gcGlobalHandleStore != NULL) + delete g_gcGlobalHandleStore; + return false; } @@ -694,9 +704,6 @@ void Ref_Shutdown() // don't destroy any of the indexed handle tables; they should // be destroyed externally. - // destroy the global handle table bucket tables - Ref_DestroyHandleTableBucket(g_HandleTableMap.pBuckets[0]); - // destroy the handle table bucket array HandleTableMap *walk = &g_HandleTableMap; while (walk) { @@ -714,26 +721,37 @@ void Ref_Shutdown() } #ifndef FEATURE_REDHAWK -// ATTENTION: interface changed -// Note: this function called only from AppDomain::Init() -HandleTableBucket *Ref_CreateHandleTableBucket(ADIndex uADIndex) +HandleTableBucket* Ref_CreateHandleTableBucket(void* context) +{ + HandleTableBucket* result = new (nothrow) HandleTableBucket(); + if (result == nullptr) + return nullptr; + + if (!Ref_InitializeHandleTableBucket(result, context)) + { + delete result; + return nullptr; + } + + return result; +} + +bool Ref_InitializeHandleTableBucket(HandleTableBucket* bucket, void* context) { CONTRACTL { - THROWS; + NOTHROW; WRAPPER(GC_TRIGGERS); - INJECT_FAULT(COMPlusThrowOM()); + INJECT_FAULT(return false); } CONTRACTL_END; - HandleTableBucket *result = NULL; - HandleTableMap *walk; - - walk = &g_HandleTableMap; + HandleTableBucket *result = bucket; + HandleTableMap *walk = &g_HandleTableMap; + HandleTableMap *last = NULL; uint32_t offset = 0; - result = new HandleTableBucket; result->pTable = NULL; // create handle table set for the bucket @@ -741,13 +759,18 @@ HandleTableBucket *Ref_CreateHandleTableBucket(ADIndex uADIndex) HandleTableBucketHolder bucketHolder(result, n_slots); - result->pTable = new HHANDLETABLE [ n_slots ]; - ZeroMemory(result->pTable, n_slots * sizeof (HHANDLETABLE)); + result->pTable = new (nothrow) HHANDLETABLE[n_slots]; + if (!result->pTable) + { + return false; + } + + ZeroMemory(result->pTable, n_slots * sizeof(HHANDLETABLE)); for (int uCPUindex=0; uCPUindex < n_slots; uCPUindex++) { - result->pTable[uCPUindex] = HndCreateHandleTable(s_rgTypeFlags, _countof(s_rgTypeFlags), uADIndex); + result->pTable[uCPUindex] = HndCreateHandleTable(s_rgTypeFlags, _countof(s_rgTypeFlags), ADIndex((DWORD)(uintptr_t)context)); if (!result->pTable[uCPUindex]) - COMPlusThrowOM(); + return false; } for (;;) { @@ -762,7 +785,7 @@ HandleTableBucket *Ref_CreateHandleTableBucket(ADIndex uADIndex) if (Interlocked::CompareExchangePointer(&walk->pBuckets[i], result, NULL) == 0) { // Get a free slot. bucketHolder.SuppressRelease(); - return result; + return true; } } } @@ -774,9 +797,18 @@ HandleTableBucket *Ref_CreateHandleTableBucket(ADIndex uADIndex) // No free slot. // Let's create a new node NewHolder newMap; - newMap = new HandleTableMap; + newMap = new (nothrow) HandleTableMap; + if (!newMap) + { + return false; + } + + newMap->pBuckets = new (nothrow) HandleTableBucket * [ INITIAL_HANDLE_TABLE_ARRAY_SIZE ]; + if (!newMap->pBuckets) + { + return false; + } - newMap->pBuckets = new HandleTableBucket * [ INITIAL_HANDLE_TABLE_ARRAY_SIZE ]; newMap.SuppressRelease(); newMap->dwMaxIndex = last->dwMaxIndex + INITIAL_HANDLE_TABLE_ARRAY_SIZE; diff --git a/src/gc/objecthandle.h b/src/gc/objecthandle.h index d3e45f8659..b3e4b58a1c 100644 --- a/src/gc/objecthandle.h +++ b/src/gc/objecthandle.h @@ -21,16 +21,6 @@ #include #endif // FEATURE_COMINTEROP -/* - * Convenience macros for accessing handles. StoreFirstObjectInHandle is like - * StoreObjectInHandle, except it only succeeds if transitioning from NULL to - * non-NULL. In other words, if this handle is being initialized for the first - * time. - */ -#define StoreObjectInHandle(handle, object) HndAssignHandle(handle, object) -#define InterlockedCompareExchangeObjectInHandle(handle, object, oldObj) HndInterlockedCompareExchangeHandle(handle, object, oldObj) -#define StoreFirstObjectInHandle(handle, object) HndFirstAssignHandle(handle, object) - typedef DPTR(struct HandleTableMap) PTR_HandleTableMap; typedef DPTR(struct HandleTableBucket) PTR_HandleTableBucket; typedef DPTR(PTR_HandleTableBucket) PTR_PTR_HandleTableBucket; @@ -90,23 +80,13 @@ void GCHandleValidatePinnedObject(OBJECTREF obj); int GetCurrentThreadHomeHeapNumber(); -inline void ResetOBJECTHANDLE(OBJECTHANDLE handle) -{ - WRAPPER_NO_CONTRACT; - - StoreObjectInHandle(handle, NULL); -} - -#ifndef FEATURE_REDHAWK -typedef Holder,ResetOBJECTHANDLE> ObjectInHandleHolder; -#endif - /* * Table maintenance routines */ bool Ref_Initialize(); void Ref_Shutdown(); -HandleTableBucket *Ref_CreateHandleTableBucket(ADIndex uADIndex); +HandleTableBucket* Ref_CreateHandleTableBucket(void* context); +bool Ref_InitializeHandleTableBucket(HandleTableBucket* bucket, void* context); BOOL Ref_HandleAsyncPinHandles(); void Ref_RelocateAsyncPinHandles(HandleTableBucket *pSource, HandleTableBucket *pTarget); void Ref_RemoveHandleTableBucket(HandleTableBucket *pBucket); diff --git a/src/gc/sample/GCSample.cpp b/src/gc/sample/GCSample.cpp index 2914ee1665..0a771b7e91 100644 --- a/src/gc/sample/GCSample.cpp +++ b/src/gc/sample/GCSample.cpp @@ -130,8 +130,8 @@ int __cdecl main(int argc, char* argv[]) // GcDacVars dacVars; IGCHeap *pGCHeap; - IGCHandleTable *pGCHandleTable; - if (!InitializeGarbageCollector(nullptr, &pGCHeap, &pGCHandleTable, &dacVars)) + IGCHandleManager *pGCHandleManager; + if (!InitializeGarbageCollector(nullptr, &pGCHeap, &pGCHandleManager, &dacVars)) { return -1; } @@ -140,9 +140,9 @@ int __cdecl main(int argc, char* argv[]) return -1; // - // Initialize handle table + // Initialize handle manager // - if (!pGCHandleTable->Initialize()) + if (!pGCHandleManager->Initialize()) return -1; // diff --git a/src/gc/unix/CMakeLists.txt b/src/gc/unix/CMakeLists.txt index 3e1aa5ad19..10258108c6 100644 --- a/src/gc/unix/CMakeLists.txt +++ b/src/gc/unix/CMakeLists.txt @@ -6,6 +6,7 @@ include(configure.cmake) set(GC_PAL_SOURCES gcenv.unix.cpp + events.cpp cgroup.cpp) add_library(gc_unix STATIC ${GC_PAL_SOURCES} ${VERSION_FILE_PATH}) diff --git a/src/gc/unix/config.h.in b/src/gc/unix/config.h.in index 7578c74c05..21980a7d08 100644 --- a/src/gc/unix/config.h.in +++ b/src/gc/unix/config.h.in @@ -10,5 +10,7 @@ #cmakedefine01 HAVE_PTHREAD_THREADID_NP #cmakedefine01 HAVE_PTHREAD_GETTHREADID_NP #cmakedefine01 HAVE_SCHED_GETCPU +#cmakedefine01 HAVE_PTHREAD_CONDATTR_SETCLOCK +#cmakedefine01 HAVE_MACH_ABSOLUTE_TIME -#endif // __CONFIG_H__ \ No newline at end of file +#endif // __CONFIG_H__ diff --git a/src/gc/unix/configure.cmake b/src/gc/unix/configure.cmake index 6e1e8fe27d..5f2bdbd8b3 100644 --- a/src/gc/unix/configure.cmake +++ b/src/gc/unix/configure.cmake @@ -37,4 +37,19 @@ check_cxx_source_runs(" } " HAVE_SCHED_GETCPU) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) \ No newline at end of file +check_library_exists(pthread pthread_condattr_setclock "" HAVE_PTHREAD_CONDATTR_SETCLOCK) + +check_cxx_source_runs(" + #include + #include + int main() + { + int ret; + mach_timebase_info_data_t timebaseInfo; + ret = mach_timebase_info(&timebaseInfo); + mach_absolute_time(); + exit(ret); + } + " HAVE_MACH_ABSOLUTE_TIME) + +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h) diff --git a/src/gc/unix/events.cpp b/src/gc/unix/events.cpp new file mode 100644 index 0000000000..7c665f4aaa --- /dev/null +++ b/src/gc/unix/events.cpp @@ -0,0 +1,323 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include "config.h" + +#ifndef __out_z +#define __out_z +#endif // __out_z + +#include "gcenv.structs.h" +#include "gcenv.base.h" +#include "gcenv.os.h" +#include "globals.h" + +#if HAVE_MACH_ABSOLUTE_TIME +mach_timebase_info_data_t g_TimebaseInfo; +#endif // MACH_ABSOLUTE_TIME + +namespace +{ + +#if HAVE_PTHREAD_CONDATTR_SETCLOCK +void TimeSpecAdd(timespec* time, uint32_t milliseconds) +{ + uint64_t nsec = time->tv_nsec + (uint64_t)milliseconds * tccMilliSecondsToNanoSeconds; + if (nsec >= tccSecondsToNanoSeconds) + { + time->tv_sec += nsec / tccSecondsToNanoSeconds; + nsec %= tccSecondsToNanoSeconds; + } + + time->tv_nsec = nsec; +} +#endif // HAVE_PTHREAD_CONDATTR_SETCLOCK + +#if HAVE_MACH_ABSOLUTE_TIME +// Convert nanoseconds to the timespec structure +// Parameters: +// nanoseconds - time in nanoseconds to convert +// t - the target timespec structure +void NanosecondsToTimeSpec(uint64_t nanoseconds, timespec* t) +{ + t->tv_sec = nanoseconds / tccSecondsToNanoSeconds; + t->tv_nsec = nanoseconds % tccSecondsToNanoSeconds; +} +#endif // HAVE_PTHREAD_CONDATTR_SETCLOCK + +} // anonymous namespace + +class GCEvent::Impl +{ + pthread_cond_t m_condition; + pthread_mutex_t m_mutex; + bool m_manualReset; + bool m_state; + bool m_isValid; + +public: + + Impl(bool manualReset, bool initialState) + : m_manualReset(manualReset), + m_state(initialState), + m_isValid(false) + { + } + + bool Initialize() + { + pthread_condattr_t attrs; + int st = pthread_condattr_init(&attrs); + if (st != 0) + { + assert(!"Failed to initialize UnixEvent condition attribute"); + return false; + } + + // TODO(segilles) implement this for CoreCLR + //PthreadCondAttrHolder attrsHolder(&attrs); + +#if HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_MACH_ABSOLUTE_TIME + // Ensure that the pthread_cond_timedwait will use CLOCK_MONOTONIC + st = pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC); + if (st != 0) + { + assert(!"Failed to set UnixEvent condition variable wait clock"); + return false; + } +#endif // HAVE_PTHREAD_CONDATTR_SETCLOCK && !HAVE_MACH_ABSOLUTE_TIME + + st = pthread_mutex_init(&m_mutex, NULL); + if (st != 0) + { + assert(!"Failed to initialize UnixEvent mutex"); + return false; + } + + st = pthread_cond_init(&m_condition, &attrs); + if (st != 0) + { + assert(!"Failed to initialize UnixEvent condition variable"); + + st = pthread_mutex_destroy(&m_mutex); + assert(st == 0 && "Failed to destroy UnixEvent mutex"); + return false; + } + + m_isValid = true; + + return true; + } + + void CloseEvent() + { + if (m_isValid) + { + int st = pthread_mutex_destroy(&m_mutex); + assert(st == 0 && "Failed to destroy UnixEvent mutex"); + + st = pthread_cond_destroy(&m_condition); + assert(st == 0 && "Failed to destroy UnixEvent condition variable"); + } + } + + uint32_t Wait(uint32_t milliseconds, bool alertable) + { + UNREFERENCED_PARAMETER(alertable); + + timespec endTime; +#if HAVE_MACH_ABSOLUTE_TIME + uint64_t endMachTime; + if (milliseconds != INFINITE) + { + uint64_t nanoseconds = (uint64_t)milliseconds * tccMilliSecondsToNanoSeconds; + NanosecondsToTimeSpec(nanoseconds, &endTime); + endMachTime = mach_absolute_time() + nanoseconds * g_TimebaseInfo.denom / g_TimebaseInfo.numer; + } +#elif HAVE_PTHREAD_CONDATTR_SETCLOCK + if (milliseconds != INFINITE) + { + clock_gettime(CLOCK_MONOTONIC, &endTime); + TimeSpecAdd(&endTime, milliseconds); + } +#else +#error Don't know how to perfom timed wait on this platform +#endif + + int st = 0; + + pthread_mutex_lock(&m_mutex); + while (!m_state) + { + if (milliseconds == INFINITE) + { + st = pthread_cond_wait(&m_condition, &m_mutex); + } + else + { +#if HAVE_MACH_ABSOLUTE_TIME + // Since OSX doesn't support CLOCK_MONOTONIC, we use relative variant of the + // timed wait and we need to handle spurious wakeups properly. + st = pthread_cond_timedwait_relative_np(&m_condition, &m_mutex, &endTime); + if ((st == 0) && !m_state) + { + uint64_t machTime = mach_absolute_time(); + if (machTime < endMachTime) + { + // The wake up was spurious, recalculate the relative endTime + uint64_t remainingNanoseconds = (endMachTime - machTime) * g_TimebaseInfo.numer / g_TimebaseInfo.denom; + NanosecondsToTimeSpec(remainingNanoseconds, &endTime); + } + else + { + // Although the timed wait didn't report a timeout, time calculated from the + // mach time shows we have already reached the end time. It can happen if + // the wait was spuriously woken up right before the timeout. + st = ETIMEDOUT; + } + } +#else // HAVE_MACH_ABSOLUTE_TIME + st = pthread_cond_timedwait(&m_condition, &m_mutex, &endTime); +#endif // HAVE_MACH_ABSOLUTE_TIME + // Verify that if the wait timed out, the event was not set + assert((st != ETIMEDOUT) || !m_state); + } + + if (st != 0) + { + // wait failed or timed out + break; + } + } + + if ((st == 0) && !m_manualReset) + { + // Clear the state for auto-reset events so that only one waiter gets released + m_state = false; + } + + pthread_mutex_unlock(&m_mutex); + + uint32_t waitStatus; + + if (st == 0) + { + waitStatus = WAIT_OBJECT_0; + } + else if (st == ETIMEDOUT) + { + waitStatus = WAIT_TIMEOUT; + } + else + { + waitStatus = WAIT_FAILED; + } + + return waitStatus; + } + + void Set() + { + pthread_mutex_lock(&m_mutex); + m_state = true; + pthread_mutex_unlock(&m_mutex); + + // Unblock all threads waiting for the condition variable + pthread_cond_broadcast(&m_condition); + } + + void Reset() + { + pthread_mutex_lock(&m_mutex); + m_state = false; + pthread_mutex_unlock(&m_mutex); + } +}; + +GCEvent::GCEvent() + : m_impl(nullptr) +{ +} + +void GCEvent::CloseEvent() +{ + assert(m_impl != nullptr); + m_impl->CloseEvent(); +} + +void GCEvent::Set() +{ + assert(m_impl != nullptr); + m_impl->Set(); +} + +void GCEvent::Reset() +{ + assert(m_impl != nullptr); + m_impl->Reset(); +} + +uint32_t GCEvent::Wait(uint32_t timeout, bool alertable) +{ + assert(m_impl != nullptr); + return m_impl->Wait(timeout, alertable); +} + +bool GCEvent::CreateAutoEventNoThrow(bool initialState) +{ + // This implementation of GCEvent makes no distinction between + // host-aware and non-host-aware events (since there will be no host). + return CreateOSAutoEventNoThrow(initialState); +} + +bool GCEvent::CreateManualEventNoThrow(bool initialState) +{ + // This implementation of GCEvent makes no distinction between + // host-aware and non-host-aware events (since there will be no host). + return CreateOSManualEventNoThrow(initialState); +} + +bool GCEvent::CreateOSAutoEventNoThrow(bool initialState) +{ + assert(m_impl == nullptr); + std::unique_ptr event(new (std::nothrow) GCEvent::Impl(false, initialState)); + if (!event) + { + return false; + } + + if (!event->Initialize()) + { + return false; + } + + m_impl = event.release(); + return true; +} + +bool GCEvent::CreateOSManualEventNoThrow(bool initialState) +{ + assert(m_impl == nullptr); + std::unique_ptr event(new (std::nothrow) GCEvent::Impl(true, initialState)); + if (!event) + { + return false; + } + + if (!event->Initialize()) + { + return false; + } + + m_impl = event.release(); + return true; +} + diff --git a/src/gc/unix/gcenv.unix.cpp b/src/gc/unix/gcenv.unix.cpp index 45489c69a7..5fc63f47d3 100644 --- a/src/gc/unix/gcenv.unix.cpp +++ b/src/gc/unix/gcenv.unix.cpp @@ -36,13 +36,13 @@ static_assert(sizeof(uint64_t) == 8, "unsigned long isn't 8 bytes"); #error "A GC-private implementation of GCToOSInterface should only be used with FEATURE_STANDALONE_GC" #endif // FEATURE_STANDALONE_GC -#ifdef HAVE_SYS_TIME_H +#if HAVE_SYS_TIME_H #include #else #error "sys/time.h required by GC PAL for the time being" #endif // HAVE_SYS_TIME_ -#ifdef HAVE_SYS_MMAN_H +#if HAVE_SYS_MMAN_H #include #else #error "sys/mman.h required by GC PAL" @@ -56,18 +56,7 @@ static_assert(sizeof(uint64_t) == 8, "unsigned long isn't 8 bytes"); #include // sched_yield #include #include // sysconf - -// The number of milliseconds in a second. -static const int tccSecondsToMilliSeconds = 1000; - -// The number of microseconds in a second. -static const int tccSecondsToMicroSeconds = 1000000; - -// The number of microseconds in a millisecond. -static const int tccMilliSecondsToMicroSeconds = 1000; - -// The number of nanoseconds in a millisecond. -static const int tccMilliSecondsToNanoSeconds = 1000000; +#include "globals.h" // The cachced number of logical CPUs observed. static uint32_t g_logicalCpuCount = 0; @@ -117,6 +106,14 @@ bool GCToOSInterface::Initialize() return false; } +#if HAVE_MACH_ABSOLUTE_TIME + kern_return_t machRet; + if ((machRet = mach_timebase_info(&g_TimebaseInfo)) != KERN_SUCCESS) + { + return false; + } +#endif // HAVE_MACH_ABSOLUTE_TIME + return true; } @@ -348,8 +345,20 @@ bool GCToOSInterface::VirtualDecommit(void* address, size_t size) // true if it has succeeded, false if it has failed bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock) { - // TODO(CoreCLR#1259) pipe to madvise? - return false; + int st; +#if HAVE_MADV_FREE + // Try to use MADV_FREE if supported. It tells the kernel that the application doesn't + // need the pages in the range. Freeing the pages can be delayed until a memory pressure + // occurs. + st = madvise(address, size, MADV_FREE); + if (st != 0) +#endif + { + // In case the MADV_FREE is not supported, use MADV_DONTNEED + st = madvise(address, size, MADV_DONTNEED); + } + + return (st == 0); } // Check if the OS supports write watching diff --git a/src/gc/unix/globals.h b/src/gc/unix/globals.h new file mode 100644 index 0000000000..bc3dc49918 --- /dev/null +++ b/src/gc/unix/globals.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef __GLOBALS_H__ +#define __GLOBALS_H__ + +#if HAVE_MACH_ABSOLUTE_TIME +#include +#endif // HAVE_MACH_ABSOLUTE_TIME + +const int tccSecondsToMilliSeconds = 1000; + +// The number of microseconds in a second. +const int tccSecondsToMicroSeconds = 1000000; + +// The number of nanoseconds in a second. +const int tccSecondsToNanoSeconds = 1000000000; + +// The number of microseconds in a millisecond. +const int tccMilliSecondsToMicroSeconds = 1000; + +// The number of nanoseconds in a millisecond. +const int tccMilliSecondsToNanoSeconds = 1000000; + +#if HAVE_MACH_ABSOLUTE_TIME +extern mach_timebase_info_data_t g_TimebaseInfo; +#endif // HAVE_MACH_ABSOLUTE_TIME + +#endif // __GLOBALS_H__ diff --git a/src/gc/windows/gcenv.windows.cpp b/src/gc/windows/gcenv.windows.cpp index 30232bfb09..3749f06a68 100644 --- a/src/gc/windows/gcenv.windows.cpp +++ b/src/gc/windows/gcenv.windows.cpp @@ -626,3 +626,145 @@ void CLRCriticalSection::Leave() { ::LeaveCriticalSection(&m_cs); } + +// WindowsEvent is an implementation of GCEvent that forwards +// directly to Win32 APIs. +class GCEvent::Impl +{ +private: + HANDLE m_hEvent; + +public: + Impl() : m_hEvent(INVALID_HANDLE_VALUE) {} + + bool IsValid() const + { + return m_hEvent != INVALID_HANDLE_VALUE; + } + + void Set() + { + assert(IsValid()); + BOOL result = SetEvent(m_hEvent); + assert(result && "SetEvent failed"); + } + + void Reset() + { + assert(IsValid()); + BOOL result = ResetEvent(m_hEvent); + assert(result && "ResetEvent failed"); + } + + uint32_t Wait(uint32_t timeout, bool alertable) + { + UNREFERENCED_PARAMETER(alertable); + assert(IsValid()); + + return WaitForSingleObject(m_hEvent, timeout); + } + + void CloseEvent() + { + assert(IsValid()); + BOOL result = CloseHandle(m_hEvent); + assert(result && "CloseHandle failed"); + m_hEvent = INVALID_HANDLE_VALUE; + } + + bool CreateAutoEvent(bool initialState) + { + m_hEvent = CreateEvent(nullptr, false, initialState, nullptr); + return IsValid(); + } + + bool CreateManualEvent(bool initialState) + { + m_hEvent = CreateEvent(nullptr, true, initialState, nullptr); + return IsValid(); + } +}; + +GCEvent::GCEvent() + : m_impl(nullptr) +{ +} + +void GCEvent::CloseEvent() +{ + assert(m_impl != nullptr); + m_impl->CloseEvent(); +} + +void GCEvent::Set() +{ + assert(m_impl != nullptr); + m_impl->Set(); +} + +void GCEvent::Reset() +{ + assert(m_impl != nullptr); + m_impl->Reset(); +} + +uint32_t GCEvent::Wait(uint32_t timeout, bool alertable) +{ + assert(m_impl != nullptr); + return m_impl->Wait(timeout, alertable); +} + +bool GCEvent::CreateAutoEventNoThrow(bool initialState) +{ + // [DESKTOP TODO] The difference between events and OS events is + // whether or not the hosting API is made aware of them. When (if) + // we implement hosting support for Local GC, we will need to be + // aware of the host here. + return CreateOSAutoEventNoThrow(initialState); +} + +bool GCEvent::CreateManualEventNoThrow(bool initialState) +{ + // [DESKTOP TODO] The difference between events and OS events is + // whether or not the hosting API is made aware of them. When (if) + // we implement hosting support for Local GC, we will need to be + // aware of the host here. + return CreateOSManualEventNoThrow(initialState); +} + +bool GCEvent::CreateOSAutoEventNoThrow(bool initialState) +{ + assert(m_impl == nullptr); + std::unique_ptr event(new (std::nothrow) GCEvent::Impl()); + if (!event) + { + return false; + } + + if (!event->CreateAutoEvent(initialState)) + { + return false; + } + + m_impl = event.release(); + return true; +} + +bool GCEvent::CreateOSManualEventNoThrow(bool initialState) +{ + assert(m_impl == nullptr); + std::unique_ptr event(new (std::nothrow) GCEvent::Impl()); + if (!event) + { + return false; + } + + if (!event->CreateManualEvent(initialState)) + { + return false; + } + + m_impl = event.release(); + return true; +} + -- cgit v1.2.3