moving recycle and shutdown to applicationinfo (#639)
fix a race condition in processing request and app_offline, and optimize code logic
This commit is contained in:
parent
ce2a5adb10
commit
8dca8f030c
|
|
@ -4,6 +4,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#define API_BUFFER_TOO_SMALL 0x80008098
|
#define API_BUFFER_TOO_SMALL 0x80008098
|
||||||
|
|
||||||
|
extern BOOL g_fRecycleProcessCalled;
|
||||||
|
|
||||||
typedef
|
typedef
|
||||||
HRESULT
|
HRESULT
|
||||||
(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)(
|
(WINAPI * PFN_ASPNETCORE_CREATE_APPLICATION)(
|
||||||
|
|
@ -128,19 +130,28 @@ public:
|
||||||
return m_pConfiguration;
|
return m_pConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
APPLICATION*
|
//
|
||||||
QueryApplication()
|
// ExtractApplication will increase the reference counter of the application
|
||||||
|
// Caller is responsible for dereference the application.
|
||||||
|
// Otherwise memory leak
|
||||||
|
//
|
||||||
|
VOID
|
||||||
|
ExtractApplication(APPLICATION** ppApplication)
|
||||||
{
|
{
|
||||||
return m_pApplication;
|
AcquireSRWLockShared(&m_srwLock);
|
||||||
|
if (m_pApplication != NULL)
|
||||||
|
{
|
||||||
|
m_pApplication->ReferenceApplication();
|
||||||
|
}
|
||||||
|
*ppApplication = m_pApplication;
|
||||||
|
ReleaseSRWLockShared(&m_srwLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
ClearAndDereferenceApplication()
|
RecycleApplication();
|
||||||
{
|
|
||||||
APPLICATION* pApplication = m_pApplication;
|
VOID
|
||||||
m_pApplication = NULL;
|
ShutDownApplication();
|
||||||
pApplication->DereferenceApplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
EnsureApplicationCreated();
|
EnsureApplicationCreated();
|
||||||
|
|
@ -156,6 +167,8 @@ private:
|
||||||
HRESULT FindNativeAssemblyFromGlobalLocation(STRU* struFilename);
|
HRESULT FindNativeAssemblyFromGlobalLocation(STRU* struFilename);
|
||||||
HRESULT FindNativeAssemblyFromHostfxr(STRU* struFilename);
|
HRESULT FindNativeAssemblyFromHostfxr(STRU* struFilename);
|
||||||
|
|
||||||
|
static VOID DoRecycleApplication(LPVOID lpParam);
|
||||||
|
|
||||||
mutable LONG m_cRefs;
|
mutable LONG m_cRefs;
|
||||||
APPLICATION_INFO_KEY m_applicationInfoKey;
|
APPLICATION_INFO_KEY m_applicationInfoKey;
|
||||||
BOOL m_fAppOfflineFound;
|
BOOL m_fAppOfflineFound;
|
||||||
|
|
|
||||||
|
|
@ -55,18 +55,6 @@ public:
|
||||||
_In_ PVOID pvContext
|
_In_ PVOID pvContext
|
||||||
);
|
);
|
||||||
|
|
||||||
static
|
|
||||||
VOID
|
|
||||||
RecycleApplication(
|
|
||||||
_In_ APPLICATION_INFO * pEntry
|
|
||||||
);
|
|
||||||
|
|
||||||
static
|
|
||||||
void
|
|
||||||
DoRecycleApplication(
|
|
||||||
LPVOID lpParam
|
|
||||||
);
|
|
||||||
|
|
||||||
static
|
static
|
||||||
VOID
|
VOID
|
||||||
ShutdownApplication(
|
ShutdownApplication(
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,8 @@ APPLICATION_INFO::UpdateAppOfflineFileHandle()
|
||||||
&strFilePath);
|
&strFilePath);
|
||||||
APP_OFFLINE_HTM *pOldAppOfflineHtm = NULL;
|
APP_OFFLINE_HTM *pOldAppOfflineHtm = NULL;
|
||||||
APP_OFFLINE_HTM *pNewAppOfflineHtm = NULL;
|
APP_OFFLINE_HTM *pNewAppOfflineHtm = NULL;
|
||||||
BOOL fLocked = FALSE;
|
|
||||||
|
ReferenceApplicationInfo();
|
||||||
|
|
||||||
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(strFilePath.QueryStr()) &&
|
if (INVALID_FILE_ATTRIBUTES == GetFileAttributes(strFilePath.QueryStr()) &&
|
||||||
GetLastError() == ERROR_FILE_NOT_FOUND)
|
GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||||
|
|
@ -156,13 +157,6 @@ APPLICATION_INFO::UpdateAppOfflineFileHandle()
|
||||||
// recycle the application
|
// recycle the application
|
||||||
if (m_pApplication != NULL)
|
if (m_pApplication != NULL)
|
||||||
{
|
{
|
||||||
// Lock here to avoid races with the application manager calling shutdown on the application.
|
|
||||||
AcquireSRWLockExclusive(&m_srwLock);
|
|
||||||
fLocked = TRUE;
|
|
||||||
if (m_pApplication == NULL)
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
STACK_STRU(strEventMsg, 256);
|
STACK_STRU(strEventMsg, 256);
|
||||||
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
|
||||||
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG,
|
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE_MSG,
|
||||||
|
|
@ -174,16 +168,11 @@ APPLICATION_INFO::UpdateAppOfflineFileHandle()
|
||||||
strEventMsg.QueryStr());
|
strEventMsg.QueryStr());
|
||||||
}
|
}
|
||||||
|
|
||||||
APPLICATION_MANAGER::RecycleApplication(this);
|
RecycleApplication();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished:
|
DereferenceApplicationInfo();
|
||||||
if (fLocked)
|
|
||||||
{
|
|
||||||
ReleaseSRWLockExclusive(&m_srwLock);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
|
|
@ -239,19 +228,26 @@ APPLICATION_INFO::EnsureApplicationCreated()
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_pfnAspNetCoreCreateApplication == NULL)
|
//
|
||||||
|
// in case of app offline, we don't want to create a new application now
|
||||||
|
//
|
||||||
|
if (!m_fAppOfflineFound)
|
||||||
{
|
{
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
if (m_pfnAspNetCoreCreateApplication == NULL)
|
||||||
goto Finished;
|
{
|
||||||
}
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION);
|
||||||
|
goto Finished;
|
||||||
|
}
|
||||||
|
|
||||||
hr = m_pfnAspNetCoreCreateApplication(m_pServer, m_pConfiguration, &pApplication);
|
hr = m_pfnAspNetCoreCreateApplication(m_pServer, m_pConfiguration, &pApplication);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
goto Finished;
|
goto Finished;
|
||||||
|
}
|
||||||
|
m_pApplication = pApplication;
|
||||||
}
|
}
|
||||||
m_pApplication = pApplication;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
if (fLocked)
|
if (fLocked)
|
||||||
{
|
{
|
||||||
|
|
@ -533,3 +529,110 @@ Finished:
|
||||||
}
|
}
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VOID
|
||||||
|
APPLICATION_INFO::RecycleApplication()
|
||||||
|
{
|
||||||
|
APPLICATION* pApplication = NULL;
|
||||||
|
HANDLE hThread = INVALID_HANDLE_VALUE;
|
||||||
|
BOOL fLockAcquired = FALSE;
|
||||||
|
|
||||||
|
if (m_pApplication != NULL)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_srwLock);
|
||||||
|
fLockAcquired = TRUE;
|
||||||
|
if (m_pApplication != NULL)
|
||||||
|
{
|
||||||
|
pApplication = m_pApplication;
|
||||||
|
if (pApplication->QueryConfig()->QueryHostingModel() == HOSTING_OUT_PROCESS)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// For inprocess, need to set m_pApplication to NULL first to
|
||||||
|
// avoid mapping new request to the recycled application.
|
||||||
|
// Outofprocess application instance will be created for new request
|
||||||
|
// For inprocess, as recycle will lead to shutdown later, leave m_pApplication
|
||||||
|
// to not block incoming requests till worker process shutdown
|
||||||
|
//
|
||||||
|
m_pApplication = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// For inprocess, need hold the application till shutdown is called
|
||||||
|
// Bump the reference counter as DoRecycleApplication will do dereference
|
||||||
|
//
|
||||||
|
pApplication->ReferenceApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
hThread = CreateThread(
|
||||||
|
NULL, // default security attributes
|
||||||
|
0, // default stack size
|
||||||
|
(LPTHREAD_START_ROUTINE)DoRecycleApplication,
|
||||||
|
pApplication, // thread function arguments
|
||||||
|
0, // default creation flags
|
||||||
|
NULL); // receive thread identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hThread == NULL)
|
||||||
|
{
|
||||||
|
if (!g_fRecycleProcessCalled)
|
||||||
|
{
|
||||||
|
g_fRecycleProcessCalled = TRUE;
|
||||||
|
g_pHttpServer->RecycleProcess(L"On Demand by AspNetCore Module for recycle application failure");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fLockAcquired)
|
||||||
|
{
|
||||||
|
ReleaseSRWLockExclusive(&m_srwLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VOID
|
||||||
|
APPLICATION_INFO::DoRecycleApplication(
|
||||||
|
LPVOID lpParam)
|
||||||
|
{
|
||||||
|
APPLICATION* pApplication = static_cast<APPLICATION*>(lpParam);
|
||||||
|
|
||||||
|
// No lock required
|
||||||
|
|
||||||
|
if (pApplication != NULL)
|
||||||
|
{
|
||||||
|
// Recycle will call shutdown for out of process
|
||||||
|
pApplication->Recycle();
|
||||||
|
|
||||||
|
// Decrement the ref count as we reference it in RecycleApplication.
|
||||||
|
pApplication->DereferenceApplication();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VOID
|
||||||
|
APPLICATION_INFO::ShutDownApplication()
|
||||||
|
{
|
||||||
|
APPLICATION* pApplication = NULL;
|
||||||
|
BOOL fLockAcquired = FALSE;
|
||||||
|
|
||||||
|
// pApplication can be NULL due to app_offline
|
||||||
|
if (m_pApplication != NULL)
|
||||||
|
{
|
||||||
|
AcquireSRWLockExclusive(&m_srwLock);
|
||||||
|
fLockAcquired = TRUE;
|
||||||
|
if (m_pApplication != NULL)
|
||||||
|
{
|
||||||
|
pApplication = m_pApplication;
|
||||||
|
|
||||||
|
// Set m_pApplication to NULL first to prevent anyone from using it
|
||||||
|
m_pApplication = NULL;
|
||||||
|
pApplication->ShutDown();
|
||||||
|
pApplication->DereferenceApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fLockAcquired)
|
||||||
|
{
|
||||||
|
ReleaseSRWLockExclusive(&m_srwLock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -370,7 +370,7 @@ APPLICATION_MANAGER::RecycleApplicationFromManager(
|
||||||
DBG_ASSERT(pRecord != NULL);
|
DBG_ASSERT(pRecord != NULL);
|
||||||
|
|
||||||
// RecycleApplication is called on a separate thread.
|
// RecycleApplication is called on a separate thread.
|
||||||
RecycleApplication(pRecord);
|
pRecord->RecycleApplication();
|
||||||
pRecord->DereferenceApplicationInfo();
|
pRecord->DereferenceApplicationInfo();
|
||||||
path = context.MultiSz.Next(path);
|
path = context.MultiSz.Next(path);
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +397,11 @@ Finished:
|
||||||
strEventMsg.QueryStr());
|
strEventMsg.QueryStr());
|
||||||
}
|
}
|
||||||
// Need to recycle the process as we cannot recycle the application
|
// Need to recycle the process as we cannot recycle the application
|
||||||
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
|
if (!g_fRecycleProcessCalled)
|
||||||
|
{
|
||||||
|
g_fRecycleProcessCalled = TRUE;
|
||||||
|
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
|
|
@ -448,64 +452,7 @@ APPLICATION_MANAGER::ShutdownApplication(
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(pvContext);
|
UNREFERENCED_PARAMETER(pvContext);
|
||||||
APPLICATION* pApplication = pEntry->QueryApplication();
|
DBG_ASSERT(pEntry != NULL);
|
||||||
pApplication->ReferenceApplication();
|
|
||||||
|
|
||||||
// Remove the application from the applicationInfo.
|
pEntry->ShutDownApplication();
|
||||||
pEntry->ClearAndDereferenceApplication();
|
|
||||||
pApplication->ShutDown();
|
|
||||||
pApplication->DereferenceApplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Function used by DoRecycleApplication thread to do the real shutdown
|
|
||||||
//
|
|
||||||
// static
|
|
||||||
VOID
|
|
||||||
APPLICATION_MANAGER::DoRecycleApplication(
|
|
||||||
LPVOID lpParam)
|
|
||||||
{
|
|
||||||
APPLICATION* pApplication = static_cast<APPLICATION*>(lpParam);
|
|
||||||
|
|
||||||
// Recycle will call shutdown for out of process
|
|
||||||
pApplication->Recycle();
|
|
||||||
|
|
||||||
// Decrement the ref count as we reference it in RecycleApplication.
|
|
||||||
pApplication->DereferenceApplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Function used to recycle an application
|
|
||||||
//
|
|
||||||
// static
|
|
||||||
VOID
|
|
||||||
APPLICATION_MANAGER::RecycleApplication(
|
|
||||||
_In_ APPLICATION_INFO * pEntry
|
|
||||||
)
|
|
||||||
{
|
|
||||||
APPLICATION* pApplication = pEntry->QueryApplication();
|
|
||||||
DBG_ASSERT(pApplication != NULL);
|
|
||||||
|
|
||||||
// Reference the application first
|
|
||||||
pApplication->ReferenceApplication();
|
|
||||||
|
|
||||||
if (pApplication->QueryConfig()->QueryHostingModel() == HOSTING_OUT_PROCESS)
|
|
||||||
{
|
|
||||||
// Need to set m_pApplication to NULL first
|
|
||||||
// to avoid mapping new request to the recycled application
|
|
||||||
// A new application instance will be created for new request
|
|
||||||
pEntry->ClearAndDereferenceApplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset application pointer to NULL
|
|
||||||
// The destructor of ApplictionInfo will not call ShutDown again
|
|
||||||
HANDLE hThread = CreateThread(
|
|
||||||
NULL, // default security attributes
|
|
||||||
0, // default stack size
|
|
||||||
(LPTHREAD_START_ROUTINE)DoRecycleApplication,
|
|
||||||
pApplication, // thread function arguments
|
|
||||||
0, // default creation flags
|
|
||||||
NULL); // receive thread identifier
|
|
||||||
|
|
||||||
CloseHandle(hThread);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -138,18 +138,18 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure assmebly is loaded and application is created
|
// make sure assmebly is loaded and application is created
|
||||||
|
|
||||||
hr = m_pApplicationInfo->EnsureApplicationCreated();
|
hr = m_pApplicationInfo->EnsureApplicationCreated();
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
pApplication = m_pApplicationInfo->QueryApplication();
|
|
||||||
DBG_ASSERT(pApplication);
|
m_pApplicationInfo->ExtractApplication(&pApplication);
|
||||||
|
|
||||||
// make sure application is in running state
|
// make sure application is in running state
|
||||||
// cannot recreate the application as we cannot reload clr for inprocess
|
// cannot recreate the application as we cannot reload clr for inprocess
|
||||||
if (pApplication->QueryStatus() != APPLICATION_STATUS::RUNNING &&
|
if (pApplication != NULL &&
|
||||||
|
pApplication->QueryStatus() != APPLICATION_STATUS::RUNNING &&
|
||||||
pApplication->QueryStatus() != APPLICATION_STATUS::STARTING)
|
pApplication->QueryStatus() != APPLICATION_STATUS::STARTING)
|
||||||
{
|
{
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED);
|
hr = HRESULT_FROM_WIN32(ERROR_SERVER_DISABLED);
|
||||||
|
|
@ -166,6 +166,7 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
|
||||||
{
|
{
|
||||||
goto Finished;
|
goto Finished;
|
||||||
}
|
}
|
||||||
|
|
||||||
retVal = m_pHandler->OnExecuteRequestHandler();
|
retVal = m_pHandler->OnExecuteRequestHandler();
|
||||||
|
|
||||||
Finished:
|
Finished:
|
||||||
|
|
@ -175,6 +176,10 @@ Finished:
|
||||||
retVal = RQ_NOTIFICATION_FINISH_REQUEST;
|
retVal = RQ_NOTIFICATION_FINISH_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pApplication != NULL)
|
||||||
|
{
|
||||||
|
pApplication->DereferenceApplication();
|
||||||
|
}
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue