Remove extra logic from file watcher (#1107)
This commit is contained in:
parent
7a4a945fd6
commit
5d4fedaeac
|
|
@ -64,8 +64,9 @@ APPLICATION_INFO::GetOrCreateApplication(
|
|||
if (m_pApplication->QueryStatus() == RECYCLED)
|
||||
{
|
||||
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;
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -26,8 +26,19 @@ public:
|
|||
APPLICATION()
|
||||
: m_cRefs(1)
|
||||
{
|
||||
InitializeSRWLock(&m_stateLock);
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
Stop(bool fServerInitiated) override
|
||||
{
|
||||
UNREFERENCED_PARAMETER(fServerInitiated);
|
||||
|
||||
m_fStopCalled = true;
|
||||
}
|
||||
|
||||
|
||||
VOID
|
||||
ReferenceApplication() override
|
||||
{
|
||||
|
|
@ -49,6 +60,9 @@ public:
|
|||
|
||||
protected:
|
||||
volatile APPLICATION_STATUS m_status = APPLICATION_STATUS::UNKNOWN;
|
||||
SRWLOCK m_stateLock;
|
||||
bool m_fStopCalled;
|
||||
|
||||
private:
|
||||
|
||||
mutable LONG m_cRefs;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
|
||||
#include "InProcessApplicationBase.h"
|
||||
#include "SRWExclusiveLock.h"
|
||||
|
||||
hostfxr_main_fn InProcessApplicationBase::s_fMainCallback = NULL;
|
||||
|
||||
|
|
@ -11,32 +10,14 @@ InProcessApplicationBase::InProcessApplicationBase(
|
|||
IHttpApplication& pHttpApplication)
|
||||
: AppOfflineTrackingApplication(pHttpApplication),
|
||||
m_fRecycleCalled(FALSE),
|
||||
m_srwLock(),
|
||||
m_pHttpServer(pHttpServer)
|
||||
{
|
||||
InitializeSRWLock(&m_srwLock);
|
||||
}
|
||||
|
||||
VOID
|
||||
InProcessApplicationBase::Stop(bool fServerInitiated)
|
||||
{
|
||||
// We need to guarantee that recycle is only called once, as calling pHttpServer->RecycleProcess
|
||||
// multiple times can lead to AVs.
|
||||
if (m_fRecycleCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
|
||||
if (m_fRecycleCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_fRecycleCalled = true;
|
||||
}
|
||||
AppOfflineTrackingApplication::Stop(fServerInitiated);
|
||||
|
||||
// Stop was initiated by server no need to do anything, server would stop on it's own
|
||||
if (fServerInitiated)
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ public:
|
|||
|
||||
protected:
|
||||
BOOL m_fRecycleCalled;
|
||||
SRWLOCK m_srwLock;
|
||||
IHttpServer& m_pHttpServer;
|
||||
// Allows to override call to hostfxr_main with custome callback
|
||||
// used in testing
|
||||
|
|
|
|||
|
|
@ -69,6 +69,16 @@ IN_PROCESS_APPLICATION::Stop(bool fServerInitiated)
|
|||
HRESULT hr = S_OK;
|
||||
CHandle hThread;
|
||||
DWORD dwThreadStatus = 0;
|
||||
|
||||
SRWExclusiveLock stopLock(m_stateLock);
|
||||
|
||||
if (m_fStopCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AppOfflineTrackingApplication::Stop(fServerInitiated);
|
||||
|
||||
DWORD dwTimeout = m_pConfig->QueryShutdownTimeLimitInMS();
|
||||
|
||||
if (IsDebuggerPresent())
|
||||
|
|
@ -149,8 +159,6 @@ IN_PROCESS_APPLICATION::ShutDownInternal()
|
|||
}
|
||||
|
||||
{
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
|
||||
if (m_fShutdownCalledFromNative ||
|
||||
m_status == APPLICATION_STATUS::STARTING ||
|
||||
m_status == APPLICATION_STATUS::FAIL)
|
||||
|
|
@ -241,7 +249,7 @@ IN_PROCESS_APPLICATION::LoadManagedApplication
|
|||
HRESULT hr = S_OK;
|
||||
DWORD dwTimeout;
|
||||
DWORD dwResult;
|
||||
|
||||
|
||||
ReferenceApplication();
|
||||
|
||||
if (m_status != APPLICATION_STATUS::STARTING)
|
||||
|
|
@ -263,7 +271,7 @@ IN_PROCESS_APPLICATION::LoadManagedApplication
|
|||
{
|
||||
// Set up stdout redirect
|
||||
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
SRWExclusiveLock lock(m_stateLock);
|
||||
|
||||
if (m_pLoggerProvider == NULL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -134,11 +134,9 @@ private:
|
|||
// The exit code of the .NET Core process
|
||||
INT m_ProcessExitCode;
|
||||
|
||||
BOOL m_fIsWebSocketsConnection;
|
||||
volatile BOOL m_fBlockCallbacksIntoManaged;
|
||||
volatile BOOL m_fShutdownCalledFromNative;
|
||||
volatile BOOL m_fShutdownCalledFromManaged;
|
||||
BOOL m_fRecycleCalled;
|
||||
BOOL m_fInitialized;
|
||||
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_pProcessManager = NULL;
|
||||
InitializeSRWLock(&m_srwLock);
|
||||
}
|
||||
|
||||
OUT_OF_PROCESS_APPLICATION::~OUT_OF_PROCESS_APPLICATION()
|
||||
{
|
||||
SRWExclusiveLock lock(m_srwLock);
|
||||
SRWExclusiveLock lock(m_stateLock);
|
||||
if (m_pProcessManager != NULL)
|
||||
{
|
||||
m_pProcessManager->Shutdown();
|
||||
|
|
@ -65,9 +64,15 @@ __override
|
|||
VOID
|
||||
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)
|
||||
{
|
||||
m_pProcessManager->Shutdown();
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ private:
|
|||
VOID SetWebsocketStatus(IHttpContext *pHttpContext);
|
||||
|
||||
PROCESS_MANAGER * m_pProcessManager;
|
||||
SRWLOCK m_srwLock;
|
||||
IHttpServer *m_pHttpServer;
|
||||
|
||||
WEBSOCKET_STATUS m_fWebSocketSupported;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,19 @@ HRESULT AppOfflineTrackingApplication::StartMonitoringAppOffline()
|
|||
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()
|
||||
{
|
||||
if (m_fileWatcher)
|
||||
|
|
@ -34,25 +47,21 @@ HRESULT AppOfflineTrackingApplication::StartMonitoringAppOflineImpl()
|
|||
}
|
||||
|
||||
m_fileWatcher = std::make_unique<FILE_WATCHER>();
|
||||
RETURN_IF_FAILED(m_fileWatcher->Create());
|
||||
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(),
|
||||
RETURN_IF_FAILED(m_fileWatcher->Create(m_applicationPath.c_str(),
|
||||
L"app_offline.htm",
|
||||
std::bind(&AppOfflineTrackingApplication::OnAppOffline, this),
|
||||
NULL));
|
||||
this));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void AppOfflineTrackingApplication::OnAppOffline()
|
||||
{
|
||||
if (m_fAppOfflineProcessed.exchange(true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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,
|
||||
EVENTLOG_INFORMATION_TYPE,
|
||||
ASPNETCORE_EVENT_RECYCLE_APPOFFLINE,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <Windows.h>
|
||||
#include "application.h"
|
||||
#include "filewatcher.h"
|
||||
#include <atomic>
|
||||
|
||||
class AppOfflineTrackingApplication: public APPLICATION
|
||||
{
|
||||
|
|
@ -13,20 +14,23 @@ public:
|
|||
AppOfflineTrackingApplication(const IHttpApplication& application)
|
||||
: m_applicationPath(application.GetApplicationPhysicalPath()),
|
||||
m_fileWatcher(nullptr),
|
||||
m_fileWatcherEntry(nullptr)
|
||||
m_fAppOfflineProcessed(false)
|
||||
{
|
||||
}
|
||||
|
||||
~AppOfflineTrackingApplication() override
|
||||
{
|
||||
if (m_fileWatcherEntry)
|
||||
if (m_fileWatcher)
|
||||
{
|
||||
m_fileWatcherEntry->StopMonitor();
|
||||
m_fileWatcher->StopMonitor();
|
||||
}
|
||||
};
|
||||
|
||||
HRESULT
|
||||
StartMonitoringAppOffline();
|
||||
|
||||
VOID
|
||||
Stop(bool fServerInitiated) override;
|
||||
|
||||
virtual
|
||||
VOID
|
||||
|
|
@ -38,5 +42,5 @@ private:
|
|||
|
||||
std::wstring m_applicationPath;
|
||||
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 "filewatcher.h"
|
||||
#include "debugutil.h"
|
||||
#include "AppOfflineTrackingApplication.h"
|
||||
|
||||
FILE_WATCHER::FILE_WATCHER() :
|
||||
m_hCompletionPort(NULL),
|
||||
|
|
@ -14,13 +15,13 @@ FILE_WATCHER::FILE_WATCHER() :
|
|||
|
||||
FILE_WATCHER::~FILE_WATCHER()
|
||||
{
|
||||
StopMonitor();
|
||||
|
||||
if (m_hChangeNotificationThread != NULL)
|
||||
{
|
||||
DWORD dwRetryCounter = 20; // totally wait for 1s
|
||||
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)
|
||||
{
|
||||
if (GetExitCodeThread(m_hChangeNotificationThread, &dwExitCode))
|
||||
|
|
@ -45,55 +46,66 @@ FILE_WATCHER::~FILE_WATCHER()
|
|||
{
|
||||
TerminateThread(m_hChangeNotificationThread, 1);
|
||||
}
|
||||
|
||||
CloseHandle(m_hChangeNotificationThread);
|
||||
m_hChangeNotificationThread = NULL;
|
||||
}
|
||||
|
||||
if (NULL != m_hCompletionPort)
|
||||
{
|
||||
CloseHandle(m_hCompletionPort);
|
||||
m_hCompletionPort = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT
|
||||
FILE_WATCHER::Create(
|
||||
VOID
|
||||
_In_ PCWSTR pszDirectoryToMonitor,
|
||||
_In_ PCWSTR pszFileNameToMonitor,
|
||||
_In_ AppOfflineTrackingApplication *pApplication
|
||||
)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
m_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
|
||||
NULL,
|
||||
0,
|
||||
0);
|
||||
RETURN_LAST_ERROR_IF_NULL(m_hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0));
|
||||
|
||||
if (m_hCompletionPort == NULL)
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
m_hChangeNotificationThread = CreateThread(NULL,
|
||||
RETURN_LAST_ERROR_IF_NULL(m_hChangeNotificationThread = CreateThread(NULL,
|
||||
0,
|
||||
(LPTHREAD_START_ROUTINE)ChangeNotificationThread,
|
||||
this,
|
||||
0,
|
||||
NULL);
|
||||
NULL));
|
||||
|
||||
if (m_hChangeNotificationThread == NULL)
|
||||
if (pszDirectoryToMonitor == NULL ||
|
||||
pszFileNameToMonitor == NULL ||
|
||||
pApplication == NULL)
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
|
||||
CloseHandle(m_hCompletionPort);
|
||||
m_hCompletionPort = NULL;
|
||||
|
||||
goto Finished;
|
||||
DBG_ASSERT(FALSE);
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
Finished:
|
||||
return hr;
|
||||
_pApplication = ReferenceApplication(pApplication);
|
||||
|
||||
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
|
||||
|
|
@ -148,107 +160,29 @@ Win32 error
|
|||
DBG_ASSERT(pOverlapped != NULL);
|
||||
if (pOverlapped != NULL)
|
||||
{
|
||||
FileWatcherCompletionRoutine(
|
||||
dwErrorStatus,
|
||||
cbCompletion,
|
||||
pOverlapped);
|
||||
pFileMonitor->HandleChangeCompletion(cbCompletion);
|
||||
|
||||
if (!pFileMonitor->_lStopMonitorCalled)
|
||||
{
|
||||
//
|
||||
// Continue monitoring
|
||||
//
|
||||
pFileMonitor->Monitor();
|
||||
}
|
||||
}
|
||||
pOverlapped = NULL;
|
||||
cbCompletion = 0;
|
||||
}
|
||||
|
||||
pFileMonitor->m_fThreadExit = TRUE;
|
||||
|
||||
|
||||
LOG_INFO("Stopping file watcher thread");
|
||||
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
|
||||
FILE_WATCHER_ENTRY::HandleChangeCompletion(
|
||||
_In_ DWORD dwCompletionStatus,
|
||||
FILE_WATCHER::HandleChangeCompletion(
|
||||
_In_ DWORD cbCompletion
|
||||
)
|
||||
/*++
|
||||
|
|
@ -269,16 +203,8 @@ HRESULT
|
|||
|
||||
--*/
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
FILE_NOTIFY_INFORMATION * pNotificationInfo;
|
||||
BOOL fFileChanged = FALSE;
|
||||
|
||||
AcquireSRWLockExclusive(&_srwLock);
|
||||
if (!_fIsValid)
|
||||
{
|
||||
goto Finished;
|
||||
}
|
||||
|
||||
// When directory handle is closed then HandleChangeCompletion
|
||||
// happens with cbCompletion = 0 and dwCompletionStatus = 0
|
||||
// From documentation it is not clear if that combination
|
||||
|
|
@ -289,7 +215,7 @@ HRESULT
|
|||
//
|
||||
if (_lStopMonitorCalled)
|
||||
{
|
||||
goto Finished;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -303,7 +229,7 @@ HRESULT
|
|||
}
|
||||
else
|
||||
{
|
||||
pNotificationInfo = (FILE_NOTIFY_INFORMATION*)_buffDirectoryChanges.QueryPtr();
|
||||
auto pNotificationInfo = (FILE_NOTIFY_INFORMATION*)_buffDirectoryChanges.QueryPtr();
|
||||
DBG_ASSERT(pNotificationInfo != NULL);
|
||||
|
||||
while (pNotificationInfo != NULL)
|
||||
|
|
@ -334,55 +260,58 @@ HRESULT
|
|||
}
|
||||
}
|
||||
|
||||
Finished:
|
||||
ReleaseSRWLockExclusive(&_srwLock);
|
||||
|
||||
if (fFileChanged)
|
||||
if (fFileChanged && !_lStopMonitorCalled)
|
||||
{
|
||||
//
|
||||
// so far we only monitoring app_offline
|
||||
//
|
||||
_pCallback();
|
||||
// Reference application before
|
||||
_pApplication->ReferenceApplication();
|
||||
RETURN_LAST_ERROR_IF(!QueueUserWorkItem(RunNotificationCallback, _pApplication.get(), WT_EXECUTEDEFAULT));
|
||||
}
|
||||
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
|
||||
FILE_WATCHER_ENTRY::Monitor(VOID)
|
||||
FILE_WATCHER::Monitor(VOID)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
DWORD cbRead;
|
||||
AcquireSRWLockExclusive(&_srwLock);
|
||||
ReferenceFileWatcherEntry();
|
||||
|
||||
ZeroMemory(&_overlapped, sizeof(_overlapped));
|
||||
|
||||
if (!ReadDirectoryChangesW(_hDirectory,
|
||||
RETURN_LAST_ERROR_IF(!ReadDirectoryChangesW(_hDirectory,
|
||||
_buffDirectoryChanges.QueryPtr(),
|
||||
_buffDirectoryChanges.QuerySize(),
|
||||
FALSE, // Watching sub dirs. Set to False now as only monitoring app_offline
|
||||
FILE_NOTIFY_VALID_MASK & ~FILE_NOTIFY_CHANGE_LAST_ACCESS,
|
||||
&cbRead,
|
||||
&_overlapped,
|
||||
NULL))
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
DereferenceFileWatcherEntry();
|
||||
}
|
||||
NULL));
|
||||
|
||||
// Check if file exist because ReadDirectoryChangesW would not fire events for existing files
|
||||
if (GetFileAttributes(_strFullName.QueryStr()) != INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
PostQueuedCompletionStatus(_pFileMonitor->QueryCompletionPort(), 0, 0, &_overlapped);
|
||||
PostQueuedCompletionStatus(m_hCompletionPort, 0, 0, &_overlapped);
|
||||
}
|
||||
|
||||
ReleaseSRWLockExclusive(&_srwLock);
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
VOID
|
||||
FILE_WATCHER_ENTRY::StopMonitor(VOID)
|
||||
FILE_WATCHER::StopMonitor()
|
||||
{
|
||||
//
|
||||
// Flag that monitoring is being stopped so that
|
||||
|
|
@ -390,123 +319,8 @@ FILE_WATCHER_ENTRY::StopMonitor(VOID)
|
|||
// can be ignored
|
||||
//
|
||||
InterlockedExchange(&_lStopMonitorCalled, 1);
|
||||
MarkEntryInValid();
|
||||
if (_hDirectory != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
AcquireSRWLockExclusive(&_srwLock);
|
||||
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;
|
||||
// signal the file watch thread to exit
|
||||
PostQueuedCompletionStatus(m_hCompletionPort, 0, FILE_WATCHER_SHUTDOWN_KEY, NULL);
|
||||
// Release application reference
|
||||
_pApplication.reset(nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@
|
|||
|
||||
#include <Windows.h>
|
||||
#include <functional>
|
||||
#include "iapplication.h"
|
||||
#include "HandleWrapper.h"
|
||||
|
||||
#define FILE_WATCHER_SHUTDOWN_KEY (ULONG_PTR)(-1)
|
||||
#define FILE_WATCHER_ENTRY_BUFFER_SIZE 4096
|
||||
#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{
|
||||
public:
|
||||
|
|
@ -20,110 +22,36 @@ public:
|
|||
|
||||
~FILE_WATCHER();
|
||||
|
||||
HRESULT Create();
|
||||
|
||||
HANDLE
|
||||
QueryCompletionPort(
|
||||
VOID
|
||||
) const
|
||||
{
|
||||
return m_hCompletionPort;
|
||||
}
|
||||
HRESULT Create(
|
||||
_In_ PCWSTR pszDirectoryToMonitor,
|
||||
_In_ PCWSTR pszFileNameToMonitor,
|
||||
_In_ AppOfflineTrackingApplication *pApplication
|
||||
);
|
||||
|
||||
static
|
||||
DWORD
|
||||
WINAPI ChangeNotificationThread(LPVOID);
|
||||
|
||||
static
|
||||
void
|
||||
WINAPI FileWatcherCompletionRoutine
|
||||
(
|
||||
DWORD dwCompletionStatus,
|
||||
DWORD cbCompletion,
|
||||
OVERLAPPED * pOverlapped
|
||||
);
|
||||
DWORD
|
||||
WINAPI RunNotificationCallback(LPVOID);
|
||||
|
||||
private:
|
||||
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 HandleChangeCompletion(DWORD cbCompletion);
|
||||
|
||||
HRESULT Monitor();
|
||||
|
||||
VOID StopMonitor();
|
||||
|
||||
HRESULT
|
||||
HandleChangeCompletion(
|
||||
_In_ DWORD dwCompletionStatus,
|
||||
_In_ DWORD cbCompletion
|
||||
);
|
||||
void StopMonitor();
|
||||
|
||||
private:
|
||||
virtual ~FILE_WATCHER_ENTRY();
|
||||
HandleWrapper<NullHandleTraits> m_hCompletionPort;
|
||||
HandleWrapper<NullHandleTraits> m_hChangeNotificationThread;
|
||||
HandleWrapper<NullHandleTraits> _hDirectory;
|
||||
volatile BOOL m_fThreadExit;
|
||||
|
||||
DWORD _dwSignature;
|
||||
BUFFER _buffDirectoryChanges;
|
||||
HANDLE _hImpersonationToken;
|
||||
HANDLE _hDirectory;
|
||||
FILE_WATCHER* _pFileMonitor;
|
||||
STRU _strFileName;
|
||||
STRU _strDirectoryName;
|
||||
STRU _strFullName;
|
||||
LONG _lStopMonitorCalled;
|
||||
mutable LONG _cRefs;
|
||||
BOOL _fIsValid;
|
||||
SRWLOCK _srwLock;
|
||||
std::function<void()> _pCallback;
|
||||
};
|
||||
|
||||
|
||||
struct FILE_WATCHER_ENTRY_DELETER
|
||||
{
|
||||
void operator()(FILE_WATCHER_ENTRY* entry) const
|
||||
{
|
||||
entry->DereferenceFileWatcherEntry();
|
||||
}
|
||||
LONG _lStopMonitorCalled {};
|
||||
OVERLAPPED _overlapped;
|
||||
std::unique_ptr<AppOfflineTrackingApplication, IAPPLICATION_DELETER> _pApplication;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
|||
/// </summary>
|
||||
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 _retryDelay = TimeSpan.FromMilliseconds(200);
|
||||
private readonly ServerManager _serverManager = new ServerManager();
|
||||
|
|
@ -106,7 +108,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
|||
_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
|
||||
// is starting up returns the COMException
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
|
|||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
|
||||
// send first request and add app_offline while app is starting
|
||||
var runningTask = AssertAppOffline(deploymentResult);
|
||||
|
||||
|
|
@ -220,14 +219,10 @@ namespace Microsoft.AspNetCore.Server.IIS.FunctionalTests.Inprocess
|
|||
{
|
||||
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.
|
||||
response = await deploymentResult.HttpClient.GetAsync("HelloWorld");
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Assert.Equal(HttpStatusCode.ServiceUnavailable, response.StatusCode);
|
||||
|
|
|
|||
Loading…
Reference in New Issue