Fix shutdown race (#1031)

This commit is contained in:
Pavel Krymets 2018-07-10 10:18:32 -07:00 committed by GitHub
parent f7936ac062
commit 86fed4de5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 114 additions and 180 deletions

View File

@ -3,6 +3,7 @@
#pragma once
#include <memory>
#include "irequesthandler.h"
enum APPLICATION_STATUS
@ -54,3 +55,18 @@ public:
_In_ IHttpContext *pHttpContext,
_Out_ IREQUEST_HANDLER **pRequestHandler) = 0;
};
struct IAPPLICATION_DELETER
{
void operator ()(IAPPLICATION* application) const
{
application->DereferenceApplication();
}
};
template< class APPLICATION >
std::unique_ptr<APPLICATION, IAPPLICATION_DELETER> ReferenceApplication(APPLICATION* application)
{
application->ReferenceApplication();
return std::unique_ptr<APPLICATION, IAPPLICATION_DELETER>(application);
};

View File

@ -208,98 +208,11 @@ IN_PROCESS_APPLICATION::ShutDownInternal()
s_Application = NULL;
}
REQUEST_NOTIFICATION_STATUS
IN_PROCESS_APPLICATION::OnAsyncCompletion(
DWORD cbCompletion,
HRESULT hrCompletionStatus,
IN_PROCESS_HANDLER* pInProcessHandler
)
{
REQUEST_NOTIFICATION_STATUS dwRequestNotificationStatus = RQ_NOTIFICATION_CONTINUE;
ReferenceApplication();
if (pInProcessHandler->QueryIsManagedRequestComplete())
{
// means PostCompletion has been called and this is the associated callback.
dwRequestNotificationStatus = pInProcessHandler->QueryAsyncCompletionStatus();
}
else if (m_fBlockCallbacksIntoManaged)
{
// this can potentially happen in ungraceful shutdown.
// Or something really wrong happening with async completions
// At this point, managed is in a shutting down state and we cannot send a request to it.
pInProcessHandler->QueryHttpContext()->GetResponse()->SetStatus(503,
"Server has been shutdown",
0,
(ULONG)HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
dwRequestNotificationStatus = RQ_NOTIFICATION_FINISH_REQUEST;
}
else
{
// Call the managed handler for async completion.
dwRequestNotificationStatus = m_AsyncCompletionHandler(pInProcessHandler->QueryManagedHttpContext(), hrCompletionStatus, cbCompletion);
}
DereferenceApplication();
return dwRequestNotificationStatus;
}
REQUEST_NOTIFICATION_STATUS
IN_PROCESS_APPLICATION::OnExecuteRequest(
_In_ IHttpContext* pHttpContext,
_In_ IN_PROCESS_HANDLER* pInProcessHandler
)
{
REQUEST_NOTIFICATION_STATUS dwRequestNotificationStatus = RQ_NOTIFICATION_CONTINUE;
PFN_REQUEST_HANDLER pRequestHandler = NULL;
ReferenceApplication();
pRequestHandler = m_RequestHandler;
if (pRequestHandler == NULL)
{
//
// return error as the application did not register callback
//
if (ANCMEvents::ANCM_EXECUTE_REQUEST_FAIL::IsEnabled(pHttpContext->GetTraceContext()))
{
ANCMEvents::ANCM_EXECUTE_REQUEST_FAIL::RaiseEvent(pHttpContext->GetTraceContext(),
NULL,
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
pHttpContext->GetResponse()->SetStatus(500,
"Internal Server Error",
0,
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
dwRequestNotificationStatus = RQ_NOTIFICATION_FINISH_REQUEST;
}
else if (m_status != APPLICATION_STATUS::RUNNING || m_fBlockCallbacksIntoManaged)
{
pHttpContext->GetResponse()->SetStatus(503,
"Server is currently shutting down.",
0,
(ULONG)HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
dwRequestNotificationStatus = RQ_NOTIFICATION_FINISH_REQUEST;
}
else
{
dwRequestNotificationStatus = pRequestHandler(pInProcessHandler, m_RequestHandlerContext);
}
DereferenceApplication();
return dwRequestNotificationStatus;
}
VOID
IN_PROCESS_APPLICATION::SetCallbackHandles(
_In_ PFN_REQUEST_HANDLER request_handler,
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
_In_ PFN_MANAGED_CONTEXT_HANDLER async_completion_handler,
_In_ PFN_ASYNC_COMPLETION_HANDLER async_completion_handler,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
)
@ -673,7 +586,7 @@ IN_PROCESS_APPLICATION::CreateHandler(
HRESULT hr = S_OK;
IREQUEST_HANDLER* pHandler = NULL;
pHandler = new IN_PROCESS_HANDLER(pHttpContext, this);
pHandler = new IN_PROCESS_HANDLER(::ReferenceApplication(this), pHttpContext, m_RequestHandler, m_RequestHandlerContext, m_AsyncCompletionHandler);
if (pHandler == NULL)
{

View File

@ -4,13 +4,13 @@
#pragma once
#include "InProcessApplicationBase.h"
#include "inprocesshandler.h"
#include "requesthandler_config.h"
#include "IOutputManager.h"
class IN_PROCESS_HANDLER;
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_REQUEST_HANDLER) (IN_PROCESS_HANDLER* pInProcessHandler, void* pvRequestHandlerContext);
typedef BOOL(WINAPI * PFN_SHUTDOWN_HANDLER) (void* pvShutdownHandlerContext);
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_MANAGED_CONTEXT_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
typedef REQUEST_NOTIFICATION_STATUS(WINAPI * PFN_ASYNC_COMPLETION_HANDLER)(void *pvManagedHttpContext, HRESULT hrCompletionStatus, DWORD cbCompletion);
class IN_PROCESS_APPLICATION : public InProcessApplicationBase
{
@ -32,7 +32,7 @@ public:
SetCallbackHandles(
_In_ PFN_REQUEST_HANDLER request_callback,
_In_ PFN_SHUTDOWN_HANDLER shutdown_callback,
_In_ PFN_MANAGED_CONTEXT_HANDLER managed_context_callback,
_In_ PFN_ASYNC_COMPLETION_HANDLER managed_context_callback,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
);
@ -60,20 +60,6 @@ public:
HRESULT hr
);
REQUEST_NOTIFICATION_STATUS
OnAsyncCompletion(
DWORD cbCompletion,
HRESULT hrCompletionStatus,
IN_PROCESS_HANDLER* pInProcessHandler
);
REQUEST_NOTIFICATION_STATUS
OnExecuteRequest
(
IHttpContext* pHttpContext,
IN_PROCESS_HANDLER* pInProcessHandler
);
VOID
StopCallsIntoManaged(
VOID
@ -117,6 +103,12 @@ public:
return m_pConfig.get();
}
bool
QueryBlockCallbacksIntoManaged() const
{
return m_fBlockCallbacksIntoManaged;
}
private:
IHttpServer & m_pHttpServer;
@ -132,7 +124,7 @@ private:
PFN_SHUTDOWN_HANDLER m_ShutdownHandler;
VOID* m_ShutdownHandlerContext;
PFN_MANAGED_CONTEXT_HANDLER m_AsyncCompletionHandler;
PFN_ASYNC_COMPLETION_HANDLER m_AsyncCompletionHandler;
// The event that gets triggered when managed initialization is complete
HANDLE m_pInitalizeEvent;

View File

@ -10,20 +10,22 @@
ALLOC_CACHE_HANDLER * IN_PROCESS_HANDLER::sm_pAlloc = NULL;
IN_PROCESS_HANDLER::IN_PROCESS_HANDLER(
_In_ std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> pApplication,
_In_ IHttpContext *pW3Context,
_In_ IN_PROCESS_APPLICATION *pApplication
): m_pW3Context(pW3Context),
m_requestNotificationStatus(RQ_NOTIFICATION_PENDING),
m_pApplication(pApplication),
m_fManagedRequestComplete(FALSE)
_In_ PFN_REQUEST_HANDLER pRequestHandler,
_In_ void * pRequestHandlerContext,
_In_ PFN_ASYNC_COMPLETION_HANDLER pAsyncCompletion
): m_pManagedHttpContext(nullptr),
m_requestNotificationStatus(RQ_NOTIFICATION_PENDING),
m_fManagedRequestComplete(FALSE),
m_pW3Context(pW3Context),
m_pApplication(std::move(pApplication)),
m_pRequestHandler(pRequestHandler),
m_pRequestHandlerContext(pRequestHandlerContext),
m_pAsyncCompletionHandler(pAsyncCompletion)
{
}
IN_PROCESS_HANDLER::~IN_PROCESS_HANDLER()
{
//todo
}
__override
REQUEST_NOTIFICATION_STATUS
IN_PROCESS_HANDLER::OnExecuteRequestHandler()
@ -38,8 +40,32 @@ IN_PROCESS_HANDLER::OnExecuteRequestHandler()
L"InProcess Application");
}
//SetHttpSysDisconnectCallback();
return m_pApplication->OnExecuteRequest(m_pW3Context, this);
if (m_pRequestHandler == NULL)
{
//
// return error as the application did not register callback
//
if (ANCMEvents::ANCM_EXECUTE_REQUEST_FAIL::IsEnabled(m_pW3Context->GetTraceContext()))
{
ANCMEvents::ANCM_EXECUTE_REQUEST_FAIL::RaiseEvent(m_pW3Context->GetTraceContext(),
NULL,
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
}
m_pW3Context->GetResponse()->SetStatus(500,
"Internal Server Error",
0,
(ULONG)E_APPLICATION_ACTIVATION_EXEC_FAILURE);
return RQ_NOTIFICATION_FINISH_REQUEST;
}
else if (m_pApplication->QueryStatus() != APPLICATION_STATUS::RUNNING || m_pApplication->
QueryBlockCallbacksIntoManaged())
{
return ServerShutdownMessage();
}
return m_pRequestHandler(this, m_pRequestHandlerContext);
}
__override
@ -49,9 +75,28 @@ IN_PROCESS_HANDLER::OnAsyncCompletion(
HRESULT hrCompletionStatus
)
{
// OnAsyncCompletion must call into the application if there was a error. We will redo calls
// to Read/Write if we called cancelIo on the IHttpContext.
return m_pApplication->OnAsyncCompletion(cbCompletion, hrCompletionStatus, this);
if (m_fManagedRequestComplete)
{
// means PostCompletion has been called and this is the associated callback.
return m_requestNotificationStatus;
}
if (m_pApplication->QueryBlockCallbacksIntoManaged())
{
// this can potentially happen in ungraceful shutdown.
// Or something really wrong happening with async completions
// At this point, managed is in a shutting down state and we cannot send a request to it.
return ServerShutdownMessage();
}
// Call the managed handler for async completion.
return m_pAsyncCompletionHandler(m_pManagedHttpContext, hrCompletionStatus, cbCompletion);
}
REQUEST_NOTIFICATION_STATUS IN_PROCESS_HANDLER::ServerShutdownMessage() const
{
m_pW3Context->GetResponse()->SetStatus(503, "Server has been shutdown", 0,
(ULONG)HRESULT_FROM_WIN32(ERROR_SHUTDOWN_IN_PROGRESS));
return RQ_NOTIFICATION_FINISH_REQUEST;
}
VOID
@ -60,31 +105,6 @@ IN_PROCESS_HANDLER::TerminateRequest(
)
{
UNREFERENCED_PARAMETER(fClientInitiated);
//todo
}
PVOID
IN_PROCESS_HANDLER::QueryManagedHttpContext(
VOID
)
{
return m_pManagedHttpContext;
}
BOOL
IN_PROCESS_HANDLER::QueryIsManagedRequestComplete(
VOID
)
{
return m_fManagedRequestComplete;
}
IHttpContext*
IN_PROCESS_HANDLER::QueryHttpContext(
VOID
)
{
return m_pW3Context;
}
VOID
@ -95,14 +115,6 @@ IN_PROCESS_HANDLER::IndicateManagedRequestComplete(
m_fManagedRequestComplete = TRUE;
}
REQUEST_NOTIFICATION_STATUS
IN_PROCESS_HANDLER::QueryAsyncCompletionStatus(
VOID
)
{
return m_requestNotificationStatus;
}
VOID
IN_PROCESS_HANDLER::SetAsyncCompletionStatus(
REQUEST_NOTIFICATION_STATUS requestNotificationStatus
@ -165,7 +177,7 @@ HRESULT
}
hr = sm_pAlloc->Initialize(sizeof(IN_PROCESS_HANDLER),
64); // nThreshold
64); // nThreshold
Finished:
if (FAILED(hr))

View File

@ -3,7 +3,11 @@
#pragma once
#include "precomp.hxx"
#include "requesthandler.h"
#include <memory>
#include "iapplication.h"
#include "inprocessapplication.h"
class IN_PROCESS_APPLICATION;
@ -11,10 +15,13 @@ class IN_PROCESS_HANDLER : public REQUEST_HANDLER
{
public:
IN_PROCESS_HANDLER(
_In_ std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> pApplication,
_In_ IHttpContext *pW3Context,
_In_ IN_PROCESS_APPLICATION *pApplication);
_In_ PFN_REQUEST_HANDLER pRequestHandler,
_In_ void * pRequestHandlerContext,
_In_ PFN_ASYNC_COMPLETION_HANDLER pAsyncCompletion);
~IN_PROCESS_HANDLER() override;
~IN_PROCESS_HANDLER() override = default;
__override
REQUEST_NOTIFICATION_STATUS
@ -32,37 +39,25 @@ public:
TerminateRequest(
bool fClientInitiated
) override;
PVOID
QueryManagedHttpContext(
IHttpContext*
QueryHttpContext(
VOID
);
) const
{
return m_pW3Context;
}
VOID
SetManagedHttpContext(
PVOID pManagedHttpContext
);
IHttpContext*
QueryHttpContext(
VOID
);
BOOL
QueryIsManagedRequestComplete(
VOID
);
VOID
IndicateManagedRequestComplete(
VOID
);
REQUEST_NOTIFICATION_STATUS
QueryAsyncCompletionStatus(
VOID
);
VOID
SetAsyncCompletionStatus(
REQUEST_NOTIFICATION_STATUS requestNotificationStatus
@ -76,12 +71,18 @@ public:
HRESULT
StaticInitialize(VOID);
private:
REQUEST_NOTIFICATION_STATUS
ServerShutdownMessage() const;
PVOID m_pManagedHttpContext;
BOOL m_fManagedRequestComplete;
REQUEST_NOTIFICATION_STATUS m_requestNotificationStatus;
IHttpContext* m_pW3Context;
IN_PROCESS_APPLICATION* m_pApplication;
std::unique_ptr<IN_PROCESS_APPLICATION, IAPPLICATION_DELETER> m_pApplication;
PFN_REQUEST_HANDLER m_pRequestHandler;
void* m_pRequestHandlerContext;
PFN_ASYNC_COMPLETION_HANDLER m_pAsyncCompletionHandler;
static ALLOC_CACHE_HANDLER * sm_pAlloc;
};

View File

@ -14,7 +14,7 @@ register_callbacks(
_In_ IN_PROCESS_APPLICATION* pInProcessApplication,
_In_ PFN_REQUEST_HANDLER request_handler,
_In_ PFN_SHUTDOWN_HANDLER shutdown_handler,
_In_ PFN_MANAGED_CONTEXT_HANDLER async_completion_handler,
_In_ PFN_ASYNC_COMPLETION_HANDLER async_completion_handler,
_In_ VOID* pvRequstHandlerContext,
_In_ VOID* pvShutdownHandlerContext
)