From ffeebfc8b931b1730b9968a1b443ec0a80c86887 Mon Sep 17 00:00:00 2001 From: pan-wang Date: Wed, 21 Mar 2018 13:37:26 -0700 Subject: [PATCH] support shutdown timeout for inprocess application (#703) --- src/CommonLib/resources.h | 1 + .../inprocess/inprocessapplication.cpp | 87 +++++++++++++++++-- .../inprocess/inprocessapplication.h | 11 +++ 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/src/CommonLib/resources.h b/src/CommonLib/resources.h index 31b981d0fa..adba68b794 100644 --- a/src/CommonLib/resources.h +++ b/src/CommonLib/resources.h @@ -21,6 +21,7 @@ #define ASPNETCORE_EVENT_INVALID_STDOUT_LOG_FILE_MSG L"Warning: Could not create stdoutLogFile %s, ErrorCode = '0x%x'." #define ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE_MSG L"Failed to gracefully shutdown process '%d'." #define ASPNETCORE_EVENT_SENT_SHUTDOWN_HTTP_REQUEST_MSG L"Sent shutdown HTTP message to process '%d' and received http status '%d'." +#define ASPNETCORE_EVENT_APP_SHUTDOWN_FAILURE_MSG L"Failed to gracefully shutdown application '%s'." #define ASPNETCORE_EVENT_LOAD_CLR_FALIURE_MSG L"Application '%s' with physical root '%s' failed to load clr and managed application, ErrorCode = '0x%x." #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)." diff --git a/src/RequestHandler/inprocess/inprocessapplication.cpp b/src/RequestHandler/inprocess/inprocessapplication.cpp index 879ea30a63..347257d545 100644 --- a/src/RequestHandler/inprocess/inprocessapplication.cpp +++ b/src/RequestHandler/inprocess/inprocessapplication.cpp @@ -40,9 +40,90 @@ IN_PROCESS_APPLICATION::~IN_PROCESS_APPLICATION() s_Application = NULL; } +//static +VOID +IN_PROCESS_APPLICATION::DoShutDown( + LPVOID lpParam +) +{ + IN_PROCESS_APPLICATION* pApplication = static_cast(lpParam); + DBG_ASSERT(pApplication); + pApplication->ShutDownInternal(); +} + __override VOID -IN_PROCESS_APPLICATION::ShutDown() +IN_PROCESS_APPLICATION::ShutDown( + VOID +) +{ + HANDLE hThread = NULL; + HRESULT hr = S_OK; + DWORD dwThreadStatus = 0; + DWORD dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS(); + + if (IsDebuggerPresent()) + { + dwTimeout = INFINITE; + } + + hThread = CreateThread( + NULL, // default security attributes + 0, // default stack size + (LPTHREAD_START_ROUTINE)DoShutDown, + this, // thread function arguments + 0, // default creation flags + NULL); // receive thread identifier + + if (hThread == NULL) + { + hr = HRESULT_FROM_WIN32(GetLastError()); + goto Finished; + } + + if (WaitForSingleObject(hThread, dwTimeout) != WAIT_OBJECT_0) + { + // 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) + { + // Calling back into managed at this point is prone to have AVs + // Calling terminate thread here may be our best solution. + TerminateThread(hThread, STATUS_CONTROL_C_EXIT); + hr = HRESULT_FROM_WIN32(ERROR_TIMEOUT); + } + } + +Finished: + + if (hThread != NULL) + { + CloseHandle(hThread); + } + m_hThread = NULL; + + if (FAILED(hr)) + { + STACK_STRU(strEventMsg, 256); + // + // Assumption: inprocess application shutdown will be called only at process shutdown + // Based on this assumption, we just let shutdown continue and process will exit + // Log a warning for ungraceful shutdown + // + if (SUCCEEDED(strEventMsg.SafeSnwprintf( + ASPNETCORE_EVENT_APP_SHUTDOWN_FAILURE_MSG, + m_pConfig->QueryConfigPath()->QueryStr()))) + { + UTILITY::LogEvent(g_hEventLog, + EVENTLOG_WARNING_TYPE, + ASPNETCORE_EVENT_GRACEFUL_SHUTDOWN_FAILURE, + strEventMsg.QueryStr()); + } + } +} + + +VOID +IN_PROCESS_APPLICATION::ShutDownInternal() { DWORD dwThreadStatus = 0; DWORD dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS(); @@ -54,10 +135,6 @@ IN_PROCESS_APPLICATION::ShutDown() { dwTimeout = INFINITE; } - else - { - dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS(); - } if (m_fShutdownCalledFromNative || m_status == APPLICATION_STATUS::STARTING || diff --git a/src/RequestHandler/inprocess/inprocessapplication.h b/src/RequestHandler/inprocess/inprocessapplication.h index 100f481e77..9de0b5255f 100644 --- a/src/RequestHandler/inprocess/inprocessapplication.h +++ b/src/RequestHandler/inprocess/inprocessapplication.h @@ -99,6 +99,17 @@ public: } private: + static + VOID + DoShutDown( + LPVOID lpParam + ); + + VOID + ShutDownInternal( + VOID + ); + // Thread executing the .NET Core process HANDLE m_hThread;