667 lines
15 KiB
C++
667 lines
15 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name :
|
|
wpcontext.cxx
|
|
|
|
Abstract:
|
|
This module defines the member functions of the WP_CONTEXT.
|
|
The WP_CONTEXT object embodies an instance of the Worker process
|
|
object. It contains a completion port, pool of worker threads,
|
|
pool of worker requests, a data channel for the worker process, etc.
|
|
It is responsible for setting up the context for processing requests
|
|
and handles delegating the processing of requests.
|
|
|
|
NYI: In the future we should be able to run WP_CONTEXT object as
|
|
a COM+ object and be run standalone using a hosting exe.
|
|
|
|
Author:
|
|
|
|
Murali R. Krishnan ( MuraliK ) 17-Nov-1998
|
|
|
|
Project:
|
|
|
|
IIS Worker Process
|
|
|
|
--*/
|
|
|
|
|
|
/************************************************************
|
|
* Include Headers
|
|
************************************************************/
|
|
|
|
# include "precomp.hxx"
|
|
# include <stdio.h>
|
|
# include <conio.h>
|
|
# include <io.h>
|
|
|
|
|
|
/**
|
|
* IdleTimeCheckCallback()
|
|
* Callback function provided for TimerQueue
|
|
* This function is called by NT thread pool every minute.
|
|
* Calls the timer's IncrementTick method.
|
|
*
|
|
* pvContext PVOID to WP_CONTEXT
|
|
* BOOLEAN not used.
|
|
*
|
|
*/
|
|
VOID
|
|
WINAPI
|
|
IdleTimeCheckCallback(
|
|
void * pvContext,
|
|
BOOLEAN
|
|
)
|
|
{
|
|
WP_IDLE_TIMER *pTimer = (WP_IDLE_TIMER *)pvContext;
|
|
|
|
DBGPRINTF((DBG_CONTEXT, "Check Idle Time Callback.\n"));
|
|
DBG_ASSERT( pTimer );
|
|
|
|
pTimer->IncrementTick();
|
|
}
|
|
|
|
/**
|
|
* WP_IDLE_TIMER constructor
|
|
*
|
|
*/
|
|
WP_IDLE_TIMER::WP_IDLE_TIMER(
|
|
ULONG IdleTime,
|
|
WP_CONTEXT* pContext
|
|
)
|
|
: m_BusySignal(0),
|
|
m_CurrentIdleTick(0),
|
|
m_IdleTime(IdleTime),
|
|
m_hIdleTimeExpiredTimer((HANDLE)NULL),
|
|
m_pContext(pContext)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* WP_IDLE_TIMER destructor
|
|
* Delete the timer from TimerQueue.
|
|
*
|
|
*/
|
|
WP_IDLE_TIMER::~WP_IDLE_TIMER(
|
|
void
|
|
)
|
|
{
|
|
// Cancel IdleTimeExpiredTimer
|
|
if (m_hIdleTimeExpiredTimer)
|
|
{
|
|
StopTimer();
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* WP_IDLE_TIMER Initialize routine
|
|
* Create timer for idle time check.
|
|
* The timer will wake up every minutes, see IdleTimeCheckCallback for detail.
|
|
*/
|
|
ULONG WP_IDLE_TIMER::Initialize(void)
|
|
{
|
|
BOOL fRet;
|
|
ULONG rc = NOERROR;
|
|
|
|
// IdleTime is stored as in minutes, 1 min = 60*1000 milliseconds.
|
|
fRet = CreateTimerQueueTimer(
|
|
&m_hIdleTimeExpiredTimer, // handle to the Timer
|
|
NULL, // Default Timer Queue
|
|
IdleTimeCheckCallback, // Callback function
|
|
this, // Context.
|
|
60000, // Due Time
|
|
60000, // Signal every minute
|
|
WT_EXECUTEINIOTHREAD
|
|
);
|
|
|
|
if (!fRet)
|
|
{
|
|
rc = GetLastError();
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Failed to create idle time expired timer. err %d\n", rc));
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* WP_IDLE_TIMER IncrementTick
|
|
*
|
|
* Gets called every minute. If we've been idle too long, signal
|
|
* the admin process.
|
|
*/
|
|
VOID
|
|
WP_IDLE_TIMER::IncrementTick(void)
|
|
{
|
|
ULONG BusySignal = m_BusySignal;
|
|
InterlockedIncrement((PLONG)&m_CurrentIdleTick);
|
|
m_BusySignal = 0;
|
|
|
|
if (!BusySignal && m_CurrentIdleTick >= m_IdleTime) {
|
|
SignalIdleTimeReached();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* SignalIdleTimeReached
|
|
* Sends a message to admin process.
|
|
*/
|
|
HRESULT WP_IDLE_TIMER::SignalIdleTimeReached(void)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Idle time reached, sent shutdown message to admin process.\n"));
|
|
|
|
return m_pContext->SendMsgToAdminProcess(IPM_WP_IDLE_TIME_REACHED);
|
|
}
|
|
|
|
/**
|
|
* StopTimer
|
|
* Stops the idle timer.
|
|
*/
|
|
VOID WP_IDLE_TIMER::StopTimer(void)
|
|
{
|
|
BOOL fRet;
|
|
|
|
DBG_ASSERT( m_hIdleTimeExpiredTimer );
|
|
|
|
fRet = DeleteTimerQueueTimer(NULL,
|
|
m_hIdleTimeExpiredTimer,
|
|
(HANDLE)-1
|
|
);
|
|
|
|
if (!fRet)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT, "Failed to delete Timer queue %08x\n",
|
|
GetLastError()));
|
|
}
|
|
m_hIdleTimeExpiredTimer = NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* OverlappedCompletionRoutine()
|
|
* Callback function provided in BindIoCompletionCallback.
|
|
* This function is called by NT thread pool.
|
|
*
|
|
* dwErrorCode Error Code
|
|
* dwNumberOfBytesTransfered Number of Bytes Transfered
|
|
* lpOverlapped Overlapped structure
|
|
*/
|
|
VOID
|
|
WINAPI
|
|
OverlappedCompletionRoutine(
|
|
DWORD dwErrorCode,
|
|
DWORD dwNumberOfBytesTransfered,
|
|
LPOVERLAPPED lpOverlapped
|
|
)
|
|
{
|
|
UL_NATIVE_REQUEST *pRequest = NULL;
|
|
|
|
if (lpOverlapped != NULL)
|
|
{
|
|
pRequest = CONTAINING_RECORD(lpOverlapped,
|
|
UL_NATIVE_REQUEST,
|
|
m_overlapped);
|
|
}
|
|
if ( pRequest != NULL)
|
|
{
|
|
pRequest->DoWork(
|
|
dwNumberOfBytesTransfered,
|
|
// BUGBUG: hack to work around thread
|
|
// pool nonsense.
|
|
RtlNtStatusToDosError(dwErrorCode),
|
|
lpOverlapped
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/************************************************************
|
|
* Functions
|
|
************************************************************/
|
|
|
|
|
|
WP_CONTEXT::WP_CONTEXT(void)
|
|
: m_hDoneEvent ( NULL),
|
|
m_ulAppPool (),
|
|
m_nreqpool (),
|
|
m_pConfigInfo ( NULL),
|
|
m_fShutdown ( FALSE),
|
|
m_pIdleTimer ( NULL)
|
|
{
|
|
IF_DEBUG( INIT_CLEAN) {
|
|
DBGPRINTF(( DBG_CONTEXT, "Initialized WP_CONTEXT(%08x)\n", this));
|
|
}
|
|
|
|
} // WP_CONTEXT::WP_CONTEXT()
|
|
|
|
|
|
WP_CONTEXT::~WP_CONTEXT(void)
|
|
{
|
|
IF_DEBUG( INIT_CLEAN) {
|
|
DBGPRINTF(( DBG_CONTEXT, "Destroying WP_CONTEXT(%08x)\n", this));
|
|
}
|
|
|
|
//Cleanup();
|
|
|
|
} // WP_CONTEXT::WP_CONTEXT()
|
|
|
|
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
Initializes the global state for the request processor.
|
|
The function initializes all the following components:
|
|
- Data Channel for UL
|
|
- Thread pool for
|
|
|
|
Arguments:
|
|
ConfigInfo - configuration information for this Worker process.
|
|
|
|
Returns:
|
|
ULONG
|
|
|
|
NYI: There are several other configuration paramaters that are important.
|
|
However at the present point, not all config parameters
|
|
are allowed in here.
|
|
|
|
--********************************************************************/
|
|
|
|
ULONG
|
|
WP_CONTEXT::Initialize(IN WP_CONFIG * pConfigInfo)
|
|
{
|
|
ULONG rc = NO_ERROR;
|
|
LPCWSTR pwszAppPoolName;
|
|
HRESULT hr = S_OK;
|
|
|
|
m_pConfigInfo = pConfigInfo;
|
|
|
|
pwszAppPoolName = m_pConfigInfo->QueryAppPoolName();
|
|
|
|
rc = m_ulAppPool.Initialize(pwszAppPoolName);
|
|
|
|
if (NO_ERROR != rc)
|
|
{
|
|
IF_DEBUG( ERROR)
|
|
{
|
|
DPERROR(( DBG_CONTEXT, rc,
|
|
"Failed to initialize AppPool\n"));
|
|
}
|
|
return (rc);
|
|
}
|
|
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "AppPool Initialized\n"));
|
|
}
|
|
|
|
//
|
|
// Initialize of the shutdown event
|
|
//
|
|
|
|
m_hDoneEvent = IIS_CREATE_EVENT(
|
|
"WP_CONTEXT::m_hDoneEvent", // name
|
|
&m_hDoneEvent, // address of storage loc
|
|
TRUE, // manual reset
|
|
FALSE // Initial State
|
|
);
|
|
|
|
if (m_hDoneEvent == NULL)
|
|
{
|
|
rc = GetLastError();
|
|
|
|
IF_DEBUG(ERROR)
|
|
{
|
|
DPERROR(( DBG_CONTEXT, rc,
|
|
"Failed to create DoneEvent.\n"
|
|
));
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
|
|
if (0 != m_pConfigInfo->QueryIdleTime())
|
|
{
|
|
m_pIdleTimer = new WP_IDLE_TIMER(m_pConfigInfo->QueryIdleTime(), this);
|
|
|
|
if (m_pIdleTimer)
|
|
{
|
|
rc = m_pIdleTimer->Initialize();
|
|
}
|
|
else
|
|
{
|
|
rc = ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
}
|
|
//
|
|
// Associate data channel and the WP_CONTEXT object with completion port
|
|
//
|
|
|
|
rc = BindIoCompletionCallback(
|
|
m_ulAppPool.GetHandle(), // UL handle
|
|
OverlappedCompletionRoutine,
|
|
0 );
|
|
|
|
if (!rc)
|
|
{
|
|
rc = GetLastError();
|
|
IF_DEBUG(ERROR)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to add App Pool handle to completion port. Error=0x%08x\n",
|
|
rc
|
|
));
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "Added Data channel to CP context\n"));
|
|
}
|
|
|
|
//
|
|
// Initialize IPM if requested to
|
|
//
|
|
if ( m_pConfigInfo->FRegisterWithWAS())
|
|
{
|
|
rc = m_WpIpm.Initialize(this);
|
|
|
|
if (NO_ERROR != rc)
|
|
{
|
|
IF_DEBUG( ERROR)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to initialize IPM. Error=0x%08x\n", rc
|
|
));
|
|
}
|
|
|
|
return (rc);
|
|
|
|
}
|
|
else
|
|
{
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "Initialized IPM\n"));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create a pool of worker requests
|
|
// NYI: Allow the worker requests limit to be configurable.
|
|
//
|
|
|
|
UL_NATIVE_REQUEST::SetRestartCount(m_pConfigInfo->QueryRestartCount());
|
|
|
|
rc = m_nreqpool.AddPoolItems( this,
|
|
(m_pConfigInfo->QueryRestartCount() == 0 ||
|
|
(m_pConfigInfo->QueryRestartCount() >=
|
|
NUM_INITIAL_REQUEST_POOL_ITEMS)) ?
|
|
NUM_INITIAL_REQUEST_POOL_ITEMS :
|
|
m_pConfigInfo->QueryRestartCount()
|
|
);
|
|
if (NO_ERROR != rc)
|
|
{
|
|
IF_DEBUG( ERROR)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to add pool of worker requests. Error=%08x\n", rc
|
|
));
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
|
|
IF_DEBUG( TRACE)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT, "Created UL_NATIVE_REQUEST pool\n"));
|
|
}
|
|
|
|
//
|
|
// Set the window title to something nice when we're running
|
|
// under the debugger.
|
|
//
|
|
|
|
if (IsDebuggerPresent())
|
|
{
|
|
STRU strTitle;
|
|
HRESULT hr = NO_ERROR;
|
|
WCHAR buffer[sizeof("iiswp[1234567890] - ")];
|
|
WCHAR buffer2[sizeof(" - wp1234567890 - mm/dd hh:mm:ss")];
|
|
|
|
wsprintf( buffer, L"iiswp[%lu] - ", GetCurrentProcessId() );
|
|
hr = strTitle.Append( buffer );
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = strTitle.Append( m_pConfigInfo->QueryAppPoolName() );
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LARGE_INTEGER sysTime;
|
|
LARGE_INTEGER localTime;
|
|
TIME_FIELDS fields;
|
|
|
|
NtQuerySystemTime( &sysTime );
|
|
RtlSystemTimeToLocalTime( &sysTime, &localTime );
|
|
RtlTimeToTimeFields( &localTime, &fields );
|
|
|
|
wsprintf(
|
|
buffer2,
|
|
L" - wp%lu - %02u/%02u %02u:%02u:%02u",
|
|
m_pConfigInfo->QueryNamedPipeId(),
|
|
fields.Month,
|
|
fields.Day,
|
|
fields.Hour,
|
|
fields.Minute,
|
|
fields.Second
|
|
);
|
|
|
|
hr = strTitle.Append( buffer2 );
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SetConsoleTitleW( strTitle.QueryStr() );
|
|
}
|
|
}
|
|
|
|
return (rc);
|
|
|
|
} // WP_CONTEXT::Initialize()
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
This function cleans up the sub-objects inside WP_CONTEXT.
|
|
It first forces a close of the handle and then waits for all
|
|
the objects to drain out.
|
|
|
|
NYI: Do two-phase shutdown logic
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
WIn32 Error
|
|
|
|
--********************************************************************/
|
|
|
|
BOOL
|
|
WP_CONTEXT::IndicateShutdown(SHUTDOWN_REASON reason)
|
|
{
|
|
m_ShutdownReason = reason;
|
|
|
|
if (FALSE == InterlockedCompareExchange((LONG *)&m_fShutdown, TRUE, FALSE))
|
|
{
|
|
return SetEvent(m_hDoneEvent);
|
|
}
|
|
else
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
This function cleans up the sub-objects inside WP_CONTEXT.
|
|
It first forces a close of the handle and then waits for all
|
|
the objects to drain out.
|
|
|
|
NYI: Do two-phase shutdown logic
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
WIn32 Error
|
|
|
|
--********************************************************************/
|
|
|
|
ULONG
|
|
WP_CONTEXT::Shutdown(void)
|
|
{
|
|
ULONG rc = NOERROR;
|
|
|
|
|
|
if (m_pIdleTimer)
|
|
{
|
|
delete m_pIdleTimer;
|
|
m_pIdleTimer = NULL;
|
|
}
|
|
|
|
//
|
|
// Cleanup the IPM.
|
|
//
|
|
|
|
if ( m_pConfigInfo->FRegisterWithWAS())
|
|
{
|
|
rc = m_WpIpm.Terminate();
|
|
|
|
if (NO_ERROR != rc)
|
|
{
|
|
IF_DEBUG( ERROR)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Counldn't shut down IPM. Error=0x%08x\n", rc
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
This function cleans up the sub-objects inside WP_CONTEXT.
|
|
It first forces a close of the handle and then waits for all
|
|
the objects to drain out.
|
|
|
|
NYI: Do two-phase shutdown logic
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
Win32 Error
|
|
|
|
--********************************************************************/
|
|
|
|
ULONG
|
|
WP_CONTEXT::Cleanup(void)
|
|
{
|
|
ULONG rc;
|
|
|
|
rc = m_ulAppPool.Cleanup();
|
|
|
|
if (NO_ERROR != rc)
|
|
{
|
|
IF_DEBUG( ERROR)
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed in UL_APP_POOL::CloseHandle(). Error=%08x\n",
|
|
rc));
|
|
}
|
|
|
|
return (rc);
|
|
}
|
|
|
|
|
|
rc = m_nreqpool.ReleaseAllWorkerRequests();
|
|
|
|
if (NO_ERROR != rc)
|
|
{
|
|
IF_DEBUG(ERROR)
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Cleanup Global State for Worker Requests failed; Error=%08x\n",
|
|
rc));
|
|
}
|
|
return (rc);
|
|
}
|
|
|
|
|
|
//
|
|
// Cleanup the Shutdown Event.
|
|
//
|
|
|
|
CloseHandle(m_hDoneEvent);
|
|
m_hDoneEvent = FALSE;
|
|
|
|
|
|
return rc;
|
|
|
|
} // WP_CONTEXT::Cleanup()
|
|
|
|
/********************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
None
|
|
|
|
Returns:
|
|
Win32 Error
|
|
--********************************************************************/
|
|
|
|
ULONG
|
|
WP_CONTEXT::RunMainThreadLoop(void)
|
|
{
|
|
do
|
|
{
|
|
DWORD result;
|
|
|
|
result = WaitForSingleObject( m_hDoneEvent, INFINITE );
|
|
DBG_ASSERT( result == WAIT_OBJECT_0 );
|
|
|
|
} while ( !m_fShutdown);
|
|
|
|
//
|
|
// NYI: cleanup happens after we return from this function.
|
|
//
|
|
|
|
return (NO_ERROR);
|
|
|
|
} // WP_CONTEXT::RunMainThreadLoop()
|
|
|
|
|
|
/************************ End of File ***********************/
|
|
|