caching handlers to avoid heap fregamentation (#967)

caching handlers to avoid heap fregamentation
This commit is contained in:
pan-wang 2018-06-27 11:46:24 -07:00 committed by GitHub
parent 754c3b0c2e
commit af1bf0e1c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 223 additions and 142 deletions

View File

@ -36,7 +36,8 @@ public:
m_pServer(NULL),
m_cRefs(1),
m_fAppOfflineFound(FALSE),
m_fAllowStart(FALSE),
m_fValid(FALSE),
m_fDoneAppCreation(FALSE),
m_pAppOfflineHtm(NULL),
m_pFileWatcherEntry(NULL),
m_pConfiguration(NULL),
@ -76,7 +77,8 @@ public:
}
}
APP_OFFLINE_HTM* QueryAppOfflineHtm()
APP_OFFLINE_HTM*
QueryAppOfflineHtm()
{
return m_pAppOfflineHtm;
}
@ -87,16 +89,16 @@ public:
return m_fAppOfflineFound;
}
BOOL QueryAllowStart()
BOOL
IsValid()
{
return m_fAllowStart;
return m_fValid;
}
VOID
UpdateAllowStartStatus(BOOL fAllowed)
MarkValid()
{
// no lock, as no expectation for concurrent accesses
m_fAllowStart = fAllowed;
m_fValid = TRUE;
}
VOID
@ -149,7 +151,8 @@ private:
mutable LONG m_cRefs;
STRU m_struInfoKey;
BOOL m_fAppOfflineFound;
BOOL m_fAllowStart; // Flag indicates whether there is (configuration) error blocking application from starting
BOOL m_fValid;
BOOL m_fDoneAppCreation;
APP_OFFLINE_HTM *m_pAppOfflineHtm;
FILE_WATCHER_ENTRY *m_pFileWatcherEntry;
ASPNETCORE_SHIM_CONFIG *m_pConfiguration;

View File

@ -172,6 +172,7 @@ APPLICATION_INFO::EnsureApplicationCreated(
IHttpContext *pHttpContext
)
{
HRESULT hr = S_OK;
IAPPLICATION *pApplication = NULL;
STRU struExeLocation;
STRU struHostFxrDllLocation;
@ -182,12 +183,25 @@ APPLICATION_INFO::EnsureApplicationCreated(
return S_OK;
}
if (m_pApplication == NULL)
// one optimization for failure scenario is to reduce the lock scope
SRWExclusiveLock lock(m_srwLock);
if (m_fDoneAppCreation)
{
// application is NULL and CreateApplication failed previously
FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
else
{
SRWExclusiveLock lock(m_srwLock);
if (m_pApplication != NULL)
{
return S_OK;
// another thread created the applicaiton
FINISHED(S_OK);
}
else if (m_fDoneAppCreation)
{
// previous CreateApplication failed
FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
//
@ -195,26 +209,40 @@ APPLICATION_INFO::EnsureApplicationCreated(
//
if (!m_fAppOfflineFound)
{
// Move the request handler check inside of the lock
// such that only one request finds and loads it.
// FindRequestHandlerAssembly obtains a global lock, but after releasing the lock,
// there is a period where we could call
RETURN_IF_FAILED(FindRequestHandlerAssembly(struExeLocation));
m_fDoneAppCreation = TRUE;
FINISHED_IF_FAILED(FindRequestHandlerAssembly(struExeLocation));
if (m_pfnAspNetCoreCreateApplication == NULL)
{
RETURN_IF_FAILED(HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION));
FINISHED(HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION));
}
RETURN_IF_FAILED(m_pfnAspNetCoreCreateApplication(m_pServer, pHttpContext->GetApplication(), &pApplication));
FINISHED_IF_FAILED(m_pfnAspNetCoreCreateApplication(m_pServer, pHttpContext->GetApplication(), &pApplication));
pApplication->SetParameter(L"InProcessExeLocation", struExeLocation.QueryStr());
m_pApplication = pApplication;
}
}
return S_OK;
Finished:
if (FAILED(hr))
{
// Log the failure and update application info to not try again
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR,
ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG,
pHttpContext->GetApplication()->GetApplicationId(),
hr);
}
return hr;
}
HRESULT
@ -226,7 +254,7 @@ APPLICATION_INFO::FindRequestHandlerAssembly(STRU& location)
if (g_fAspnetcoreRHLoadedError)
{
FINISHED_IF_FAILED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
else if (!g_fAspnetcoreRHAssemblyLoaded)
{
@ -234,7 +262,7 @@ APPLICATION_INFO::FindRequestHandlerAssembly(STRU& location)
if (g_fAspnetcoreRHLoadedError)
{
FINISHED_IF_FAILED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
FINISHED(E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
if (g_fAspnetcoreRHAssemblyLoaded)
{
@ -438,7 +466,7 @@ APPLICATION_INFO::FindNativeAssemblyFromHostfxr(
}
}
FINISHED_IF_FAILED(hr = struNativeSearchPaths.SyncWithBuffer());
FINISHED_IF_FAILED(struNativeSearchPaths.SyncWithBuffer());
fFound = FALSE;
@ -526,7 +554,6 @@ APPLICATION_INFO::RecycleApplication()
// In process application failed to start for whatever reason, need to recycle the work process
m_pServer->RecycleProcess(L"AspNetCore InProcess Recycle Process on Demand");
}
}
if (hThread == NULL)

View File

@ -7,6 +7,7 @@
#include "utility.h"
#include "resources.h"
#include "SRWExclusiveLock.h"
#include "exceptions.h"
// The application manager is a singleton across ANCM.
APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL;
@ -47,8 +48,7 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
SRWSharedLock lock(m_srwLock);
if (g_fInShutdown)
{
hr = HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
goto Finished;
FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS));
}
m_pApplicationInfoHash->FindKey(pszApplicationId, ppApplicationInfo);
}
@ -56,52 +56,36 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
if (*ppApplicationInfo == NULL)
{
pApplicationInfo = new APPLICATION_INFO();
if (pApplicationInfo == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
hr = pApplicationInfo->Initialize(pServer, pHttpContext->GetApplication(), m_pFileWatcher);
if (FAILED(hr))
{
goto Finished;
}
FINISHED_IF_FAILED(pApplicationInfo->Initialize(pServer, pHttpContext->GetApplication(), m_pFileWatcher));
SRWExclusiveLock lock(m_srwLock);
if (g_fInShutdown)
{
// Already in shuting down. No need to create the application
hr = HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
goto Finished;
FINISHED(HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS));
}
m_pApplicationInfoHash->FindKey(pszApplicationId, ppApplicationInfo);
if (*ppApplicationInfo != NULL)
{
// someone else created the application
goto Finished;
}
hr = m_pApplicationInfoHash->InsertRecord(pApplicationInfo);
if (FAILED(hr))
{
goto Finished;
FINISHED(S_OK);
}
hostingModel = pApplicationInfo->QueryConfig()->QueryHostingModel();
if (m_pApplicationInfoHash->Count() == 1)
if (m_pApplicationInfoHash->Count() == 0)
{
m_hostingModel = hostingModel;
pApplicationInfo->UpdateAllowStartStatus(TRUE);
pApplicationInfo->MarkValid();
}
else
{
if (hostingModel == HOSTING_OUT_PROCESS && hostingModel == m_hostingModel)
{
pApplicationInfo->UpdateAllowStartStatus(TRUE);
pApplicationInfo->MarkValid();
}
else
{
@ -116,6 +100,9 @@ APPLICATION_MANAGER::GetOrCreateApplicationInfo(
}
}
FINISHED_IF_FAILED(m_pApplicationInfoHash->InsertRecord(pApplicationInfo));
*ppApplicationInfo = pApplicationInfo;
pApplicationInfo->StartMonitoringAppOffline();
@ -212,8 +199,7 @@ APPLICATION_MANAGER::RecycleApplicationFromManager(
DWORD dwPreviousCounter = 0;
APPLICATION_INFO_HASH* table = NULL;
CONFIG_CHANGE_CONTEXT context;
BOOL fKeepTable = FALSE;
if (g_fInShutdown)
{
// We are already shutting down, ignore this event as a global configuration change event
@ -251,25 +237,9 @@ APPLICATION_MANAGER::RecycleApplicationFromManager(
// Removed the applications which are impacted by the configurtion change
m_pApplicationInfoHash->DeleteIf(FindConfigChangedApplication, (PVOID)&context);
if (dwPreviousCounter != m_pApplicationInfoHash->Count())
{
if (m_hostingModel == HOSTING_IN_PROCESS)
{
// When we are inprocess, we need to keep the application the
// application manager that is being deleted. This is because we will always need to recycle the worker
// process and any requests that hit this worker process must be rejected (while out of process can
// start a new dotnet process). We will immediately call Recycle after this call.
DBG_ASSERT(m_pApplicationInfoHash->Count() == 0);
delete m_pApplicationInfoHash;
// We don't want to delete the table as m_pApplicationInfoHash = table
fKeepTable = TRUE;
m_pApplicationInfoHash = table;
}
}
if (m_pApplicationInfoHash->Count() == 0)
if (m_pApplicationInfoHash->Count() == 0 && m_hostingModel == HOSTING_OUT_PROCESS)
{
// reuse current process
m_hostingModel = HOSTING_UNKNOWN;
}
}
@ -310,7 +280,7 @@ APPLICATION_MANAGER::RecycleApplicationFromManager(
}
Finished:
if (table != NULL && !fKeepTable)
if (table != NULL)
{
table->Clear();
delete table;

View File

@ -100,8 +100,6 @@ HRESULT
UNREFERENCED_PARAMETER(dwServerVersion);
//LoadGlobalConfiguration();
InitializeSRWLock(&g_srwLock);
g_pModuleId = pModuleInfo->GetId();
@ -169,43 +167,28 @@ HRESULT
//
pFactory = new ASPNET_CORE_PROXY_MODULE_FACTORY;
hr = pModuleInfo->SetRequestNotifications(
FINISHED_IF_FAILED(pModuleInfo->SetRequestNotifications(
pFactory,
RQ_EXECUTE_REQUEST_HANDLER,
0);
if (FAILED(hr))
{
goto Finished;
}
0));
pFactory = NULL;
pApplicationManager = APPLICATION_MANAGER::GetInstance();
hr = pApplicationManager->Initialize();
if(FAILED(hr))
{
goto Finished;
}
FINISHED_IF_FAILED(pApplicationManager->Initialize());
pGlobalModule = NULL;
pGlobalModule = new ASPNET_CORE_GLOBAL_MODULE(pApplicationManager);
hr = pModuleInfo->SetGlobalNotifications(
pGlobalModule,
GL_CONFIGURATION_CHANGE | // Configuration change trigers IIS application stop
GL_STOP_LISTENING); // worker process stop or recycle
FINISHED_IF_FAILED(pModuleInfo->SetGlobalNotifications(
pGlobalModule,
GL_CONFIGURATION_CHANGE | // Configuration change trigers IIS application stop
GL_STOP_LISTENING)); // worker process stop or recycle
if (FAILED(hr))
{
goto Finished;
}
pGlobalModule = NULL;
hr = ALLOC_CACHE_HANDLER::StaticInitialize();
if (FAILED(hr))
{
goto Finished;
}
FINISHED_IF_FAILED(ALLOC_CACHE_HANDLER::StaticInitialize());
Finished:
if (pGlobalModule != NULL)

View File

@ -92,18 +92,18 @@ ASPNET_CORE_PROXY_MODULE::OnExecuteRequestHandler(
hr = pApplicationManager->GetOrCreateApplicationInfo(
g_pHttpServer,
pHttpContext,
pHttpContext,
&m_pApplicationInfo);
if (FAILED(hr))
{
goto Finished;
}
if (!m_pApplicationInfo->QueryAllowStart())
{
// Application cannot be started due to wrong hosting mode
// the error should already been logged to window event log for the first request
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
if (!m_pApplicationInfo->IsValid())
{
// Application cannot be started due to wrong hosting mode
// the error should already been logged to window event log for the first request
hr = E_APPLICATION_ACTIVATION_EXEC_FAILURE;
goto Finished;
}

View File

@ -29,7 +29,6 @@
#define ASPNETCORE_EVENT_DUPLICATED_INPROCESS_APP_MSG L"Only one inprocess application is allowed per IIS application pool. Please assign the application '%s' to a different IIS application pool."
#define ASPNETCORE_EVENT_MIXED_HOSTING_MODEL_ERROR_MSG L"Mixed hosting model is not supported. Application '%s' configured with different hostingModel value '%d' other than the one of running application(s)."
#define ASPNETCORE_EVENT_ADD_APPLICATION_ERROR_MSG L"Failed to start application '%s', ErrorCode '0x%x'."
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDERR_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. First 4KB characters of captured stderr logs on startup:\r\n%s"
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Last 4KB characters of captured stdout and stderr logs:\r\n%s"
#define ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_MSG L"Application '%s' with physical root '%s' hit unexpected managed background thread exit, ErrorCode = '0x%x. Please check the stderr logs for more information."
#define ASPNETCORE_EVENT_APP_IN_SHUTDOWN_MSG L"Application shutting down."

View File

@ -25,43 +25,36 @@ HINSTANCE g_hWinHttpModule;
HINSTANCE g_hAspNetCoreModule;
HANDLE g_hEventLog = NULL;
VOID
HRESULT
InitializeGlobalConfiguration(
IHttpServer * pServer
)
{
BOOL fLocked = FALSE;
if (!g_fGlobalInitialize)
{
AcquireSRWLockExclusive(&g_srwLockRH);
fLocked = TRUE;
SRWExclusiveLock lock(g_srwLockRH);
if (g_fGlobalInitialize)
if (!g_fGlobalInitialize)
{
// Done by another thread
goto Finished;
}
g_pHttpServer = pServer;
RETURN_IF_FAILED(ALLOC_CACHE_HANDLER::StaticInitialize());
RETURN_IF_FAILED(IN_PROCESS_HANDLER::StaticInitialize());
g_pHttpServer = pServer;
if (pServer->IsCommandLineLaunch())
{
g_hEventLog = RegisterEventSource(NULL, ASPNETCORE_IISEXPRESS_EVENT_PROVIDER);
}
else
{
g_hEventLog = RegisterEventSource(NULL, ASPNETCORE_EVENT_PROVIDER);
}
if (pServer->IsCommandLineLaunch())
{
g_hEventLog = RegisterEventSource(NULL, ASPNETCORE_IISEXPRESS_EVENT_PROVIDER);
}
else
{
g_hEventLog = RegisterEventSource(NULL, ASPNETCORE_EVENT_PROVIDER);
}
DebugInitialize();
g_fGlobalInitialize = TRUE;
}
Finished:
if (fLocked)
{
ReleaseSRWLockExclusive(&g_srwLockRH);
DebugInitialize();
g_fGlobalInitialize = TRUE;
}
}
return S_OK;
}
BOOL APIENTRY DllMain(HMODULE hModule,
@ -93,11 +86,12 @@ CreateApplication(
_Out_ IAPPLICATION **ppApplication
)
{
InitializeGlobalConfiguration(pServer);
REQUESTHANDLER_CONFIG *pConfig = NULL;
try
{
REQUESTHANDLER_CONFIG *pConfig = NULL;
// Initialze some global variables here
RETURN_IF_FAILED(InitializeGlobalConfiguration(pServer));
RETURN_IF_FAILED(REQUESTHANDLER_CONFIG::CreateRequestHandlerConfig(pServer, pHttpApplication, &pConfig));
auto config = std::unique_ptr<REQUESTHANDLER_CONFIG>(pConfig);

View File

@ -601,17 +601,20 @@ IN_PROCESS_APPLICATION::LogErrorsOnMainExit(
// This will be a common place for errors as it means the hostfxr_main returned
// or there was an exception.
//
STRA struStdErrOutput;
if (m_pLoggerProvider->GetStdOutContent(&struStdErrOutput))
STRA straStdErrOutput;
STRU struStdMsg;
if (m_pLoggerProvider->GetStdOutContent(&straStdErrOutput))
{
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG,
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
hr,
struStdErrOutput.QueryStr());
if (SUCCEEDED(struStdMsg.CopyA(straStdErrOutput.QueryStr()))) {
UTILITY::LogEventF(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT,
ASPNETCORE_EVENT_INPROCESS_THREAD_EXIT_STDOUT_MSG,
m_pConfig->QueryApplicationPath()->QueryStr(),
m_pConfig->QueryApplicationPhysicalPath()->QueryStr(),
hr,
struStdMsg.QueryStr());
}
}
else
{

View File

@ -157,10 +157,6 @@ private:
BOOL m_fInitialized;
// Thread for capturing startup stderr logs when logging is disabled
HANDLE m_hErrThread;
CHAR m_pzFileContents[4096] = { 0 };
DWORD m_dwStdErrReadTotal;
static IN_PROCESS_APPLICATION* s_Application;
IOutputManager* m_pLoggerProvider;

View File

@ -5,6 +5,8 @@
#include "inprocessapplication.h"
#include "aspnetcore_event.h"
ALLOC_CACHE_HANDLER * IN_PROCESS_HANDLER::sm_pAlloc = NULL;
IN_PROCESS_HANDLER::IN_PROCESS_HANDLER(
_In_ IHttpContext *pW3Context,
_In_ IN_PROCESS_APPLICATION *pApplication
@ -113,3 +115,63 @@ IN_PROCESS_HANDLER::SetManagedHttpContext(
{
m_pManagedHttpContext = pManagedHttpContext;
}
// static
void * IN_PROCESS_HANDLER::operator new(size_t)
{
DBG_ASSERT(sm_pAlloc != NULL);
if (sm_pAlloc == NULL)
{
return NULL;
}
return sm_pAlloc->Alloc();
}
// static
void IN_PROCESS_HANDLER::operator delete(void * pMemory)
{
DBG_ASSERT(sm_pAlloc != NULL);
if (sm_pAlloc != NULL)
{
sm_pAlloc->Free(pMemory);
}
}
// static
HRESULT
IN_PROCESS_HANDLER::StaticInitialize(VOID)
/*++
Routine Description:
Global initialization routine for IN_PROCESS_HANDLER
Return Value:
HRESULT
--*/
{
HRESULT hr = S_OK;
sm_pAlloc = new ALLOC_CACHE_HANDLER;
if (sm_pAlloc == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}
hr = sm_pAlloc->Initialize(sizeof(IN_PROCESS_HANDLER),
64); // nThreshold
Finished:
if (FAILED(hr))
{
if (sm_pAlloc != NULL)
{
delete sm_pAlloc;
sm_pAlloc = NULL;
}
}
return hr;
}

View File

@ -69,11 +69,20 @@ public:
REQUEST_NOTIFICATION_STATUS requestNotificationStatus
);
static void * operator new(size_t size);
static void operator delete(void * pMemory);
static
HRESULT
StaticInitialize(VOID);
private:
PVOID m_pManagedHttpContext;
BOOL m_fManagedRequestComplete;
REQUEST_NOTIFICATION_STATUS m_requestNotificationStatus;
IHttpContext* m_pW3Context;
IN_PROCESS_APPLICATION* m_pApplication;
static ALLOC_CACHE_HANDLER * sm_pAlloc;
};

View File

@ -40,6 +40,10 @@
#undef WINVER
#undef _WIN32_WINNT
#include <acache.h>
#include <exceptions.h>
#include <SRWExclusiveLock.h>
#define NTDDI_VERSION 0x06010000
#define WINVER 0x0601
#define _WIN32_WINNT 0x0601

View File

@ -214,6 +214,12 @@ EnsureOutOfProcessInitializtion()
goto Finished;
}
hr = ALLOC_CACHE_HANDLER::StaticInitialize();
if (FAILED(hr))
{
goto Finished;
}
hr = FORWARDING_HANDLER::StaticInitialize(g_fEnableReferenceCountTracing);
if (FAILED(hr))
{

View File

@ -725,7 +725,7 @@ HRESULT
}
hr = sm_pAlloc->Initialize(sizeof(FORWARDING_HANDLER),
64); // nThreshold
64); // nThreshold
if (FAILED(hr))
{
goto Finished;
@ -815,6 +815,27 @@ FORWARDING_HANDLER::StaticTerminate()
}
}
// static
void * FORWARDING_HANDLER::operator new(size_t)
{
DBG_ASSERT(sm_pAlloc != NULL);
if (sm_pAlloc == NULL)
{
return NULL;
}
return sm_pAlloc->Alloc();
}
// static
void FORWARDING_HANDLER::operator delete(void * pMemory)
{
DBG_ASSERT(sm_pAlloc != NULL);
if (sm_pAlloc != NULL)
{
sm_pAlloc->Free(pMemory);
}
}
HRESULT
FORWARDING_HANDLER::GetHeaders(
_In_ const PROTOCOL_CONFIG * pProtocol,

View File

@ -72,6 +72,10 @@ public:
bool fClientInitiated
);
static void * operator new(size_t size);
static void operator delete(void * pMemory);
private:
VOID