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>
<AspNetCoreModuleVersionMajor>7</AspNetCoreModuleVersionMajor>
<AspNetCoreModuleVersionMinor>1</AspNetCoreModuleVersionMinor>
<AspNetCoreModuleVersionPatch>1968</AspNetCoreModuleVersionPatch>
<AspNetCoreModuleVersionPatch>1987</AspNetCoreModuleVersionPatch>
<AspNetCoreModuleVersionSuffix>-RTM</AspNetCoreModuleVersionSuffix>
</PropertyGroup>
</Project>

View File

@ -95,6 +95,7 @@ private:
BOOL m_fManagedAppLoaded;
BOOL m_fLoadManagedAppError;
BOOL m_fInitialized;
BOOL m_fIsWebSocketsConnection;
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_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_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_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."

View File

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

View File

@ -5,17 +5,26 @@
ASPNETCORE_CONFIG::~ASPNETCORE_CONFIG()
{
if (QueryHostingModel() == HOSTING_IN_PROCESS &&
!g_fRecycleProcessCalled &&
!g_pHttpServer->IsCommandLineLaunch())
if (QueryHostingModel() == HOSTING_IN_PROCESS &&
!g_fRecycleProcessCalled &&
(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
// In case of configuration change for in-process app
// 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");
}
// 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();
if (m_pEnvironmentVariables != NULL)
{

View File

@ -1043,7 +1043,6 @@ FORWARDING_HANDLER::OnExecuteRequestHandler(
IHttpRequest *pRequest = m_pW3Context->GetRequest();
IHttpResponse *pResponse = m_pW3Context->GetResponse();
PROTOCOL_CONFIG *pProtocol = &sm_ProtocolConfig;
APPLICATION_MANAGER *pApplicationManager = NULL;
SERVER_PROCESS *pServerProcess = NULL;
USHORT cchHostName = 0;
BOOL fSecure = FALSE;
@ -1427,7 +1426,7 @@ Failure:
}
else
{
if (SUCCEEDED(pApplicationManager->Get502ErrorPage(&pDataChunk)))
if (SUCCEEDED(APPLICATION_MANAGER::GetInstance()->Get502ErrorPage(&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() :
m_ProcessExitCode ( 0 ),
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());
goto Finished;
}
m_fInitialized = TRUE;
Finished:
return hr;
@ -636,50 +638,57 @@ IN_PROCESS_APPLICATION::Recycle(
VOID
)
{
DWORD dwThreadStatus = 0;
DWORD dwTimeout = m_pConfiguration->QueryShutdownTimeLimitInMS();
AcquireSRWLockExclusive(&m_srwLock);
if (!g_pHttpServer->IsCommandLineLaunch() && !g_fRecycleProcessCalled)
if (m_fInitialized)
{
// IIS scenario.
// notify IIS first so that new request will be routed to new worker process
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;
}
DWORD dwThreadStatus = 0;
DWORD dwTimeout = m_pConfiguration->QueryShutdownTimeLimitInMS();
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)
AcquireSRWLockExclusive(&m_srwLock);
if (!g_pHttpServer->IsCommandLineLaunch() &&
!g_fRecycleProcessCalled &&
(g_pHttpServer->GetAdminManager() != NULL))
{
// 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)
// IIS scenario.
// 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);
m_hThread = NULL;
s_Application = NULL;
ReleaseSRWLockExclusive(&m_srwLock);
CloseHandle(m_hThread);
m_hThread = NULL;
s_Application = NULL;
if (g_pHttpServer->IsCommandLineLaunch())
{
// IISExpress scenario
// Can only call exit to terminate current process
exit(0);
ReleaseSRWLockExclusive(&m_srwLock);
if (g_pHttpServer && g_pHttpServer->IsCommandLineLaunch())
{
// IISExpress scenario
// Can only call exit to terminate current process
exit(0);
}
}
}