aspnetcore/src/AspNetCoreModuleV2/AspNetCore/applicationmanager.cpp

188 lines
6.2 KiB
C++

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
#include "applicationmanager.h"
#include "proxymodule.h"
#include "resources.h"
#include "SRWExclusiveLock.h"
#include "exceptions.h"
#include "EventLog.h"
extern BOOL g_fInShutdown;
// The application manager is a singleton across ANCM.
APPLICATION_MANAGER* APPLICATION_MANAGER::sm_pApplicationManager = NULL;
//
// Retrieves the application info from the application manager
// Will create the application info if it isn't initalized
//
HRESULT
APPLICATION_MANAGER::GetOrCreateApplicationInfo(
_In_ IHttpContext& pHttpContext,
_Out_ std::shared_ptr<APPLICATION_INFO>& ppApplicationInfo
)
{
auto &pApplication = *pHttpContext.GetApplication();
// The configuration path is unique for each application and is used for the
// key in the applicationInfoHash.
std::wstring pszApplicationId = pApplication.GetApplicationId();
{
// When accessing the m_pApplicationInfoHash, we need to acquire the application manager
// lock to avoid races on setting state.
SRWSharedLock readLock(m_srwLock);
if (g_fInShutdown)
{
return HRESULT_FROM_WIN32(ERROR_SERVER_SHUTDOWN_IN_PROGRESS);
}
const auto pair = m_pApplicationInfoHash.find(pszApplicationId);
if (pair != m_pApplicationInfoHash.end())
{
ppApplicationInfo = pair->second;
return S_OK;
}
// It's important to release read lock here so exclusive lock
// can be reacquired later as SRW lock doesn't allow upgrades
}
// Take exclusive lock before creating the application
SRWExclusiveLock writeLock(m_srwLock);
if (!m_fDebugInitialize)
{
DebugInitializeFromConfig(m_pHttpServer, pApplication);
m_fDebugInitialize = TRUE;
}
// Check if other thread created the application
const auto pair = m_pApplicationInfoHash.find(pszApplicationId);
if (pair != m_pApplicationInfoHash.end())
{
ppApplicationInfo = pair->second;
return S_OK;
}
ppApplicationInfo = std::make_shared<APPLICATION_INFO>(m_pHttpServer, pApplication, m_handlerResolver);
m_pApplicationInfoHash.emplace(pszApplicationId, ppApplicationInfo);
return S_OK;
}
//
// Finds any applications affected by a configuration change and calls Recycle on them
// InProcess: Triggers g_httpServer->RecycleProcess() and keep the application inside of the manager.
// This will cause a shutdown event to occur through the global stop listening event.
// OutOfProcess: Removes all applications in the application manager and calls Recycle, which will call Shutdown,
// on each application.
//
HRESULT
APPLICATION_MANAGER::RecycleApplicationFromManager(
_In_ LPCWSTR pszApplicationId
)
{
try
{
std::vector<std::shared_ptr<APPLICATION_INFO>> applicationsToRecycle;
if (g_fInShutdown)
{
// We are already shutting down, ignore this event as a global configuration change event
// can occur after global stop listening for some reason.
return S_OK;
}
{
SRWExclusiveLock lock(m_srwLock);
if (g_fInShutdown)
{
return S_OK;
}
const std::wstring configurationPath = pszApplicationId;
auto itr = m_pApplicationInfoHash.begin();
while (itr != m_pApplicationInfoHash.end())
{
if (itr->second->ConfigurationPathApplies(configurationPath))
{
applicationsToRecycle.emplace_back(itr->second);
itr = m_pApplicationInfoHash.erase(itr);
}
else
{
++itr;
}
}
// All applications were unloaded reset handler resolver validation logic
if (m_pApplicationInfoHash.empty())
{
m_handlerResolver.ResetHostingModel();
}
}
// If we receive a request at this point.
// OutOfProcess: we will create a new application with new configuration
// InProcess: the request would have to be rejected, as we are about to call g_HttpServer->RecycleProcess
// on the worker proocess
if (!applicationsToRecycle.empty())
{
for (auto& application : applicationsToRecycle)
{
try
{
application->ShutDownApplication(/* fServerInitiated */ false);
}
catch (...)
{
LOG_ERRORF("Failed to stop application %S", application->QueryApplicationInfoKey().c_str());
OBSERVE_CAUGHT_EXCEPTION();
// Failed to recycle an application. Log an event
EventLog::Error(
ASPNETCORE_EVENT_RECYCLE_APP_FAILURE,
ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG,
pszApplicationId);
// Need to recycle the process as we cannot recycle the application
if (!g_fRecycleProcessCalled)
{
g_fRecycleProcessCalled = TRUE;
m_pHttpServer.RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
}
}
}
}
}
CATCH_RETURN();
return S_OK;
}
//
// Shutsdown all applications in the application hashtable
// Only called by OnGlobalStopListening.
//
VOID
APPLICATION_MANAGER::ShutDown()
{
// We are guaranteed to only have one outstanding OnGlobalStopListening event at a time
// However, it is possible to receive multiple OnGlobalStopListening events
// Protect against this by checking if we already shut down.
g_fInShutdown = TRUE;
// During shutdown we lock until we delete the application
SRWExclusiveLock lock(m_srwLock);
for (auto &pair : m_pApplicationInfoHash)
{
pair.second->ShutDownApplication(/* fServerInitiated */ true);
pair.second = nullptr;
}
}