#include <stdint.h>
#include <stddef.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <assert.h>
return reinterpret_cast<void*>(ALIGN_DOWN(as_size_t, alignment));
}
+inline int GetRandomInt(int max)
+{
+ return rand() % max;
+}
+
typedef struct _PROCESSOR_NUMBER {
uint16_t Group;
uint8_t Number;
static bool IsGCThread();
static bool WasCurrentThreadCreatedByGC();
static bool CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name);
+ static void WalkAsyncPinnedForPromotion(Object* object, ScanContext* sc, promote_func* callback);
+ static void WalkAsyncPinned(Object* object, void* context, void(*callback)(Object*, Object*, void*));
};
#endif // __GCENV_EE_H__
return g_theGCToCLR->CreateThread(threadStart, arg, is_suspendable, name);
}
+inline void GCToEEInterface::WalkAsyncPinnedForPromotion(Object* object, ScanContext* sc, promote_func* callback)
+{
+ assert(g_theGCToCLR != nullptr);
+ return g_theGCToCLR->WalkAsyncPinnedForPromotion(object, sc, callback);
+}
+
+inline void GCToEEInterface::WalkAsyncPinned(Object* object, void* context, void(*callback)(Object*, Object*, void*))
+{
+ assert(g_theGCToCLR != nullptr);
+ return g_theGCToCLR->WalkAsyncPinned(object, context, callback);
+}
#endif // __GCTOENV_EE_STANDALONE_INL__
return handle;
}
-void GCHandleStore::RelocateAsyncPinnedHandles(IGCHandleStore* pTarget)
+void GCHandleStore::RelocateAsyncPinnedHandles(IGCHandleStore* pTarget, void (*clearIfComplete)(Object*), void (*setHandle)(Object*, OBJECTHANDLE))
{
// assumption - the IGCHandleStore is an instance of GCHandleStore
GCHandleStore* other = static_cast<GCHandleStore*>(pTarget);
- ::Ref_RelocateAsyncPinHandles(&_underlyingBucket, &other->_underlyingBucket);
+ ::Ref_RelocateAsyncPinHandles(&_underlyingBucket, &other->_underlyingBucket, clearIfComplete, setHandle);
}
bool GCHandleStore::EnumerateAsyncPinnedHandles(async_pin_enum_fn callback, void* context)
virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary);
- virtual void RelocateAsyncPinnedHandles(IGCHandleStore* pTarget);
+ virtual void RelocateAsyncPinnedHandles(IGCHandleStore* pTarget, void (*clearIfCompleteCallback)(Object* object), void (*setHandle)(Object* object, OBJECTHANDLE handle));
virtual bool EnumerateAsyncPinnedHandles(async_pin_enum_fn callback, void* context);
// or a server GC thread.
virtual
bool WasCurrentThreadCreatedByGC() = 0;
+
+ // Given an object, if this object is an instance of `System.Threading.OverlappedData`,
+ // and the runtime treats instances of this class specially, traverses the objects that
+ // are directly or (once) indirectly pinned by this object and reports them to the GC for
+ // the purposes of relocation and promotion.
+ //
+ // Overlapped objects are very special and as such the objects they wrap can't be promoted in
+ // the same manner as normal objects. This callback gives the EE the opportunity to hide these
+ // details, if they are implemented at all.
+ //
+ // This function is a no-op if "object" is not an OverlappedData object.
+ virtual
+ void WalkAsyncPinnedForPromotion(Object* object, ScanContext* sc, promote_func* callback) = 0;
+
+ // Given an object, if this object is an instance of `System.Threading.OverlappedData` and the
+ // runtime treats instances of this class specially, traverses the objects that are directly
+ // or once indirectly pinned by this object and invokes the given callback on them. The callback
+ // is passed the following arguments:
+ // Object* "from" - The object that "caused" the "to" object to be pinned. If a single object
+ // is pinned directly by this OverlappedData, this object will be the
+ // OverlappedData object itself. If an array is pinned by this OverlappedData,
+ // this object will be the pinned array.
+ // Object* "to" - The object that is pinned by the "from" object. If a single object is pinned
+ // by an OverlappedData, "to" will be that single object. If an array is pinned
+ // by an OverlappedData, the callback will be invoked on all elements of that
+ // array and each element will be a "to" object.
+ // void* "context" - Passed verbatim from "WalkOverlappedObject" to the callback function.
+ // The "context" argument will be passed directly to the callback without modification or inspection.
+ //
+ // This function is a no-op if "object" is not an OverlappedData object.
+ virtual
+ void WalkAsyncPinned(Object* object, void* context, void(*callback)(Object*, Object*, void*)) = 0;
};
#endif // _GCINTERFACE_EE_H_
uint8_t* write_watch_table;
};
+// Opaque type for tracking object pointers
+#ifndef DACCESS_COMPILE
+struct OBJECTHANDLE__
+{
+ void* unused;
+};
+typedef struct OBJECTHANDLE__* OBJECTHANDLE;
+#else
+typedef uintptr_t OBJECTHANDLE;
+#endif
+
/*
* Scanning callback.
*/
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
-struct OBJECTHANDLE__
-{
- void* unused;
-};
-typedef struct OBJECTHANDLE__* OBJECTHANDLE;
-#else
-typedef uintptr_t OBJECTHANDLE;
-#endif
+
class IGCHandleStore {
public:
virtual OBJECTHANDLE CreateDependentHandle(Object* primary, Object* secondary) = 0;
- virtual void RelocateAsyncPinnedHandles(IGCHandleStore* pTarget) = 0;
+ // Relocates async pinned handles from a condemned handle store to the default domain's handle store.
+ //
+ // The two callbacks are called when:
+ // 1. clearIfComplete is called whenever the handle table observes an async pin that is still live.
+ // The callback gives a chance for the EE to unpin the referents if the overlapped operation is complete.
+ // 2. setHandle is called whenever the GC has relocated the async pin to a new handle table. The passed-in
+ // handle is the newly-allocated handle in the default domain that should be assigned to the overlapped object.
+ virtual void RelocateAsyncPinnedHandles(IGCHandleStore* pTarget, void (*clearIfComplete)(Object*), void (*setHandle)(Object*, OBJECTHANDLE)) = 0;
virtual bool EnumerateAsyncPinnedHandles(async_pin_enum_fn callback, void* context) = 0;
#include "objecthandle.h"
#include "handletablepriv.h"
-#ifndef FEATURE_REDHAWK
-#include "nativeoverlapped.h"
-#endif
-
/****************************************************************************
*
* FORWARD DECLARATIONS
FireEtwSetGCHandle((void*) handle, value, hndType, generation, (int64_t) pAppDomain, GetClrInstanceId());
FireEtwPrvSetGCHandle((void*) handle, value, hndType, generation, (int64_t) pAppDomain, GetClrInstanceId());
-#ifndef FEATURE_REDHAWK
// Also fire the things pinned by Async pinned handles
if (hndType == HNDTYPE_ASYNCPINNED)
{
- if (value->GetMethodTable() == g_pOverlappedDataClass)
+ // the closure passed to "WalkOverlappedObject" is not permitted to implicitly
+ // capture any variables in this scope, since WalkForOverlappedObject takes a bare
+ // function pointer and context pointer as arguments. We can still /explicitly/
+ // close over values in this scope by doing what the compiler would do and introduce
+ // a structure that contains all of the things we closed over, while passing a pointer
+ // to this structure as our closure's context pointer.
+ struct ClosureCapture
{
- OverlappedDataObject* overlapped = (OverlappedDataObject*) value;
- if (overlapped->m_isArray)
- {
- ArrayBase* pUserObject = (ArrayBase*)OBJECTREFToObject(overlapped->m_userObject);
- Object **ppObj = (Object**)pUserObject->GetDataPtr(TRUE);
- size_t num = pUserObject->GetNumComponents();
- for (size_t i = 0; i < num; i ++)
- {
- value = ppObj[i];
- uint32_t generation = value != 0 ? g_theGCHeap->WhichGeneration(value) : 0;
- FireEtwSetGCHandle(overlapped, value, HNDTYPE_PINNED, generation, (int64_t) pAppDomain, GetClrInstanceId());
- }
- }
- else
- {
- value = OBJECTREF_TO_UNCHECKED_OBJECTREF(overlapped->m_userObject);
- uint32_t generation = value != 0 ? g_theGCHeap->WhichGeneration(value) : 0;
- FireEtwSetGCHandle(overlapped, value, HNDTYPE_PINNED, generation, (int64_t) pAppDomain, GetClrInstanceId());
- }
- }
+ AppDomain* pAppDomain;
+ Object* overlapped;
+ };
+
+ ClosureCapture captured;
+ captured.pAppDomain = pAppDomain;
+ captured.overlapped = value;
+ GCToEEInterface::WalkAsyncPinned(value, &captured, [](Object*, Object* to, void* ctx)
+ {
+ ClosureCapture* captured = reinterpret_cast<ClosureCapture*>(ctx);
+ uint32_t generation = to != nullptr ? g_theGCHeap->WhichGeneration(to) : 0;
+ FireEtwSetGCHandle(captured->overlapped, to, HNDTYPE_PINNED, generation, (int64_t) captured->pAppDomain, GetClrInstanceId());
+ });
}
-#endif // FEATURE_REDHAWK
}
#else
UNREFERENCED_PARAMETER(handle);
int generation = g_theGCHeap->WhichGeneration(value);
uint32_t uType = HandleFetchType(handle);
-#ifndef FEATURE_REDHAWK
//OverlappedData need special treatment: because all user data pointed by it needs to be reported by this handle,
//its age is consider to be min age of the user data, to be simple, we just make it 0
- if (uType == HNDTYPE_ASYNCPINNED && objref->GetGCSafeMethodTable () == g_pOverlappedDataClass)
+ if (uType == HNDTYPE_ASYNCPINNED)
{
generation = 0;
}
-#endif // !FEATURE_REDHAWK
if (uType == HNDTYPE_DEPENDENT)
{
BOOL Ref_HandleAsyncPinHandles(async_pin_enum_fn asyncPinCallback, void* context)
{
-#ifndef FEATURE_REDHAWK
CONTRACTL
{
NOTHROW;
}
return result;
-#else
- return true;
-#endif // !FEATURE_REDHAWK
}
-void Ref_RelocateAsyncPinHandles(HandleTableBucket *pSource, HandleTableBucket *pTarget)
+void Ref_RelocateAsyncPinHandles(HandleTableBucket *pSource,
+ HandleTableBucket *pTarget,
+ void (*clearIfComplete)(Object* object),
+ void (*setHandle)(Object* object, OBJECTHANDLE handle))
{
-#ifndef FEATURE_REDHAWK
CONTRACTL
{
NOTHROW;
int limit = getNumberOfSlots();
for (int n = 0; n < limit; n ++ )
{
- TableRelocateAsyncPinHandles(Table(pSource->pTable[n]), Table(pTarget->pTable[n]));
+ TableRelocateAsyncPinHandles(Table(pSource->pTable[n]), Table(pTarget->pTable[n]), clearIfComplete, setHandle);
}
-#endif // !FEATURE_REDHAWK
}
/*--------------------------------------------------------------------------*/
#include "gcenv.h"
#include "gcenv.inl"
#include "gc.h"
-
-#ifndef FEATURE_REDHAWK
-#include "nativeoverlapped.h"
-#endif // FEATURE_REDHAWK
-
#include "handletablepriv.h"
/****************************************************************************
pSegment->rgFreeMask[uMask] &= ~(1<<uBit);
}
-#ifndef FEATURE_REDHAWK
// Prepare a segment to be moved to default domain.
// Remove all non-async pin handles.
-void SegmentPreCompactAsyncPinHandles(TableSegment *pSegment)
+void SegmentPreCompactAsyncPinHandles(TableSegment *pSegment, void (*clearIfComplete)(Object*))
{
CONTRACTL
{
_UNCHECKED_OBJECTREF value = *pValue;
if (!HndIsNullOrDestroyedHandle(value))
{
- _ASSERTE (value->GetMethodTable() == g_pOverlappedDataClass);
- OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(ObjectToOBJECTREF((Object*)(value)));
- if (overlapped->HasCompleted())
- {
- // IO has finished. We don't need to pin the user buffer any longer.
- overlapped->m_userObject = NULL;
- }
- BashMTForPinnedObject(ObjectToOBJECTREF(value));
+ clearIfComplete((Object*)value);
}
else
{
return TRUE;
}
-void SegmentCompactAsyncPinHandles(TableSegment *pSegment, TableSegment **ppWorkerSegment)
+void SegmentCompactAsyncPinHandles(TableSegment *pSegment, TableSegment **ppWorkerSegment, void (*clearIfComplete)(Object*))
{
CONTRACTL
{
_UNCHECKED_OBJECTREF value = *pValue;
if (!HndIsNullOrDestroyedHandle(value))
{
- _ASSERTE (value->GetMethodTable() == g_pOverlappedDataClass);
- OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(ObjectToOBJECTREF((Object*)value));
- if (overlapped->HasCompleted())
- {
- // IO has finished. We don't need to pin the user buffer any longer.
- overlapped->m_userObject = NULL;
- }
- BashMTForPinnedObject(ObjectToOBJECTREF(value));
+ clearIfComplete((Object*)value);
fNeedNewSegment = !SegmentCopyAsyncPinHandle(*ppWorkerSegment,pValue);
}
if (fNeedNewSegment)
_ASSERTE ((*ppWorkerSegment)->rgFreeCount[HNDTYPE_ASYNCPINNED] == 0 &&
(*ppWorkerSegment)->bFreeList == BLOCK_INVALID);
TableSegment *pNextSegment = (*ppWorkerSegment)->pNextSegment;
- SegmentPreCompactAsyncPinHandles(pNextSegment);
+ SegmentPreCompactAsyncPinHandles(pNextSegment, clearIfComplete);
*ppWorkerSegment = pNextSegment;
if (pNextSegment == pSegment)
{
}
// Replace an async pin handle with one from default domain
-bool SegmentRelocateAsyncPinHandles (TableSegment *pSegment, HandleTable *pTargetTable)
+bool SegmentRelocateAsyncPinHandles (TableSegment *pSegment,
+ HandleTable *pTargetTable,
+ void (*clearIfComplete)(Object*),
+ void (*setHandle)(Object*, OBJECTHANDLE))
{
CONTRACTL
{
_UNCHECKED_OBJECTREF value = *pValue;
if (!HndIsNullOrDestroyedHandle(value))
{
- _ASSERTE (value->GetMethodTable() == g_pOverlappedDataClass);
- OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(ObjectToOBJECTREF((Object*)value));
- if (overlapped->HasCompleted())
- {
- // IO has finished. We don't need to pin the user buffer any longer.
- overlapped->m_userObject = NULL;
- }
- BashMTForPinnedObject(ObjectToOBJECTREF(value));
-
- overlapped->m_pinSelf = HndCreateHandle((HHANDLETABLE)pTargetTable, HNDTYPE_ASYNCPINNED, ObjectToOBJECTREF(value));
- if (!overlapped->m_pinSelf)
+ clearIfComplete((Object*)value);
+ OBJECTHANDLE selfHandle = HndCreateHandle((HHANDLETABLE)pTargetTable, HNDTYPE_ASYNCPINNED, ObjectToOBJECTREF(value));
+ if (!selfHandle)
{
// failed to allocate a new handle - callers have to handle this.
return false;
}
+ setHandle((Object*)value, selfHandle);
*pValue = NULL;
}
pValue ++;
}
CONTRACTL_END;
- _ASSERTE (pTable->uADIndex.m_dwIndex == DefaultADID);
-
BOOL result = FALSE;
TableSegment *pSegment = pTable->pSegmentList;
// from a again.
// c. After copying all handles to worker segments, move the segments to default domain.
// It is very important that in step 2, we should not fail for OOM, which means no memory allocation.
-void TableRelocateAsyncPinHandles(HandleTable *pTable, HandleTable *pTargetTable)
+void TableRelocateAsyncPinHandles(HandleTable *pTable,
+ HandleTable *pTargetTable,
+ void (*clearIfComplete)(Object*),
+ void (*setHandle)(Object*, OBJECTHANDLE))
{
CONTRACTL
{
// Step 1: replace pinning handles with ones from default domain
while (pSegment)
{
- wasSuccessful = wasSuccessful && SegmentRelocateAsyncPinHandles (pSegment, pTargetTable);
+ wasSuccessful = wasSuccessful && SegmentRelocateAsyncPinHandles (pSegment, pTargetTable, clearIfComplete, setHandle);
if (!wasSuccessful)
{
break;
// Compact async pinning handles into the smallest number of leading segments we can (the worker
// segments).
TableSegment *pWorkerSegment = pTable->pSegmentList;
- SegmentPreCompactAsyncPinHandles (pWorkerSegment);
+ SegmentPreCompactAsyncPinHandles (pWorkerSegment, clearIfComplete);
pSegment = pWorkerSegment->pNextSegment;
while (pSegment)
{
- SegmentCompactAsyncPinHandles (pSegment, &pWorkerSegment);
+ SegmentCompactAsyncPinHandles (pSegment, &pWorkerSegment, clearIfComplete);
pSegment= pSegment->pNextSegment;
}
break;
}
}
-#endif // !FEATURE_REDHAWK
/*
* Check if a handle is part of a HandleTable
* Replaces async pin handles with ones in default domain.
*
*/
-void TableRelocateAsyncPinHandles(HandleTable *pTable, HandleTable *pTargetTable);
+void TableRelocateAsyncPinHandles(HandleTable *pTable, HandleTable *pTargetTable, void (*clearIfComplete)(Object*), void (*setHandle)(Object*, OBJECTHANDLE));
/*
* Check if a handle is part of a HandleTable
#include "objecthandle.h"
#include "handletablepriv.h"
-#ifndef FEATURE_REDHAWK
-#include "nativeoverlapped.h"
-#endif // FEATURE_REDHAWK
-
/****************************************************************************
*
if (minAge > thisAge)
minAge = thisAge;
-#ifndef FEATURE_REDHAWK
- if ((*pValue)->GetGCSafeMethodTable() == g_pOverlappedDataClass)
- {
- // reporting the pinned user objects
- OverlappedDataObject *pOverlapped = (OverlappedDataObject *)(*pValue);
- if (pOverlapped->m_userObject != NULL)
+ GCToEEInterface::WalkAsyncPinned(*pValue, &minAge,
+ [](Object*, Object* to, void* ctx)
{
- Object * pUserObject = OBJECTREFToObject(pOverlapped->m_userObject);
- thisAge = g_theGCHeap->WhichGeneration(pUserObject);
- if (minAge > thisAge)
- minAge = thisAge;
- if (pOverlapped->m_isArray)
+ int* minAge = reinterpret_cast<int*>(ctx);
+ int generation = g_theGCHeap->WhichGeneration(to);
+ if (*minAge > generation)
{
- ArrayBase* pUserArrayObject = (ArrayBase*)pUserObject;
- Object **pObj = (Object**)pUserArrayObject->GetDataPtr(TRUE);
- size_t num = pUserArrayObject->GetNumComponents();
- for (size_t i = 0; i < num; i ++)
- {
- thisAge = g_theGCHeap->WhichGeneration(pObj[i]);
- if (minAge > thisAge)
- minAge = thisAge;
- }
- }
- }
- }
-#endif // !FEATURE_REDHAWK
- }
+ *minAge = generation;
+ }
+ });
+ }
}
_ASSERTE(FitsInU1(minAge));
((uint8_t *)pSegment->rgGeneration)[uClump] = static_cast<uint8_t>(minAge);
#endif // FEATURE_REDHAWK
}
-static void VerifyObjectAndAge(_UNCHECKED_OBJECTREF *pValue, _UNCHECKED_OBJECTREF from, _UNCHECKED_OBJECTREF obj, uint8_t minAge)
+static void VerifyObjectAndAge(_UNCHECKED_OBJECTREF from, _UNCHECKED_OBJECTREF obj, uint8_t minAge)
{
- UNREFERENCED_PARAMETER(pValue);
VerifyObject(from, obj);
int thisAge = g_theGCHeap->WhichGeneration(obj);
{
if (!HndIsNullOrDestroyedHandle(*pValue))
{
- VerifyObjectAndAge(pValue, (*pValue), (*pValue), minAge);
-#ifndef FEATURE_REDHAWK
- if ((*pValue)->GetGCSafeMethodTable() == g_pOverlappedDataClass)
- {
- // reporting the pinned user objects
- OverlappedDataObject *pOverlapped = (OverlappedDataObject *)(*pValue);
- if (pOverlapped->m_userObject != NULL)
+ VerifyObjectAndAge((*pValue), (*pValue), minAge);
+ GCToEEInterface::WalkAsyncPinned(*pValue, &minAge,
+ [](Object* from, Object* object, void* age)
{
- Object * pUserObject = OBJECTREFToObject(pOverlapped->m_userObject);
- VerifyObjectAndAge(pValue, (*pValue), pUserObject, minAge);
- if (pOverlapped->m_isArray)
- {
- ArrayBase* pUserArrayObject = (ArrayBase*)pUserObject;
- Object **pObj = (Object**)pUserArrayObject->GetDataPtr(TRUE);
- size_t num = pUserArrayObject->GetNumComponents();
- for (size_t i = 0; i < num; i ++)
- {
- VerifyObjectAndAge(pValue, pUserObject, pObj[i], minAge);
- }
- }
- }
- }
-#endif // !FEATURE_REDHAWK
+ uint8_t* minAge = reinterpret_cast<uint8_t*>(age);
+ VerifyObjectAndAge(from, object, *minAge);
+ });
if (uType == HNDTYPE_DEPENDENT)
{
#ifdef FEATURE_COMINTEROP
#include "comcallablewrapper.h"
#endif // FEATURE_COMINTEROP
-#ifndef FEATURE_REDHAWK
-#include "nativeoverlapped.h"
-#endif // FEATURE_REDHAWK
#endif // BUILD_AS_STANDALONE
HandleTableMap g_HandleTableMap;
_ASSERTE(lp2);
promote_func* callback = (promote_func*) lp2;
callback(pRef, (ScanContext *)lp1, GC_CALL_PINNED);
+}
-#ifndef FEATURE_REDHAWK
- Object * pPinnedObj = *pRef;
+void CALLBACK AsyncPinObject(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
+{
+ UNREFERENCED_PARAMETER(pExtraInfo);
- if (!HndIsNullOrDestroyedHandle(pPinnedObj) && pPinnedObj->GetGCSafeMethodTable() == g_pOverlappedDataClass)
- {
- // reporting the pinned user objects
- OverlappedDataObject *pOverlapped = (OverlappedDataObject *)pPinnedObj;
- if (pOverlapped->m_userObject != NULL)
- {
- //callback(OBJECTREF_TO_UNCHECKED_OBJECTREF(pOverlapped->m_userObject), (ScanContext *)lp1, GC_CALL_PINNED);
- if (pOverlapped->m_isArray)
- {
- pOverlapped->m_userObjectInternal = static_cast<void*>(OBJECTREFToObject(pOverlapped->m_userObject));
- ArrayBase* pUserObject = (ArrayBase*)OBJECTREFToObject(pOverlapped->m_userObject);
- Object **ppObj = (Object**)pUserObject->GetDataPtr(TRUE);
- size_t num = pUserObject->GetNumComponents();
- for (size_t i = 0; i < num; i ++)
- {
- callback(ppObj + i, (ScanContext *)lp1, GC_CALL_PINNED);
- }
- }
- else
- {
- callback(&OBJECTREF_TO_UNCHECKED_OBJECTREF(pOverlapped->m_userObject), (ScanContext *)lp1, GC_CALL_PINNED);
- }
- }
+ LOG((LF_GC, LL_WARNING, LOG_HANDLE_OBJECT_CLASS("WARNING: ", pObjRef, "causes (async) pinning of ", *pObjRef)));
- if (pOverlapped->GetAppDomainId() != DefaultADID && pOverlapped->GetAppDomainIndex().m_dwIndex == DefaultADID)
- {
- OverlappedDataObject::MarkCleanupNeededFromGC();
- }
+ Object **pRef = (Object **)pObjRef;
+ _ASSERTE(lp2);
+ promote_func* callback = (promote_func*)lp2;
+ callback(pRef, (ScanContext *)lp1, GC_CALL_PINNED);
+ Object* pPinnedObj = *pRef;
+ if (!HndIsNullOrDestroyedHandle(pPinnedObj))
+ {
+ GCToEEInterface::WalkAsyncPinnedForPromotion(pPinnedObj, (ScanContext *)lp1, callback);
}
-#endif // !FEATURE_REDHAWK
}
*/
void CALLBACK ScanPointerForProfilerAndETW(_UNCHECKED_OBJECTREF *pObjRef, uintptr_t *pExtraInfo, uintptr_t lp1, uintptr_t lp2)
{
-#ifndef FEATURE_REDHAWK
CONTRACTL
{
NOTHROW;
GC_NOTRIGGER;
}
CONTRACTL_END;
-#endif // FEATURE_REDHAWK
UNREFERENCED_PARAMETER(pExtraInfo);
handle_scan_fn fn = (handle_scan_fn)lp2;
sc->pCurrentDomain = SystemDomain::GetAppDomainAtIndex(HndGetHandleTableADIndex(hTable));
}
#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING
- HndScanHandlesForGC(hTable, PinObject, uintptr_t(sc), uintptr_t(fn), types, _countof(types), condemned, maxgen, flags);
+
+ // Pinned handles and async pinned handles are scanned in separate passes, since async pinned
+ // handles may require a callback into the EE in order to fully trace an async pinned
+ // object's object graph.
+ HndScanHandlesForGC(hTable, PinObject, uintptr_t(sc), uintptr_t(fn), &types[0], 1, condemned, maxgen, flags);
+ HndScanHandlesForGC(hTable, AsyncPinObject, uintptr_t(sc), uintptr_t(fn), &types[1], 1, condemned, maxgen, flags);
}
}
walk = walk->pNext;
HandleTableBucket* Ref_CreateHandleTableBucket(void* context);
bool Ref_InitializeHandleTableBucket(HandleTableBucket* bucket, void* context);
BOOL Ref_HandleAsyncPinHandles(async_pin_enum_fn callback, void* context);
-void Ref_RelocateAsyncPinHandles(HandleTableBucket *pSource, HandleTableBucket *pTarget);
+void Ref_RelocateAsyncPinHandles(HandleTableBucket *pSource, HandleTableBucket *pTarget, void (*clearIfComplete)(Object*), void (*setHandle)(Object*, OBJECTHANDLE));
void Ref_RemoveHandleTableBucket(HandleTableBucket *pBucket);
void Ref_DestroyHandleTableBucket(HandleTableBucket *pBucket);
{
return false;
}
+
+void GCToEEInterface::WalkAsyncPinnedForPromotion(Object* object, ScanContext* sc, promote_func* callback)
+{
+}
+
+void GCToEEInterface::WalkAsyncPinned(Object* object, void* context, void (*callback)(Object*, Object*, void*))
+{
+}
// 4. Then we can delete all AsyncPinHandle marked with READYTOCLEAN.
IGCHandleStore *pBucketInDefault = SystemDomain::System()->DefaultDomain()->m_handleStore;
- pBucket->RelocateAsyncPinnedHandles(pBucketInDefault);
+ auto clearIfComplete = [](Object* object)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ assert(object != nullptr);
+ if (object->GetGCSafeMethodTable() != g_pOverlappedDataClass)
+ {
+ return;
+ }
+
+ OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(ObjectToOBJECTREF((Object*)object));
+ if (overlapped->HasCompleted())
+ {
+ // IO has finished. We don't need to pin the user buffer any longer.
+ overlapped->m_userObject = NULL;
+ }
+
+ BashMTForPinnedObject(ObjectToOBJECTREF(object));
+ };
+
+ auto setHandle = [](Object* object, OBJECTHANDLE handle)
+ {
+ LIMITED_METHOD_CONTRACT;
+
+ assert(object != nullptr);
+ assert(handle);
+
+ if (object->GetGCSafeMethodTable() != g_pOverlappedDataClass)
+ {
+ return;
+ }
+
+ OverlappedDataObject* overlapped = (OverlappedDataObject*)object;
+ overlapped->m_pinSelf = handle;
+ };
+
+ pBucket->RelocateAsyncPinnedHandles(pBucketInDefault, clearIfComplete, setHandle);
OverlappedDataObject::RequestCleanup();
}
};
InlineSString<MaxThreadNameSize> wideName;
- const WCHAR* namePtr;
+ const WCHAR* namePtr = nullptr;
EX_TRY
{
if (name != nullptr)
{
// we're not obligated to provide a name - if it's not valid,
// just report nullptr as the name.
- namePtr = nullptr;
}
EX_END_CATCH(SwallowAllExceptions)
return CreateNonSuspendableThread(threadStart, arg, name);
}
}
+
+void GCToEEInterface::WalkAsyncPinnedForPromotion(Object* object, ScanContext* sc, promote_func* callback)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ assert(object != nullptr);
+ assert(sc != nullptr);
+ assert(callback != nullptr);
+ if (object->GetGCSafeMethodTable() != g_pOverlappedDataClass)
+ {
+ // not an overlapped data object - nothing to do.
+ return;
+ }
+
+ // reporting the pinned user objects
+ OverlappedDataObject *pOverlapped = (OverlappedDataObject *)object;
+ if (pOverlapped->m_userObject != NULL)
+ {
+ //callback(OBJECTREF_TO_UNCHECKED_OBJECTREF(pOverlapped->m_userObject), (ScanContext *)lp1, GC_CALL_PINNED);
+ if (pOverlapped->m_isArray)
+ {
+ // OverlappedDataObject is very special. An async pin handle keeps it alive.
+ // During GC, we also make sure
+ // 1. m_userObject itself does not move if m_userObject is not array
+ // 2. Every object pointed by m_userObject does not move if m_userObject is array
+ // We do not want to pin m_userObject if it is array. But m_userObject may be updated
+ // during relocation phase before OverlappedDataObject is doing relocation.
+ // m_userObjectInternal is used to track the location of the m_userObject before it is updated.
+ pOverlapped->m_userObjectInternal = static_cast<void*>(OBJECTREFToObject(pOverlapped->m_userObject));
+ ArrayBase* pUserObject = (ArrayBase*)OBJECTREFToObject(pOverlapped->m_userObject);
+ Object **ppObj = (Object**)pUserObject->GetDataPtr(TRUE);
+ size_t num = pUserObject->GetNumComponents();
+ for (size_t i = 0; i < num; i++)
+ {
+ callback(ppObj + i, sc, GC_CALL_PINNED);
+ }
+ }
+ else
+ {
+ callback(&OBJECTREF_TO_UNCHECKED_OBJECTREF(pOverlapped->m_userObject), (ScanContext *)sc, GC_CALL_PINNED);
+ }
+ }
+
+ if (pOverlapped->GetAppDomainId() != DefaultADID && pOverlapped->GetAppDomainIndex().m_dwIndex == DefaultADID)
+ {
+ OverlappedDataObject::MarkCleanupNeededFromGC();
+ }
+}
+
+void GCToEEInterface::WalkAsyncPinned(Object* object, void* context, void (*callback)(Object*, Object*, void*))
+{
+ LIMITED_METHOD_CONTRACT;
+
+ assert(object != nullptr);
+ assert(callback != nullptr);
+
+ if (object->GetGCSafeMethodTable() != g_pOverlappedDataClass)
+ {
+ return;
+ }
+
+ OverlappedDataObject *pOverlapped = (OverlappedDataObject *)(object);
+ if (pOverlapped->m_userObject != NULL)
+ {
+ Object * pUserObject = OBJECTREFToObject(pOverlapped->m_userObject);
+ callback(object, pUserObject, context);
+ if (pOverlapped->m_isArray)
+ {
+ ArrayBase* pUserArrayObject = (ArrayBase*)pUserObject;
+ Object **pObj = (Object**)pUserArrayObject->GetDataPtr(TRUE);
+ size_t num = pUserArrayObject->GetNumComponents();
+ for (size_t i = 0; i < num; i ++)
+ {
+ callback(pUserObject, pObj[i], context);
+ }
+ }
+ }
+}
bool IsGCThread();
bool WasCurrentThreadCreatedByGC();
bool CreateThread(void (*threadStart)(void*), void* arg, bool is_suspendable, const char* name);
+ void WalkAsyncPinnedForPromotion(Object* object, ScanContext* sc, promote_func* callback);
+ void WalkAsyncPinned(Object* object, void* context, void(*callback)(Object*, Object*, void*));
};
} // namespace standalone
#include "gcenv.h"
#include "gcenv.ee.h"
#include "threadsuspend.h"
+#include "nativeoverlapped.h"
#ifdef FEATURE_COMINTEROP
#include "runtimecallablewrapper.h"
#include "gcenv.ee.cpp"
-} // namespace standalone
\ No newline at end of file
+} // namespace standalone
#include "gcenv.h"
#include "../gc/env/gcenv.ee.h"
#include "threadsuspend.h"
+#include "nativeoverlapped.h"
#ifdef FEATURE_COMINTEROP
#include "runtimecallablewrapper.h"
// Finalizes a weak reference directly.
extern void FinalizeWeakReference(Object* obj);
-#include "gcenv.ee.cpp"
\ No newline at end of file
+#include "gcenv.ee.cpp"