Remove extra logic from file watcher (#1107)

This commit is contained in:
Pavel Krymets 2018-07-30 14:37:33 -07:00 committed by GitHub
parent 7a4a945fd6
commit 5d4fedaeac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 180 additions and 423 deletions

View File

@ -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

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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)
{

View File

@ -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;

View File

@ -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();

View File

@ -56,7 +56,6 @@ private:
VOID SetWebsocketStatus(IHttpContext *pHttpContext);
PROCESS_MANAGER * m_pProcessManager;
SRWLOCK m_srwLock;
IHttpServer *m_pHttpServer;
WEBSOCKET_STATUS m_fWebSocketSupported;

View File

@ -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,

View File

@ -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;
};

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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

View File

@ -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);