diff options
author | Jan Kotas <jkotas@microsoft.com> | 2018-06-09 13:39:58 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-06-09 13:39:58 -0700 |
commit | 911d332c523848023e3c6564788b72b7f419fca1 (patch) | |
tree | ae979e6b048630ed0393983d14bde5d43942459f /src/vm/nativeoverlapped.cpp | |
parent | 618f9c2d18e88566ac61f93529bac58123c50cba (diff) | |
download | coreclr-911d332c523848023e3c6564788b72b7f419fca1.tar.gz coreclr-911d332c523848023e3c6564788b72b7f419fca1.tar.bz2 coreclr-911d332c523848023e3c6564788b72b7f419fca1.zip |
Avoid NativeOverlapped pinning by allocating unmanaged memory for it (#18360)
It makes PinnableBufferCache unnecessary
Diffstat (limited to 'src/vm/nativeoverlapped.cpp')
-rw-r--r-- | src/vm/nativeoverlapped.cpp | 407 |
1 files changed, 23 insertions, 384 deletions
diff --git a/src/vm/nativeoverlapped.cpp b/src/vm/nativeoverlapped.cpp index 6f053ea267..94e61c63fd 100644 --- a/src/vm/nativeoverlapped.cpp +++ b/src/vm/nativeoverlapped.cpp @@ -22,11 +22,6 @@ #include "comthreadpool.h" #include "marshalnative.h" -LONG OverlappedDataObject::s_CleanupRequestCount = 0; -BOOL OverlappedDataObject::s_CleanupInProgress = FALSE; -BOOL OverlappedDataObject::s_GCDetectsCleanup = FALSE; -BOOL OverlappedDataObject::s_CleanupFreeHandle = FALSE; - // //The function is called from managed code to quicly check if a packet is available. //This is a perf-critical function. Even helper method frames are not created. We fall @@ -38,7 +33,6 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, #ifndef FEATURE_PAL Thread *pThread = GetThread(); - DWORD adid = pThread->GetDomain()->GetId().m_dwId; size_t key=0; _ASSERTE(pThread); @@ -46,7 +40,7 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, //Poll and wait if GC is in progress, to avoid blocking GC for too long. FC_GC_POLL(); - *lpOverlapped = ThreadpoolMgr::CompletionPortDispatchWorkWithinAppDomain(pThread, errorCode, numBytes, &key, adid); + *lpOverlapped = ThreadpoolMgr::CompletionPortDispatchWorkWithinAppDomain(pThread, errorCode, numBytes, &key, DefaultADID); if(*lpOverlapped == NULL) { return; @@ -54,17 +48,10 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(*lpOverlapped)); - _ASSERTE(overlapped->GetAppDomainId() == adid); - - if(overlapped->m_iocb == NULL) + if (overlapped->m_callback == NULL) { - // no user delegate to callback - _ASSERTE((overlapped->m_iocbHelper == NULL) || !"This is benign, but should be optimized"); - - { - //We're not initialized yet, go back to the Vm, and process the packet there. - ThreadpoolMgr::StoreOverlappedInfoInThread(pThread, *errorCode, *numBytes, key, *lpOverlapped); - } + //We're not initialized yet, go back to the Vm, and process the packet there. + ThreadpoolMgr::StoreOverlappedInfoInThread(pThread, *errorCode, *numBytes, key, *lpOverlapped); *lpOverlapped = NULL; return; @@ -94,7 +81,7 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, // if this will be "dispatched" to the managed callback fire the IODequeue event: if (*lpOverlapped != NULL && ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadPoolIODequeue)) - FireEtwThreadPoolIODequeue(*lpOverlapped, (BYTE*)(*lpOverlapped) - offsetof(OverlappedDataObject, Internal), GetClrInstanceId()); + FireEtwThreadPoolIODequeue(*lpOverlapped, OverlappedDataObject::GetOverlapped(*lpOverlapped), GetClrInstanceId()); #else // !FEATURE_PAL *lpOverlapped = NULL; @@ -104,17 +91,17 @@ FCIMPL3(void, CheckVMForIOPacket, LPOVERLAPPED* lpOverlapped, DWORD* errorCode, } FCIMPLEND -FCIMPL1(void*, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE) +FCIMPL1(LPOVERLAPPED, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE) { FCALL_CONTRACT; + LPOVERLAPPED lpOverlapped; + OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(overlappedUNSAFE); OBJECTREF userObject = overlapped->m_userObject; HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB_2(Frame::FRAME_ATTR_NONE, overlapped, userObject); - AsyncPinningHandleHolder handle; - if (g_pOverlappedDataClass == NULL) { g_pOverlappedDataClass = MscorlibBinder::GetClass(CLASS__OVERLAPPEDDATA); @@ -126,11 +113,9 @@ FCIMPL1(void*, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE) CONSISTENCY_CHECK(overlapped->GetMethodTable() == g_pOverlappedDataClass); - overlapped->m_AppDomainId = GetAppDomain()->GetId().m_dwId; - if (userObject != NULL) { - if (overlapped->m_isArray == 1) + if (userObject->GetMethodTable() == g_pPredefinedArrayTypes[ELEMENT_TYPE_OBJECT]->GetMethodTable()) { BASEARRAYREF asArray = (BASEARRAYREF) userObject; OBJECTREF *pObj = (OBJECTREF*)(asArray->GetDataPtr()); @@ -140,33 +125,32 @@ FCIMPL1(void*, AllocateNativeOverlapped, OverlappedDataObject* overlappedUNSAFE) { ValidatePinnedObject(pObj[i]); } - for (i = 0; i < num; i ++) - { - asArray = (BASEARRAYREF) userObject; - AddMTForPinHandle(pObj[i]); - } } else { ValidatePinnedObject(userObject); - AddMTForPinHandle(userObject); } - } - handle = GetAppDomain()->CreateTypedHandle(overlapped, HNDTYPE_ASYNCPINNED); + NewHolder<NATIVEOVERLAPPED_AND_HANDLE> overlappedHolder(new NATIVEOVERLAPPED_AND_HANDLE()); + overlappedHolder->m_handle = GetAppDomain()->CreateTypedHandle(overlapped, HNDTYPE_ASYNCPINNED); + lpOverlapped = &(overlappedHolder.Extract()->m_overlapped); + lpOverlapped->Internal = 0; + lpOverlapped->InternalHigh = 0; + lpOverlapped->Offset = overlapped->m_offsetLow; + lpOverlapped->OffsetHigh = overlapped->m_offsetHigh; + lpOverlapped->hEvent = (HANDLE)overlapped->m_eventHandle; - handle.SuppressRelease(); - overlapped->m_pinSelf = handle; + overlapped->m_pNativeOverlapped = lpOverlapped; HELPER_METHOD_FRAME_END(); LOG((LF_INTEROP, LL_INFO10000, "In AllocNativeOperlapped thread 0x%x\n", GetThread())); if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadPoolIODequeue)) - FireEtwThreadPoolIOPack(&overlapped->Internal, overlappedUNSAFE, GetClrInstanceId()); + FireEtwThreadPoolIOPack(lpOverlapped, overlappedUNSAFE, GetClrInstanceId()); - return &overlapped->Internal; + return lpOverlapped; } FCIMPLEND @@ -176,28 +160,11 @@ FCIMPL1(void, FreeNativeOverlapped, LPOVERLAPPED lpOverlapped) HELPER_METHOD_FRAME_BEGIN_0(); - OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped)); - CONSISTENCY_CHECK(g_pOverlappedDataClass && (overlapped->GetMethodTable() == g_pOverlappedDataClass)); + CONSISTENCY_CHECK(g_pOverlappedDataClass && (OverlappedDataObject::GetOverlapped(lpOverlapped)->GetMethodTable() == g_pOverlappedDataClass)); - // We don't want to call HasCompleted in the default domain, because we don't have - // overlapped handle support. - if ((!overlapped->HasCompleted ())) - { -#ifdef MDA_SUPPORTED - MdaOverlappedFreeError *pFreeError = MDA_GET_ASSISTANT(OverlappedFreeError); - if (pFreeError) - { - pFreeError->ReportError((LPVOID) OVERLAPPEDDATAREFToObject(overlapped)); - - // If we entered ReportError then our overlapped OBJECTREF became technically invalid, - // since a gc can be triggered. That causes an assert from FreeAsyncPinHandles() below. - // (I say technically because the object is pinned and won't really move) - overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped)); - } -#endif // MDA_SUPPORTED - } + DestroyAsyncPinningHandle(((NATIVEOVERLAPPED_AND_HANDLE*)lpOverlapped)->m_handle); + delete lpOverlapped; - overlapped->FreeAsyncPinHandles(); HELPER_METHOD_FRAME_END(); } FCIMPLEND @@ -211,331 +178,3 @@ FCIMPL1(OverlappedDataObject*, GetOverlappedFromNative, LPOVERLAPPED lpOverlappe return OverlappedDataObject::GetOverlapped(lpOverlapped); } 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 - { - NOTHROW; - GC_NOTRIGGER; - MODE_COOPERATIVE; - SO_TOLERANT; - } - CONTRACTL_END; - - // This cannot throw or return error, and cannot force SO because it is called - // from CCLRIoCompletionManager::OnComplete which probes. - CONTRACT_VIOLATION(SOToleranceViolation); - - CONSISTENCY_CHECK(g_pOverlappedDataClass && (this->GetMethodTable() == g_pOverlappedDataClass)); - - _ASSERTE(GetThread() != NULL); - - if (m_pinSelf) - { - OBJECTHANDLE h = m_pinSelf; - if (FastInterlockCompareExchangePointer(&m_pinSelf, static_cast<OBJECTHANDLE>(NULL), h) == h) - { - DestroyAsyncPinningHandle(h); - } - } - - EventHandle = 0; -} - - -void OverlappedDataObject::StartCleanup() -{ - CONTRACTL - { - NOTHROW; - if (GetThread()) {MODE_COOPERATIVE;} else {DISABLED(MODE_COOPERATIVE);} - GC_TRIGGERS; - } - CONTRACTL_END; - - if (s_CleanupRequestCount == 0) - { - return; - } - - LONG curCount = s_CleanupRequestCount; - if (FastInterlockExchange((LONG*)&s_CleanupInProgress, TRUE) == FALSE) - { - { - BOOL HasJob = HandleAsyncPinHandles(); - if (!HasJob) - { - s_CleanupInProgress = FALSE; - FastInterlockExchangeAdd (&s_CleanupRequestCount, -curCount); - return; - } - } - - if (!ThreadpoolMgr::DrainCompletionPortQueue()) - { - s_CleanupInProgress = FALSE; - } - else - { - FastInterlockExchangeAdd (&s_CleanupRequestCount, -curCount); - } - } -} - - -void OverlappedDataObject::FinishCleanup(bool wasDrained) -{ - WRAPPER_NO_CONTRACT; - - if (wasDrained) - { - GCX_COOP(); - - s_CleanupFreeHandle = TRUE; - HandleAsyncPinHandles(); - s_CleanupFreeHandle = FALSE; - - s_CleanupInProgress = FALSE; - if (s_CleanupRequestCount > 0) - { - StartCleanup(); - } - } - else - { - s_CleanupInProgress = FALSE; - } -} - - -void OverlappedDataObject::HandleAsyncPinHandle() -{ - WRAPPER_NO_CONTRACT; - - _ASSERTE (s_CleanupInProgress); - if (m_toBeCleaned || !ThreadpoolMgr::IsCompletionPortInitialized()) - { - OBJECTHANDLE h = m_pinSelf; - if (h) - { - if (FastInterlockCompareExchangePointer(&m_pinSelf, (OBJECTHANDLE)NULL, h) == h) - { - DestroyAsyncPinningHandle(h); - } - } - } - else if (!s_CleanupFreeHandle) - { - m_toBeCleaned = 1; - } -} - - -// A hash table to track size of objects that may be moved to default domain -typedef EEHashTable<size_t, EEPtrHashTableHelper<size_t>, FALSE> EEHashTableOfMT; -EEHashTableOfMT *s_pPinHandleTable; - -CrstStatic s_PinHandleTableCrst; - -void InitializePinHandleTable() -{ - WRAPPER_NO_CONTRACT; - - s_PinHandleTableCrst.Init(CrstPinHandle); - LockOwner lock = {&s_PinHandleTableCrst, IsOwnerOfCrst}; - s_pPinHandleTable = new EEHashTableOfMT(); - s_pPinHandleTable->Init(10, &lock); -} - -// We can not fail due to OOM when we move an object to default domain during AD unload. -// If we may need a dummy MethodTable later, we allocate the MethodTable here. -void AddMTForPinHandle(OBJECTREF obj) -{ - CONTRACTL - { - THROWS; - WRAPPER(GC_TRIGGERS); - } - CONTRACTL_END; - - if (obj == NULL) - { - return; - } - - _ASSERTE (g_pOverlappedDataClass != NULL); - - SSIZE_T size = 0; - MethodTable *pMT = obj->GetMethodTable(); - - if (pMT->GetLoaderModule()->IsSystem()) - { - return; - } - - if (pMT->IsArray()) - { -#ifdef _DEBUG - BASEARRAYREF asArray = (BASEARRAYREF) obj; - TypeHandle th = asArray->GetArrayElementTypeHandle(); - _ASSERTE (!th.IsTypeDesc()); - MethodTable *pElemMT = th.AsMethodTable(); - _ASSERTE (pElemMT->IsValueType() && pElemMT->IsBlittable()); - _ASSERTE (!pElemMT->GetLoaderModule()->IsSystem()); -#endif - - // Create an ArrayMethodTable that has the same element size - // Use negative number for arrays of structs - it assumes that - // the maximum type base size is less than 2GB. - size = - (SSIZE_T)pMT->GetComponentSize(); - _ASSERTE(size < 0); - } - else - { - size = pMT->GetBaseSize(); - _ASSERTE(size >= 0); - } - - HashDatum data; - if (s_pPinHandleTable->GetValue(size, &data) == FALSE) - { - CrstHolder csh(&s_PinHandleTableCrst); - if (s_pPinHandleTable->GetValue(size, &data) == FALSE) - { - // We do not need to include GCDescr here, since this - // methodtable does not contain pointers. - BYTE *buffer = new BYTE[sizeof(MethodTable)]; - memset (buffer, 0, sizeof(MethodTable)); - MethodTable *pNewMT = (MethodTable *)buffer; - NewArrayHolder<BYTE> pMTHolder(buffer); - pNewMT->SetIsAsyncPinType(); - if (size >= 0) - { - pNewMT->SetBaseSize(static_cast<DWORD>(size)); - } - else - { - pNewMT->SetBaseSize(ARRAYBASE_BASESIZE); - pNewMT->SetComponentSize(static_cast<WORD>(-size)); - } - s_pPinHandleTable->InsertValue(size, (HashDatum)pNewMT); - pMTHolder.SuppressRelease(); - } - } -} - -// We need to ensure that the MethodTable of an object is valid in default domain when the object -// is move to default domain duing AD unload. -void BashMTForPinnedObject(OBJECTREF obj) -{ - CONTRACTL - { - GC_NOTRIGGER; - NOTHROW; - } - CONTRACTL_END; - - if (obj == NULL) - { - return; - } - - ADIndex adIndx = obj->GetAppDomainIndex(); - ADIndex defaultAdIndx = SystemDomain::System()->DefaultDomain()->GetIndex(); - if (adIndx.m_dwIndex != 0 && adIndx != defaultAdIndx) - { - obj->GetHeader()->ResetAppDomainIndexNoFailure(defaultAdIndx); - } - SSIZE_T size = 0; - MethodTable *pMT = obj->GetMethodTable(); - - if (pMT == g_pOverlappedDataClass) - { - // Managed Overlapped - OVERLAPPEDDATAREF overlapped = (OVERLAPPEDDATAREF)(obj); - overlapped->m_asyncResult = NULL; - overlapped->m_iocb = NULL; - overlapped->m_iocbHelper = NULL; - overlapped->m_overlapped = NULL; - - if (overlapped->m_userObject != NULL) - { - if (overlapped->m_isArray == 1) - { - BASEARRAYREF asArray = (BASEARRAYREF) (overlapped->m_userObject); - OBJECTREF *pObj = (OBJECTREF*)asArray->GetDataPtr (TRUE); - SIZE_T num = asArray->GetNumComponents(); - for (SIZE_T i = 0; i < num; i ++) - { - BashMTForPinnedObject(pObj[i]); - } - } - else - { - BashMTForPinnedObject(overlapped->m_userObject); - } - } - STRESS_LOG1 (LF_APPDOMAIN | LF_GC, LL_INFO100, "OverlappedData %p:MT is bashed\n", OBJECTREFToObject (overlapped)); - return; - } - - if (pMT->GetLoaderModule()->IsSystem()) - { - return; - } - - if (pMT->IsArray()) - { -#ifdef _DEBUG - BASEARRAYREF asArray = (BASEARRAYREF) obj; - TypeHandle th = asArray->GetArrayElementTypeHandle(); - _ASSERTE (!th.IsTypeDesc()); - MethodTable *pElemMT = th.AsMethodTable(); - _ASSERTE (pElemMT->IsValueType() && pElemMT->IsBlittable()); - _ASSERTE (!pElemMT->GetLoaderModule()->IsSystem()); -#endif - - // Create an ArrayMethodTable that has the same element size - size = - (SSIZE_T)pMT->GetComponentSize(); - } - else - { - _ASSERTE (pMT->IsBlittable()); - size = pMT->GetBaseSize(); - } - - HashDatum data = NULL; - BOOL fRet; - fRet = s_pPinHandleTable->GetValue(size, &data); - _ASSERTE(fRet); - PREFIX_ASSUME(data != NULL); - obj->SetMethodTable((MethodTable*)data); -} |