From 3092adcf46c1df85d1d56231692b26dc63c597c1 Mon Sep 17 00:00:00 2001 From: Sean Gillespie Date: Tue, 16 May 2017 10:19:36 -0700 Subject: [PATCH] [Local GC] Add async pinned handle methods to the handle interface (dotnet/coreclr#11569) * [Local GC] Add async pinned handle methods to the handle interface * Add a callback to HandleAsyncPinnedHandles for handle enumeration * Rename HandleAsyncPinnedHandles -> EnumerateAsyncPinnedHandles * Introduce typedef for function pointer type Commit migrated from https://github.com/dotnet/coreclr/commit/eb12b78102f2b54dc082caabcd1b59b42166509b --- src/coreclr/src/gc/gchandletable.cpp | 12 ++++++++++++ src/coreclr/src/gc/gchandletableimpl.h | 4 ++++ src/coreclr/src/gc/gcinterface.h | 5 +++++ src/coreclr/src/gc/handletable.cpp | 13 +++++++++---- src/coreclr/src/gc/handletablecore.cpp | 13 ++++++------- src/coreclr/src/gc/handletablepriv.h | 33 ++++++++++++++++++++++++++++++++- src/coreclr/src/gc/objecthandle.h | 2 +- src/coreclr/src/vm/appdomain.cpp | 8 +++----- src/coreclr/src/vm/nativeoverlapped.cpp | 31 +++++++++++++++++++++++++++++-- 9 files changed, 101 insertions(+), 20 deletions(-) diff --git a/src/coreclr/src/gc/gchandletable.cpp b/src/coreclr/src/gc/gchandletable.cpp index ea02308..63f2f79 100644 --- a/src/coreclr/src/gc/gchandletable.cpp +++ b/src/coreclr/src/gc/gchandletable.cpp @@ -56,6 +56,18 @@ OBJECTHANDLE GCHandleStore::CreateDependentHandle(Object* primary, Object* secon return handle; } +void GCHandleStore::RelocateAsyncPinnedHandles(IGCHandleStore* pTarget) +{ + // assumption - the IGCHandleStore is an instance of GCHandleStore + GCHandleStore* other = static_cast(pTarget); + ::Ref_RelocateAsyncPinHandles(&_underlyingBucket, &other->_underlyingBucket); +} + +bool GCHandleStore::EnumerateAsyncPinnedHandles(async_pin_enum_fn callback, void* context) +{ + return !!::Ref_HandleAsyncPinHandles(callback, context); +} + GCHandleStore::~GCHandleStore() { ::Ref_DestroyHandleTableBucket(&_underlyingBucket); diff --git a/src/coreclr/src/gc/gchandletableimpl.h b/src/coreclr/src/gc/gchandletableimpl.h index a0a03ae..4be346f 100644 --- a/src/coreclr/src/gc/gchandletableimpl.h +++ b/src/coreclr/src/gc/gchandletableimpl.h @@ -23,6 +23,10 @@ public: virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary); + virtual void RelocateAsyncPinnedHandles(IGCHandleStore* pTarget); + + virtual bool EnumerateAsyncPinnedHandles(async_pin_enum_fn callback, void* context); + virtual ~GCHandleStore(); HandleTableBucket _underlyingBucket; diff --git a/src/coreclr/src/gc/gcinterface.h b/src/coreclr/src/gc/gcinterface.h index 3cea6a1..3eb332c 100644 --- a/src/coreclr/src/gc/gcinterface.h +++ b/src/coreclr/src/gc/gcinterface.h @@ -399,6 +399,7 @@ typedef void (* record_surv_fn)(uint8_t* begin, uint8_t* end, ptrdiff_t reloc, v typedef void (* fq_walk_fn)(bool, void*); typedef void (* fq_scan_fn)(Object** ppObject, ScanContext *pSC, uint32_t dwFlags); typedef void (* handle_scan_fn)(Object** pRef, Object* pSec, uint32_t flags, ScanContext* context, bool isDependent); +typedef bool (* async_pin_enum_fn)(Object* object, void* context); // Opaque type for tracking object pointers #ifndef DACCESS_COMPILE @@ -426,6 +427,10 @@ public: virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary) = 0; + virtual void RelocateAsyncPinnedHandles(IGCHandleStore* pTarget) = 0; + + virtual bool EnumerateAsyncPinnedHandles(async_pin_enum_fn callback, void* context) = 0; + virtual ~IGCHandleStore() {}; }; diff --git a/src/coreclr/src/gc/handletable.cpp b/src/coreclr/src/gc/handletable.cpp index 05137e4..da50483 100644 --- a/src/coreclr/src/gc/handletable.cpp +++ b/src/coreclr/src/gc/handletable.cpp @@ -1286,9 +1286,9 @@ uint32_t HndCountAllHandles(BOOL fUseLocks) return uCount; } -#ifndef FEATURE_REDHAWK -BOOL Ref_HandleAsyncPinHandles() +BOOL Ref_HandleAsyncPinHandles(async_pin_enum_fn asyncPinCallback, void* context) { +#ifndef FEATURE_REDHAWK CONTRACTL { NOTHROW; @@ -1297,22 +1297,27 @@ BOOL Ref_HandleAsyncPinHandles() } CONTRACTL_END; + AsyncPinCallbackContext callbackCtx(asyncPinCallback, context); HandleTableBucket *pBucket = g_HandleTableMap.pBuckets[0]; BOOL result = FALSE; int limit = getNumberOfSlots(); for (int n = 0; n < limit; n ++ ) { - if (TableHandleAsyncPinHandles(Table(pBucket->pTable[n]))) + if (TableHandleAsyncPinHandles(Table(pBucket->pTable[n]), callbackCtx)) { result = TRUE; } } return result; +#else + return true; +#endif // !FEATURE_REDHAWK } void Ref_RelocateAsyncPinHandles(HandleTableBucket *pSource, HandleTableBucket *pTarget) { +#ifndef FEATURE_REDHAWK CONTRACTL { NOTHROW; @@ -1325,8 +1330,8 @@ void Ref_RelocateAsyncPinHandles(HandleTableBucket *pSource, HandleTableBucket { TableRelocateAsyncPinHandles(Table(pSource->pTable[n]), Table(pTarget->pTable[n])); } -} #endif // !FEATURE_REDHAWK +} /*--------------------------------------------------------------------------*/ diff --git a/src/coreclr/src/gc/handletablecore.cpp b/src/coreclr/src/gc/handletablecore.cpp index 228b8bf..a0ffb37 100644 --- a/src/coreclr/src/gc/handletablecore.cpp +++ b/src/coreclr/src/gc/handletablecore.cpp @@ -908,7 +908,7 @@ void SegmentCompactAsyncPinHandles(TableSegment *pSegment, TableSegment **ppWork // Mark AsyncPinHandles ready to be cleaned when the marker job is processed -BOOL SegmentHandleAsyncPinHandles (TableSegment *pSegment) +BOOL SegmentHandleAsyncPinHandles (TableSegment *pSegment, const AsyncPinCallbackContext &callbackCtx) { CONTRACTL { @@ -945,11 +945,10 @@ BOOL SegmentHandleAsyncPinHandles (TableSegment *pSegment) _UNCHECKED_OBJECTREF value = *pValue; if (!HndIsNullOrDestroyedHandle(value)) { - _ASSERTE (value->GetMethodTable() == g_pOverlappedDataClass); - OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(ObjectToOBJECTREF((Object*)value)); - if (overlapped->GetAppDomainId() != DefaultADID && overlapped->HasCompleted()) + // calls back into the VM using the callback given to + // Ref_HandleAsyncPinHandles + if (callbackCtx.Invoke((Object*)value)) { - overlapped->HandleAsyncPinHandle(); result = TRUE; } } @@ -1024,7 +1023,7 @@ bool SegmentRelocateAsyncPinHandles (TableSegment *pSegment, HandleTable *pTarge // We will queue a marker Overlapped to io completion port. We use the marker // to make sure that all iocompletion jobs before this marker have been processed. // After that we can free the async pinned handles. -BOOL TableHandleAsyncPinHandles(HandleTable *pTable) +BOOL TableHandleAsyncPinHandles(HandleTable *pTable, const AsyncPinCallbackContext &callbackCtx) { CONTRACTL { @@ -1043,7 +1042,7 @@ BOOL TableHandleAsyncPinHandles(HandleTable *pTable) while (pSegment) { - if (SegmentHandleAsyncPinHandles (pSegment)) + if (SegmentHandleAsyncPinHandles (pSegment, callbackCtx)) { result = TRUE; } diff --git a/src/coreclr/src/gc/handletablepriv.h b/src/coreclr/src/gc/handletablepriv.h index 59c08ca..cda1cb0 100644 --- a/src/coreclr/src/gc/handletablepriv.h +++ b/src/coreclr/src/gc/handletablepriv.h @@ -341,6 +341,37 @@ struct HandleTypeCache int32_t lFreeIndex; }; +/* + * Async pin EE callback context, used to call back tot he EE when enumerating + * over async pinned handles. + */ +class AsyncPinCallbackContext +{ +private: + async_pin_enum_fn m_callback; + void* m_context; + +public: + /* + * Constructs a new AsyncPinCallbackContext from a callback and a context, + * which will be passed to the callback as its second parameter every time + * it is invoked. + */ + AsyncPinCallbackContext(async_pin_enum_fn callback, void* context) + : m_callback(callback), m_context(context) + {} + + /* + * Invokes the callback with the given argument, returning the callback's + * result.' + */ + bool Invoke(Object* argument) const + { + assert(m_callback != nullptr); + return m_callback(argument, m_context); + } +}; + /*---------------------------------------------------------------------------*/ @@ -759,7 +790,7 @@ void SegmentFree(TableSegment *pSegment); * Mark ready for all non-pending OverlappedData that get moved to default domain. * */ -BOOL TableHandleAsyncPinHandles(HandleTable *pTable); +BOOL TableHandleAsyncPinHandles(HandleTable *pTable, const AsyncPinCallbackContext& callbackCtx); /* * TableRelocateAsyncPinHandles diff --git a/src/coreclr/src/gc/objecthandle.h b/src/coreclr/src/gc/objecthandle.h index b3e4b58..6ae75b4 100644 --- a/src/coreclr/src/gc/objecthandle.h +++ b/src/coreclr/src/gc/objecthandle.h @@ -87,7 +87,7 @@ bool Ref_Initialize(); void Ref_Shutdown(); HandleTableBucket* Ref_CreateHandleTableBucket(void* context); bool Ref_InitializeHandleTableBucket(HandleTableBucket* bucket, void* context); -BOOL Ref_HandleAsyncPinHandles(); +BOOL Ref_HandleAsyncPinHandles(async_pin_enum_fn callback, void* context); void Ref_RelocateAsyncPinHandles(HandleTableBucket *pSource, HandleTableBucket *pTarget); void Ref_RemoveHandleTableBucket(HandleTableBucket *pBucket); void Ref_DestroyHandleTableBucket(HandleTableBucket *pBucket); diff --git a/src/coreclr/src/vm/appdomain.cpp b/src/coreclr/src/vm/appdomain.cpp index 7468f0b..56b5aa1 100644 --- a/src/coreclr/src/vm/appdomain.cpp +++ b/src/coreclr/src/vm/appdomain.cpp @@ -9084,18 +9084,16 @@ void AppDomain::HandleAsyncPinHandles() } CONTRACTL_END; - // TODO: Temporarily casting stuff here until Ref_RelocateAsyncPinHandles is moved to the interface. - HandleTableBucket *pBucket = (HandleTableBucket*)m_handleStore; + IGCHandleStore *pBucket = m_handleStore; // IO completion port picks IO job using FIFO. Here is how we know which AsyncPinHandle can be freed. // 1. We mark all non-pending AsyncPinHandle with READYTOCLEAN. // 2. We queue a dump Overlapped to the IO completion as a marker. // 3. When the Overlapped is picked up by completion port, we wait until all previous IO jobs are processed. // 4. Then we can delete all AsyncPinHandle marked with READYTOCLEAN. - HandleTableBucket *pBucketInDefault = (HandleTableBucket*)SystemDomain::System()->DefaultDomain()->m_handleStore; + IGCHandleStore *pBucketInDefault = SystemDomain::System()->DefaultDomain()->m_handleStore; - // TODO: When this function is moved to the interface it will take void*s - Ref_RelocateAsyncPinHandles(pBucket, pBucketInDefault); + pBucket->RelocateAsyncPinnedHandles(pBucketInDefault); OverlappedDataObject::RequestCleanup(); } diff --git a/src/coreclr/src/vm/nativeoverlapped.cpp b/src/coreclr/src/vm/nativeoverlapped.cpp index 3a77c52..027af31 100644 --- a/src/coreclr/src/vm/nativeoverlapped.cpp +++ b/src/coreclr/src/vm/nativeoverlapped.cpp @@ -211,6 +211,33 @@ FCIMPL1(OverlappedDataObject*, GetOverlappedFromNative, LPOVERLAPPED lpOverlappe } FCIMPLEND +namespace +{ + +// Sets up an enumeration of all async pinned handles, such that all enumerated +// async pinned handles are processed by calling HandleAsyncPinHandle on the +// underlying overlapped instance. +BOOL HandleAsyncPinHandles() +{ + auto callback = [](Object* value, void*) + { + _ASSERTE (value->GetMethodTable() == g_pOverlappedDataClass); + OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(ObjectToOBJECTREF(value)); + if (overlapped->GetAppDomainId() != DefaultADID && overlapped->HasCompleted()) + { + overlapped->HandleAsyncPinHandle(); + return true; + } + + return false; + }; + + IGCHandleManager* mgr = GCHandleUtilities::GetGCHandleManager(); + return mgr->GetGlobalHandleStore()->EnumerateAsyncPinnedHandles(callback, nullptr); +} + +} // anonymous namespace + void OverlappedDataObject::FreeAsyncPinHandles() { CONTRACTL @@ -262,7 +289,7 @@ void OverlappedDataObject::StartCleanup() if (FastInterlockExchange((LONG*)&s_CleanupInProgress, TRUE) == FALSE) { { - BOOL HasJob = Ref_HandleAsyncPinHandles(); + BOOL HasJob = HandleAsyncPinHandles(); if (!HasJob) { s_CleanupInProgress = FALSE; @@ -292,7 +319,7 @@ void OverlappedDataObject::FinishCleanup(bool wasDrained) GCX_COOP(); s_CleanupFreeHandle = TRUE; - Ref_HandleAsyncPinHandles(); + HandleAsyncPinHandles(); s_CleanupFreeHandle = FALSE; s_CleanupInProgress = FALSE; -- 2.7.4