diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index 60560e43fc7a69..a440561b2b8ae0 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -20,6 +20,7 @@ $(MSBuildThisFileDirectory)src\ILLink\ true + true @@ -236,7 +237,6 @@ - diff --git a/src/coreclr/System.Private.CoreLib/src/System/Threading/Timer.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/Threading/Timer.CoreCLR.cs deleted file mode 100644 index e91e8966e31e64..00000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/Threading/Timer.CoreCLR.cs +++ /dev/null @@ -1,75 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using Microsoft.Win32.SafeHandles; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace System.Threading -{ - internal sealed partial class TimerQueue - { - #region interface to native per-AppDomain timer - - // We use a SafeHandle to ensure that the native timer is destroyed when the AppDomain is unloaded. - private sealed class AppDomainTimerSafeHandle : SafeHandleZeroOrMinusOneIsInvalid - { - public AppDomainTimerSafeHandle() - : base(true) - { - } - - protected override bool ReleaseHandle() - { - return DeleteAppDomainTimer(handle); - } - } - - private readonly int _id; // TimerQueues[_id] == this - - private AppDomainTimerSafeHandle? m_appDomainTimer; - - private TimerQueue(int id) - { - _id = id; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool SetTimer(uint actualDuration) - { - if (m_appDomainTimer == null || m_appDomainTimer.IsInvalid) - { - Debug.Assert(!_isTimerScheduled); - Debug.Assert(_id >= 0 && _id < Instances.Length && this == Instances[_id]); - - m_appDomainTimer = CreateAppDomainTimer(actualDuration, _id); - return !m_appDomainTimer.IsInvalid; - } - else - { - return ChangeAppDomainTimer(m_appDomainTimer, actualDuration); - } - } - - // The VM calls this when a native timer fires. - internal static void AppDomainTimerCallback(int id) - { - Debug.Assert(id >= 0 && id < Instances.Length && Instances[id]._id == id); - Instances[id].FireNextTimers(); - } - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AppDomainTimer_Create")] - private static partial AppDomainTimerSafeHandle CreateAppDomainTimer(uint dueTime, int id); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AppDomainTimer_Change")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool ChangeAppDomainTimer(AppDomainTimerSafeHandle handle, uint dueTime); - - [LibraryImport(RuntimeHelpers.QCall, EntryPoint = "AppDomainTimer_Delete")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool DeleteAppDomainTimer(IntPtr handle); - - #endregion - } -} diff --git a/src/coreclr/debug/daccess/request.cpp b/src/coreclr/debug/daccess/request.cpp index 4b7bee7cbd1d40..6aae7bd1b4c131 100644 --- a/src/coreclr/debug/daccess/request.cpp +++ b/src/coreclr/debug/daccess/request.cpp @@ -348,20 +348,9 @@ ClrDataAccess::GetThreadpoolData(struct DacpThreadpoolData *threadpoolData) threadpoolData->MaxLimitTotalCPThreads = ThreadpoolMgr::MaxLimitTotalCPThreads; threadpoolData->CurrentLimitTotalCPThreads = (LONG)(counts.NumActive); //legacy: currently has no meaning threadpoolData->MinLimitTotalCPThreads = ThreadpoolMgr::MinLimitTotalCPThreads; - - TADDR pEntry = DacGetTargetAddrForHostAddr(&ThreadpoolMgr::TimerQueue,true); - ThreadpoolMgr::LIST_ENTRY entry; - DacReadAll(pEntry,&entry,sizeof(ThreadpoolMgr::LIST_ENTRY),true); - TADDR node = (TADDR) entry.Flink; threadpoolData->NumTimers = 0; - while (node && node != pEntry) - { - threadpoolData->NumTimers++; - DacReadAll(node,&entry,sizeof(ThreadpoolMgr::LIST_ENTRY),true); - node = (TADDR) entry.Flink; - } + threadpoolData->AsyncTimerCallbackCompletionFPtr = NULL; - threadpoolData->AsyncTimerCallbackCompletionFPtr = (CLRDATA_ADDRESS) GFN_TADDR(ThreadpoolMgr__AsyncTimerCallbackCompletion); SOSDacLeave(); return hr; } diff --git a/src/coreclr/inc/dacvars.h b/src/coreclr/inc/dacvars.h index 5ce668353c4fb1..4e91e8fa189f37 100644 --- a/src/coreclr/inc/dacvars.h +++ b/src/coreclr/inc/dacvars.h @@ -110,7 +110,6 @@ DEFINE_DACVAR(ThreadpoolMgr::ThreadCounter, ThreadpoolMgr__CPThreadCounter, Thre DEFINE_DACVAR(LONG, ThreadpoolMgr__MaxFreeCPThreads, ThreadpoolMgr::MaxFreeCPThreads) DEFINE_DACVAR(LONG, ThreadpoolMgr__MaxLimitTotalCPThreads, ThreadpoolMgr::MaxLimitTotalCPThreads) DEFINE_DACVAR(LONG, ThreadpoolMgr__MinLimitTotalCPThreads, ThreadpoolMgr::MinLimitTotalCPThreads) -DEFINE_DACVAR(LIST_ENTRY, ThreadpoolMgr__TimerQueue, ThreadpoolMgr::TimerQueue) DEFINE_DACVAR_NO_DUMP(SIZE_T, dac__HillClimbingLog, ::HillClimbingLog) DEFINE_DACVAR(int, dac__HillClimbingLogFirstIndex, ::HillClimbingLogFirstIndex) DEFINE_DACVAR(int, dac__HillClimbingLogSize, ::HillClimbingLogSize) diff --git a/src/coreclr/inc/gfunc_list.h b/src/coreclr/inc/gfunc_list.h index ddb05092db5b41..d5c5b67d9633e3 100644 --- a/src/coreclr/inc/gfunc_list.h +++ b/src/coreclr/inc/gfunc_list.h @@ -10,7 +10,6 @@ #define DEFINE_DACGFN_STATIC(class, func) #endif -DEFINE_DACGFN_STATIC(ThreadpoolMgr, AsyncTimerCallbackCompletion) DEFINE_DACGFN(DACNotifyCompilationFinished) DEFINE_DACGFN(ThePreStub) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index f9444ab439ae6d..8f65ae980430de 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3898,16 +3898,6 @@ inline BOOL IsGateSpecialThread () return !!(t_ThreadType & ThreadType_Gate); } -// check if current thread is a Timer thread -inline BOOL IsTimerSpecialThread () -{ - STATIC_CONTRACT_NOTHROW; - STATIC_CONTRACT_GC_NOTRIGGER; - STATIC_CONTRACT_MODE_ANY; - - return !!(t_ThreadType & ThreadType_Timer); -} - // check if current thread is a debugger helper thread inline BOOL IsDbgHelperSpecialThread () { diff --git a/src/coreclr/vm/comthreadpool.cpp b/src/coreclr/vm/comthreadpool.cpp index 9cf77c6c3b201b..6c3f82f4fdc42b 100644 --- a/src/coreclr/vm/comthreadpool.cpp +++ b/src/coreclr/vm/comthreadpool.cpp @@ -852,144 +852,3 @@ FCIMPL1(FC_BOOL_RET, ThreadPoolNative::CorPostQueuedCompletionStatus, LPOVERLAPP FC_RETURN_BOOL(res); } FCIMPLEND - - -/********************************************************************************************************************/ - - -/******************************************************************************************/ -/* */ -/* Timer Functions */ -/* */ -/******************************************************************************************/ - -void AppDomainTimerCallback_Worker(LPVOID ptr) -{ - CONTRACTL - { - GC_TRIGGERS; - THROWS; - MODE_COOPERATIVE; - } - CONTRACTL_END; - -#ifdef _DEBUG - MethodDesc *pMeth = CoreLibBinder::GetMethod(METHOD__TIMER_QUEUE__APPDOMAIN_TIMER_CALLBACK); - LogCall(pMeth,"AppDomainTimerCallback"); -#endif - - ThreadpoolMgr::TimerInfoContext* pTimerInfoContext = (ThreadpoolMgr::TimerInfoContext*)ptr; - ARG_SLOT args[] = { PtrToArgSlot(pTimerInfoContext->TimerId) }; - MethodDescCallSite(METHOD__TIMER_QUEUE__APPDOMAIN_TIMER_CALLBACK).Call(args); -} - -VOID WINAPI AppDomainTimerCallback(PVOID callbackState, BOOLEAN timerOrWaitFired) -{ - Thread* pThread = GetThreadNULLOk(); - 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; - } - CONTRACTL_END; - - GCX_COOP(); - - ThreadpoolMgr::TimerInfoContext* pTimerInfoContext = (ThreadpoolMgr::TimerInfoContext*)callbackState; - ManagedThreadBase::ThreadPool(AppDomainTimerCallback_Worker, pTimerInfoContext); -} - -extern "C" HANDLE QCALLTYPE AppDomainTimer_Create(INT32 dueTime, INT32 timerId) -{ - QCALL_CONTRACT; - - HANDLE hTimer = NULL; - BEGIN_QCALL; - - _ASSERTE(dueTime >= 0); - _ASSERTE(timerId >= 0); - - AppDomain* pAppDomain = GetThread()->GetDomain(); - - ThreadpoolMgr::TimerInfoContext* timerContext = new ThreadpoolMgr::TimerInfoContext(); - timerContext->TimerId = timerId; - NewHolder timerContextHolder(timerContext); - - BOOL res = ThreadpoolMgr::CreateTimerQueueTimer( - &hTimer, - (WAITORTIMERCALLBACK)AppDomainTimerCallback, - (PVOID)timerContext, - (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 - { - timerContextHolder.SuppressRelease(); - } - - END_QCALL; - return hTimer; -} - -extern "C" BOOL QCALLTYPE AppDomainTimer_Delete(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; -} - - -extern "C" BOOL QCALLTYPE AppDomainTimer_Change(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; -} diff --git a/src/coreclr/vm/comthreadpool.h b/src/coreclr/vm/comthreadpool.h index 1b700cd91fd810..46d9fda34d857b 100644 --- a/src/coreclr/vm/comthreadpool.h +++ b/src/coreclr/vm/comthreadpool.h @@ -60,9 +60,6 @@ class ThreadPoolNative extern "C" INT64 QCALLTYPE ThreadPool_GetCompletedWorkItemCount(); extern "C" BOOL QCALLTYPE ThreadPool_RequestWorkerThread(); extern "C" BOOL QCALLTYPE ThreadPool_PerformGateActivities(INT32 cpuUtilization); -extern "C" HANDLE QCALLTYPE AppDomainTimer_Create(INT32 dueTime, INT32 timerId); -extern "C" BOOL QCALLTYPE AppDomainTimer_Change(HANDLE hTimer, INT32 dueTime); -extern "C" BOOL QCALLTYPE AppDomainTimer_Delete(HANDLE hTimer); VOID QueueUserWorkItemManagedCallback(PVOID pArg); void WINAPI BindIoCompletionCallbackStub(DWORD ErrorCode, diff --git a/src/coreclr/vm/corelib.h b/src/coreclr/vm/corelib.h index c7ea9e3c40fedd..0e73fbccbf2e4a 100644 --- a/src/coreclr/vm/corelib.h +++ b/src/coreclr/vm/corelib.h @@ -906,9 +906,6 @@ DEFINE_METHOD(TPWAITORTIMER_HELPER, PERFORM_WAITORTIMER_CALLBACK, DEFINE_CLASS(TP_WAIT_CALLBACK, Threading, _ThreadPoolWaitCallback) DEFINE_METHOD(TP_WAIT_CALLBACK, PERFORM_WAIT_CALLBACK, PerformWaitCallback, SM_RetBool) -DEFINE_CLASS(TIMER_QUEUE, Threading, TimerQueue) -DEFINE_METHOD(TIMER_QUEUE, APPDOMAIN_TIMER_CALLBACK, AppDomainTimerCallback, SM_Int_RetVoid) - DEFINE_CLASS(THREAD_POOL, Threading, ThreadPool) DEFINE_METHOD(THREAD_POOL, ENSURE_GATE_THREAD_RUNNING, EnsureGateThreadRunning, SM_RetVoid) DEFINE_METHOD(THREAD_POOL, UNSAFE_QUEUE_UNMANAGED_WORK_ITEM, UnsafeQueueUnmanagedWorkItem, SM_IntPtr_IntPtr_RetVoid) diff --git a/src/coreclr/vm/qcallentrypoints.cpp b/src/coreclr/vm/qcallentrypoints.cpp index 92c36b3089458b..5b5f4c8a8ac1b8 100644 --- a/src/coreclr/vm/qcallentrypoints.cpp +++ b/src/coreclr/vm/qcallentrypoints.cpp @@ -212,9 +212,6 @@ static const Entry s_QCall[] = DllImportEntry(ThreadPool_GetCompletedWorkItemCount) DllImportEntry(ThreadPool_RequestWorkerThread) DllImportEntry(ThreadPool_PerformGateActivities) - DllImportEntry(AppDomainTimer_Create) - DllImportEntry(AppDomainTimer_Change) - DllImportEntry(AppDomainTimer_Delete) #ifdef TARGET_UNIX DllImportEntry(WaitHandle_CorWaitOnePrioritizedNative) #endif diff --git a/src/coreclr/vm/threadpoolrequest.cpp b/src/coreclr/vm/threadpoolrequest.cpp index 6e1caf5187650c..ddccec27b6d5d9 100644 --- a/src/coreclr/vm/threadpoolrequest.cpp +++ b/src/coreclr/vm/threadpoolrequest.cpp @@ -385,8 +385,7 @@ void UnManagedPerAppDomainTPCount::QueueUnmanagedWorkRequest(LPTHREAD_START_ROUT _ASSERTE(pWorkRequest != NULL); PREFIX_ASSUME(pWorkRequest != NULL); - if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolEnqueue) && - !ThreadpoolMgr::AreEtwQueueEventsSpeciallyHandled(function)) + if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolEnqueue)) FireEtwThreadPoolEnqueue(pWorkRequest, GetClrInstanceId()); m_lock.Init(LOCK_TYPE_DEFAULT); @@ -493,8 +492,7 @@ void UnManagedPerAppDomainTPCount::DispatchWorkItem(bool* foundWork, bool* wasNo wrFunction = pWorkRequest->Function; wrContext = pWorkRequest->Context; - if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolDequeue) && - !ThreadpoolMgr::AreEtwQueueEventsSpeciallyHandled(wrFunction)) + if (ETW_EVENT_ENABLED(MICROSOFT_WINDOWS_DOTNETRUNTIME_PROVIDER_DOTNET_Context, ThreadPoolDequeue)) FireEtwThreadPoolDequeue(pWorkRequest, GetClrInstanceId()); ThreadpoolMgr::FreeWorkRequest(pWorkRequest); diff --git a/src/coreclr/vm/threads.cpp b/src/coreclr/vm/threads.cpp index f7cdf70d02d195..85e0c6df70cc04 100644 --- a/src/coreclr/vm/threads.cpp +++ b/src/coreclr/vm/threads.cpp @@ -703,7 +703,7 @@ Thread* SetupThread() pThread->SetThreadState(Thread::TS_CompletionPortThread); pThread->SetBackground(TRUE); } - else if (IsTimerSpecialThread() || IsWaitSpecialThread()) + else if (IsWaitSpecialThread()) { pThread->SetThreadState(Thread::TS_TPWorkerThread); pThread->SetBackground(TRUE); @@ -794,7 +794,7 @@ Thread* SetupThread() { pThread->SetThreadState(Thread::TS_CompletionPortThread); } - else if (IsTimerSpecialThread() || IsWaitSpecialThread()) + else if (IsWaitSpecialThread()) { pThread->SetThreadState(Thread::TS_TPWorkerThread); } @@ -7183,7 +7183,6 @@ BOOL Thread::HaveExtraWorkForFinalizer() LIMITED_METHOD_CONTRACT; return RequireSyncBlockCleanup() - || ThreadpoolMgr::HaveTimerInfosToFlush() || Thread::CleanupNeededForFinalizedThread() || (m_DetachCount > 0) || SystemDomain::System()->RequireAppDomainCleanup() @@ -7231,9 +7230,6 @@ void Thread::DoExtraWorkForFinalizer() Thread::CleanupDetachedThreads(); } - // If there were any TimerInfos waiting to be released, they'll get flushed now - ThreadpoolMgr::FlushQueueOfTimerInfos(); - if (YieldProcessorNormalization::IsMeasurementScheduled()) { GCX_PREEMP(); @@ -7585,7 +7581,7 @@ void ManagedThreadBase::KickOff(ADCallBackFcnType pTarget, LPVOID args) ManagedThreadBase_FullTransition(pTarget, args, ManagedThread); } -// The IOCompletion, QueueUserWorkItem, AddTimer, RegisterWaitForSingleObject cases in the ThreadPool +// The IOCompletion, QueueUserWorkItem, RegisterWaitForSingleObject cases in the ThreadPool void ManagedThreadBase::ThreadPool(ADCallBackFcnType pTarget, LPVOID args) { WRAPPER_NO_CONTRACT; diff --git a/src/coreclr/vm/threads.h b/src/coreclr/vm/threads.h index 99b547bf639d5d..ee2ca0a1de9489 100644 --- a/src/coreclr/vm/threads.h +++ b/src/coreclr/vm/threads.h @@ -352,17 +352,6 @@ const CLONE_QUEUE_USER_APC_FLAGS SpecialUserModeApcWithContextFlags = #endif // FEATURE_SPECIAL_USER_MODE_APC -/***************************************************************************/ -// Public enum shared between thread and threadpool -// These are two kinds of threadpool thread that the threadpool mgr needs -// to keep track of -enum ThreadpoolThreadType -{ - WorkerThread, - CompletionPortThread, - WaitThread, - TimerMgrThread -}; //*************************************************************************** // Public functions // @@ -6013,7 +6002,7 @@ struct ManagedThreadBase static void KickOff(ADCallBackFcnType pTarget, LPVOID args); - // The IOCompletion, QueueUserWorkItem, AddTimer, RegisterWaitForSingleObject cases in + // The IOCompletion, QueueUserWorkItem, RegisterWaitForSingleObject cases in // the ThreadPool static void ThreadPool(ADCallBackFcnType pTarget, LPVOID args); diff --git a/src/coreclr/vm/win32threadpool.cpp b/src/coreclr/vm/win32threadpool.cpp index e8e49f4179802a..609ac1c6a77b06 100644 --- a/src/coreclr/vm/win32threadpool.cpp +++ b/src/coreclr/vm/win32threadpool.cpp @@ -121,8 +121,6 @@ DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) unsigned int ThreadpoolMgr::LastDequeueTime; SPTR_IMPL(WorkRequest,ThreadpoolMgr,WorkRequestHead); // Head of work request queue SPTR_IMPL(WorkRequest,ThreadpoolMgr,WorkRequestTail); // Head of work request queue -SVAL_IMPL(ThreadpoolMgr::LIST_ENTRY,ThreadpoolMgr,TimerQueue); // queue of timers - //unsigned int ThreadpoolMgr::LastCpuSamplingTime=0; // last time cpu utilization was sampled by gate thread unsigned int ThreadpoolMgr::LastCPThreadCreation=0; // last time a completion port thread was created unsigned int ThreadpoolMgr::NumberOfProcessors; // = NumberOfWorkerThreads - no. of blocked threads @@ -136,21 +134,12 @@ ThreadpoolMgr::LIST_ENTRY ThreadpoolMgr::WaitThreadsHead; CLRLifoSemaphore* ThreadpoolMgr::WorkerSemaphore; CLRLifoSemaphore* ThreadpoolMgr::RetiredWorkerSemaphore; -CrstStatic ThreadpoolMgr::TimerQueueCriticalSection; -HANDLE ThreadpoolMgr::TimerThread=NULL; -Thread *ThreadpoolMgr::pTimerThread=NULL; - -// Cacheline aligned, hot variable -DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) DWORD ThreadpoolMgr::LastTickCount; - // Cacheline aligned, hot variable DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) LONG ThreadpoolMgr::GateThreadStatus=GATE_THREAD_STATUS_NOT_RUNNING; // Move out of from preceeding variables' cache line DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) ThreadpoolMgr::RecycledListsWrapper ThreadpoolMgr::RecycledLists; -ThreadpoolMgr::TimerInfo *ThreadpoolMgr::TimerInfosToBeRecycled = NULL; - BOOL ThreadpoolMgr::IsApcPendingOnWaitThread = FALSE; #ifndef DACCESS_COMPILE @@ -369,7 +358,6 @@ BOOL ThreadpoolMgr::Initialize() { WorkerCriticalSection.Init(CrstThreadpoolWorker); } - TimerQueueCriticalSection.Init(CrstThreadpoolTimerQueue); if (!UsePortableThreadPool()) { @@ -377,9 +365,6 @@ BOOL ThreadpoolMgr::Initialize() InitializeListHead(&WaitThreadsHead); } - // initialize TimerQueue - InitializeListHead(&TimerQueue); - if (!UsePortableThreadPoolForIO()) { RetiredCPWakeupEvent = new CLREvent(); @@ -423,7 +408,6 @@ BOOL ThreadpoolMgr::Initialize() { WorkerCriticalSection.Destroy(); } - TimerQueueCriticalSection.Destroy(); bExceptionCaught = TRUE; } @@ -4289,782 +4273,4 @@ BOOL ThreadpoolMgr::SufficientDelaySinceLastDequeue() #endif #endif -/************************************************************************/ - -struct CreateTimerThreadParams { - CLREvent event; - BOOL setupSucceeded; -}; - -BOOL ThreadpoolMgr::CreateTimerQueueTimer(PHANDLE phNewTimer, - WAITORTIMERCALLBACK Callback, - PVOID Parameter, - DWORD DueTime, - DWORD Period, - ULONG Flag) -{ - CONTRACTL - { - THROWS; // EnsureInitialized, CreateAutoEvent can throw - if (GetThreadNULLOk()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} // There can be calls thru ICorThreadpool - MODE_ANY; - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACTL_END; - - EnsureInitialized(); - - // For now we use just one timer thread. Consider using multiple timer threads if - // number of timers in the queue exceeds a certain threshold. The logic and code - // would be similar to the one for creating wait threads. - if (NULL == TimerThread) - { - CrstHolder csh(&TimerQueueCriticalSection); - - // check again - if (NULL == TimerThread) - { - CreateTimerThreadParams params; - params.event.CreateAutoEvent(FALSE); - - params.setupSucceeded = FALSE; - - HANDLE TimerThreadHandle = Thread::CreateUtilityThread(Thread::StackSize_Small, TimerThreadStart, ¶ms, W(".NET Timer")); - - if (TimerThreadHandle == NULL) - { - params.event.CloseEvent(); - ThrowOutOfMemory(); - } - - { - GCX_PREEMP(); - for(;;) - { - // if a host throws because it couldnt allocate another thread, - // just retry the wait. - if (SafeWait(¶ms.event,INFINITE, FALSE) != WAIT_TIMEOUT) - break; - } - } - params.event.CloseEvent(); - - if (!params.setupSucceeded) - { - CloseHandle(TimerThreadHandle); - *phNewTimer = NULL; - return FALSE; - } - - TimerThread = TimerThreadHandle; - } - - } - - - NewHolder timerInfoHolder; - TimerInfo * timerInfo = new (nothrow) TimerInfo; - if (NULL == timerInfo) - ThrowOutOfMemory(); - - timerInfoHolder.Assign(timerInfo); - - timerInfo->FiringTime = DueTime; - timerInfo->Function = Callback; - timerInfo->Context = Parameter; - timerInfo->Period = Period; - timerInfo->state = 0; - timerInfo->flag = Flag; - timerInfo->ExternalCompletionEvent = INVALID_HANDLE; - timerInfo->ExternalEventSafeHandle = NULL; - - *phNewTimer = (HANDLE)timerInfo; - - BOOL status = QueueUserAPC((PAPCFUNC)InsertNewTimer,TimerThread,(size_t)timerInfo); - if (FALSE == status) - { - *phNewTimer = NULL; - return FALSE; - } - - timerInfoHolder.SuppressRelease(); - return TRUE; -} - -#ifdef _MSC_VER -#ifdef HOST_64BIT -#pragma warning (disable : 4716) -#else -#pragma warning (disable : 4715) -#endif -#endif -DWORD WINAPI ThreadpoolMgr::TimerThreadStart(LPVOID p) -{ - ClrFlsSetThreadType (ThreadType_Timer); - - STATIC_CONTRACT_THROWS; - STATIC_CONTRACT_GC_TRIGGERS; // due to SetApartment - STATIC_CONTRACT_MODE_PREEMPTIVE; - /* cannot use contract because of SEH - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_PREEMPTIVE; - } - CONTRACTL_END;*/ - - CreateTimerThreadParams* params = (CreateTimerThreadParams*)p; - - Thread* pThread = SetupThreadNoThrow(); - - params->setupSucceeded = (pThread == NULL) ? 0 : 1; - params->event.Set(); - - if (pThread == NULL) - return 0; - - pTimerThread = pThread; - // Timer threads never die - - LastTickCount = GetTickCount(); - -#ifdef FEATURE_COMINTEROP - if (pThread->SetApartment(Thread::AS_InMTA) != Thread::AS_InMTA) - { - // @todo: should we log the failure - return 0; - } -#endif // FEATURE_COMINTEROP - - for (;;) - { - // moved to its own function since EX_TRY consumes stack -#ifdef _MSC_VER -#pragma inline_depth (0) // the function containing EX_TRY can't be inlined here -#endif - TimerThreadFire(); -#ifdef _MSC_VER -#pragma inline_depth (20) -#endif - } - - // unreachable -} - -void ThreadpoolMgr::TimerThreadFire() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - EX_TRY { - DWORD timeout = FireTimers(); - ClrSleepEx(timeout, TRUE); - - // the thread could wake up either because an APC completed or the sleep timeout - // in both case, we need to sweep the timer queue, firing timers, and readjusting - // the next firing time - - } - EX_CATCH { - // Assert on debug builds since a dead timer thread is a fatal error - _ASSERTE(FALSE); - EX_RETHROW; - } - EX_END_CATCH(SwallowAllExceptions); -} - -#ifdef _MSC_VER -#ifdef HOST_64BIT -#pragma warning (default : 4716) -#else -#pragma warning (default : 4715) -#endif -#endif - -// Executed as an APC in timer thread -void ThreadpoolMgr::InsertNewTimer(TimerInfo* pArg) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE(pArg); - TimerInfo * timerInfo = pArg; - - if (timerInfo->state & TIMER_DELETE) - { // timer was deleted before it could be registered - DeleteTimer(timerInfo); - return; - } - - // set the firing time = current time + due time (note initially firing time = due time) - DWORD currentTime = GetTickCount(); - if (timerInfo->FiringTime == (ULONG) -1) - { - timerInfo->state = TIMER_REGISTERED; - timerInfo->refCount = 1; - - } - else - { - timerInfo->FiringTime += currentTime; - - timerInfo->state = (TIMER_REGISTERED | TIMER_ACTIVE); - timerInfo->refCount = 1; - - // insert the timer in the queue - InsertTailList(&TimerQueue,(&timerInfo->link)); - } - - return; -} - - -// executed by the Timer thread -// sweeps through the list of timers, readjusting the firing times, queueing APCs for -// those that have expired, and returns the next firing time interval -DWORD ThreadpoolMgr::FireTimers() -{ - CONTRACTL - { - THROWS; // QueueUserWorkItem can throw - if (GetThreadNULLOk()) { GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} - if (GetThreadNULLOk()) { MODE_PREEMPTIVE;} else { DISABLED(MODE_ANY);} - } - CONTRACTL_END; - - DWORD currentTime = GetTickCount(); - DWORD nextFiringInterval = (DWORD) -1; - TimerInfo* timerInfo = NULL; - - EX_TRY - { - for (LIST_ENTRY* node = (LIST_ENTRY*) TimerQueue.Flink; - node != &TimerQueue; - ) - { - timerInfo = (TimerInfo*) node; - node = (LIST_ENTRY*) node->Flink; - - if (TimeExpired(LastTickCount, currentTime, timerInfo->FiringTime)) - { - if (timerInfo->Period == 0 || timerInfo->Period == (ULONG) -1) - { - DeactivateTimer(timerInfo); - } - - InterlockedIncrement(&timerInfo->refCount); - - if (UsePortableThreadPool()) - { - GCX_COOP(); - - ARG_SLOT args[] = { PtrToArgSlot(AsyncTimerCallbackCompletion), PtrToArgSlot(timerInfo) }; - MethodDescCallSite(METHOD__THREAD_POOL__UNSAFE_QUEUE_UNMANAGED_WORK_ITEM).Call(args); - } - else - { - QueueUserWorkItem(AsyncTimerCallbackCompletion, - timerInfo, - QUEUE_ONLY /* TimerInfo take care of deleting*/); - } - - if (timerInfo->Period != 0 && timerInfo->Period != (ULONG)-1) - { - ULONG nextFiringTime = timerInfo->FiringTime + timerInfo->Period; - DWORD firingInterval; - if (TimeExpired(timerInfo->FiringTime, currentTime, nextFiringTime)) - { - // Enough time has elapsed to fire the timer yet again. The timer is not able to keep up with the short - // period, have it fire 1 ms from now to avoid spinning without a delay. - timerInfo->FiringTime = currentTime + 1; - firingInterval = 1; - } - else - { - timerInfo->FiringTime = nextFiringTime; - firingInterval = TimeInterval(nextFiringTime, currentTime); - } - - if (firingInterval < nextFiringInterval) - nextFiringInterval = firingInterval; - } - } - else - { - DWORD firingInterval = TimeInterval(timerInfo->FiringTime, currentTime); - if (firingInterval < nextFiringInterval) - nextFiringInterval = firingInterval; - } - } - } - EX_CATCH - { - // If QueueUserWorkItem throws OOM, swallow the exception and retry on - // the next call to FireTimers(), otherwise retrhow. - Exception *ex = GET_EXCEPTION(); - // undo the call to DeactivateTimer() - InterlockedDecrement(&timerInfo->refCount); - timerInfo->state = timerInfo->state & TIMER_ACTIVE; - InsertTailList(&TimerQueue, (&timerInfo->link)); - if (ex->GetHR() != E_OUTOFMEMORY) - { - EX_RETHROW; - } - } - EX_END_CATCH(RethrowTerminalExceptions); - - LastTickCount = currentTime; - - return nextFiringInterval; -} - -DWORD WINAPI ThreadpoolMgr::AsyncTimerCallbackCompletion(PVOID pArgs) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - Thread* pThread = GetThreadNULLOk(); - if (pThread == NULL) - { - HRESULT hr = ERROR_SUCCESS; - - ClrFlsSetThreadType(ThreadType_Threadpool_Worker); - pThread = SetupThreadNoThrow(&hr); - - if (pThread == NULL) - { - return hr; - } - } - - { - TimerInfo* timerInfo = (TimerInfo*) pArgs; - ((WAITORTIMERCALLBACKFUNC) timerInfo->Function) (timerInfo->Context, TRUE) ; - - if (InterlockedDecrement(&timerInfo->refCount) == 0) - { - DeleteTimer(timerInfo); - } - } - - return ERROR_SUCCESS; -} - - -// removes the timer from the timer queue, thereby cancelling it -// there may still be pending callbacks that haven't completed -void ThreadpoolMgr::DeactivateTimer(TimerInfo* timerInfo) -{ - LIMITED_METHOD_CONTRACT; - - RemoveEntryList((LIST_ENTRY*) timerInfo); - - // This timer info could go into another linked list of timer infos - // waiting to be released. Reinitialize the list pointers - InitializeListHead(&timerInfo->link); - timerInfo->state = timerInfo->state & ~TIMER_ACTIVE; -} - -DWORD WINAPI ThreadpoolMgr::AsyncDeleteTimer(PVOID pArgs) -{ - CONTRACTL - { - THROWS; - MODE_PREEMPTIVE; - GC_TRIGGERS; - } - CONTRACTL_END; - - Thread * pThread = GetThreadNULLOk(); - if (pThread == NULL) - { - HRESULT hr = ERROR_SUCCESS; - - ClrFlsSetThreadType(ThreadType_Threadpool_Worker); - pThread = SetupThreadNoThrow(&hr); - - if (pThread == NULL) - { - return hr; - } - } - - DeleteTimer((TimerInfo*) pArgs); - - return ERROR_SUCCESS; -} - -void ThreadpoolMgr::DeleteTimer(TimerInfo* timerInfo) -{ - CONTRACTL - { - if (GetThreadNULLOk() == pTimerThread) { NOTHROW; } else { THROWS; } - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - _ASSERTE((timerInfo->state & TIMER_ACTIVE) == 0); - - _ASSERTE(!(timerInfo->flag & WAIT_FREE_CONTEXT)); - - if (timerInfo->flag & WAIT_INTERNAL_COMPLETION) - { - timerInfo->InternalCompletionEvent.Set(); - return; // the timerInfo will be deleted by the thread that's waiting on InternalCompletionEvent - } - - // ExternalCompletionEvent comes from Host, ExternalEventSafeHandle from managed code. - // They are mutually exclusive. - _ASSERTE(!(timerInfo->ExternalCompletionEvent != INVALID_HANDLE && - timerInfo->ExternalEventSafeHandle != NULL)); - - if (timerInfo->ExternalCompletionEvent != INVALID_HANDLE) - { - SetEvent(timerInfo->ExternalCompletionEvent); - timerInfo->ExternalCompletionEvent = INVALID_HANDLE; - } - - // We cannot block the timer thread, so some cleanup is deferred to other threads. - if (GetThreadNULLOk() == pTimerThread) - { - // Notify the ExternalEventSafeHandle with an user work item - if (timerInfo->ExternalEventSafeHandle != NULL) - { - BOOL success = FALSE; - EX_TRY - { - if (QueueUserWorkItem(AsyncDeleteTimer, - timerInfo, - QUEUE_ONLY) != FALSE) - { - success = TRUE; - } - } - EX_CATCH - { - } - EX_END_CATCH(SwallowAllExceptions); - - // If unable to queue a user work item, fall back to queueing timer for release - // which will happen *sometime* in the future. - if (success == FALSE) - { - QueueTimerInfoForRelease(timerInfo); - } - - return; - } - - // Releasing GC handles can block. So we wont do this on the timer thread. - // We'll put it in a list which will be processed by a worker thread - if (timerInfo->Context != NULL) - { - QueueTimerInfoForRelease(timerInfo); - return; - } - } - - // To get here we are either not the Timer thread or there is no blocking work to be done - - if (timerInfo->Context != NULL) - { - GCX_COOP(); - delete (ThreadpoolMgr::TimerInfoContext*)timerInfo->Context; - } - - if (timerInfo->ExternalEventSafeHandle != NULL) - { - ReleaseTimerInfo(timerInfo); - } - - delete timerInfo; - -} - -// We add TimerInfos from deleted timers into a linked list. -// A worker thread will later release the handles held by the TimerInfo -// and recycle them if possible. -void ThreadpoolMgr::QueueTimerInfoForRelease(TimerInfo *pTimerInfo) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - // The synchronization in this method depends on the fact that - // - There is only one timer thread - // - The one and only timer thread is executing this method. - // - This function wont go into an alertable state. That could trigger another APC. - // Else two threads can be queueing timerinfos and a race could - // lead to leaked memory and handles - _ASSERTE(pTimerThread == GetThread()); - TimerInfo *pHead = NULL; - - // Make sure this timer info has been deactivated and removed from any other lists - _ASSERTE((pTimerInfo->state & TIMER_ACTIVE) == 0); - //_ASSERTE(pTimerInfo->link.Blink == &(pTimerInfo->link) && - // pTimerInfo->link.Flink == &(pTimerInfo->link)); - // Make sure "link" is the first field in TimerInfo - _ASSERTE(pTimerInfo == (PVOID)&pTimerInfo->link); - - // Grab any previously published list - if ((pHead = InterlockedExchangeT(&TimerInfosToBeRecycled, NULL)) != NULL) - { - // If there already is a list, just append - InsertTailList((LIST_ENTRY *)pHead, &pTimerInfo->link); - pTimerInfo = pHead; - } - else - // If this is the head, make its next and previous ptrs point to itself - InitializeListHead((LIST_ENTRY*)&pTimerInfo->link); - - // Publish the list - (void) InterlockedExchangeT(&TimerInfosToBeRecycled, pTimerInfo); - -} - -void ThreadpoolMgr::FlushQueueOfTimerInfos() -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - MODE_ANY; - } - CONTRACTL_END; - - TimerInfo *pHeadTimerInfo = NULL, *pCurrTimerInfo = NULL; - LIST_ENTRY *pNextInfo = NULL; - - if ((pHeadTimerInfo = InterlockedExchangeT(&TimerInfosToBeRecycled, NULL)) == NULL) - return; - - do - { - RemoveHeadList((LIST_ENTRY *)pHeadTimerInfo, pNextInfo); - _ASSERTE(pNextInfo != NULL); - - pCurrTimerInfo = (TimerInfo *) pNextInfo; - - GCX_COOP(); - if (pCurrTimerInfo->Context != NULL) - { - delete (ThreadpoolMgr::TimerInfoContext*)pCurrTimerInfo->Context; - } - - if (pCurrTimerInfo->ExternalEventSafeHandle != NULL) - { - ReleaseTimerInfo(pCurrTimerInfo); - } - - delete pCurrTimerInfo; - - } - while ((TimerInfo *)pNextInfo != pHeadTimerInfo); -} - -/************************************************************************/ -BOOL ThreadpoolMgr::ChangeTimerQueueTimer( - HANDLE Timer, - ULONG DueTime, - ULONG Period) -{ - CONTRACTL - { - THROWS; - MODE_ANY; - GC_NOTRIGGER; - INJECT_FAULT(COMPlusThrowOM()); - } - CONTRACTL_END; - - _ASSERTE(IsInitialized()); - _ASSERTE(Timer); // not possible to give invalid handle in managed code - - NewHolder updateInfoHolder; - TimerUpdateInfo *updateInfo = new TimerUpdateInfo; - updateInfoHolder.Assign(updateInfo); - - updateInfo->Timer = (TimerInfo*) Timer; - updateInfo->DueTime = DueTime; - updateInfo->Period = Period; - - BOOL status = QueueUserAPC((PAPCFUNC)UpdateTimer, - TimerThread, - (size_t) updateInfo); - - if (status) - updateInfoHolder.SuppressRelease(); - - return(status); -} - -void ThreadpoolMgr::UpdateTimer(TimerUpdateInfo* pArgs) -{ - CONTRACTL - { - NOTHROW; - GC_NOTRIGGER; - MODE_ANY; - } - CONTRACTL_END; - - TimerUpdateInfo* updateInfo = (TimerUpdateInfo*) pArgs; - TimerInfo* timerInfo = updateInfo->Timer; - - timerInfo->Period = updateInfo->Period; - - if (updateInfo->DueTime == (ULONG) -1) - { - if (timerInfo->state & TIMER_ACTIVE) - { - DeactivateTimer(timerInfo); - } - // else, noop (the timer was already inactive) - _ASSERTE((timerInfo->state & TIMER_ACTIVE) == 0); - - delete updateInfo; - return; - } - - DWORD currentTime = GetTickCount(); - timerInfo->FiringTime = currentTime + updateInfo->DueTime; - - delete updateInfo; - - if (! (timerInfo->state & TIMER_ACTIVE)) - { - // timer not active (probably a one shot timer that has expired), so activate it - timerInfo->state |= TIMER_ACTIVE; - _ASSERTE(timerInfo->refCount >= 1); - // insert the timer in the queue - InsertTailList(&TimerQueue,(&timerInfo->link)); - - } - - return; -} - -/************************************************************************/ -BOOL ThreadpoolMgr::DeleteTimerQueueTimer( - HANDLE Timer, - HANDLE Event) -{ - CONTRACTL - { - THROWS; - MODE_ANY; - GC_TRIGGERS; - } - CONTRACTL_END; - - _ASSERTE(IsInitialized()); // cannot call delete before creating timer - _ASSERTE(Timer); // not possible to give invalid handle in managed code - - // make volatile to avoid compiler reordering check after async call. - // otherwise, DeregisterTimer could delete timerInfo before the comparison. - VolatilePtr timerInfo = (TimerInfo*) Timer; - - if (Event == (HANDLE) -1) - { - //CONTRACT_VIOLATION(ThrowsViolation); - timerInfo->InternalCompletionEvent.CreateAutoEvent(FALSE); - timerInfo->flag |= WAIT_INTERNAL_COMPLETION; - } - else if (Event) - { - timerInfo->ExternalCompletionEvent = Event; - } -#ifdef _DEBUG - else /* Event == NULL */ - { - _ASSERTE(timerInfo->ExternalCompletionEvent == INVALID_HANDLE); - } -#endif - - BOOL isBlocking = timerInfo->flag & WAIT_INTERNAL_COMPLETION; - - BOOL status = QueueUserAPC((PAPCFUNC)DeregisterTimer, - TimerThread, - (size_t)(TimerInfo*)timerInfo); - - if (FALSE == status) - { - if (isBlocking) - timerInfo->InternalCompletionEvent.CloseEvent(); - return FALSE; - } - - if (isBlocking) - { - _ASSERTE(timerInfo->ExternalEventSafeHandle == NULL); - _ASSERTE(timerInfo->ExternalCompletionEvent == INVALID_HANDLE); - _ASSERTE(GetThreadNULLOk() != pTimerThread); - - timerInfo->InternalCompletionEvent.Wait(INFINITE,TRUE /*alertable*/); - timerInfo->InternalCompletionEvent.CloseEvent(); - // Release handles and delete TimerInfo - _ASSERTE(timerInfo->refCount == 0); - // if WAIT_INTERNAL_COMPLETION flag is not set, timerInfo will be deleted in DeleteTimer. - timerInfo->flag &= ~WAIT_INTERNAL_COMPLETION; - DeleteTimer(timerInfo); - } - return status; -} - -void ThreadpoolMgr::DeregisterTimer(TimerInfo* pArgs) -{ - CONTRACTL - { - NOTHROW; - GC_TRIGGERS; - MODE_PREEMPTIVE; - } - CONTRACTL_END; - - TimerInfo* timerInfo = (TimerInfo*) pArgs; - - if (! (timerInfo->state & TIMER_REGISTERED) ) - { - // set state to deleted, so that it does not get registered - timerInfo->state |= TIMER_DELETE ; - - // since the timer has not even been registered, we dont need an interlock to decrease the RefCount - timerInfo->refCount--; - - return; - } - - if (timerInfo->state & TIMER_ACTIVE) - { - DeactivateTimer(timerInfo); - } - - if (InterlockedDecrement(&timerInfo->refCount) == 0 ) - { - DeleteTimer(timerInfo); - } - return; -} - #endif // !DACCESS_COMPILE diff --git a/src/coreclr/vm/win32threadpool.h b/src/coreclr/vm/win32threadpool.h index b08d7ca32e9c2f..b0fbe2531d374f 100644 --- a/src/coreclr/vm/win32threadpool.h +++ b/src/coreclr/vm/win32threadpool.h @@ -33,10 +33,6 @@ Revision History: #define WAIT_ACTIVE 0x02 #define WAIT_DELETE 0x04 -#define TIMER_REGISTERED 0x01 -#define TIMER_ACTIVE 0x02 -#define TIMER_DELETE 0x04 - #define WAIT_SINGLE_EXECUTION 0x00000001 #define WAIT_FREE_CONTEXT 0x00000002 #define WAIT_INTERNAL_COMPLETION 0x00000004 @@ -97,7 +93,6 @@ class ThreadpoolMgr friend class ClrDataAccess; friend struct DelegateInfo; friend class ThreadPoolNative; - friend class TimerNative; friend class UnManagedPerAppDomainTPCount; friend class ManagedPerAppDomainTPCount; friend class PerAppDomainTPCountList; @@ -219,10 +214,6 @@ class ThreadpoolMgr MEMTYPE_COUNT = 3, }; - typedef struct { - INT32 TimerId; - } TimerInfoContext; - #ifndef DACCESS_COMPILE static void StaticInitialize() { @@ -318,25 +309,8 @@ class ThreadpoolMgr VolatileStore(&LastDequeueTime, (unsigned int)GetTickCount()); } - static BOOL CreateTimerQueueTimer(PHANDLE phNewTimer, - WAITORTIMERCALLBACK Callback, - PVOID Parameter, - DWORD DueTime, - DWORD Period, - ULONG Flags); - - static BOOL ChangeTimerQueueTimer(HANDLE Timer, - ULONG DueTime, - ULONG Period); - static BOOL DeleteTimerQueueTimer(HANDLE Timer, - HANDLE CompletionEvent); - static void RecycleMemory(LPVOID mem, enum MemType memType); - static void FlushQueueOfTimerInfos(); - - static BOOL HaveTimerInfosToFlush() { return TimerInfosToBeRecycled != NULL; } - #ifndef TARGET_UNIX static LPOVERLAPPED CompletionPortDispatchWorkWithinAppDomain(Thread* pThread, DWORD* pErrorCode, DWORD* pNumBytes, size_t* pKey); static void StoreOverlappedInfoInThread(Thread* pThread, DWORD dwErrorCode, DWORD dwNumBytes, size_t key, LPOVERLAPPED lpOverlapped); @@ -345,12 +319,6 @@ class ThreadpoolMgr // Enable filtering of correlation ETW events for cases handled at a higher abstraction level #ifndef DACCESS_COMPILE - static FORCEINLINE BOOL AreEtwQueueEventsSpeciallyHandled(LPTHREAD_START_ROUTINE Function) - { - // Timer events are handled at a higher abstraction level: in the managed Timer class - return (Function == ThreadpoolMgr::AsyncTimerCallbackCompletion); - } - static FORCEINLINE BOOL AreEtwIOQueueEventsSpeciallyHandled(LPOVERLAPPED_COMPLETION_ROUTINE Function) { // We handle registered waits at a higher abstraction level @@ -568,21 +536,6 @@ class ThreadpoolMgr HANDLE Handle; } WaitEvent ; - // Timer - typedef struct { - LIST_ENTRY link; // doubly linked list of timers - ULONG FiringTime; // TickCount of when to fire next - WAITORTIMERCALLBACK Function; // Function to call when timer fires - PVOID Context; // Context to pass to function when timer fires - ULONG Period; - DWORD flag; // How do we deal with the context - DWORD state; - LONG refCount; - HANDLE ExternalCompletionEvent; // only one of this is used, but cant do a union since CLREvent has a non-default constructor - CLREvent InternalCompletionEvent; // flags indicates which one is being used - OBJECTHANDLE ExternalEventSafeHandle; - } TimerInfo; - static VOID AcquireWaitInfo(WaitInfo *pInfo) { } @@ -594,26 +547,8 @@ class ThreadpoolMgr pInfo->ExternalCompletionEvent); #endif } - static VOID AcquireTimerInfo(TimerInfo *pInfo) - { - } - static VOID ReleaseTimerInfo(TimerInfo *pInfo) - { - WRAPPER_NO_CONTRACT; -#ifndef DACCESS_COMPILE - ReleaseInfo(pInfo->ExternalEventSafeHandle, - pInfo->ExternalCompletionEvent); -#endif - } typedef Holder WaitInfoHolder; - typedef Holder TimerInfoHolder; - - typedef struct { - TimerInfo* Timer; // timer to be updated - ULONG DueTime ; // new due time - ULONG Period ; // new period - } TimerUpdateInfo; // Definitions and data structures to support recycling of high-frequency // memory blocks. We use a spin-lock to access the list @@ -913,8 +848,6 @@ class ThreadpoolMgr static DWORD WINAPI AsyncCallbackCompletion(PVOID pArgs); - static void QueueTimerInfoForRelease(TimerInfo *pTimerInfo); - static void DeactivateWait(WaitInfo* waitInfo); static void DeactivateNthWait(WaitInfo* waitInfo, DWORD index); @@ -985,18 +918,6 @@ class ThreadpoolMgr static LPVOID GetRecycledMemory(enum MemType memType); - static DWORD WINAPI TimerThreadStart(LPVOID args); - static void TimerThreadFire(); // helper method used by TimerThreadStart - static void WINAPI InsertNewTimer(TimerInfo* pArg); - static DWORD FireTimers(); - static DWORD WINAPI AsyncTimerCallbackCompletion(PVOID pArgs); - static void DeactivateTimer(TimerInfo* timerInfo); - static DWORD WINAPI AsyncDeleteTimer(PVOID pArgs); - static void DeleteTimer(TimerInfo* timerInfo); - static void WINAPI UpdateTimer(TimerUpdateInfo* pArgs); - - static void WINAPI DeregisterTimer(TimerInfo* pArgs); - inline static DWORD QueueDeregisterWait(HANDLE waitThread, WaitInfo* waitInfo) { CONTRACTL @@ -1085,13 +1006,6 @@ class ThreadpoolMgr static CrstStatic WaitThreadsCriticalSection; static LIST_ENTRY WaitThreadsHead; // queue of wait threads, each thread can handle upto 64 waits - static TimerInfo *TimerInfosToBeRecycled; // list of delegate infos associated with deleted timers - static CrstStatic TimerQueueCriticalSection; // critical section to synchronize timer queue access - SVAL_DECL(LIST_ENTRY,TimerQueue); // queue of timers - static HANDLE TimerThread; // Currently we only have one timer thread - static Thread* pTimerThread; - DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static DWORD LastTickCount; // the count just before timer thread goes to sleep - static BOOL InitCompletionPortThreadpool; // flag indicating whether completion port threadpool has been initialized static HANDLE GlobalCompletionPort; // used for binding io completions on file handles diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs index 1177300c711d70..2816f0ab6a858e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/TimerQueue.Portable.cs @@ -40,7 +40,7 @@ private static List InitializeScheduledTimerManager_Locked() // using UnsafeStart() instead of Start() Thread timerThread = new Thread(TimerThread) { - Name = ".NET Timers", + Name = ".NET Timer", IsBackground = true }; timerThread.UnsafeStart();