summaryrefslogtreecommitdiff
path: root/src/vm/comthreadpool.cpp
diff options
context:
space:
mode:
authordotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
committerdotnet-bot <dotnet-bot@microsoft.com>2015-01-30 14:14:42 -0800
commitef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch)
treedee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/vm/comthreadpool.cpp
downloadcoreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2
coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/vm/comthreadpool.cpp')
-rw-r--r--src/vm/comthreadpool.cpp1018
1 files changed, 1018 insertions, 0 deletions
diff --git a/src/vm/comthreadpool.cpp b/src/vm/comthreadpool.cpp
new file mode 100644
index 0000000000..98616526ef
--- /dev/null
+++ b/src/vm/comthreadpool.cpp
@@ -0,0 +1,1018 @@
+//
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+//
+
+
+/*============================================================
+**
+** Header: COMThreadPool.cpp
+**
+** Purpose: Native methods on System.ThreadPool
+** and its inner classes
+**
+**
+===========================================================*/
+
+/********************************************************************************************************************/
+#include "common.h"
+#include "comdelegate.h"
+#include "comthreadpool.h"
+#include "threadpoolrequest.h"
+#include "win32threadpool.h"
+#include "class.h"
+#include "object.h"
+#include "field.h"
+#include "excep.h"
+#include "security.h"
+#include "eeconfig.h"
+#include "corhost.h"
+#include "nativeoverlapped.h"
+#include "comsynchronizable.h"
+#ifdef FEATURE_REMOTING
+#include "crossdomaincalls.h"
+#else
+#include "callhelpers.h"
+#endif
+#include "appdomain.inl"
+/*****************************************************************************************************/
+#ifdef _DEBUG
+void LogCall(MethodDesc* pMD, LPCUTF8 api)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ LPCUTF8 cls = pMD->GetMethodTable()->GetDebugClassName();
+ LPCUTF8 name = pMD->GetName();
+
+ LOG((LF_THREADPOOL,LL_INFO1000,"%s: ", api));
+ LOG((LF_THREADPOOL, LL_INFO1000,
+ " calling %s.%s\n", cls, name));
+}
+#else
+#define LogCall(pMd,api)
+#endif
+
+VOID
+AcquireDelegateInfo(DelegateInfo *pDelInfo)
+{
+ LIMITED_METHOD_CONTRACT;
+}
+
+VOID
+ReleaseDelegateInfo(DelegateInfo *pDelInfo)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ // The release methods of holders can be called with preemptive GC enabled. Ensure we're in cooperative mode
+ // before calling pDelInfo->Release(), since that requires coop mode.
+ GCX_COOP();
+
+ pDelInfo->Release();
+ ThreadpoolMgr::RecycleMemory( pDelInfo, ThreadpoolMgr::MEMTYPE_DelegateInfo );
+}
+
+//typedef Holder<DelegateInfo *, AcquireDelegateInfo, ReleaseDelegateInfo> DelegateInfoHolder;
+
+typedef Wrapper<DelegateInfo *, AcquireDelegateInfo, ReleaseDelegateInfo> DelegateInfoHolder;
+
+/*****************************************************************************************************/
+// Caller has to GC protect Objectrefs being passed in
+DelegateInfo *DelegateInfo::MakeDelegateInfo(AppDomain *pAppDomain,
+ OBJECTREF *state,
+ OBJECTREF *waitEvent,
+ OBJECTREF *registeredWaitHandle)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ if (state != NULL || waitEvent != NULL || registeredWaitHandle != NULL)
+ {
+ MODE_COOPERATIVE;
+ }
+ else
+ {
+ MODE_ANY;
+ }
+ PRECONDITION(state == NULL || IsProtectedByGCFrame(state));
+ PRECONDITION(waitEvent == NULL || IsProtectedByGCFrame(waitEvent));
+ PRECONDITION(registeredWaitHandle == NULL || IsProtectedByGCFrame(registeredWaitHandle));
+ PRECONDITION(CheckPointer(pAppDomain));
+ INJECT_FAULT(COMPlusThrowOM());
+ }
+ CONTRACTL_END;
+
+ // If there were any DelegateInfos waiting to be released, they'll get flushed now
+ ThreadpoolMgr::FlushQueueOfTimerInfos();
+
+ DelegateInfoHolder delegateInfo = (DelegateInfo*) ThreadpoolMgr::GetRecycledMemory(ThreadpoolMgr::MEMTYPE_DelegateInfo);
+
+ delegateInfo->m_appDomainId = pAppDomain->GetId();
+
+ if (state != NULL)
+ delegateInfo->m_stateHandle = pAppDomain->CreateHandle(*state);
+ else
+ delegateInfo->m_stateHandle = NULL;
+
+ if (waitEvent != NULL)
+ delegateInfo->m_eventHandle = pAppDomain->CreateHandle(*waitEvent);
+ else
+ delegateInfo->m_eventHandle = NULL;
+
+ if (registeredWaitHandle != NULL)
+ delegateInfo->m_registeredWaitHandle = pAppDomain->CreateHandle(*registeredWaitHandle);
+ else
+ delegateInfo->m_registeredWaitHandle = NULL;
+
+ delegateInfo->m_overridesCount = 0;
+ delegateInfo->m_hasSecurityInfo = FALSE;
+
+ delegateInfo.SuppressRelease();
+
+ return delegateInfo;
+}
+
+/*****************************************************************************************************/
+FCIMPL2(FC_BOOL_RET, ThreadPoolNative::CorSetMaxThreads,DWORD workerThreads, DWORD completionPortThreads)
+{
+ FCALL_CONTRACT;
+
+ BOOL bRet = FALSE;
+ HELPER_METHOD_FRAME_BEGIN_RET_0(); // Eventually calls BEGIN_SO_INTOLERANT_CODE_NOTHROW
+
+ bRet = ThreadpoolMgr::SetMaxThreads(workerThreads,completionPortThreads);
+ HELPER_METHOD_FRAME_END();
+ FC_RETURN_BOOL(bRet);
+}
+FCIMPLEND
+
+/*****************************************************************************************************/
+FCIMPL2(VOID, ThreadPoolNative::CorGetMaxThreads,DWORD* workerThreads, DWORD* completionPortThreads)
+{
+ FCALL_CONTRACT;
+
+ ThreadpoolMgr::GetMaxThreads(workerThreads,completionPortThreads);
+ return;
+}
+FCIMPLEND
+
+/*****************************************************************************************************/
+FCIMPL2(FC_BOOL_RET, ThreadPoolNative::CorSetMinThreads,DWORD workerThreads, DWORD completionPortThreads)
+{
+ FCALL_CONTRACT;
+
+ BOOL bRet = FALSE;
+ HELPER_METHOD_FRAME_BEGIN_RET_0(); // Eventually calls BEGIN_SO_INTOLERANT_CODE_NOTHROW
+
+ bRet = ThreadpoolMgr::SetMinThreads(workerThreads,completionPortThreads);
+ HELPER_METHOD_FRAME_END();
+ FC_RETURN_BOOL(bRet);
+}
+FCIMPLEND
+
+/*****************************************************************************************************/
+FCIMPL2(VOID, ThreadPoolNative::CorGetMinThreads,DWORD* workerThreads, DWORD* completionPortThreads)
+{
+ FCALL_CONTRACT;
+
+ ThreadpoolMgr::GetMinThreads(workerThreads,completionPortThreads);
+ return;
+}
+FCIMPLEND
+
+/*****************************************************************************************************/
+FCIMPL2(VOID, ThreadPoolNative::CorGetAvailableThreads,DWORD* workerThreads, DWORD* completionPortThreads)
+{
+ FCALL_CONTRACT;
+
+ ThreadpoolMgr::GetAvailableThreads(workerThreads,completionPortThreads);
+ return;
+}
+FCIMPLEND
+
+/*****************************************************************************************************/
+
+FCIMPL0(VOID, ThreadPoolNative::NotifyRequestProgress)
+{
+ FCALL_CONTRACT;
+
+ ThreadpoolMgr::NotifyWorkItemCompleted();
+
+ if (ThreadpoolMgr::ShouldAdjustMaxWorkersActive())
+ {
+ DangerousNonHostedSpinLockTryHolder tal(&ThreadpoolMgr::ThreadAdjustmentLock);
+ if (tal.Acquired())
+ {
+ HELPER_METHOD_FRAME_BEGIN_0();
+ ThreadpoolMgr::AdjustMaxWorkersActive();
+ HELPER_METHOD_FRAME_END();
+ }
+ else
+ {
+ // the lock is held by someone else, so they will take care of this for us.
+ }
+ }
+}
+FCIMPLEND
+
+FCIMPL1(VOID, ThreadPoolNative::ReportThreadStatus, CLR_BOOL isWorking)
+{
+ FCALL_CONTRACT;
+ ThreadpoolMgr::ReportThreadStatus(isWorking);
+}
+FCIMPLEND
+
+FCIMPL0(FC_BOOL_RET, ThreadPoolNative::NotifyRequestComplete)
+{
+ FCALL_CONTRACT;
+
+ ThreadpoolMgr::NotifyWorkItemCompleted();
+
+ //
+ // Now we need to possibly do one or both of: reset the thread's state, and/or perform a
+ // "worker thread adjustment" (i.e., invoke Hill Climbing). We try to avoid these at all costs,
+ // because they require an expensive helper method frame. So we first try a minimal thread reset,
+ // then check if it covered everything that was needed, and we ask ThreadpoolMgr whether
+ // we need a thread adjustment, before setting up the frame.
+ //
+ Thread *pThread = GetThread();
+ _ASSERTE (pThread);
+
+ INT32 priority = pThread->ResetManagedThreadObjectInCoopMode(ThreadNative::PRIORITY_NORMAL);
+
+ bool needReset =
+ priority != ThreadNative::PRIORITY_NORMAL ||
+ pThread->HasThreadStateNC(Thread::TSNC_SOWorkNeeded) ||
+ !pThread->IsBackground() ||
+ pThread->HasCriticalRegion() ||
+ pThread->HasThreadAffinity();
+
+ bool shouldAdjustWorkers = ThreadpoolMgr::ShouldAdjustMaxWorkersActive();
+
+ //
+ // If it's time for a thread adjustment, try to get the lock. This is just a "try," it won't block,
+ // so it's ok to do this in cooperative mode. If we can't get the lock, then some other thread is
+ // already doing the thread adjustment, so we needn't bother.
+ //
+ DangerousNonHostedSpinLockTryHolder tal(&ThreadpoolMgr::ThreadAdjustmentLock, shouldAdjustWorkers);
+ if (!tal.Acquired())
+ shouldAdjustWorkers = false;
+
+ if (needReset || shouldAdjustWorkers)
+ {
+ HELPER_METHOD_FRAME_BEGIN_RET_0();
+
+ if (shouldAdjustWorkers)
+ {
+ ThreadpoolMgr::AdjustMaxWorkersActive();
+ tal.Release();
+ }
+
+ if (needReset)
+ pThread->InternalReset (FALSE, TRUE, TRUE, FALSE);
+
+ HELPER_METHOD_FRAME_END();
+ }
+
+ //
+ // Finally, ask ThreadpoolMgr whether it's ok to keep running work on this thread. Maybe Hill Climbing
+ // wants this thread back.
+ //
+ BOOL result = ThreadpoolMgr::ShouldWorkerKeepRunning() ? TRUE : FALSE;
+ FC_RETURN_BOOL(result);
+}
+FCIMPLEND
+
+
+/*****************************************************************************************************/
+
+void QCALLTYPE ThreadPoolNative::InitializeVMTp(CLR_BOOL* pEnableWorkerTracking)
+{
+ QCALL_CONTRACT;
+
+ BEGIN_QCALL;
+ ThreadpoolMgr::EnsureInitialized();
+ *pEnableWorkerTracking = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_ThreadPool_EnableWorkerTracking) ? TRUE : FALSE;
+ END_QCALL;
+}
+
+
+FCIMPL0(FC_BOOL_RET, ThreadPoolNative::IsThreadPoolHosted)
+{
+ FCALL_CONTRACT;
+
+ FCUnique(0x22);
+
+ FC_RETURN_BOOL(ThreadpoolMgr::IsThreadPoolHosted());
+}
+FCIMPLEND
+
+/*****************************************************************************************************/
+
+struct RegisterWaitForSingleObjectCallback_Args
+{
+ DelegateInfo *delegateInfo;
+ BOOLEAN TimerOrWaitFired;
+};
+
+static VOID
+RegisterWaitForSingleObjectCallback_Worker(LPVOID ptr)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+ OBJECTREF orState = NULL;
+
+ GCPROTECT_BEGIN( orState );
+
+ RegisterWaitForSingleObjectCallback_Args *args = (RegisterWaitForSingleObjectCallback_Args *) ptr;
+ orState = ObjectFromHandle(((DelegateInfo*) args->delegateInfo)->m_stateHandle);
+
+#ifdef _DEBUG
+ MethodDesc *pMeth = MscorlibBinder::GetMethod(METHOD__TPWAITORTIMER_HELPER__PERFORM_WAITORTIMER_CALLBACK);
+ LogCall(pMeth,"RWSOCallback");
+#endif
+
+ // Caution: the args are not protected, we have to garantee there's no GC from here till
+ // the managed call happens.
+ PREPARE_NONVIRTUAL_CALLSITE(METHOD__TPWAITORTIMER_HELPER__PERFORM_WAITORTIMER_CALLBACK);
+ DECLARE_ARGHOLDER_ARRAY(arg, 2);
+ arg[ARGNUM_0] = OBJECTREF_TO_ARGHOLDER(orState);
+ arg[ARGNUM_1] = DWORD_TO_ARGHOLDER(args->TimerOrWaitFired);
+
+ // Call the method...
+ CALL_MANAGED_METHOD_NORET(arg);
+
+ GCPROTECT_END();
+}
+
+
+void ResetThreadSecurityState(Thread* pThread)
+{
+ CONTRACTL
+ {
+ THROWS;
+ GC_TRIGGERS;
+ MODE_ANY;
+ } CONTRACTL_END;
+
+ if (pThread)
+ {
+ pThread->ResetSecurityInfo();
+ }
+}
+
+// this holder resets our thread's security state
+typedef Holder<Thread*, DoNothing<Thread*>, ResetThreadSecurityState> ThreadSecurityStateHolder;
+
+VOID NTAPI RegisterWaitForSingleObjectCallback(PVOID delegateInfo, BOOLEAN TimerOrWaitFired)
+{
+ Thread* pThread = GetThread();
+ if (pThread == NULL)
+ {
+ ClrFlsSetThreadType(ThreadType_Threadpool_Worker);
+ pThread = SetupThreadNoThrow();
+ if (pThread == NULL) {
+ return;
+ }
+ }
+
+ CONTRACTL
+ {
+ MODE_PREEMPTIVE; // Worker thread will be in preempt mode. We switch to coop below.
+ THROWS;
+ GC_TRIGGERS;
+
+ PRECONDITION(CheckPointer(delegateInfo));
+ }
+ CONTRACTL_END;
+
+ // This thread should not have any locks held at entry point.
+ _ASSERTE(pThread->m_dwLockCount == 0);
+
+ GCX_COOP();
+
+ // this holder resets our thread's security state when exiting this scope
+ ThreadSecurityStateHolder secState(pThread);
+
+ RegisterWaitForSingleObjectCallback_Args args = { ((DelegateInfo*) delegateInfo), TimerOrWaitFired };
+
+ ManagedThreadBase::ThreadPool(((DelegateInfo*) delegateInfo)->m_appDomainId, RegisterWaitForSingleObjectCallback_Worker, &args);
+
+ // We should have released all locks.
+ _ASSERTE(g_fEEShutDown || pThread->m_dwLockCount == 0 || pThread->m_fRudeAborted);
+ return;
+}
+
+void ThreadPoolNative::Init()
+{
+
+}
+
+
+FCIMPL7(LPVOID, ThreadPoolNative::CorRegisterWaitForSingleObject,
+ Object* waitObjectUNSAFE,
+ Object* stateUNSAFE,
+ UINT32 timeout,
+ CLR_BOOL executeOnlyOnce,
+ Object* registeredWaitObjectUNSAFE,
+ StackCrawlMark* stackMark,
+ CLR_BOOL compressStack)
+{
+ FCALL_CONTRACT;
+
+ HANDLE handle = 0;
+ struct _gc
+ {
+ WAITHANDLEREF waitObject;
+ OBJECTREF state;
+ OBJECTREF registeredWaitObject;
+ } gc;
+ gc.waitObject = (WAITHANDLEREF) ObjectToOBJECTREF(waitObjectUNSAFE);
+ gc.state = (OBJECTREF) stateUNSAFE;
+ gc.registeredWaitObject = (OBJECTREF) registeredWaitObjectUNSAFE;
+ HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc); // Eventually calls BEGIN_SO_INTOLERANT_CODE_NOTHROW
+
+ if(gc.waitObject == NULL)
+ COMPlusThrow(kArgumentNullException, W("ArgumentNull_Obj"));
+
+ _ASSERTE(gc.registeredWaitObject != NULL);
+
+ ULONG flag = executeOnlyOnce ? WAIT_SINGLE_EXECUTION | WAIT_FREE_CONTEXT : WAIT_FREE_CONTEXT;
+
+ HANDLE hWaitHandle = gc.waitObject->GetWaitHandle();
+ _ASSERTE(hWaitHandle);
+
+ Thread* pCurThread = GetThread();
+ _ASSERTE( pCurThread);
+
+ AppDomain* appDomain = pCurThread->GetDomain();
+ _ASSERTE(appDomain);
+
+ DelegateInfoHolder delegateInfo = DelegateInfo::MakeDelegateInfo(appDomain,
+ &gc.state,
+ (OBJECTREF *)&gc.waitObject,
+ &gc.registeredWaitObject);
+
+ if (compressStack)
+ {
+ delegateInfo->SetThreadSecurityInfo( pCurThread, stackMark );
+ }
+
+
+
+ if (!(ThreadpoolMgr::RegisterWaitForSingleObject(&handle,
+ hWaitHandle,
+ RegisterWaitForSingleObjectCallback,
+ (PVOID) delegateInfo,
+ (ULONG) timeout,
+ flag)))
+
+ {
+ _ASSERTE(GetLastError() != ERROR_CALL_NOT_IMPLEMENTED);
+
+ COMPlusThrowWin32();
+ }
+
+ delegateInfo.SuppressRelease();
+ HELPER_METHOD_FRAME_END();
+ return (LPVOID) handle;
+}
+FCIMPLEND
+
+
+VOID QueueUserWorkItemManagedCallback(PVOID pArg)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_COOPERATIVE;
+ } CONTRACTL_END;
+
+ _ASSERTE(NULL != pArg);
+
+ // This thread should not have any locks held at entry point.
+ _ASSERTE(GetThread()->m_dwLockCount == 0);
+
+ bool* wasNotRecalled = (bool*)pArg;
+
+ MethodDescCallSite dispatch(METHOD__TP_WAIT_CALLBACK__PERFORM_WAIT_CALLBACK);
+ *wasNotRecalled = dispatch.Call_RetBool(NULL);
+}
+
+
+BOOL QCALLTYPE ThreadPoolNative::RequestWorkerThread()
+{
+ QCALL_CONTRACT;
+
+ BOOL res = FALSE;
+
+ BEGIN_QCALL;
+
+ ThreadpoolMgr::SetAppDomainRequestsActive();
+
+ res = ThreadpoolMgr::QueueUserWorkItem(NULL,
+ NULL,
+ 0,
+ FALSE);
+ if (!res)
+ {
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ COMPlusThrow(kNotSupportedException);
+ else
+ COMPlusThrowWin32();
+ }
+
+ END_QCALL;
+ return res;
+}
+
+
+/********************************************************************************************************************/
+
+FCIMPL2(FC_BOOL_RET, ThreadPoolNative::CorUnregisterWait, LPVOID WaitHandle, Object* objectToNotify)
+{
+ FCALL_CONTRACT;
+
+ BOOL retVal = false;
+ SAFEHANDLEREF refSH = (SAFEHANDLEREF) ObjectToOBJECTREF(objectToNotify);
+ HELPER_METHOD_FRAME_BEGIN_RET_1(refSH); // Eventually calls BEGIN_SO_INTOLERANT_CODE_NOTHROW
+
+ HANDLE hWait = (HANDLE) WaitHandle;
+ HANDLE hObjectToNotify = NULL;
+
+ ThreadpoolMgr::WaitInfo *pWaitInfo = (ThreadpoolMgr::WaitInfo *)hWait;
+ _ASSERTE(pWaitInfo != NULL);
+
+ ThreadpoolMgr::WaitInfoHolder wiHolder(NULL);
+
+ if (refSH != NULL)
+ {
+ // Create a GCHandle in the WaitInfo, so that it can hold on to the safe handle
+ pWaitInfo->ExternalEventSafeHandle = GetAppDomain()->CreateHandle(NULL);
+ pWaitInfo->handleOwningAD = GetAppDomain()->GetId();
+
+ // Holder will now release objecthandle in face of exceptions
+ wiHolder.Assign(pWaitInfo);
+
+ // Store SafeHandle in object handle. Holder will now release both safehandle and objecthandle
+ // in case of exceptions
+ StoreObjectInHandle(pWaitInfo->ExternalEventSafeHandle, refSH);
+
+ // Acquire safe handle to examine its handle, then release.
+ SafeHandleHolder shHolder(&refSH);
+
+ if (refSH->GetHandle() == INVALID_HANDLE_VALUE)
+ {
+ hObjectToNotify = INVALID_HANDLE_VALUE;
+ // We do not need the ObjectHandle, refcount on the safehandle etc
+ wiHolder.Release();
+ _ASSERTE(pWaitInfo->ExternalEventSafeHandle == NULL);
+ }
+ }
+
+ _ASSERTE(hObjectToNotify == NULL || hObjectToNotify == INVALID_HANDLE_VALUE);
+
+ // When hObjectToNotify is NULL ExternalEventSafeHandle contains event to notify (if it is non NULL).
+ // When hObjectToNotify is INVALID_HANDLE_VALUE, UnregisterWaitEx blocks until dispose is complete.
+ retVal = ThreadpoolMgr::UnregisterWaitEx(hWait, hObjectToNotify);
+
+ if (retVal)
+ wiHolder.SuppressRelease();
+
+ HELPER_METHOD_FRAME_END();
+ FC_RETURN_BOOL(retVal);
+}
+FCIMPLEND
+
+/********************************************************************************************************************/
+FCIMPL1(void, ThreadPoolNative::CorWaitHandleCleanupNative, LPVOID WaitHandle)
+{
+ FCALL_CONTRACT;
+
+ HELPER_METHOD_FRAME_BEGIN_0(); // Eventually calls BEGIN_SO_INTOLERANT_CODE_NOTHROW
+
+ HANDLE hWait = (HANDLE)WaitHandle;
+ ThreadpoolMgr::WaitHandleCleanup(hWait);
+
+ HELPER_METHOD_FRAME_END();
+}
+FCIMPLEND
+
+/********************************************************************************************************************/
+
+/********************************************************************************************************************/
+
+struct BindIoCompletion_Args
+{
+ DWORD ErrorCode;
+ DWORD numBytesTransferred;
+ LPOVERLAPPED lpOverlapped;
+ BOOL *pfProcessed;
+};
+
+void SetAsyncResultProperties(
+ OVERLAPPEDDATAREF overlapped,
+ DWORD dwErrorCode,
+ DWORD dwNumBytes
+)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_NOTRIGGER;
+ STATIC_CONTRACT_MODE_ANY;
+ STATIC_CONTRACT_SO_TOLERANT;
+
+ ASYNCRESULTREF asyncResult = overlapped->m_asyncResult;
+ // only filestream is expected to have a null delegate in which
+ // case we do the necessary book-keeping here. However, for robustness
+ // we should make sure that the asyncResult is indeed an instance of
+ // FileStreamAsyncResult
+ if (asyncResult->GetMethodTable() == g_pAsyncFileStream_AsyncResultClass)
+ {
+ // Handle reading from & writing to closed pipes. It's possible for
+ // an async read on a pipe to be issued and then the pipe is closed,
+ // returning this error. This may very well be necessary. -BG
+ if (dwErrorCode == ERROR_BROKEN_PIPE || dwErrorCode == ERROR_NO_DATA)
+ dwErrorCode = 0;
+ asyncResult->SetErrorCode(dwErrorCode);
+ asyncResult->SetNumBytes(dwNumBytes);
+ asyncResult->SetCompletedAsynchronously();
+ asyncResult->SetIsComplete();
+
+ // Signal the event - the OS does not do this for us.
+ WAITHANDLEREF waitHandle = asyncResult->GetWaitHandle();
+ HANDLE h = waitHandle->GetWaitHandle();
+ if ((h != NULL) && (h != (HANDLE) -1))
+ UnsafeSetEvent(h);
+ }
+}
+
+VOID BindIoCompletionCallBack_Worker(LPVOID args)
+{
+ STATIC_CONTRACT_THROWS;
+ STATIC_CONTRACT_GC_TRIGGERS;
+ STATIC_CONTRACT_MODE_ANY;
+ STATIC_CONTRACT_SO_INTOLERANT;
+
+ DWORD ErrorCode = ((BindIoCompletion_Args *)args)->ErrorCode;
+ DWORD numBytesTransferred = ((BindIoCompletion_Args *)args)->numBytesTransferred;
+ LPOVERLAPPED lpOverlapped = ((BindIoCompletion_Args *)args)->lpOverlapped;
+
+ OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped));
+
+ GCPROTECT_BEGIN(overlapped);
+ *(((BindIoCompletion_Args *)args)->pfProcessed) = TRUE;
+ // we set processed to TRUE, now it's our responsibility to guarantee proper cleanup
+
+#ifdef _DEBUG
+ MethodDesc *pMeth = MscorlibBinder::GetMethod(METHOD__IOCB_HELPER__PERFORM_IOCOMPLETION_CALLBACK);
+ LogCall(pMeth,"IOCallback");
+#endif
+
+ if (overlapped->m_iocb != NULL)
+ {
+ // Caution: the args are not protected, we have to garantee there's no GC from here till
+ PREPARE_NONVIRTUAL_CALLSITE(METHOD__IOCB_HELPER__PERFORM_IOCOMPLETION_CALLBACK);
+ DECLARE_ARGHOLDER_ARRAY(arg, 3);
+ arg[ARGNUM_0] = DWORD_TO_ARGHOLDER(ErrorCode);
+ arg[ARGNUM_1] = DWORD_TO_ARGHOLDER(numBytesTransferred);
+ arg[ARGNUM_2] = PTR_TO_ARGHOLDER(lpOverlapped);
+
+ // Call the method...
+ CALL_MANAGED_METHOD_NORET(arg);
+ }
+ else
+ { // no user delegate to callback
+ _ASSERTE((overlapped->m_iocbHelper == NULL) || !"This is benign, but should be optimized");
+
+ // we cannot do this at threadpool initialization time since mscorlib may not have been loaded
+ if (!g_pAsyncFileStream_AsyncResultClass)
+ {
+ g_pAsyncFileStream_AsyncResultClass = MscorlibBinder::GetClass(CLASS__FILESTREAM_ASYNCRESULT);
+ }
+
+ SetAsyncResultProperties(overlapped, ErrorCode, numBytesTransferred);
+ }
+ GCPROTECT_END();
+}
+
+
+void __stdcall BindIoCompletionCallbackStubEx(DWORD ErrorCode,
+ DWORD numBytesTransferred,
+ LPOVERLAPPED lpOverlapped,
+ BOOL setStack)
+{
+ Thread* pThread = GetThread();
+ if (pThread == NULL)
+ {
+ // TODO: how do we notify user of OOM here?
+ ClrFlsSetThreadType(ThreadType_Threadpool_Worker);
+ pThread = SetupThreadNoThrow();
+ if (pThread == NULL) {
+ return;
+ }
+ }
+
+ CONTRACTL
+ {
+ THROWS;
+ MODE_ANY;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+ }
+ CONTRACTL_END;
+
+ // This thread should not have any locks held at entry point.
+ _ASSERTE(pThread->m_dwLockCount == 0);
+
+ LOG((LF_INTEROP, LL_INFO10000, "In IO_CallBackStub thread 0x%x retCode 0x%x, overlap 0x%x\n", pThread, ErrorCode, lpOverlapped));
+
+ GCX_COOP();
+
+ // NOTE: there is a potential race between the time we retrieve the app domain pointer,
+ // and the time which this thread enters the domain.
+ //
+ // To solve the race, we rely on the fact that there is a thread sync (via GC)
+ // between releasing an app domain's handle, and destroying the app domain. Thus
+ // it is important that we not go into preemptive gc mode in that window.
+ //
+
+ //IMPORTANT - do not gc protect overlapped here - it belongs to another appdomain
+ //so if it stops being pinned it should be able to go away
+ OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped));
+ AppDomainFromIDHolder appDomain(ADID(overlapped->GetAppDomainId()), TRUE);
+ BOOL fProcessed = FALSE;
+ if (!appDomain.IsUnloaded())
+ {
+ // this holder resets our thread's security state when exiting this scope,
+ // but only if setStack is TRUE.
+ Thread* pHolderThread = NULL;
+ if (setStack)
+ {
+ pHolderThread = pThread;
+ }
+
+ ThreadSecurityStateHolder secState(pHolderThread);
+
+ BindIoCompletion_Args args = {ErrorCode, numBytesTransferred, lpOverlapped, &fProcessed};
+ appDomain.Release();
+ ManagedThreadBase::ThreadPool(ADID(overlapped->GetAppDomainId()), BindIoCompletionCallBack_Worker, &args);
+ }
+
+
+
+
+ LOG((LF_INTEROP, LL_INFO10000, "Leaving IO_CallBackStub thread 0x%x retCode 0x%x, overlap 0x%x\n", pThread, ErrorCode, lpOverlapped));
+ // We should have released all locks.
+ _ASSERTE(g_fEEShutDown || pThread->m_dwLockCount == 0 || pThread->m_fRudeAborted);
+ return;
+}
+
+void WINAPI BindIoCompletionCallbackStub(DWORD ErrorCode,
+ DWORD numBytesTransferred,
+ LPOVERLAPPED lpOverlapped)
+{
+ WRAPPER_NO_CONTRACT;
+ BindIoCompletionCallbackStubEx(ErrorCode, numBytesTransferred, lpOverlapped, TRUE);
+
+#ifndef FEATURE_PAL
+ extern Volatile<ULONG> g_fCompletionPortDrainNeeded;
+
+ Thread *pThread = GetThread();
+ if (g_fCompletionPortDrainNeeded && pThread)
+ {
+ // We have started draining completion port.
+ // The next job picked up by this thread is going to be after our special marker.
+ if (!pThread->IsCompletionPortDrained())
+ {
+ pThread->MarkCompletionPortDrained();
+ }
+ }
+#endif // !FEATURE_PAL
+}
+
+FCIMPL1(FC_BOOL_RET, ThreadPoolNative::CorBindIoCompletionCallback, HANDLE fileHandle)
+{
+ FCALL_CONTRACT;
+
+ BOOL retVal = FALSE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_0(); // Eventually calls BEGIN_SO_INTOLERANT_CODE_NOTHROW
+
+ HANDLE hFile = (HANDLE) fileHandle;
+ DWORD errCode = 0;
+
+ retVal = ThreadpoolMgr::BindIoCompletionCallback(hFile,
+ BindIoCompletionCallbackStub,
+ 0, // reserved, must be 0
+ OUT errCode);
+ if (!retVal)
+ {
+ if (errCode == ERROR_CALL_NOT_IMPLEMENTED)
+ COMPlusThrow(kPlatformNotSupportedException);
+ else
+ {
+ SetLastError(errCode);
+ COMPlusThrowWin32();
+ }
+ }
+
+ HELPER_METHOD_FRAME_END();
+ FC_RETURN_BOOL(retVal);
+}
+FCIMPLEND
+
+FCIMPL1(FC_BOOL_RET, ThreadPoolNative::CorPostQueuedCompletionStatus, LPOVERLAPPED lpOverlapped)
+{
+ FCALL_CONTRACT;
+
+ OVERLAPPEDDATAREF overlapped = ObjectToOVERLAPPEDDATAREF(OverlappedDataObject::GetOverlapped(lpOverlapped));
+
+ BOOL res = FALSE;
+
+ HELPER_METHOD_FRAME_BEGIN_RET_1(overlapped); // Eventually calls BEGIN_SO_INTOLERANT_CODE_NOTHROW
+
+ // OS doesn't signal handle, so do it here
+ overlapped->Internal = 0;
+
+ if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_Context, ThreadPoolIOEnqueue))
+ FireEtwThreadPoolIOEnqueue(lpOverlapped, OBJECTREFToObject(overlapped), false, GetClrInstanceId());
+
+ res = ThreadpoolMgr::PostQueuedCompletionStatus(lpOverlapped,
+ BindIoCompletionCallbackStub);
+
+ if (!res)
+ {
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ COMPlusThrow(kPlatformNotSupportedException);
+ else
+ COMPlusThrowWin32();
+ }
+
+ HELPER_METHOD_FRAME_END();
+ FC_RETURN_BOOL(res);
+}
+FCIMPLEND
+
+
+/********************************************************************************************************************/
+
+
+/******************************************************************************************/
+/* */
+/* Timer Functions */
+/* */
+/******************************************************************************************/
+
+void AppDomainTimerCallback_Worker(LPVOID ptr)
+{
+ CONTRACTL
+ {
+ GC_TRIGGERS;
+ THROWS;
+ MODE_COOPERATIVE;
+ }
+ CONTRACTL_END;
+
+#ifdef _DEBUG
+ MethodDesc *pMeth = MscorlibBinder::GetMethod(METHOD__TIMER_QUEUE__APPDOMAIN_TIMER_CALLBACK);
+ LogCall(pMeth,"AppDomainTimerCallback");
+#endif
+
+ MethodDescCallSite(METHOD__TIMER_QUEUE__APPDOMAIN_TIMER_CALLBACK).Call(NULL);
+}
+
+VOID WINAPI AppDomainTimerCallback(PVOID delegateInfo, BOOLEAN timerOrWaitFired)
+{
+ Thread* pThread = GetThread();
+ if (pThread == NULL)
+ {
+ // TODO: how do we notify user of OOM here?
+ ClrFlsSetThreadType(ThreadType_Threadpool_Worker);
+ pThread = SetupThreadNoThrow();
+ if (pThread == NULL) {
+ return;
+ }
+ }
+
+ CONTRACTL
+ {
+ THROWS;
+ MODE_ANY;
+ GC_TRIGGERS;
+ SO_INTOLERANT;
+
+ PRECONDITION(CheckPointer(delegateInfo));
+ }
+ CONTRACTL_END;
+
+ // This thread should not have any locks held at entry point.
+ _ASSERTE(pThread->m_dwLockCount == 0);
+
+ GCX_COOP();
+
+ {
+ ThreadSecurityStateHolder secState(pThread);
+ ManagedThreadBase::ThreadPool(((DelegateInfo*)delegateInfo)->m_appDomainId, AppDomainTimerCallback_Worker, NULL);
+ }
+
+ // We should have released all locks.
+ _ASSERTE(g_fEEShutDown || pThread->m_dwLockCount == 0 || pThread->m_fRudeAborted);
+}
+
+HANDLE QCALLTYPE AppDomainTimerNative::CreateAppDomainTimer(INT32 dueTime)
+{
+ QCALL_CONTRACT;
+
+ HANDLE hTimer = NULL;
+ BEGIN_QCALL;
+
+ _ASSERTE(dueTime >= 0);
+
+ AppDomain* pAppDomain = GetThread()->GetDomain();
+ ADID adid = pAppDomain->GetId();
+
+ DelegateInfoHolder delegateInfo = DelegateInfo::MakeDelegateInfo(
+ pAppDomain,
+ NULL,
+ NULL,
+ NULL);
+
+ BOOL res = ThreadpoolMgr::CreateTimerQueueTimer(
+ &hTimer,
+ (WAITORTIMERCALLBACK)AppDomainTimerCallback,
+ (PVOID)delegateInfo,
+ (ULONG)dueTime,
+ (ULONG)-1 /* this timer doesn't repeat */,
+ 0 /* no flags */);
+
+ if (!res)
+ {
+ if (GetLastError() == ERROR_CALL_NOT_IMPLEMENTED)
+ COMPlusThrow(kNotSupportedException);
+ else
+ COMPlusThrowWin32();
+ }
+ else
+ {
+ delegateInfo.SuppressRelease();
+ }
+
+ END_QCALL;
+ return hTimer;
+}
+
+BOOL QCALLTYPE AppDomainTimerNative::DeleteAppDomainTimer(HANDLE hTimer)
+{
+ QCALL_CONTRACT;
+
+ BOOL res = FALSE;
+ BEGIN_QCALL;
+
+ _ASSERTE(hTimer != NULL && hTimer != INVALID_HANDLE_VALUE);
+ res = ThreadpoolMgr::DeleteTimerQueueTimer(hTimer, NULL);
+
+ if (!res)
+ {
+ DWORD errorCode = ::GetLastError();
+ if (errorCode != ERROR_IO_PENDING)
+ COMPlusThrowWin32(HRESULT_FROM_WIN32(errorCode));
+ }
+
+ END_QCALL;
+ return res;
+}
+
+
+BOOL QCALLTYPE AppDomainTimerNative::ChangeAppDomainTimer(HANDLE hTimer, INT32 dueTime)
+{
+ QCALL_CONTRACT;
+
+ BOOL res = FALSE;
+ BEGIN_QCALL;
+
+ _ASSERTE(hTimer != NULL && hTimer != INVALID_HANDLE_VALUE);
+ _ASSERTE(dueTime >= 0);
+
+ res = ThreadpoolMgr::ChangeTimerQueueTimer(
+ hTimer,
+ (ULONG)dueTime,
+ (ULONG)-1 /* this timer doesn't repeat */);
+
+ if (!res)
+ COMPlusThrowWin32();
+
+ END_QCALL;
+ return res;
+}