[automated] Merge branch 'release/2.2' => 'master' (#1122)
* Remove extra logic from file watcher (#1107) * Fix ServerShutsDownWhenMainExits test (#1120)
This commit is contained in:
parent
d4d6db80eb
commit
75316cc0a4
|
|
@ -64,8 +64,9 @@ APPLICATION_INFO::GetOrCreateApplication(
|
||||||
if (m_pApplication->QueryStatus() == RECYCLED)
|
if (m_pApplication->QueryStatus() == RECYCLED)
|
||||||
{
|
{
|
||||||
LOG_INFO("Application went offline");
|
LOG_INFO("Application went offline");
|
||||||
// Application that went offline
|
|
||||||
// are supposed to recycle themselves
|
// Call to wait for application to complete stopping
|
||||||
|
m_pApplication->Stop(/* fServerInitiated */ false);
|
||||||
m_pApplication = nullptr;
|
m_pApplication = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,19 @@ public:
|
||||||
APPLICATION()
|
APPLICATION()
|
||||||
: m_cRefs(1)
|
: m_cRefs(1)
|
||||||
{
|
{
|
||||||
|
InitializeSRWLock(&m_stateLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
VOID
|
||||||
|
Stop(bool fServerInitiated) override
|
||||||
|
{
|
||||||
|
UNREFERENCED_PARAMETER(fServerInitiated);
|
||||||
|
|
||||||
|
m_fStopCalled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
ReferenceApplication() override
|
ReferenceApplication() override
|
||||||
{
|
{
|
||||||
|
|
@ -49,6 +60,9 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
volatile APPLICATION_STATUS m_status = APPLICATION_STATUS::UNKNOWN;
|
volatile APPLICATION_STATUS m_status = APPLICATION_STATUS::UNKNOWN;
|
||||||
|
SRWLOCK m_stateLock;
|
||||||
|
bool m_fStopCalled;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
mutable LONG m_cRefs;
|
mutable LONG m_cRefs;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#include "InProcessApplicationBase.h"
|
#include "InProcessApplicationBase.h"
|
||||||
#include "SRWExclusiveLock.h"
|
|
||||||
|
|
||||||
hostfxr_main_fn InProcessApplicationBase::s_fMainCallback = NULL;
|
hostfxr_main_fn InProcessApplicationBase::s_fMainCallback = NULL;
|
||||||
|
|
||||||
|
|
@ -11,32 +10,14 @@ InProcessApplicationBase::InProcessApplicationBase(
|
||||||
IHttpApplication& pHttpApplication)
|
IHttpApplication& pHttpApplication)
|
||||||
: AppOfflineTrackingApplication(pHttpApplication),
|
: AppOfflineTrackingApplication(pHttpApplication),
|
||||||
m_fRecycleCalled(FALSE),
|
m_fRecycleCalled(FALSE),
|
||||||
m_srwLock(),
|
|
||||||
m_pHttpServer(pHttpServer)
|
m_pHttpServer(pHttpServer)
|
||||||
{
|
{
|
||||||
InitializeSRWLock(&m_srwLock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
InProcessApplicationBase::Stop(bool fServerInitiated)
|
InProcessApplicationBase::Stop(bool fServerInitiated)
|
||||||
{
|
{
|
||||||
// We need to guarantee that recycle is only called once, as calling pHttpServer->RecycleProcess
|
AppOfflineTrackingApplication::Stop(fServerInitiated);
|
||||||
// multiple times can lead to AVs.
|
|
||||||
if (m_fRecycleCalled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
SRWExclusiveLock lock(m_srwLock);
|
|
||||||
|
|
||||||
if (m_fRecycleCalled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_fRecycleCalled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop was initiated by server no need to do anything, server would stop on it's own
|
// Stop was initiated by server no need to do anything, server would stop on it's own
|
||||||
if (fServerInitiated)
|
if (fServerInitiated)
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
BOOL m_fRecycleCalled;
|
BOOL m_fRecycleCalled;
|
||||||
SRWLOCK m_srwLock;
|
|
||||||
IHttpServer& m_pHttpServer;
|
IHttpServer& m_pHttpServer;
|
||||||
// Allows to override call to hostfxr_main with custome callback
|
// Allows to override call to hostfxr_main with custome callback
|
||||||
// used in testing
|
// used in testing
|
||||||
|
|
|
||||||
|
|
@ -69,6 +69,16 @@ IN_PROCESS_APPLICATION::Stop(bool fServerInitiated)
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
CHandle hThread;
|
CHandle hThread;
|
||||||
DWORD dwThreadStatus = 0;
|
DWORD dwThreadStatus = 0;
|
||||||
|
|
||||||
|
SRWExclusiveLock stopLock(m_stateLock);
|
||||||
|
|
||||||
|
if (m_fStopCalled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppOfflineTrackingApplication::Stop(fServerInitiated);
|
||||||
|
|
||||||
DWORD dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS();
|
DWORD dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS();
|
||||||
|
|
||||||
if (IsDebuggerPresent())
|
if (IsDebuggerPresent())
|
||||||
|
|
@ -149,8 +159,6 @@ IN_PROCESS_APPLICATION::ShutDownInternal()
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
SRWExclusiveLock lock(m_srwLock);
|
|
||||||
|
|
||||||
if (m_fShutdownCalledFromNative ||
|
if (m_fShutdownCalledFromNative ||
|
||||||
m_status == APPLICATION_STATUS::STARTING ||
|
m_status == APPLICATION_STATUS::STARTING ||
|
||||||
m_status == APPLICATION_STATUS::FAIL)
|
m_status == APPLICATION_STATUS::FAIL)
|
||||||
|
|
@ -241,7 +249,7 @@ IN_PROCESS_APPLICATION::LoadManagedApplication
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
DWORD dwTimeout;
|
DWORD dwTimeout;
|
||||||
DWORD dwResult;
|
DWORD dwResult;
|
||||||
|
|
||||||
ReferenceApplication();
|
ReferenceApplication();
|
||||||
|
|
||||||
if (m_status != APPLICATION_STATUS::STARTING)
|
if (m_status != APPLICATION_STATUS::STARTING)
|
||||||
|
|
@ -263,7 +271,7 @@ IN_PROCESS_APPLICATION::LoadManagedApplication
|
||||||
{
|
{
|
||||||
// Set up stdout redirect
|
// Set up stdout redirect
|
||||||
|
|
||||||
SRWExclusiveLock lock(m_srwLock);
|
SRWExclusiveLock lock(m_stateLock);
|
||||||
|
|
||||||
if (m_pLoggerProvider == NULL)
|
if (m_pLoggerProvider == NULL)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -134,11 +134,9 @@ private:
|
||||||
// The exit code of the .NET Core process
|
// The exit code of the .NET Core process
|
||||||
INT m_ProcessExitCode;
|
INT m_ProcessExitCode;
|
||||||
|
|
||||||
BOOL m_fIsWebSocketsConnection;
|
|
||||||
volatile BOOL m_fBlockCallbacksIntoManaged;
|
volatile BOOL m_fBlockCallbacksIntoManaged;
|
||||||
volatile BOOL m_fShutdownCalledFromNative;
|
volatile BOOL m_fShutdownCalledFromNative;
|
||||||
volatile BOOL m_fShutdownCalledFromManaged;
|
volatile BOOL m_fShutdownCalledFromManaged;
|
||||||
BOOL m_fRecycleCalled;
|
|
||||||
BOOL m_fInitialized;
|
BOOL m_fInitialized;
|
||||||
std::unique_ptr<REQUESTHANDLER_CONFIG> m_pConfig;
|
std::unique_ptr<REQUESTHANDLER_CONFIG> m_pConfig;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,11 @@ OUT_OF_PROCESS_APPLICATION::OUT_OF_PROCESS_APPLICATION(
|
||||||
{
|
{
|
||||||
m_status = APPLICATION_STATUS::RUNNING;
|
m_status = APPLICATION_STATUS::RUNNING;
|
||||||
m_pProcessManager = NULL;
|
m_pProcessManager = NULL;
|
||||||
InitializeSRWLock(&m_srwLock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OUT_OF_PROCESS_APPLICATION::~OUT_OF_PROCESS_APPLICATION()
|
OUT_OF_PROCESS_APPLICATION::~OUT_OF_PROCESS_APPLICATION()
|
||||||
{
|
{
|
||||||
SRWExclusiveLock lock(m_srwLock);
|
SRWExclusiveLock lock(m_stateLock);
|
||||||
if (m_pProcessManager != NULL)
|
if (m_pProcessManager != NULL)
|
||||||
{
|
{
|
||||||
m_pProcessManager->Shutdown();
|
m_pProcessManager->Shutdown();
|
||||||
|
|
@ -65,9 +64,15 @@ __override
|
||||||
VOID
|
VOID
|
||||||
OUT_OF_PROCESS_APPLICATION::Stop(bool fServerInitiated)
|
OUT_OF_PROCESS_APPLICATION::Stop(bool fServerInitiated)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(fServerInitiated);
|
SRWExclusiveLock lock(m_stateLock);
|
||||||
|
|
||||||
|
if (m_fStopCalled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppOfflineTrackingApplication::Stop(fServerInitiated);
|
||||||
|
|
||||||
SRWExclusiveLock lock(m_srwLock);
|
|
||||||
if (m_pProcessManager != NULL)
|
if (m_pProcessManager != NULL)
|
||||||
{
|
{
|
||||||
m_pProcessManager->Shutdown();
|
m_pProcessManager->Shutdown();
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,6 @@ private:
|
||||||
VOID SetWebsocketStatus(IHttpContext *pHttpContext);
|
VOID SetWebsocketStatus(IHttpContext *pHttpContext);
|
||||||
|
|
||||||
PROCESS_MANAGER * m_pProcessManager;
|
PROCESS_MANAGER * m_pProcessManager;
|
||||||
SRWLOCK m_srwLock;
|
|
||||||
IHttpServer *m_pHttpServer;
|
IHttpServer *m_pHttpServer;
|
||||||
|
|
||||||
WEBSOCKET_STATUS m_fWebSocketSupported;
|
WEBSOCKET_STATUS m_fWebSocketSupported;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,19 @@ HRESULT AppOfflineTrackingApplication::StartMonitoringAppOffline()
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppOfflineTrackingApplication::Stop(bool fServerInitiated)
|
||||||
|
{
|
||||||
|
APPLICATION::Stop(fServerInitiated);
|
||||||
|
|
||||||
|
m_status = APPLICATION_STATUS::RECYCLED;
|
||||||
|
|
||||||
|
if (m_fileWatcher)
|
||||||
|
{
|
||||||
|
m_fileWatcher->StopMonitor();
|
||||||
|
m_fileWatcher = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT AppOfflineTrackingApplication::StartMonitoringAppOflineImpl()
|
HRESULT AppOfflineTrackingApplication::StartMonitoringAppOflineImpl()
|
||||||
{
|
{
|
||||||
if (m_fileWatcher)
|
if (m_fileWatcher)
|
||||||
|
|
@ -34,25 +47,21 @@ HRESULT AppOfflineTrackingApplication::StartMonitoringAppOflineImpl()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_fileWatcher = std::make_unique<FILE_WATCHER>();
|
m_fileWatcher = std::make_unique<FILE_WATCHER>();
|
||||||
RETURN_IF_FAILED(m_fileWatcher->Create());
|
RETURN_IF_FAILED(m_fileWatcher->Create(m_applicationPath.c_str(),
|
||||||
m_fileWatcherEntry.reset(new FILE_WATCHER_ENTRY(m_fileWatcher.get()));
|
|
||||||
// FileWatcherCompletionRoutine calls dereference so we need to get ourselves a reference count
|
|
||||||
m_fileWatcherEntry->ReferenceFileWatcherEntry();
|
|
||||||
RETURN_IF_FAILED(m_fileWatcherEntry->Create(
|
|
||||||
m_applicationPath.c_str(),
|
|
||||||
L"app_offline.htm",
|
L"app_offline.htm",
|
||||||
std::bind(&AppOfflineTrackingApplication::OnAppOffline, this),
|
this));
|
||||||
NULL));
|
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AppOfflineTrackingApplication::OnAppOffline()
|
void AppOfflineTrackingApplication::OnAppOffline()
|
||||||
{
|
{
|
||||||
|
if (m_fAppOfflineProcessed.exchange(true))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_INFOF("Received app_offline notification in application %S", m_applicationPath.c_str());
|
LOG_INFOF("Received app_offline notification in application %S", m_applicationPath.c_str());
|
||||||
m_fileWatcherEntry->StopMonitor();
|
|
||||||
m_fileWatcherEntry.reset(nullptr);
|
|
||||||
m_status = APPLICATION_STATUS::RECYCLED;
|
|
||||||
UTILITY::LogEventF(g_hEventLog,
|
UTILITY::LogEventF(g_hEventLog,
|
||||||
EVENTLOG_INFORMATION_TYPE,
|
EVENTLOG_INFORMATION_TYPE,
|
||||||
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
|
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "filewatcher.h"
|
#include "filewatcher.h"
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
class AppOfflineTrackingApplication: public APPLICATION
|
class AppOfflineTrackingApplication: public APPLICATION
|
||||||
{
|
{
|
||||||
|
|
@ -13,20 +14,23 @@ public:
|
||||||
AppOfflineTrackingApplication(const IHttpApplication& application)
|
AppOfflineTrackingApplication(const IHttpApplication& application)
|
||||||
: m_applicationPath(application.GetApplicationPhysicalPath()),
|
: m_applicationPath(application.GetApplicationPhysicalPath()),
|
||||||
m_fileWatcher(nullptr),
|
m_fileWatcher(nullptr),
|
||||||
m_fileWatcherEntry(nullptr)
|
m_fAppOfflineProcessed(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
~AppOfflineTrackingApplication() override
|
~AppOfflineTrackingApplication() override
|
||||||
{
|
{
|
||||||
if (m_fileWatcherEntry)
|
if (m_fileWatcher)
|
||||||
{
|
{
|
||||||
m_fileWatcherEntry->StopMonitor();
|
m_fileWatcher->StopMonitor();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
StartMonitoringAppOffline();
|
StartMonitoringAppOffline();
|
||||||
|
|
||||||
|
VOID
|
||||||
|
Stop(bool fServerInitiated) override;
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
VOID
|
VOID
|
||||||
|
|
@ -38,5 +42,5 @@ private:
|
||||||
|
|
||||||
std::wstring m_applicationPath;
|
std::wstring m_applicationPath;
|
||||||
std::unique_ptr<FILE_WATCHER> m_fileWatcher;
|
std::unique_ptr<FILE_WATCHER> m_fileWatcher;
|
||||||
std::unique_ptr<FILE_WATCHER_ENTRY, FILE_WATCHER_ENTRY_DELETER> m_fileWatcherEntry;
|
std::atomic_bool m_fAppOfflineProcessed;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include "stdafx.h"
|
#include "stdafx.h"
|
||||||
#include "filewatcher.h"
|
#include "filewatcher.h"
|
||||||
#include "debugutil.h"
|
#include "debugutil.h"
|
||||||
|
#include "AppOfflineTrackingApplication.h"
|
||||||
|
|
||||||
FILE_WATCHER::FILE_WATCHER() :
|
FILE_WATCHER::FILE_WATCHER() :
|
||||||
m_hCompletionPort(NULL),
|
m_hCompletionPort(NULL),
|
||||||
|
|
@ -14,13 +15,13 @@ FILE_WATCHER::FILE_WATCHER() :
|
||||||
|
|
||||||
FILE_WATCHER::~FILE_WATCHER()
|
FILE_WATCHER::~FILE_WATCHER()
|
||||||
{
|
{
|
||||||
|
StopMonitor();
|
||||||
|
|
||||||
if (m_hChangeNotificationThread != NULL)
|
if (m_hChangeNotificationThread != NULL)
|
||||||
{
|
{
|
||||||
DWORD dwRetryCounter = 20; // totally wait for 1s
|
DWORD dwRetryCounter = 20; // totally wait for 1s
|
||||||
DWORD dwExitCode = STILL_ACTIVE;
|
DWORD dwExitCode = STILL_ACTIVE;
|
||||||
|
|
||||||
// signal the file watch thread to exit
|
|
||||||
PostQueuedCompletionStatus(m_hCompletionPort, 0, FILE_WATCHER_SHUTDOWN_KEY, NULL);
|
|
||||||
while (!m_fThreadExit && dwRetryCounter > 0)
|
while (!m_fThreadExit && dwRetryCounter > 0)
|
||||||
{
|
{
|
||||||
if (GetExitCodeThread(m_hChangeNotificationThread, &dwExitCode))
|
if (GetExitCodeThread(m_hChangeNotificationThread, &dwExitCode))
|
||||||
|
|
@ -45,55 +46,66 @@ FILE_WATCHER::~FILE_WATCHER()
|
||||||
{
|
{
|
||||||
TerminateThread(m_hChangeNotificationThread, 1);
|
TerminateThread(m_hChangeNotificationThread, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
CloseHandle(m_hChangeNotificationThread);
|
|
||||||
m_hChangeNotificationThread = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (NULL != m_hCompletionPort)
|
|
||||||
{
|
|
||||||
CloseHandle(m_hCompletionPort);
|
|
||||||
m_hCompletionPort = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
FILE_WATCHER::Create(
|
FILE_WATCHER::Create(
|
||||||
VOID
|
_In_ PCWSTR pszDirectoryToMonitor,
|
||||||
|
_In_ PCWSTR pszFileNameToMonitor,
|
||||||
|
_In_ AppOfflineTrackingApplication *pApplication
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
|
||||||
|
|
||||||
m_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
|
RETURN_LAST_ERROR_IF_NULL(m_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0));
|
||||||
NULL,
|
|
||||||
0,
|
|
||||||
0);
|
|
||||||
|
|
||||||
if (m_hCompletionPort == NULL)
|
RETURN_LAST_ERROR_IF_NULL(m_hChangeNotificationThread = CreateThread(NULL,
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_hChangeNotificationThread = CreateThread(NULL,
|
|
||||||
0,
|
0,
|
||||||
(LPTHREAD_START_ROUTINE)ChangeNotificationThread,
|
(LPTHREAD_START_ROUTINE)ChangeNotificationThread,
|
||||||
this,
|
this,
|
||||||
0,
|
0,
|
||||||
NULL);
|
NULL));
|
||||||
|
|
||||||
if (m_hChangeNotificationThread == NULL)
|
if (pszDirectoryToMonitor == NULL ||
|
||||||
|
pszFileNameToMonitor == NULL ||
|
||||||
|
pApplication == NULL)
|
||||||
{
|
{
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
DBG_ASSERT(FALSE);
|
||||||
|
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
||||||
CloseHandle(m_hCompletionPort);
|
|
||||||
m_hCompletionPort = NULL;
|
|
||||||
|
|
||||||
goto Finished;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished:
|
_pApplication = ReferenceApplication(pApplication);
|
||||||
return hr;
|
|
||||||
|
RETURN_IF_FAILED(_strFileName.Copy(pszFileNameToMonitor));
|
||||||
|
RETURN_IF_FAILED(_strDirectoryName.Copy(pszDirectoryToMonitor));
|
||||||
|
RETURN_IF_FAILED(_strFullName.Append(_strDirectoryName));
|
||||||
|
RETURN_IF_FAILED(_strFullName.Append(_strFileName));
|
||||||
|
|
||||||
|
//
|
||||||
|
// Resize change buffer to something "reasonable"
|
||||||
|
//
|
||||||
|
RETURN_LAST_ERROR_IF(!_buffDirectoryChanges.Resize(FILE_WATCHER_ENTRY_BUFFER_SIZE));
|
||||||
|
|
||||||
|
_hDirectory = CreateFileW(
|
||||||
|
_strDirectoryName.QueryStr(),
|
||||||
|
FILE_LIST_DIRECTORY,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
RETURN_LAST_ERROR_IF_NULL(_hDirectory);
|
||||||
|
|
||||||
|
RETURN_LAST_ERROR_IF_NULL(CreateIoCompletionPort(
|
||||||
|
_hDirectory,
|
||||||
|
m_hCompletionPort,
|
||||||
|
NULL,
|
||||||
|
0));
|
||||||
|
|
||||||
|
RETURN_IF_FAILED(Monitor());
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD
|
DWORD
|
||||||
|
|
@ -148,107 +160,29 @@ Win32 error
|
||||||
DBG_ASSERT(pOverlapped != NULL);
|
DBG_ASSERT(pOverlapped != NULL);
|
||||||
if (pOverlapped != NULL)
|
if (pOverlapped != NULL)
|
||||||
{
|
{
|
||||||
FileWatcherCompletionRoutine(
|
pFileMonitor->HandleChangeCompletion(cbCompletion);
|
||||||
dwErrorStatus,
|
|
||||||
cbCompletion,
|
if (!pFileMonitor->_lStopMonitorCalled)
|
||||||
pOverlapped);
|
{
|
||||||
|
//
|
||||||
|
// Continue monitoring
|
||||||
|
//
|
||||||
|
pFileMonitor->Monitor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pOverlapped = NULL;
|
pOverlapped = NULL;
|
||||||
cbCompletion = 0;
|
cbCompletion = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pFileMonitor->m_fThreadExit = TRUE;
|
pFileMonitor->m_fThreadExit = TRUE;
|
||||||
|
|
||||||
LOG_INFO("Stopping file watcher thread");
|
LOG_INFO("Stopping file watcher thread");
|
||||||
ExitThread(0);
|
ExitThread(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
|
||||||
WINAPI
|
|
||||||
FILE_WATCHER::FileWatcherCompletionRoutine(
|
|
||||||
DWORD dwCompletionStatus,
|
|
||||||
DWORD cbCompletion,
|
|
||||||
OVERLAPPED * pOverlapped
|
|
||||||
)
|
|
||||||
/*++
|
|
||||||
|
|
||||||
Routine Description:
|
|
||||||
|
|
||||||
Called when ReadDirectoryChangesW() completes
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
|
|
||||||
dwCompletionStatus - Error of completion
|
|
||||||
cbCompletion - Bytes of completion
|
|
||||||
pOverlapped - State of completion
|
|
||||||
|
|
||||||
Return Value:
|
|
||||||
|
|
||||||
None
|
|
||||||
|
|
||||||
--*/
|
|
||||||
{
|
|
||||||
FILE_WATCHER_ENTRY * pMonitorEntry;
|
|
||||||
pMonitorEntry = CONTAINING_RECORD(pOverlapped, FILE_WATCHER_ENTRY, _overlapped);
|
|
||||||
|
|
||||||
DBG_ASSERT(pMonitorEntry != NULL);
|
|
||||||
|
|
||||||
pMonitorEntry->HandleChangeCompletion(dwCompletionStatus, cbCompletion);
|
|
||||||
|
|
||||||
if (pMonitorEntry->QueryIsValid())
|
|
||||||
{
|
|
||||||
//
|
|
||||||
// Continue monitoring
|
|
||||||
//
|
|
||||||
pMonitorEntry->Monitor();
|
|
||||||
}
|
|
||||||
//
|
|
||||||
// Deference the counter not matter whether the monitor is valid
|
|
||||||
// Valid: Monitor increases the counter, need to reduce one
|
|
||||||
// InValid: Reduce the counter to free the entry
|
|
||||||
//
|
|
||||||
pMonitorEntry->DereferenceFileWatcherEntry();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
FILE_WATCHER_ENTRY::FILE_WATCHER_ENTRY(FILE_WATCHER * pFileMonitor) :
|
|
||||||
_pFileMonitor(pFileMonitor),
|
|
||||||
_hDirectory(INVALID_HANDLE_VALUE),
|
|
||||||
_hImpersonationToken(NULL),
|
|
||||||
_pCallback(),
|
|
||||||
_lStopMonitorCalled(0),
|
|
||||||
_cRefs(1),
|
|
||||||
_fIsValid(TRUE)
|
|
||||||
{
|
|
||||||
_dwSignature = FILE_WATCHER_ENTRY_SIGNATURE;
|
|
||||||
InitializeSRWLock(&_srwLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE_WATCHER_ENTRY::~FILE_WATCHER_ENTRY()
|
|
||||||
{
|
|
||||||
DBG_ASSERT(_cRefs == 0);
|
|
||||||
|
|
||||||
_dwSignature = FILE_WATCHER_ENTRY_SIGNATURE_FREE;
|
|
||||||
|
|
||||||
if (_hDirectory != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
CloseHandle(_hDirectory);
|
|
||||||
_hDirectory = INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_hImpersonationToken != NULL)
|
|
||||||
{
|
|
||||||
CloseHandle(_hImpersonationToken);
|
|
||||||
_hImpersonationToken = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma warning(disable:4100)
|
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
FILE_WATCHER_ENTRY::HandleChangeCompletion(
|
FILE_WATCHER::HandleChangeCompletion(
|
||||||
_In_ DWORD dwCompletionStatus,
|
|
||||||
_In_ DWORD cbCompletion
|
_In_ DWORD cbCompletion
|
||||||
)
|
)
|
||||||
/*++
|
/*++
|
||||||
|
|
@ -269,16 +203,8 @@ HRESULT
|
||||||
|
|
||||||
--*/
|
--*/
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
|
||||||
FILE_NOTIFY_INFORMATION * pNotificationInfo;
|
|
||||||
BOOL fFileChanged = FALSE;
|
BOOL fFileChanged = FALSE;
|
||||||
|
|
||||||
AcquireSRWLockExclusive(&_srwLock);
|
|
||||||
if (!_fIsValid)
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When directory handle is closed then HandleChangeCompletion
|
// When directory handle is closed then HandleChangeCompletion
|
||||||
// happens with cbCompletion = 0 and dwCompletionStatus = 0
|
// happens with cbCompletion = 0 and dwCompletionStatus = 0
|
||||||
// From documentation it is not clear if that combination
|
// From documentation it is not clear if that combination
|
||||||
|
|
@ -289,7 +215,7 @@ HRESULT
|
||||||
//
|
//
|
||||||
if (_lStopMonitorCalled)
|
if (_lStopMonitorCalled)
|
||||||
{
|
{
|
||||||
goto Finished;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
@ -303,7 +229,7 @@ HRESULT
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
pNotificationInfo = (FILE_NOTIFY_INFORMATION*)_buffDirectoryChanges.QueryPtr();
|
auto pNotificationInfo = (FILE_NOTIFY_INFORMATION*)_buffDirectoryChanges.QueryPtr();
|
||||||
DBG_ASSERT(pNotificationInfo != NULL);
|
DBG_ASSERT(pNotificationInfo != NULL);
|
||||||
|
|
||||||
while (pNotificationInfo != NULL)
|
while (pNotificationInfo != NULL)
|
||||||
|
|
@ -334,55 +260,58 @@ HRESULT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Finished:
|
if (fFileChanged && !_lStopMonitorCalled)
|
||||||
ReleaseSRWLockExclusive(&_srwLock);
|
|
||||||
|
|
||||||
if (fFileChanged)
|
|
||||||
{
|
{
|
||||||
//
|
// Reference application before
|
||||||
// so far we only monitoring app_offline
|
_pApplication->ReferenceApplication();
|
||||||
//
|
RETURN_LAST_ERROR_IF(!QueueUserWorkItem(RunNotificationCallback, _pApplication.get(), WT_EXECUTEDEFAULT));
|
||||||
_pCallback();
|
|
||||||
}
|
}
|
||||||
return hr;
|
|
||||||
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning( error : 4100 )
|
DWORD
|
||||||
|
WINAPI
|
||||||
|
FILE_WATCHER::RunNotificationCallback(
|
||||||
|
LPVOID pvArg
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Recapture application instance into unique_ptr
|
||||||
|
auto pApplication = std::unique_ptr<AppOfflineTrackingApplication, IAPPLICATION_DELETER>(static_cast<AppOfflineTrackingApplication*>(pvArg));
|
||||||
|
DBG_ASSERT(pFileMonitor != NULL);
|
||||||
|
pApplication->OnAppOffline();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT
|
HRESULT
|
||||||
FILE_WATCHER_ENTRY::Monitor(VOID)
|
FILE_WATCHER::Monitor(VOID)
|
||||||
{
|
{
|
||||||
HRESULT hr = S_OK;
|
HRESULT hr = S_OK;
|
||||||
DWORD cbRead;
|
DWORD cbRead;
|
||||||
AcquireSRWLockExclusive(&_srwLock);
|
|
||||||
ReferenceFileWatcherEntry();
|
|
||||||
ZeroMemory(&_overlapped, sizeof(_overlapped));
|
ZeroMemory(&_overlapped, sizeof(_overlapped));
|
||||||
|
|
||||||
if (!ReadDirectoryChangesW(_hDirectory,
|
RETURN_LAST_ERROR_IF(!ReadDirectoryChangesW(_hDirectory,
|
||||||
_buffDirectoryChanges.QueryPtr(),
|
_buffDirectoryChanges.QueryPtr(),
|
||||||
_buffDirectoryChanges.QuerySize(),
|
_buffDirectoryChanges.QuerySize(),
|
||||||
FALSE, // Watching sub dirs. Set to False now as only monitoring app_offline
|
FALSE, // Watching sub dirs. Set to False now as only monitoring app_offline
|
||||||
FILE_NOTIFY_VALID_MASK & ~FILE_NOTIFY_CHANGE_LAST_ACCESS,
|
FILE_NOTIFY_VALID_MASK & ~FILE_NOTIFY_CHANGE_LAST_ACCESS,
|
||||||
&cbRead,
|
&cbRead,
|
||||||
&_overlapped,
|
&_overlapped,
|
||||||
NULL))
|
NULL));
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
DereferenceFileWatcherEntry();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if file exist because ReadDirectoryChangesW would not fire events for existing files
|
// Check if file exist because ReadDirectoryChangesW would not fire events for existing files
|
||||||
if (GetFileAttributes(_strFullName.QueryStr()) != INVALID_FILE_ATTRIBUTES)
|
if (GetFileAttributes(_strFullName.QueryStr()) != INVALID_FILE_ATTRIBUTES)
|
||||||
{
|
{
|
||||||
PostQueuedCompletionStatus(_pFileMonitor->QueryCompletionPort(), 0, 0, &_overlapped);
|
PostQueuedCompletionStatus(m_hCompletionPort, 0, 0, &_overlapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseSRWLockExclusive(&_srwLock);
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
VOID
|
VOID
|
||||||
FILE_WATCHER_ENTRY::StopMonitor(VOID)
|
FILE_WATCHER::StopMonitor()
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
// Flag that monitoring is being stopped so that
|
// Flag that monitoring is being stopped so that
|
||||||
|
|
@ -390,123 +319,8 @@ FILE_WATCHER_ENTRY::StopMonitor(VOID)
|
||||||
// can be ignored
|
// can be ignored
|
||||||
//
|
//
|
||||||
InterlockedExchange(&_lStopMonitorCalled, 1);
|
InterlockedExchange(&_lStopMonitorCalled, 1);
|
||||||
MarkEntryInValid();
|
// signal the file watch thread to exit
|
||||||
if (_hDirectory != INVALID_HANDLE_VALUE)
|
PostQueuedCompletionStatus(m_hCompletionPort, 0, FILE_WATCHER_SHUTDOWN_KEY, NULL);
|
||||||
{
|
// Release application reference
|
||||||
AcquireSRWLockExclusive(&_srwLock);
|
_pApplication.reset(nullptr);
|
||||||
if (_hDirectory != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
CloseHandle(_hDirectory);
|
|
||||||
_hDirectory = INVALID_HANDLE_VALUE;
|
|
||||||
DereferenceFileWatcherEntry();
|
|
||||||
}
|
|
||||||
ReleaseSRWLockExclusive(&_srwLock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
FILE_WATCHER_ENTRY::Create(
|
|
||||||
_In_ PCWSTR pszDirectoryToMonitor,
|
|
||||||
_In_ PCWSTR pszFileNameToMonitor,
|
|
||||||
_In_ std::function<void()> pCallback,
|
|
||||||
_In_ HANDLE hImpersonationToken
|
|
||||||
)
|
|
||||||
{
|
|
||||||
HRESULT hr = S_OK;
|
|
||||||
BOOL fRet = FALSE;
|
|
||||||
|
|
||||||
if (pszDirectoryToMonitor == NULL ||
|
|
||||||
pszFileNameToMonitor == NULL ||
|
|
||||||
pCallback == NULL)
|
|
||||||
{
|
|
||||||
DBG_ASSERT(FALSE);
|
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
_pCallback = pCallback;
|
|
||||||
|
|
||||||
if (FAILED(hr = _strFileName.Copy(pszFileNameToMonitor)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr = _strDirectoryName.Copy(pszDirectoryToMonitor)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr = _strFullName.Append(_strDirectoryName)) ||
|
|
||||||
FAILED(hr = _strFullName.Append(_strFileName)))
|
|
||||||
{
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Resize change buffer to something "reasonable"
|
|
||||||
//
|
|
||||||
if (!_buffDirectoryChanges.Resize(FILE_WATCHER_ENTRY_BUFFER_SIZE))
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hImpersonationToken != NULL)
|
|
||||||
{
|
|
||||||
fRet = DuplicateHandle(GetCurrentProcess(),
|
|
||||||
hImpersonationToken,
|
|
||||||
GetCurrentProcess(),
|
|
||||||
&_hImpersonationToken,
|
|
||||||
0,
|
|
||||||
FALSE,
|
|
||||||
DUPLICATE_SAME_ACCESS);
|
|
||||||
|
|
||||||
if (!fRet)
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_hImpersonationToken != NULL)
|
|
||||||
{
|
|
||||||
CloseHandle(_hImpersonationToken);
|
|
||||||
_hImpersonationToken = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_hDirectory = CreateFileW(
|
|
||||||
_strDirectoryName.QueryStr(),
|
|
||||||
FILE_LIST_DIRECTORY,
|
|
||||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
||||||
NULL,
|
|
||||||
OPEN_EXISTING,
|
|
||||||
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (_hDirectory == INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CreateIoCompletionPort(
|
|
||||||
_hDirectory,
|
|
||||||
_pFileMonitor->QueryCompletionPort(),
|
|
||||||
NULL,
|
|
||||||
0) == NULL)
|
|
||||||
{
|
|
||||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
||||||
goto Finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Start monitoring
|
|
||||||
//
|
|
||||||
hr = Monitor();
|
|
||||||
|
|
||||||
Finished:
|
|
||||||
|
|
||||||
return hr;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include "iapplication.h"
|
||||||
|
#include "HandleWrapper.h"
|
||||||
|
|
||||||
#define FILE_WATCHER_SHUTDOWN_KEY (ULONG_PTR)(-1)
|
#define FILE_WATCHER_SHUTDOWN_KEY (ULONG_PTR)(-1)
|
||||||
#define FILE_WATCHER_ENTRY_BUFFER_SIZE 4096
|
#define FILE_WATCHER_ENTRY_BUFFER_SIZE 4096
|
||||||
#define FILE_NOTIFY_VALID_MASK 0x00000fff
|
#define FILE_NOTIFY_VALID_MASK 0x00000fff
|
||||||
#define FILE_WATCHER_ENTRY_SIGNATURE ((DWORD) 'FWES')
|
|
||||||
#define FILE_WATCHER_ENTRY_SIGNATURE_FREE ((DWORD) 'sewf')
|
class AppOfflineTrackingApplication;
|
||||||
|
|
||||||
class FILE_WATCHER{
|
class FILE_WATCHER{
|
||||||
public:
|
public:
|
||||||
|
|
@ -20,110 +22,36 @@ public:
|
||||||
|
|
||||||
~FILE_WATCHER();
|
~FILE_WATCHER();
|
||||||
|
|
||||||
HRESULT Create();
|
HRESULT Create(
|
||||||
|
_In_ PCWSTR pszDirectoryToMonitor,
|
||||||
HANDLE
|
_In_ PCWSTR pszFileNameToMonitor,
|
||||||
QueryCompletionPort(
|
_In_ AppOfflineTrackingApplication *pApplication
|
||||||
VOID
|
);
|
||||||
) const
|
|
||||||
{
|
|
||||||
return m_hCompletionPort;
|
|
||||||
}
|
|
||||||
|
|
||||||
static
|
static
|
||||||
DWORD
|
DWORD
|
||||||
WINAPI ChangeNotificationThread(LPVOID);
|
WINAPI ChangeNotificationThread(LPVOID);
|
||||||
|
|
||||||
static
|
static
|
||||||
void
|
DWORD
|
||||||
WINAPI FileWatcherCompletionRoutine
|
WINAPI RunNotificationCallback(LPVOID);
|
||||||
(
|
|
||||||
DWORD dwCompletionStatus,
|
|
||||||
DWORD cbCompletion,
|
|
||||||
OVERLAPPED * pOverlapped
|
|
||||||
);
|
|
||||||
|
|
||||||
private:
|
HRESULT HandleChangeCompletion(DWORD cbCompletion);
|
||||||
HANDLE m_hCompletionPort;
|
|
||||||
HANDLE m_hChangeNotificationThread;
|
|
||||||
volatile BOOL m_fThreadExit;
|
|
||||||
};
|
|
||||||
|
|
||||||
class FILE_WATCHER_ENTRY
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
FILE_WATCHER_ENTRY(FILE_WATCHER * pFileMonitor);
|
|
||||||
|
|
||||||
OVERLAPPED _overlapped;
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
Create(
|
|
||||||
_In_ PCWSTR pszDirectoryToMonitor,
|
|
||||||
_In_ PCWSTR pszFileNameToMonitor,
|
|
||||||
_In_ std::function<void()> pCallback,
|
|
||||||
_In_ HANDLE hImpersonationToken
|
|
||||||
);
|
|
||||||
|
|
||||||
VOID
|
|
||||||
ReferenceFileWatcherEntry() const
|
|
||||||
{
|
|
||||||
InterlockedIncrement(&_cRefs);
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID
|
|
||||||
DereferenceFileWatcherEntry() const
|
|
||||||
{
|
|
||||||
if (InterlockedDecrement(&_cRefs) == 0)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL
|
|
||||||
QueryIsValid() const
|
|
||||||
{
|
|
||||||
return _fIsValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
VOID
|
|
||||||
MarkEntryInValid()
|
|
||||||
{
|
|
||||||
_fIsValid = FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT Monitor();
|
HRESULT Monitor();
|
||||||
|
void StopMonitor();
|
||||||
VOID StopMonitor();
|
|
||||||
|
|
||||||
HRESULT
|
|
||||||
HandleChangeCompletion(
|
|
||||||
_In_ DWORD dwCompletionStatus,
|
|
||||||
_In_ DWORD cbCompletion
|
|
||||||
);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~FILE_WATCHER_ENTRY();
|
HandleWrapper<NullHandleTraits> m_hCompletionPort;
|
||||||
|
HandleWrapper<NullHandleTraits> m_hChangeNotificationThread;
|
||||||
|
HandleWrapper<NullHandleTraits> _hDirectory;
|
||||||
|
volatile BOOL m_fThreadExit;
|
||||||
|
|
||||||
DWORD _dwSignature;
|
|
||||||
BUFFER _buffDirectoryChanges;
|
BUFFER _buffDirectoryChanges;
|
||||||
HANDLE _hImpersonationToken;
|
|
||||||
HANDLE _hDirectory;
|
|
||||||
FILE_WATCHER* _pFileMonitor;
|
|
||||||
STRU _strFileName;
|
STRU _strFileName;
|
||||||
STRU _strDirectoryName;
|
STRU _strDirectoryName;
|
||||||
STRU _strFullName;
|
STRU _strFullName;
|
||||||
LONG _lStopMonitorCalled;
|
LONG _lStopMonitorCalled {};
|
||||||
mutable LONG _cRefs;
|
OVERLAPPED _overlapped;
|
||||||
BOOL _fIsValid;
|
std::unique_ptr<AppOfflineTrackingApplication, IAPPLICATION_DELETER> _pApplication;
|
||||||
SRWLOCK _srwLock;
|
|
||||||
std::function<void()> _pCallback;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
struct FILE_WATCHER_ENTRY_DELETER
|
|
||||||
{
|
|
||||||
void operator()(FILE_WATCHER_ENTRY* entry) const
|
|
||||||
{
|
|
||||||
entry->DereferenceFileWatcherEntry();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal class IISApplication
|
internal class IISApplication
|
||||||
{
|
{
|
||||||
|
internal const int ERROR_OBJECT_NOT_FOUND = unchecked((int)0x800710D8);
|
||||||
|
|
||||||
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(10);
|
private static readonly TimeSpan _timeout = TimeSpan.FromSeconds(10);
|
||||||
private static readonly TimeSpan _retryDelay = TimeSpan.FromMilliseconds(200);
|
private static readonly TimeSpan _retryDelay = TimeSpan.FromMilliseconds(200);
|
||||||
private readonly ServerManager _serverManager = new ServerManager();
|
private readonly ServerManager _serverManager = new ServerManager();
|
||||||
|
|
@ -106,7 +108,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
_logger.LogInformation($"Tried to start site, state: {state.ToString()}");
|
_logger.LogInformation($"Tried to start site, state: {state.ToString()}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex) when (ex is DllNotFoundException || (ex is COMException && (uint)ex.HResult == 0x800710D8) )
|
catch (Exception ex) when (ex is DllNotFoundException || (ex is COMException && ex.HResult == ERROR_OBJECT_NOT_FOUND) )
|
||||||
{
|
{
|
||||||
// Accessing the site.State property while the site
|
// Accessing the site.State property while the site
|
||||||
// is starting up returns the COMException
|
// is starting up returns the COMException
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,6 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 10; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
// send first request and add app_offline while app is starting
|
// send first request and add app_offline while app is starting
|
||||||
var runningTask = AssertAppOffline(deploymentResult);
|
var runningTask = AssertAppOffline(deploymentResult);
|
||||||
|
|
||||||
|
|
@ -220,14 +219,10 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
|
||||||
{
|
{
|
||||||
HttpResponseMessage response = null;
|
HttpResponseMessage response = null;
|
||||||
|
|
||||||
for (var i = 0; i < 5; i++)
|
for (var i = 0; i < 5 && response?.StatusCode != HttpStatusCode.ServiceUnavailable; i++)
|
||||||
{
|
{
|
||||||
// Keep retrying until app_offline is present.
|
// Keep retrying until app_offline is present.
|
||||||
response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
|
response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||||
if (!response.IsSuccessStatusCode)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
|
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
using Microsoft.AspNetCore.Server.IIS.FunctionalTests.Utilities;
|
||||||
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
using Microsoft.AspNetCore.Server.IntegrationTesting;
|
||||||
|
|
@ -22,8 +25,14 @@ namespace Microsoft.AspNetCore.Server.IISIntegration.FunctionalTests
|
||||||
{
|
{
|
||||||
var parameters = Helpers.GetBaseDeploymentParameters(publish: true);
|
var parameters = Helpers.GetBaseDeploymentParameters(publish: true);
|
||||||
var result = await DeployAsync(parameters);
|
var result = await DeployAsync(parameters);
|
||||||
|
try
|
||||||
var response = await result.RetryingHttpClient.GetAsync("/Shutdown");
|
{
|
||||||
|
await result.HttpClient.GetAsync("/Shutdown");
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex) when (ex.InnerException is IOException)
|
||||||
|
{
|
||||||
|
// Server might close a connection before request completes
|
||||||
|
}
|
||||||
Assert.True(result.HostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout));
|
Assert.True(result.HostShutdownToken.WaitHandle.WaitOne(TimeoutExtensions.DefaultTimeout));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue