Skip to content
This repository was archived by the owner on Nov 1, 2018. It is now read-only.

Commit

Permalink
# This is a combination of 9 commits.
Browse files Browse the repository at this point in the history
# This is the 1st commit message:

fix app recycle logic

# The commit message #2 will be skipped:

# adding parallel shutdown logic

# The commit message #3 will be skipped:

# some update

# The commit message #4 will be skipped:

# more change

# The commit message #5 will be skipped:

# close hkey

# The commit message #6 will be skipped:

# update logging

# The commit message #7 will be skipped:

# remove unused code

# The commit message #8 will be skipped:

# format change

# The commit message #9 will be skipped:

# exclude not-subapp case while sudstring does match
# Please enter the commit message for your changes. Lines starting
  • Loading branch information
pan-wang authored and jkotalik committed Mar 2, 2018
1 parent f954e95 commit 7bc5983
Show file tree
Hide file tree
Showing 12 changed files with 263 additions and 54 deletions.
19 changes: 19 additions & 0 deletions src/AspNetCore/Inc/applicationinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ class APPLICATION_INFO
return m_pApplication;
}

VOID
ResetApplication()
{
m_pApplication->DereferenceApplication();
m_pApplication = NULL;
}

HRESULT
EnsureApplicationCreated();

Expand Down Expand Up @@ -211,6 +218,18 @@ class APPLICATION_INFO_HASH :
pApplicationInfo->DereferenceApplicationInfo();
}

static
VOID
ReferenceCopyToTable(
APPLICATION_INFO * pEntry,
PVOID pvData
)
{
APPLICATION_INFO_HASH *pHash = static_cast<APPLICATION_INFO_HASH *>(pvData);
DBG_ASSERT(pHash);
pHash->InsertRecord(pEntry);
}

private:

APPLICATION_INFO_HASH(const APPLICATION_INFO_HASH &);
Expand Down
29 changes: 28 additions & 1 deletion src/AspNetCore/Inc/applicationmanager.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@
// It should be global singleton.
// Should always call GetInstance to get the object instance
//

struct CONFIG_CHANGE_CONTEXT
{
PCWSTR pstrPath;
MULTISZ MultiSz;
};

class APPLICATION_MANAGER
{
public:
Expand Down Expand Up @@ -41,6 +48,26 @@ class APPLICATION_MANAGER
}
}

static
BOOL
FindConfigChangedApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext
);

static
VOID
ShutDownApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext
);

static
void
DoShutDownApplication(
LPVOID lpParam
);

HRESULT
GetApplicationInfo(
_In_ IHttpServer* pServer,
Expand Down Expand Up @@ -132,4 +159,4 @@ class APPLICATION_MANAGER
SRWLOCK m_srwLock;
APP_HOSTING_MODEL m_hostingModel;
bool m_fInShutdown;
};
};
4 changes: 2 additions & 2 deletions src/AspNetCore/Inc/globalmodule.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class ASPNET_CORE_GLOBAL_MODULE : public CGlobalModule
);

GLOBAL_NOTIFICATION_STATUS
OnGlobalApplicationStop(
_In_ IHttpApplicationStopProvider * pProvider
OnGlobalConfigurationChange(
_In_ IGlobalConfigurationChangeProvider * pProvider
);

private:
Expand Down
4 changes: 2 additions & 2 deletions src/AspNetCore/Src/applicationinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,6 @@ APPLICATION_INFO::UpdateAppOfflineFileHandle()
}
else
{
m_fAppOfflineFound = TRUE;
pNewAppOfflineHtm = new APP_OFFLINE_HTM(strFilePath.QueryStr());

if (pNewAppOfflineHtm != NULL)
Expand All @@ -132,6 +131,8 @@ APPLICATION_INFO::UpdateAppOfflineFileHandle()
}
}

m_fAppOfflineFound = TRUE;

// recycle the application
if (m_pApplication != NULL)
{
Expand All @@ -149,7 +150,6 @@ APPLICATION_INFO::UpdateAppOfflineFileHandle()
m_pApplication->ShutDown();
m_pApplication->DereferenceApplication();
m_pApplication = NULL;

}
}
}
Expand Down
209 changes: 178 additions & 31 deletions src/AspNetCore/Src/applicationmanager.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,31 @@ APPLICATION_MANAGER::GetApplicationInfo(
return hr;
}

BOOL
APPLICATION_MANAGER::FindConfigChangedApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext)
{
CONFIG_CHANGE_CONTEXT* pContext = static_cast<CONFIG_CHANGE_CONTEXT*>(pvContext);
STRU* pstruConfigPath = pEntry->QueryConfig()->QueryConfigPath();
BOOL fChanged = pstruConfigPath->StartsWith(pContext->pstrPath, true);
if (fChanged)
{
DWORD dwLen = (DWORD)wcslen(pContext->pstrPath);
WCHAR wChar = pstruConfigPath->QueryStr()[dwLen];
if (wChar != L'\0' && wChar != L'/')
{
// not current app or sub app
fChanged = FALSE;
}
else
{
pContext->MultiSz.Append(*pstruConfigPath);
}
}
return fChanged;
}

HRESULT
APPLICATION_MANAGER::RecycleApplication(
_In_ LPCWSTR pszApplicationId
Expand All @@ -197,72 +222,194 @@ APPLICATION_MANAGER::RecycleApplication(
HRESULT hr = S_OK;
APPLICATION_INFO_KEY key;
DWORD dwPreviousCounter = 0;
APPLICATION_INFO_HASH* table = NULL;
CONFIG_CHANGE_CONTEXT context;

hr = key.Initialize(pszApplicationId);
if (FAILED(hr))
{
goto Finished;
}

table = new APPLICATION_INFO_HASH();

if(table == NULL)
{
hr = E_OUTOFMEMORY;
goto Finished;
}

//
// few application expected, small bucket size for hash table
//
if (FAILED(hr = table->Initialize(17 /*prime*/)))
{
goto Finished;
}

context.pstrPath = pszApplicationId;

AcquireSRWLockExclusive(&m_srwLock);
dwPreviousCounter = m_pApplicationInfoHash->Count();

m_pApplicationInfoHash->DeleteKey(&key);
// We don't want to hold the lock for long time as it will block all incoming requests
// Make a shallow copy of existing hashtable as we may nee to remove nodes
// This also make sure application shutdown will not be called inside the lock
m_pApplicationInfoHash->Apply(APPLICATION_INFO_HASH::ReferenceCopyToTable, static_cast<PVOID>(table));
DBG_ASSERT(dwPreviousCounter == table->Count());

// Removed the applications which are impacted by the configurtion change
m_pApplicationInfoHash->DeleteIf(FindConfigChangedApplication, (PVOID)&context);

if(dwPreviousCounter != m_pApplicationInfoHash->Count())
if (dwPreviousCounter != m_pApplicationInfoHash->Count())
{
// Application got recycled. Log an event
STACK_STRU(strEventMsg, 256);
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG,
pszApplicationId)))
if (m_hostingModel == HOSTING_IN_PROCESS)
{
UTILITY::LogEvent(g_hEventLog,
EVENTLOG_INFORMATION_TYPE,
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION,
strEventMsg.QueryStr());
APPLICATION_INFO_HASH* tmp = m_pApplicationInfoHash;
m_pApplicationInfoHash = table;
table = tmp;
// Keep the original applicationinfo hashtable to continue serve request
// Trigger a worker process recycle and let the process shutdown code path to handle it
// So that we will drop/reject the incoming requests before WAS spins another worker process
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand Due to In-process Application Configuration Changed");
}

}

if (m_pApplicationInfoHash->Count() == 0)
{
m_hostingModel = HOSTING_UNKNOWN;
}

ReleaseSRWLockExclusive(&m_srwLock);

if (g_fAspnetcoreRHLoadedError)
if(!context.MultiSz.IsEmpty() && (m_hostingModel == HOSTING_OUT_PROCESS || m_hostingModel == HOSTING_UNKNOWN))
{
// We had assembly loading failure
// this error blocked the start of all applications
// Let's recycle the worker process if user redeployed any application
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand due to assembly loading failure");
}
// some out-of-process applications were removed from the hashtable, i.e., need to be recycled
PCWSTR path = context.MultiSz.First();
while (path != NULL)
{
APPLICATION_INFO* pRecord;

ReleaseSRWLockExclusive(&m_srwLock);
// Application got recycled. Log an event
STACK_STRU(strEventMsg, 256);
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION_MSG,
path)))
{
UTILITY::LogEvent(g_hEventLog,
EVENTLOG_INFORMATION_TYPE,
ASPNETCORE_EVENT_RECYCLE_CONFIGURATION,
strEventMsg.QueryStr());
}

hr = key.Initialize(path);
if (FAILED(hr))
{
goto Finished;
}

table->FindKey(&key, &pRecord);
DBG_ASSERT(pRecord != NULL);

// shutdown will be done in another thread
ShutDownApplication(pRecord, NULL);
pRecord->DereferenceApplicationInfo();
path = context.MultiSz.Next(path);
}
}

Finished:
if (table != NULL)
{
table->Clear();
delete table;
}

if (FAILED(hr))
{
// Failed to recycle an application. Log an event
STACK_STRU(strEventMsg, 256);
if (SUCCEEDED(strEventMsg.SafeSnwprintf(
ASPNETCORE_EVENT_RECYCLE_FAILURE_CONFIGURATION_MSG,
hr)))
{
UTILITY::LogEvent(g_hEventLog,
EVENTLOG_ERROR_TYPE,
ASPNETCORE_EVENT_RECYCLE_APP_FAILURE,
strEventMsg.QueryStr());
}
// Need to recycle the process as we cannot recycle the application
g_pHttpServer->RecycleProcess(L"AspNetCore Recycle Process on Demand Due Application Recycle Error");
}

return hr;
}

VOID
APPLICATION_MANAGER::ShutDown()
{
m_fInShutdown = TRUE;
if (m_pApplicationInfoHash != NULL)
if (!m_fInShutdown)
{
AcquireSRWLockExclusive(&m_srwLock);
m_fInShutdown = TRUE;
// stop filewatcher monitoring thread
if (m_pFileWatcher != NULL)
{
delete m_pFileWatcher;
m_pFileWatcher = NULL;
}

// clean up the hash table so that the application will be informed on shutdown
m_pApplicationInfoHash->Clear();
if (m_pApplicationInfoHash != NULL)
{
AcquireSRWLockExclusive(&m_srwLock);

ReleaseSRWLockExclusive(&m_srwLock);
}
// clean up the hash table so that the application will be informed on shutdown
m_pApplicationInfoHash->Apply(ShutDownApplication, NULL);

// stop filewatcher monitoring thread
if (m_pFileWatcher != NULL)
{
delete m_pFileWatcher;
m_pFileWatcher = NULL;
ReleaseSRWLockExclusive(&m_srwLock);
}
}
}

//
// Function used by ShutDownApplication thread to do the real shutdown
//
// static
VOID
APPLICATION_MANAGER::DoShutDownApplication(
LPVOID lpParam)
{
APPLICATION* pApplication = static_cast<APPLICATION*>(lpParam);
pApplication->ShutDown();
pApplication->DereferenceApplication();
}

//
// Function used to shutdown an application
//
// static
VOID
APPLICATION_MANAGER::ShutDownApplication(
_In_ APPLICATION_INFO * pEntry,
_In_ PVOID pvContext
)
{
UNREFERENCED_PARAMETER(pvContext);

APPLICATION* pApplication = pEntry->QueryApplication();

// Reference the application first
pApplication->ReferenceApplication();

// Reset application pointer to NULL
// The destructor of ApplictionInfo will not call ShutDown again
pEntry->ResetApplication();
HANDLE hThread = CreateThread(
NULL, // default security attributes
0, // default stack size
(LPTHREAD_START_ROUTINE)DoShutDownApplication,
pApplication, // thread function arguments
0, // default creation flags
NULL); // receive thread identifier

CloseHandle(hThread);
}
4 changes: 3 additions & 1 deletion src/AspNetCore/Src/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ HRESULT
{
fDisableANCM = (dwData != 0);
}

RegCloseKey(hKey);
}

if (fDisableANCM)
Expand Down Expand Up @@ -202,7 +204,7 @@ HRESULT

hr = pModuleInfo->SetGlobalNotifications(
pGlobalModule,
GL_APPLICATION_STOP | // Configuration change trigers IIS application stop
GL_CONFIGURATION_CHANGE | // Configuration change trigers IIS application stop
GL_STOP_LISTENING); // worker process stop or recycle

if (FAILED(hr))
Expand Down
Loading

0 comments on commit 7bc5983

Please sign in to comment.