fix the AV in recycle process, issue #192. (#201)

* fix the AV in recycle process. this is due to we call Recycle again when the background thread exists

* more fixes

* reset hosting mode when all applications got removed
This commit is contained in:
pan-wang 2017-10-21 13:40:45 -07:00 committed by GitHub
parent 02cffd16ec
commit 448a2afed8
7 changed files with 67 additions and 45 deletions

View File

@ -3,7 +3,7 @@
<PropertyGroup> <PropertyGroup>
<AspNetCoreModuleVersionMajor>7</AspNetCoreModuleVersionMajor> <AspNetCoreModuleVersionMajor>7</AspNetCoreModuleVersionMajor>
<AspNetCoreModuleVersionMinor>1</AspNetCoreModuleVersionMinor> <AspNetCoreModuleVersionMinor>1</AspNetCoreModuleVersionMinor>
<AspNetCoreModuleVersionPatch>1968</AspNetCoreModuleVersionPatch> <AspNetCoreModuleVersionPatch>1987</AspNetCoreModuleVersionPatch>
<AspNetCoreModuleVersionSuffix>-RTM</AspNetCoreModuleVersionSuffix> <AspNetCoreModuleVersionSuffix>-RTM</AspNetCoreModuleVersionSuffix>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -95,6 +95,7 @@ private:
BOOL m_fManagedAppLoaded; BOOL m_fManagedAppLoaded;
BOOL m_fLoadManagedAppError; BOOL m_fLoadManagedAppError;
BOOL m_fInitialized;
BOOL m_fIsWebSocketsConnection; BOOL m_fIsWebSocketsConnection;
static IN_PROCESS_APPLICATION* s_Application; static IN_PROCESS_APPLICATION* s_Application;

View File

@ -11,7 +11,7 @@
#define ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG L"Maximum rapid fail count per minute of '%d' exceeded." #define ASPNETCORE_EVENT_RAPID_FAIL_COUNT_EXCEEDED_MSG L"Maximum rapid fail count per minute of '%d' exceeded."
#define ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG L"Application '%s' failed to parse processPath and arguments due to internal error, ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_PROCESS_START_INTERNAL_ERROR_MSG L"Application '%s' failed to parse processPath and arguments due to internal error, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s'but failed to get its status, ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_PROCESS_START_POSTCREATE_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s'but failed to get its status, ErrorCode = '0x%x'."
#define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s', ErrorCode = '0x%x : %x." #define ASPNETCORE_EVENT_PROCESS_START_ERROR_MSG L"Application '%s' with physical root '%s' failed to start process with commandline '%s', ErrorCode = '0x%x' processStatus code '%x'."
#define ASPNETCORE_EVENT_PROCESS_START_WRONGPORT_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but failed to listen on the given port '%d'" #define ASPNETCORE_EVENT_PROCESS_START_WRONGPORT_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but failed to listen on the given port '%d'"
#define ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but either crashed or did not reponse or did not listen on the given port '%d', ErrorCode = '0x%x'" #define ASPNETCORE_EVENT_PROCESS_START_NOTREADY_ERROR_MSG L"Application '%s' with physical root '%s' created process with commandline '%s' but either crashed or did not reponse or did not listen on the given port '%d', ErrorCode = '0x%x'"
#define ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG L"Warning: Could not create stdoutLogFile %s, ErrorCode = %d." #define ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG L"Warning: Could not create stdoutLogFile %s, ErrorCode = %d."

View File

@ -221,6 +221,10 @@ APPLICATION_MANAGER::RecycleApplication(
} }
AcquireSRWLockExclusive(&m_srwLock); AcquireSRWLockExclusive(&m_srwLock);
m_pApplicationHash->DeleteKey(&key); m_pApplicationHash->DeleteKey(&key);
if (m_pApplicationHash->Count() == 0)
{
m_hostingModel = HOSTING_UNKNOWN;
}
ReleaseSRWLockExclusive(&m_srwLock); ReleaseSRWLockExclusive(&m_srwLock);
Finished: Finished:

View File

@ -5,17 +5,26 @@
ASPNETCORE_CONFIG::~ASPNETCORE_CONFIG() ASPNETCORE_CONFIG::~ASPNETCORE_CONFIG()
{ {
if (QueryHostingModel() == HOSTING_IN_PROCESS && if (QueryHostingModel() == HOSTING_IN_PROCESS &&
!g_fRecycleProcessCalled && !g_fRecycleProcessCalled &&
!g_pHttpServer->IsCommandLineLaunch()) (g_pHttpServer->GetAdminManager() != NULL))
{ {
// There is a bug in IHttpServer::RecycleProcess. It will hit AV when worker process
// has already been in recycling state.
// To workaround, do null check on GetAdminManager(). If it is NULL, worker process is in recycling
// Do not call RecycleProcess again
// RecycleProcess can olny be called once // RecycleProcess can olny be called once
// In case of configuration change for in-process app // In case of configuration change for in-process app
// We want notify IIS first to let new request routed to new worker process // We want notify IIS first to let new request routed to new worker process
g_fRecycleProcessCalled = TRUE;
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Configuration Change"); g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Configuration Change");
} }
// It's safe for us to set this g_fRecycleProcessCalled
// as in_process scenario will always recycle the worker process for configuration change
g_fRecycleProcessCalled = TRUE;
m_struApplicationFullPath.Reset(); m_struApplicationFullPath.Reset();
if (m_pEnvironmentVariables != NULL) if (m_pEnvironmentVariables != NULL)
{ {

View File

@ -1043,7 +1043,6 @@ FORWARDING_HANDLER::OnExecuteRequestHandler(
IHttpRequest *pRequest = m_pW3Context->GetRequest(); IHttpRequest *pRequest = m_pW3Context->GetRequest();
IHttpResponse *pResponse = m_pW3Context->GetResponse(); IHttpResponse *pResponse = m_pW3Context->GetResponse();
PROTOCOL_CONFIG *pProtocol = &sm_ProtocolConfig; PROTOCOL_CONFIG *pProtocol = &sm_ProtocolConfig;
APPLICATION_MANAGER *pApplicationManager = NULL;
SERVER_PROCESS *pServerProcess = NULL; SERVER_PROCESS *pServerProcess = NULL;
USHORT cchHostName = 0; USHORT cchHostName = 0;
BOOL fSecure = FALSE; BOOL fSecure = FALSE;
@ -1427,7 +1426,7 @@ Failure:
} }
else else
{ {
if (SUCCEEDED(pApplicationManager->Get502ErrorPage(&pDataChunk))) if (SUCCEEDED(APPLICATION_MANAGER::GetInstance()->Get502ErrorPage(&pDataChunk)))
{ {
if (FAILED(hr = pResponse->WriteEntityChunkByReference(pDataChunk))) if (FAILED(hr = pResponse->WriteEntityChunkByReference(pDataChunk)))
{ {

View File

@ -335,7 +335,8 @@ IN_PROCESS_APPLICATION* IN_PROCESS_APPLICATION::s_Application = NULL;
IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION() : IN_PROCESS_APPLICATION::IN_PROCESS_APPLICATION() :
m_ProcessExitCode ( 0 ), m_ProcessExitCode ( 0 ),
m_fManagedAppLoaded ( FALSE ), m_fManagedAppLoaded ( FALSE ),
m_fLoadManagedAppError ( FALSE ) m_fLoadManagedAppError ( FALSE ),
m_fInitialized ( FALSE )
{ {
} }
@ -510,6 +511,7 @@ IN_PROCESS_APPLICATION::Initialize(
hr = HRESULT_FROM_WIN32(GetLastError()); hr = HRESULT_FROM_WIN32(GetLastError());
goto Finished; goto Finished;
} }
m_fInitialized = TRUE;
Finished: Finished:
return hr; return hr;
@ -636,50 +638,57 @@ IN_PROCESS_APPLICATION::Recycle(
VOID VOID
) )
{ {
DWORD dwThreadStatus = 0; if (m_fInitialized)
DWORD dwTimeout = m_pConfiguration->QueryShutdownTimeLimitInMS();
AcquireSRWLockExclusive(&m_srwLock);
if (!g_pHttpServer->IsCommandLineLaunch() && !g_fRecycleProcessCalled)
{ {
// IIS scenario. DWORD dwThreadStatus = 0;
// notify IIS first so that new request will be routed to new worker process DWORD dwTimeout = m_pConfiguration->QueryShutdownTimeLimitInMS();
g_fRecycleProcessCalled = TRUE;
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand");
}
// First call into the managed server and shutdown
if (m_ShutdownHandler != NULL)
{
m_ShutdownHandler(m_ShutdownHandlerContext);
m_ShutdownHandler = NULL;
}
if (m_hThread != NULL && AcquireSRWLockExclusive(&m_srwLock);
GetExitCodeThread(m_hThread, &dwThreadStatus) != 0 &&
dwThreadStatus == STILL_ACTIVE) if (!g_pHttpServer->IsCommandLineLaunch() &&
{ !g_fRecycleProcessCalled &&
// wait for gracefullshut down, i.e., the exit of the background thread or timeout (g_pHttpServer->GetAdminManager() != NULL))
if (WaitForSingleObject(m_hThread, dwTimeout) != WAIT_OBJECT_0)
{ {
// if the thread is still running, we need kill it first before exit to avoid AV // IIS scenario.
if (GetExitCodeThread(m_hThread, &dwThreadStatus) != 0 && dwThreadStatus == STILL_ACTIVE) // notify IIS first so that new request will be routed to new worker process
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand");
}
g_fRecycleProcessCalled = TRUE;
// First call into the managed server and shutdown
if (m_ShutdownHandler != NULL)
{
m_ShutdownHandler(m_ShutdownHandlerContext);
m_ShutdownHandler = NULL;
}
if (m_hThread != NULL &&
GetExitCodeThread(m_hThread, &dwThreadStatus) != 0 &&
dwThreadStatus == STILL_ACTIVE)
{
// wait for gracefullshut down, i.e., the exit of the background thread or timeout
if (WaitForSingleObject(m_hThread, dwTimeout) != WAIT_OBJECT_0)
{ {
TerminateThread(m_hThread, STATUS_CONTROL_C_EXIT); // if the thread is still running, we need kill it first before exit to avoid AV
if (GetExitCodeThread(m_hThread, &dwThreadStatus) != 0 && dwThreadStatus == STILL_ACTIVE)
{
TerminateThread(m_hThread, STATUS_CONTROL_C_EXIT);
}
} }
} }
}
CloseHandle(m_hThread); CloseHandle(m_hThread);
m_hThread = NULL; m_hThread = NULL;
s_Application = NULL; s_Application = NULL;
ReleaseSRWLockExclusive(&m_srwLock);
if (g_pHttpServer->IsCommandLineLaunch()) ReleaseSRWLockExclusive(&m_srwLock);
{ if (g_pHttpServer && g_pHttpServer->IsCommandLineLaunch())
// IISExpress scenario {
// Can only call exit to terminate current process // IISExpress scenario
exit(0); // Can only call exit to terminate current process
exit(0);
}
} }
} }