+++ /dev/null
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-
-// ---------------------------------------------------------------------------
-// APIThreadStress.cpp (API thread stresser)
-// ---------------------------------------------------------------------------
-
-#include "stdafx.h"
-
-#ifdef STRESS_THREAD
-
-#include "apithreadstress.h"
-#include "clrhost.h"
-#include "ex.h"
-#include "log.h"
-
-
-
-// For now, thread stress is incompatible with hosting. We need a host CreateThread
-// to fix this.
-#undef SetEvent
-#undef ResetEvent
-
-int APIThreadStress::s_threadStressCount = 0;
-
-APIThreadStress::APIThreadStress()
-{
- WRAPPER_NO_CONTRACT;
-
- m_threadCount = 0;
-
- // Don't "fork" stress threads
- if (ClrFlsGetValue(TlsIdx_StressThread) == NULL)
- m_threadCount = s_threadStressCount;
-
- if (m_threadCount != 0)
- {
- m_setupOK = TRUE;
-
- m_hThreadArray = new (nothrow) HANDLE [ m_threadCount ];
- if (m_hThreadArray == NULL)
- m_setupOK = FALSE;
- else
- {
- HANDLE *p = m_hThreadArray;
- HANDLE *pEnd = p + m_threadCount;
-
- while (p < pEnd)
- {
- DWORD id;
- *p = ::CreateThread(NULL, 0, StartThread, this, 0, &id);
- if (*p == NULL)
- m_setupOK = FALSE;
- p++;
- }
- }
-
- m_syncEvent = ClrCreateManualEvent(FALSE);
- if (m_syncEvent == INVALID_HANDLE_VALUE)
- m_setupOK = FALSE;
- }
-}
-
-APIThreadStress::~APIThreadStress()
-{
- WRAPPER_NO_CONTRACT;
-
- if (m_threadCount > 0)
- {
- HANDLE *p = m_hThreadArray;
- HANDLE *pEnd = p + m_threadCount;
-
- if (p != NULL)
- {
- while (p < pEnd)
- {
- if (*p != NULL)
- {
- if (m_threadCount > 0 && m_setupOK)
- WaitForSingleObjectEx(*p, INFINITE, FALSE);
-
- ::CloseHandle(*p);
- }
- p++;
- }
- delete [] m_hThreadArray;
- }
-
- if (m_syncEvent != INVALID_HANDLE_VALUE)
- CloseHandle(m_syncEvent);
- }
-}
-
-void APIThreadStress::SetThreadStressCount(int threadCount)
-{
- LIMITED_METHOD_CONTRACT;
-
- s_threadStressCount = threadCount;
-}
-
-
-DWORD WINAPI APIThreadStress::StartThread(void *arg)
-{
- WRAPPER_NO_CONTRACT;
- STATIC_CONTRACT_SO_NOT_MAINLINE; // not mainline so scenario
-
- APIThreadStress *pThis = (APIThreadStress *) arg;
-
- ClrFlsSetValue(TlsIdx_StressThread, pThis);
-
- EX_TRY
- {
- // Perform initial synchronization
- WaitForSingleObjectEx(pThis->m_syncEvent, INFINITE, FALSE);
- InterlockedIncrement(&pThis->m_runCount);
-
- LOG((LF_ALL, LL_INFO100, "Stressing operation on thread %d\n", GetCurrentThreadId()));
- ((APIThreadStress *)arg)->Invoke();
- LOG((LF_ALL, LL_INFO100, "End stress operation on thread %d\n", GetCurrentThreadId()));
-
- if (InterlockedDecrement(&pThis->m_runCount) == 0)
- ::SetEvent(pThis->m_syncEvent);
- }
- EX_CATCH
- {
- LOG((LF_ALL, LL_ERROR, "Exception during stress operation on thread %d\n", GetCurrentThreadId()));
- }
- EX_END_CATCH(SwallowAllExceptions);
-
- return 0;
-}
-
-BOOL APIThreadStress::DoThreadStress()
-{
- WRAPPER_NO_CONTRACT;
-
- if (m_threadCount > 0 && m_setupOK)
- {
- HANDLE *p = m_hThreadArray;
- HANDLE *pEnd = p + m_threadCount;
-
- while (p < pEnd)
- {
- ::ResumeThread(*p);
- p++;
- }
-
- // Start the threads at the same time
- ::SetEvent(m_syncEvent);
-
- return TRUE;
- }
- else
- {
- SyncThreadStress();
- return FALSE;
- }
-}
-
-void APIThreadStress::SyncThreadStress()
-{
- WRAPPER_NO_CONTRACT;
-
- APIThreadStress *pThis = (APIThreadStress *) ClrFlsGetValue(TlsIdx_StressThread);
-
- if (pThis != NULL)
- {
- LOG((LF_ALL, LL_INFO1000, "Syncing stress operation on thread %d\n", GetCurrentThreadId()));
-
- ::ResetEvent(pThis->m_syncEvent);
-
- if (InterlockedDecrement(&pThis->m_runCount) == 0)
- ::SetEvent(pThis->m_syncEvent);
- else
- WaitForSingleObjectEx(pThis->m_syncEvent, INFINITE, FALSE);
- InterlockedIncrement(&pThis->m_runCount);
-
- LOG((LF_ALL, LL_INFO1000, "Resuming stress operation on thread %d\n", GetCurrentThreadId()));
- }
-}
-
-#endif // STRESS_THREAD
EntryCrtMain // unsigned main(void)
};
-#ifdef STRESS_THREAD
-
-struct Stress_Thread_Param
-{
- MethodDesc *pFD;
- GlobalStrongHandleHolder argHandle;
- short numSkipArgs;
- CorEntryPointType EntryType;
- Thread* pThread;
-
-public:
- Stress_Thread_Param()
- : pFD(NULL),
- argHandle(),
- numSkipArgs(0),
- EntryType(EntryManagedMain),
- pThread(NULL)
- { LIMITED_METHOD_CONTRACT; }
-
- Stress_Thread_Param* Clone ()
- {
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- NewHolder<Stress_Thread_Param> retVal= new Stress_Thread_Param;
-
- retVal->pFD = pFD;
- if (argHandle.GetValue()!=NULL)
- {
- GCX_COOP();
- retVal->argHandle.Assign(CreateDuplicateHandle(argHandle.GetValue()));
- }
- retVal->numSkipArgs = numSkipArgs;
- retVal->EntryType = EntryType;
- retVal->pThread = pThread;
- return retVal.Extract();
- }
-};
-
-struct Stress_Thread_Worker_Param
-{
- Stress_Thread_Param *lpParameter;
- ULONG retVal;
-};
-
-static void Stress_Thread_Proc_Worker_Impl(Stress_Thread_Worker_Param * args)
-{
- STATIC_CONTRACT_THROWS;
-
- args->retVal = E_FAIL;
-
- Stress_Thread_Param* lpParam = (Stress_Thread_Param *)args->lpParameter;
-
- ARG_SLOT stackVar = 0;
-
- MethodDescCallSite threadStart(lpParam->pFD);
-
- // Build the parameter array and invoke the method.
- if (lpParam->EntryType == EntryManagedMain)
- {
- PTRARRAYREF StrArgArray = (PTRARRAYREF)ObjectFromHandle(lpParam->argHandle.GetValue());
- stackVar = ObjToArgSlot(StrArgArray);
- }
-
- if (lpParam->pFD->IsVoid())
- {
- threadStart.Call(&stackVar);
- args->retVal = GetLatchedExitCode();
- }
- else
- {
- // We are doing the same cast as in RunMain. Main is required to return INT32 if it returns.
- ARG_SLOT retVal = (INT32)threadStart.Call_RetArgSlot(&stackVar);
- args->retVal = static_cast<ULONG>(retVal);
- }
-}
-
-// wrap into EX_TRY_NOCATCH and call the real thing
-static void Stress_Thread_Proc_Worker (LPVOID ptr)
-{
- STATIC_CONTRACT_THROWS;
-
- EX_TRY_NOCATCH(Stress_Thread_Worker_Param *, args, (Stress_Thread_Worker_Param *) ptr)
- {
- Stress_Thread_Proc_Worker_Impl(args);
- //<TODO>
- // When we get mainCRTStartup from the C++ then this should be able to go away.</TODO>
- fflush(stdout);
- fflush(stderr);
- }
- EX_END_NOCATCH
-}
-
-static DWORD WINAPI __stdcall Stress_Thread_Proc (LPVOID lpParameter)
-{
- STATIC_CONTRACT_THROWS;
-
- Stress_Thread_Worker_Param args = {(Stress_Thread_Param*)lpParameter,0};
- Stress_Thread_Param *lpParam = (Stress_Thread_Param *)lpParameter;
- Thread *pThread = lpParam->pThread;
- if (!pThread->HasStarted())
- return 0;
-
- _ASSERTE(::GetAppDomain() != NULL);
- BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return E_FAIL);
- EX_TRY
- {
-
- ADID KickOffDomain = pThread->GetKickOffDomainId();
-
- // should always have a kickoff domain - a thread should never start in a domain that is unloaded
- // because otherwise it would have been collected because nobody can hold a reference to thread object
- // in a domain that has been unloaded. But it is possible that we started the unload, in which
- // case this thread wouldn't be allowed in or would be punted anyway.
- if (KickOffDomain != lpParam->pThread->GetDomain()->GetId())
- pThread->DoADCallBack(KickOffDomain, Stress_Thread_Proc_Worker, &args);
- else
- Stress_Thread_Proc_Worker(&args);
-
- }
- EX_CATCH
- {
- }
- EX_END_CATCH(SwallowAllExceptions);
-
- delete (Stress_Thread_Param *) lpParameter;
- // Enable preemptive GC so a GC thread can suspend me.
- GCX_PREEMP_NO_DTOR();
- DestroyThread(pThread);
-
- END_SO_INTOLERANT_CODE;
- return args.retVal;
-}
-
-static void Stress_Thread_Start (LPVOID lpParameter)
-{
- CONTRACT_VOID
- {
- THROWS;
- GC_TRIGGERS;
- INJECT_FAULT(COMPlusThrowOM());
- MODE_ANY;
- }
- CONTRACT_END;
-
- Thread *pCurThread = GetThread();
- if (pCurThread->m_stressThreadCount == -1) {
- pCurThread->m_stressThreadCount = g_pConfig->GetStressThreadCount();
- }
- DWORD dwThreads = pCurThread->m_stressThreadCount;
- if (dwThreads <= 1)
- RETURN;
-
- Thread ** threads = new Thread* [dwThreads-1];
-
- DWORD n;
- for (n = 0; n < dwThreads-1; n ++)
- {
- threads[n] = SetupUnstartedThread();
-
- threads[n]->m_stressThreadCount = dwThreads/2;
- Stress_Thread_Param *param = ((Stress_Thread_Param*)lpParameter)->Clone();
- param->pThread = threads[n];
- if (!threads[n]->CreateNewThread(0, Stress_Thread_Proc, param))
- {
- delete param;
- threads[n]->DecExternalCount(FALSE);
- ThrowOutOfMemory();
- }
- threads[n]->SetThreadPriority (THREAD_PRIORITY_NORMAL);
- }
-
- for (n = 0; n < dwThreads-1; n ++)
- {
- threads[n]->StartThread();
- }
- __SwitchToThread (0, CALLER_LIMITS_SPINNING);
-
- RETURN;
-}
-
-void Stress_Thread_RunMain(MethodDesc* pFD, CorEntryPointType EntryType, short numSkipArgs, OBJECTHANDLE argHandle)
-{
- CONTRACTL
- {
- THROWS;
- GC_TRIGGERS;
- MODE_ANY;
- }
- CONTRACTL_END;
-
- Stress_Thread_Param Param;
- Param.pFD = pFD;
- Param.argHandle.Assign(argHandle);
- Param.numSkipArgs = numSkipArgs;
- Param.EntryType = EntryType;
- Param.pThread = NULL;
- Stress_Thread_Start (&Param);
-}
-
-
-#endif // STRESS_THREAD
-
void DECLSPEC_NORETURN ThrowMainMethodException(MethodDesc* pMD, UINT resID)
{
CONTRACTL
StrArgArray = *pParam->stringArgs;
}
-#ifdef STRESS_THREAD
- OBJECTHANDLE argHandle = (StrArgArray != NULL) ? CreateGlobalStrongHandle (StrArgArray) : NULL;
- Stress_Thread_RunMain(pParam->pFD, pParam->EntryType, pParam->numSkipArgs, argHandle);
-#endif
-
ARG_SLOT stackVar = ObjToArgSlot(StrArgArray);
if (pParam->pFD->IsVoid())