|
|
//+--------------------------------------------------------------------------
//
// Copyright (c) 1997-1999 Microsoft Corporation
//
// File: jobmgr.cpp
//
// Contents: Job scheduler
//
// History:
//
//---------------------------------------------------------------------------
#include "pch.cpp"
#include <process.h>
#include "server.h"
#include "jobmgr.h"
#include "debug.h"
//------------------------------------------------------------
//
//
CLASS_PRIVATE BOOL CWorkManager::SignalJobRunning( IN CWorkObject *ptr ) /*++
Abstract:
Class private routine for work object to 'signal' work manger it has started processing.
Parameter:
ptr : Pointer to CWorkObject that is ready to run.
Returns:
TRUE/FALSE
--*/ { DWORD dwStatus = ERROR_SUCCESS; INPROCESSINGJOBLIST::iterator it;
if(ptr != NULL) { m_InProcessingListLock.Lock();
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("WorkManager : SignalJobRunning() Job %p ...\n"), ptr );
//
// find our pointer in processing list
//
it = m_InProcessingList.find(ptr);
if(it != m_InProcessingList.end()) { // TODO - make processing thread handle a list.
if((*it).second.m_hThread == NULL) { HANDLE hHandle; BOOL bSuccess;
bSuccess = DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &hHandle, DUPLICATE_SAME_ACCESS, FALSE, 0 );
if(bSuccess == FALSE) { //
// non-critical error, if we fail, we won't be able to
// cancel our rpc call.
//
SetLastError(dwStatus = GetLastError());
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("WorkManager : SignalJobRunning() duplicate handle return %d...\n"), dwStatus ); } else { //
// set processing thread handle of job.
//
(*it).second.m_hThread = hHandle; } } } else { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("WorkManager : SignalJobRunning can't find job %p in processing list...\n"), ptr );
//
// timing problem, job might be re-scheduled and actually execute before we have
// time to remove from our in-processing list.
//
//TLSASSERT(FALSE);
}
m_InProcessingListLock.UnLock(); } else { SetLastError(dwStatus = ERROR_INVALID_DATA); }
return dwStatus; }
//----------------------------------------------------------------
//
CLASS_PRIVATE void CWorkManager::CancelInProcessingJob() /*++
Abstract:
Class private : cancel job currently in processing state, only invoked at the time of service shutdown.
Parameter:
None.
Return:
None.
--*/ { INPROCESSINGJOBLIST::iterator it;
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("WorkManager : CancelInProcessingJob...\n") ); m_InProcessingListLock.Lock();
for(it = m_InProcessingList.begin(); it != m_InProcessingList.end(); it++ ) { if((*it).second.m_hThread != NULL) { // cancel everything and ignore error.
(VOID)RpcCancelThread((*it).second.m_hThread); } }
m_InProcessingListLock.UnLock(); return; }
//------------------------------------------------------------
//
//
CLASS_PRIVATE DWORD CWorkManager::AddJobToProcessingList( IN CWorkObject *ptr ) /*++
Abstract:
Class private, move job from wait queue to in-process queue.
Parameter:
ptr : Pointer to job.
Parameter:
ERROR_SUCESS or error code.
--*/ { DWORD dwStatus = ERROR_SUCCESS; INPROCESSINGJOBLIST::iterator it; WorkMangerInProcessJob job;
if(ptr == NULL) { SetLastError(dwStatus = ERROR_INVALID_DATA); } else { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("WorkManager : Add Job <%s> to processing list...\n"), ptr->GetJobDescription() );
m_InProcessingListLock.Lock();
it = m_InProcessingList.find(ptr); if(it != m_InProcessingList.end()) { // increase the reference counter.
InterlockedIncrement(&((*it).second.m_refCounter)); } else { job.m_refCounter = 1; job.m_hThread = NULL; // job not run yet.
m_InProcessingList[ptr] = job; }
ResetEvent(m_hJobInProcessing); m_InProcessingListLock.UnLock(); }
return dwStatus; }
//------------------------------------------------------------
//
//
CLASS_PRIVATE DWORD CWorkManager::RemoveJobFromProcessingList( IN CWorkObject *ptr ) /*++
Abstract:
Class private, remove a job from in-processing list.
Parameter:
ptr : Pointer to job to be removed from list.
Returns:
ERROR_SUCCESS or error code. --*/ { DWORD dwStatus = ERROR_SUCCESS; INPROCESSINGJOBLIST::iterator it;
if(ptr != NULL) { m_InProcessingListLock.Lock();
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("WorkManager : RemoveJobFromProcessingList Job %p from processing list...\n"), ptr );
it = m_InProcessingList.find(ptr);
if(it != m_InProcessingList.end()) { // decrease the reference counter
InterlockedDecrement(&((*it).second.m_refCounter));
if((*it).second.m_refCounter <= 0) { // close thread handle.
if((*it).second.m_hThread != NULL) { CloseHandle((*it).second.m_hThread); }
m_InProcessingList.erase(it); } else { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("WorkManager : RemoveJobFromProcessingList job %p reference counter = %d...\n"), ptr, (*it).second ); } } else { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("WorkManager : RemoveJobFromProcessingList can't find job %p in processing list...\n"), ptr );
//
// timing problem, job might be re-scheduled and actually execute before we have
// time to remove from our in-processing list.
//
//TLSASSERT(FALSE);
}
if(m_InProcessingList.empty() == TRUE) { //
// Inform Work Manager that no job is in processing.
//
SetEvent(m_hJobInProcessing); }
m_InProcessingListLock.UnLock(); } else { SetLastError(dwStatus = ERROR_INVALID_DATA); }
return dwStatus; } //------------------------------------------------------------
//
//
CLASS_PRIVATE BOOL CWorkManager::WaitForObjectOrShutdown( IN HANDLE hHandle ) /*++
Abstract:
Class private, Wait for a sync. handle or service shutdown event.
Parameter:
hHandle : handle to wait for,
Returns:
TRUE if sucessful, FALSE if service shutdown or error. --*/ { HANDLE handles[] = {hHandle, m_hShutdown}; DWORD dwStatus;
dwStatus = WaitForMultipleObjects( sizeof(handles)/sizeof(handles[0]), handles, FALSE, INFINITE );
return (dwStatus == WAIT_OBJECT_0); }
//------------------------------------------------------------
//
//
CLASS_PRIVATE DWORD CWorkManager::RunJob( IN CWorkObject* ptr, IN BOOL bImmediate ) /*++
Abstract:
Process a job object via QueueUserWorkItem() Win32 API subject to our max. concurrent job limitation.
Parameter:
ptr : Pointer to CWorkObject. bImmediate : TRUE if job must be process immediately, FALSE otherwise.
Returns:
ERROR_SUCCESS or Error code.
--*/ { BOOL bSuccess; DWORD dwStatus = ERROR_SUCCESS;
if(ptr != NULL) { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("WorkManager : RunJob <%s>...\n"), ptr->GetJobDescription() );
//
// Wait if we exceed max. concurrent job
//
bSuccess = (bImmediate) ? bImmediate : m_hMaxJobLock.AcquireEx(m_hShutdown);
if(bSuccess == TRUE) { DWORD dwFlag; DWORD dwJobRunningAttribute;
dwJobRunningAttribute = ptr->GetJobRunningAttribute(); dwFlag = TranslateJobRunningAttributeToThreadPoolFlag( dwJobRunningAttribute );
dwStatus = AddJobToProcessingList(ptr); if(dwStatus == ERROR_SUCCESS) { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("RunJob() : queuing user work item %p...\n"), ptr );
// need immediate attention.
bSuccess = QueueUserWorkItem( CWorkManager::ExecuteWorkObject, ptr, dwFlag );
if(bSuccess == FALSE) { dwStatus = GetLastError();
DBGPrintf( DBG_ERROR, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("RunJob() : queuing user work item %p failed with 0x%08x...\n"), ptr, dwStatus );
//TLSASSERT(dwStatus == ERROR_SUCCESS);
dwStatus = RemoveJobFromProcessingList(ptr); } } else { bSuccess = FALSE; } if(bSuccess == FALSE) { dwStatus = GetLastError(); //TLSASSERT(FALSE);
}
//
// release max. concurrent job lock
//
if(bImmediate == FALSE) { m_hMaxJobLock.Release(); } } else { dwStatus = TLS_I_WORKMANAGER_SHUTDOWN; } } else { SetLastError(dwStatus = ERROR_INVALID_PARAMETER); }
return dwStatus; }
//------------------------------------------------------------
//
//
CLASS_PRIVATE DWORD CWorkManager::ProcessScheduledJob() /*++
Abstract:
Class private, process a scheduled job.
Parameter:
None.
Return:
ERROR_SUCCESS or error code.
--*/ { DWORD dwStatus = ERROR_SUCCESS; BOOL bSuccess = TRUE; BOOL bFlag = FALSE;
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("CWorkManager::ProcessScheduledJob(), %d %d\n"), GetNumberJobInStorageQueue(), GetNumberJobInMemoryQueue() );
if(GetNumberJobInStorageQueue() != 0 && IsShuttingDown() == FALSE) { //
// Could have use work item to process both
// queue but this uses one extra handle, and
// work manager thread will just doing nothing
//
ResetEvent(m_hInStorageWait);
//
// Queue a user work item to thread pool to process
// in storage job
//
bSuccess = QueueUserWorkItem( CWorkManager::ProcessInStorageScheduledJob, this, WT_EXECUTELONGFUNCTION ); if(bSuccess == FALSE) { dwStatus = GetLastError(); DBGPrintf( DBG_ERROR, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CWorkManager::ProcessScheduledJob() queue user work iterm returns 0x%08x\n"), dwStatus );
TLSASSERT(dwStatus == ERROR_SUCCESS); } else { bFlag = TRUE; } }
if(bSuccess == TRUE) { dwStatus = ProcessInMemoryScheduledJob(this);
if(bFlag == TRUE) { if(WaitForObjectOrShutdown(m_hInStorageWait) == FALSE) { dwStatus = TLS_I_WORKMANAGER_SHUTDOWN; } } }
return dwStatus; }
//------------------------------------------------------------
//
//
CLASS_PRIVATE CLASS_STATIC DWORD WINAPI CWorkManager::ProcessInMemoryScheduledJob( IN PVOID pContext ) /*++
Abstract:
Static class private, process in-memory scheduled jobs. WorkManagerThread kick off two threads, one to process in-memory job and the other to process persistent job.
Parameter:
pContext : Pointer to work manager object.
Return:
ERROR_SUCCESS or error code.
--*/ { DWORD ulCurrentTime; DWORD dwJobTime; CWorkObject* pInMemoryWorkObject = NULL; BOOL bSuccess = TRUE; BOOL dwStatus = ERROR_SUCCESS; CWorkManager* pWkMgr = (CWorkManager *)pContext;
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("CWorkManager::ProcessInMemoryScheduledJob()\n") );
if(pWkMgr == NULL) { SetLastError(ERROR_INVALID_PARAMETER); TLSASSERT(pWkMgr != NULL); return ERROR_INVALID_PARAMETER; }
do { if(pWkMgr->IsShuttingDown() == TRUE) { dwStatus = TLS_I_WORKMANAGER_SHUTDOWN; break; }
ulCurrentTime = time(NULL); dwJobTime = ulCurrentTime; pInMemoryWorkObject = pWkMgr->GetNextJobInMemoryQueue(&dwJobTime);
if(pInMemoryWorkObject != NULL) { // TLSASSERT(dwJobTime <= ulCurrentTime);
if(dwJobTime <= ulCurrentTime) { dwStatus = pWkMgr->RunJob( pInMemoryWorkObject, FALSE );
if(dwStatus != ERROR_SUCCESS) { //
// consider to re-schedule job again.
//
pInMemoryWorkObject->EndJob();
if(pInMemoryWorkObject->CanBeDelete() == TRUE) { pInMemoryWorkObject->SelfDestruct(); } } } else { //
// Very expansive operation, GetNextJobInMemoryQueue() must be
// wrong.
//
dwStatus = pWkMgr->AddJobIntoMemoryQueue( dwJobTime, pInMemoryWorkObject );
if(dwStatus != ERROR_SUCCESS) { //
// delete the job
//
pInMemoryWorkObject->EndJob();
if(pInMemoryWorkObject->CanBeDelete() == TRUE) { pInMemoryWorkObject->SelfDestruct(); } } } } } while(dwStatus == ERROR_SUCCESS && (pInMemoryWorkObject != NULL && dwJobTime <= ulCurrentTime));
return dwStatus; }
//------------------------------------------------------------
//
//
CLASS_PRIVATE CLASS_STATIC DWORD WINAPI CWorkManager::ProcessInStorageScheduledJob( IN PVOID pContext ) /*++
Abstract:
Static class private, process scheduled persistent jobs. WorkManagerThread kick off two threads, one to process in-memory job and the other to process persistent job.
Parameter:
pContext : Pointer to work manager object.
Return:
ERROR_SUCCESS or error code.
--*/ { DWORD ulCurrentTime = 0; DWORD dwJobScheduledTime = 0; CWorkObject* pInStorageWorkObject = NULL; DWORD dwStatus = ERROR_SUCCESS; BOOL bSuccess = TRUE; CWorkManager* pWkMgr = (CWorkManager *)pContext;
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("CWorkManager::ProcessInStorageScheduledJob()\n") );
if(pWkMgr == NULL) { TLSASSERT(pWkMgr != NULL); SetLastError(ERROR_INVALID_PARAMETER); return ERROR_INVALID_PARAMETER; } TLSASSERT(pWkMgr->m_pPersistentWorkStorage != NULL);
if(pWkMgr->m_pPersistentWorkStorage->GetNumJobs() > 0) { do { if(pWkMgr->IsShuttingDown() == TRUE) { dwStatus = TLS_I_WORKMANAGER_SHUTDOWN; break; }
ulCurrentTime = time(NULL); pInStorageWorkObject = pWkMgr->m_pPersistentWorkStorage->GetNextJob(&dwJobScheduledTime);
if(pInStorageWorkObject == NULL) { //
// Something wrong in persistent storage???
//
DBGPrintf( DBG_WARNING, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CWorkManager::ProcessInStorageScheduledJob() : Persistent work storage return NULL job\n") );
break; } else if(dwJobScheduledTime > ulCurrentTime) { DBGPrintf( DBG_WARNING, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CWorkManager::ProcessInStorageScheduledJob() : return job back to persistent storage\n") );
pWkMgr->m_pPersistentWorkStorage->EndProcessingJob( ENDPROCESSINGJOB_RETURN, dwJobScheduledTime, pInStorageWorkObject ); } else { pInStorageWorkObject->SetScheduledTime(dwJobScheduledTime); pWkMgr->m_pPersistentWorkStorage->BeginProcessingJob(pInStorageWorkObject);
dwStatus = pWkMgr->RunJob( pInStorageWorkObject, FALSE );
if(dwStatus != ERROR_SUCCESS) { DBGPrintf( DBG_WARNING, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CWorkManager::ProcessInStorageScheduledJob() : unable to queue job, return job back ") \ _TEXT("to persistent storage\n") );
pWkMgr->m_pPersistentWorkStorage->EndProcessingJob( ENDPROCESSINGJOB_RETURN, pInStorageWorkObject->GetScheduledTime(), pInStorageWorkObject ); } } } while(dwStatus == ERROR_SUCCESS && ulCurrentTime >= dwJobScheduledTime); }
//
// Signal we are done
//
SetEvent(pWkMgr->m_hInStorageWait); return dwStatus; }
//------------------------------------------------------------
//
//
CLASS_PRIVATE CLASS_STATIC unsigned int __stdcall CWorkManager::WorkManagerThread( IN PVOID pContext ) /*++
Abstract:
Static class private, this is the work manager thread to handle job scheduling and process scheduled job. WorkManagerThread() will not terminate until m_hShutdown event is signal.
Parameter: pContext : Pointer to work manager object.
Returns:
ERROR_SUCCESS
--*/ { DWORD dwTimeToNextJob = INFINITE; CWorkManager* pWkMgr = (CWorkManager *)pContext; DWORD dwHandleFlag; TLSASSERT(pWkMgr != NULL); TLSASSERT(GetHandleInformation(pWkMgr->m_hNewJobArrive, &dwHandleFlag) == TRUE); TLSASSERT(GetHandleInformation(pWkMgr->m_hShutdown, &dwHandleFlag) == TRUE);
HANDLE m_hWaitHandles[] = {pWkMgr->m_hShutdown, pWkMgr->m_hNewJobArrive}; DWORD dwWaitStatus = WAIT_TIMEOUT; DWORD dwStatus = ERROR_SUCCESS; //
// Get the time to next job
//
while(dwWaitStatus != WAIT_OBJECT_0 && dwStatus == ERROR_SUCCESS) { DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("CWorkManager::WorkManagerThread() : Time to next job %d\n"), dwTimeToNextJob ); dwWaitStatus = WaitForMultipleObjectsEx( sizeof(m_hWaitHandles) / sizeof(m_hWaitHandles[0]), m_hWaitHandles, FALSE, dwTimeToNextJob * 1000, TRUE // we might need this thread to do some work
);
switch( dwWaitStatus ) { case WAIT_OBJECT_0: dwStatus = ERROR_SUCCESS;
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CWorkManager::WorkManagerThread() : shutdown ...\n") );
break;
case WAIT_OBJECT_0 + 1: // still a possibility that we might not catch a new job
ResetEvent(pWkMgr->m_hNewJobArrive); // New Job arrived
dwTimeToNextJob = pWkMgr->GetTimeToNextJob(); break; case WAIT_TIMEOUT: // Time to process job.
dwStatus = pWkMgr->ProcessScheduledJob(); dwTimeToNextJob = pWkMgr->GetTimeToNextJob(); break;
default: DBGPrintf( DBG_ERROR, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CWorkManager::WorkManagerThread() : unexpected return %d\n"), dwStatus );
dwStatus = TLS_E_WORKMANAGER_INTERNAL; TLSASSERT(FALSE); } }
if(dwStatus != ERROR_SUCCESS && dwStatus != TLS_I_WORKMANAGER_SHUTDOWN) { DBGPrintf( DBG_ERROR, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CWorkManager::WorkManagerThread() : unexpected return %d, generate console event\n"), dwStatus );
// immediately shut down server
GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); } _endthreadex(dwStatus); return dwStatus; }
//----------------------------------------------------------------
//
//
CLASS_PRIVATE CLASS_STATIC DWORD WINAPI CWorkManager::ExecuteWorkObject( IN PVOID pContext ) /*++
Abstract:
Static class private, execute a work object.
Parameter:
pContext : Pointer to work object to be process.
Returns:
ERROR_SUCCESS or error code. --*/ { DWORD dwStatus = ERROR_SUCCESS; CWorkObject* pWorkObject = (CWorkObject *)pContext; DWORD dwJobRescheduleTime; BOOL bStorageJobCompleted; CWorkManager* pWkMgr = NULL; BOOL bPersistentJob = FALSE;
if(pContext == NULL) { TLSASSERT(FALSE); dwStatus = ERROR_INVALID_PARAMETER; return dwStatus; }
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("CWorkManager::ExecuteWorkObject() : executing %p <%s>\n"), pWorkObject, pWorkObject->GetJobDescription() );
//
// Set RPC cancel timeout, thread dependent.
(VOID)RpcMgmtSetCancelTimeout(DEFAULT_RPCCANCEL_TIMEOUT);
bPersistentJob = pWorkObject->IsWorkPersistent();
pWkMgr = pWorkObject->GetWorkManager();
if(pWkMgr != NULL) { pWkMgr->SignalJobRunning(pWorkObject); // tell work manager that we are running
pWorkObject->ExecuteWorkObject();
if(bPersistentJob == TRUE) { //
// Persistent work object, let work storage handle
// its re-scheduling
//
bStorageJobCompleted = pWorkObject->IsJobCompleted(); pWorkObject->GetWorkManager()->m_pPersistentWorkStorage->EndProcessingJob( ENDPROCESSINGJOB_SUCCESS, pWorkObject->GetScheduledTime(), pWorkObject );
if(bStorageJobCompleted == FALSE) { //
// This job might be re-scheduled
// before our work manager thread wakes up,
// so signal job is ready
//
pWkMgr->SignalJobArrive(); } } else { //
// Reschedule job if necessary
//
dwJobRescheduleTime = pWorkObject->GetSuggestedScheduledTime(); if(dwJobRescheduleTime != INFINITE) { dwStatus = pWorkObject->ScheduleJob(dwJobRescheduleTime); }
if(dwJobRescheduleTime == INFINITE || dwStatus != ERROR_SUCCESS) { //
// if can't schedule job again, go ahead and delete it.
//
pWorkObject->EndJob(); if(pWorkObject->CanBeDelete() == TRUE) { pWorkObject->SelfDestruct(); } } } }
if(pWkMgr) { // Delete this job from in-processing list.
pWkMgr->EndProcessingScheduledJob(pWorkObject); }
return dwStatus; }
//----------------------------------------------------------------
//
//
CWorkManager::CWorkManager() : m_hWorkMgrThread(NULL), m_hNewJobArrive(NULL), m_hShutdown(NULL), m_hInStorageWait(NULL), m_hJobInProcessing(NULL), m_dwNextInMemoryJobTime(WORKMANAGER_WAIT_FOREVER), m_dwNextInStorageJobTime(WORKMANAGER_WAIT_FOREVER), m_dwMaxCurrentJob(DEFAULT_NUM_CONCURRENTJOB), m_dwDefaultInterval(DEFAULT_WORK_INTERVAL) { }
//----------------------------------------------------------------
//
CWorkManager::~CWorkManager() { Shutdown();
if(m_hNewJobArrive != NULL) { CloseHandle(m_hNewJobArrive); }
if(m_hWorkMgrThread != NULL) { CloseHandle(m_hWorkMgrThread); }
if(m_hShutdown != NULL) { CloseHandle(m_hShutdown); }
if(m_hInStorageWait != NULL) { CloseHandle(m_hInStorageWait); }
if(m_hJobInProcessing != NULL) { CloseHandle(m_hJobInProcessing); } }
//----------------------------------------------------------------
//
DWORD CWorkManager::Startup( IN CWorkStorage* pPersistentWorkStorage, IN DWORD dwWorkInterval, // DEFAULT_WORK_INTERVAL
IN DWORD dwNumConcurrentJob // DEFAULT_NUM_CONCURRENTJOB
)
/*++
Abstract:
Initialize work manager
Parameters:
pPersistentWorkStorage : A C++ object that derived from CPersistentWorkStorage class dwWorkInterval : Default schedule job interval dwNumConcurrentJob : Max. number of concurrent job to be fired at the same time
Return:
ERROR_SUCCESS or Erro Code.
--*/
{ DWORD index; DWORD dwStatus = ERROR_SUCCESS; unsigned dump; BOOL bSuccess; unsigned threadid;
#ifdef __TEST_WORKMGR__
_set_new_handler(handle_new_failed); #endif
if(dwNumConcurrentJob == 0 || dwWorkInterval == 0 || pPersistentWorkStorage == NULL) { SetLastError(dwStatus = ERROR_INVALID_PARAMETER); goto cleanup; }
if(m_hMaxJobLock.IsGood() == FALSE) { if(m_hMaxJobLock.Init(dwNumConcurrentJob, dwNumConcurrentJob) == FALSE) { //
// out of resource
//
dwStatus = GetLastError(); goto cleanup; } }
m_dwDefaultInterval = dwWorkInterval; m_dwMaxCurrentJob = dwNumConcurrentJob; m_pPersistentWorkStorage = pPersistentWorkStorage;
if(m_hJobInProcessing == NULL) { //
// initial state is signal, no job in processing
//
m_hJobInProcessing = CreateEvent( NULL, TRUE, TRUE, NULL ); if(m_hJobInProcessing == NULL) { dwStatus = GetLastError(); goto cleanup; } }
if(m_hShutdown == NULL) { //
// Create a handle for signaling shutdown
//
m_hShutdown = CreateEvent( NULL, TRUE, FALSE, NULL );
if(m_hShutdown == NULL) { dwStatus = GetLastError(); goto cleanup; } }
if(m_hNewJobArrive == NULL) { //
// initial state is signal so work manager thread can
// update wait time
//
m_hNewJobArrive = CreateEvent( NULL, TRUE, TRUE, NULL );
if(m_hNewJobArrive == NULL) { dwStatus = GetLastError(); goto cleanup; } }
if(m_hInStorageWait == NULL) { m_hInStorageWait = CreateEvent( NULL, TRUE, TRUE, // signal state
NULL );
if(m_hInStorageWait == NULL) { dwStatus = GetLastError(); goto cleanup; } }
//
// Startup Work Storage first.
//
if(m_pPersistentWorkStorage->Startup(this) == FALSE) { DBGPrintf( DBG_ERROR, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_DETAILSIMPLE, _TEXT("CWorkManager::Startup() : Persistent storage has failed to startup - 0x%08x\n"), GetLastError() ); dwStatus = GetLastError();
if (dwStatus == ERROR_SUCCESS) dwStatus = TLS_E_WORKMANAGER_PERSISTENJOB;
goto cleanup; }
//
// Get time to next persistent job.
//
if(UpdateTimeToNextPersistentJob() == FALSE) { dwStatus = TLS_E_WORKMANAGER_PERSISTENJOB; goto cleanup; }
if(m_hWorkMgrThread == NULL) { //
// Create work manager thread, suspended first
//
m_hWorkMgrThread = (HANDLE)_beginthreadex( NULL, 0, CWorkManager::WorkManagerThread, this, 0, &threadid );
if(m_hWorkMgrThread == NULL) { dwStatus = GetLastError(); goto cleanup; } }
cleanup:
return dwStatus; }
//----------------------------------------------------------------
void CWorkManager::Shutdown() /*++
Abstract:
Shutdown work manager.
Parameter:
None.
Return:
None.
--*/ { HANDLE handles[] = {m_hInStorageWait, m_hJobInProcessing}; DWORD dwStatus;
//
// Signal we are shuting down
//
if(m_hShutdown != NULL) { SetEvent(m_hShutdown); }
//
// Wait for dispatch thread to terminate so no job can be
// dispatched.
//
if(m_hWorkMgrThread != NULL) { dwStatus = WaitForSingleObject( m_hWorkMgrThread, INFINITE );
TLSASSERT(dwStatus != WAIT_FAILED); CloseHandle(m_hWorkMgrThread); m_hWorkMgrThread = NULL; }
//
// Cancel all in progress job
//
CancelInProcessingJob();
//
// Inform all existing job to shutdown.
//
DeleteAllJobsInMemoryQueue();
//
// Wait for all processing job to terminate
//
if(m_hInStorageWait != NULL && m_hJobInProcessing != NULL) { dwStatus = WaitForMultipleObjects( sizeof(handles)/sizeof(handles[0]), handles, TRUE, INFINITE );
TLSASSERT(dwStatus != WAIT_FAILED);
CloseHandle(m_hInStorageWait); m_hInStorageWait = NULL;
CloseHandle(m_hJobInProcessing); m_hJobInProcessing = NULL; }
if(m_pPersistentWorkStorage != NULL) { //
// Signal we are shutting down, no job is in
// processing and we are not taking any
// new job
//
m_pPersistentWorkStorage->Shutdown(); m_pPersistentWorkStorage = NULL; } TLSASSERT( GetNumberJobInProcessing() == 0 ); // TLSASSERT( GetNumberJobInMemoryQueue() == 0 );
if(m_hNewJobArrive != NULL) { CloseHandle(m_hNewJobArrive); m_hNewJobArrive = NULL; }
if(m_hWorkMgrThread != NULL) { CloseHandle(m_hWorkMgrThread); m_hWorkMgrThread = NULL; }
if(m_hShutdown != NULL) { CloseHandle(m_hShutdown); m_hShutdown = NULL; }
if(m_hInStorageWait != NULL) { CloseHandle(m_hInStorageWait); m_hInStorageWait = NULL; }
if(m_hJobInProcessing != NULL) { CloseHandle(m_hJobInProcessing); m_hJobInProcessing = NULL; }
return; }
//----------------------------------------------------------------
CLASS_PRIVATE DWORD CWorkManager::GetTimeToNextJob() /*++
Abstract:
Class private, return time to next scheduled job.
Parameter:
None.
Return:
Time to next job in second.
--*/ { DWORD dwNextJobTime = WORKMANAGER_WAIT_FOREVER; DWORD dwNumPersistentJob = GetNumberJobInStorageQueue(); DWORD dwNumInMemoryJob = GetNumberJobInMemoryQueue(); DWORD dwCurrentTime = time(NULL);
if( dwNumPersistentJob == 0 && dwNumInMemoryJob == 0 ) { // DO NOTHING
// dwTimeToNextJob = WORKMANAGER_WAIT_FOREVER;
} else { UpdateTimeToNextInMemoryJob(); UpdateTimeToNextPersistentJob();
dwNextJobTime = min((DWORD)m_dwNextInMemoryJobTime, (DWORD)m_dwNextInStorageJobTime);
if((DWORD)dwNextJobTime < (DWORD)dwCurrentTime) { dwNextJobTime = 0; } else { dwNextJobTime -= dwCurrentTime; } }
return dwNextJobTime; }
//----------------------------------------------------------------
//
CLASS_PRIVATE CWorkObject* CWorkManager::GetNextJobInMemoryQueue( PDWORD pdwTime ) /*++
Abstract:
Class private, return pointer to next scheduled in memory job.
Parameter:
pdwTime : Pointer to DWORD to receive time to the scheduled job.
Returns:
Pointer to CWorkObject.
Note:
Remove the job from queue if job is <= time.
--*/ { SCHEDULEJOBMAP::iterator it; DWORD dwWantedJobTime; CWorkObject* ptr = NULL;
SetLastError(ERROR_SUCCESS);
if(pdwTime != NULL) { dwWantedJobTime = *pdwTime; m_JobLock.Acquire(READER_LOCK);
it = m_Jobs.begin(); if(it != m_Jobs.end()) { *pdwTime = (*it).first;
if(dwWantedJobTime >= *pdwTime) { ptr = (*it).second;
// remove job from queue
m_Jobs.erase(it); } } m_JobLock.Release(READER_LOCK); } else { SetLastError(ERROR_INVALID_PARAMETER); }
return ptr; }
//----------------------------------------------------------------
//
CLASS_PRIVATE void CWorkManager::DeleteAllJobsInMemoryQueue() /*++
Abstract:
Class private, unconditionally delete all in-memory job.
Parameter:
None.
Return:
None.
--*/ { m_JobLock.Acquire(WRITER_LOCK);
SCHEDULEJOBMAP::iterator it;
for(it = m_Jobs.begin(); it != m_Jobs.end(); it++) { //
// let calling routine to delete it
//
(*it).second->EndJob(); if((*it).second->CanBeDelete() == TRUE) { (*it).second->SelfDestruct(); } (*it).second = NULL; }
m_Jobs.erase(m_Jobs.begin(), m_Jobs.end()); m_JobLock.Release(WRITER_LOCK); return; }
//----------------------------------------------------------------
//
CLASS_PRIVATE BOOL CWorkManager::RemoveJobFromInMemoryQueue( IN DWORD ulTime, IN CWorkObject* ptr ) /*++
Abstract:
Class private, remove a scheduled job.
Parameters:
ulTime : Job scheduled time. ptr : Pointer to Job to be deleted.
Returns:
TRUE/FALSE.
Note:
A job might be scheduled multiple time so we need to pass in the time.
--*/ { BOOL bSuccess = FALSE;
m_JobLock.Acquire(WRITER_LOCK);
SCHEDULEJOBMAP::iterator low = m_Jobs.lower_bound(ulTime); SCHEDULEJOBMAP::iterator high = m_Jobs.upper_bound(ulTime);
for(;low != m_Jobs.end() && low != high; low++) { if( (*low).second == ptr ) { //
// let calling routine to delete it
//
(*low).second = NULL; m_Jobs.erase(low); bSuccess = TRUE; break; } }
if(bSuccess == FALSE) { SetLastError(ERROR_INVALID_DATA); TLSASSERT(FALSE); }
m_JobLock.Release(WRITER_LOCK); return bSuccess; } //----------------------------------------------------------------
//
CLASS_PRIVATE DWORD CWorkManager::AddJobIntoMemoryQueue( IN DWORD dwTime, // suggested scheduled time
IN CWorkObject* pJob // Job to be scheduled
) /*++
Abstract:
Class private, add a job into in-memory list.
Parameters:
dwTime : suggested scheduled time. pJob : Pointer to job to be added.
Returns:
ERROR_SUCCESS or error code.
--*/ { DWORD dwStatus = ERROR_SUCCESS; BOOL bSuccess = FALSE; DWORD dwJobScheduleTime = time(NULL) + dwTime;
if(IsShuttingDown() == TRUE) { dwStatus = TLS_I_WORKMANAGER_SHUTDOWN; return dwStatus; }
m_JobLock.Acquire(WRITER_LOCK);
//
// insert a job into our queue
//
m_Jobs.insert( SCHEDULEJOBMAP::value_type( dwJobScheduleTime, pJob ) ); AddJobUpdateInMemoryJobWaitTimer(dwJobScheduleTime); m_JobLock.Release(WRITER_LOCK); return dwStatus; } //----------------------------------------------------------------
//
DWORD CWorkManager::ScheduleJob( IN DWORD ulTime, // suggested scheduled time
IN CWorkObject* pJob // Job to be scheduled
)
/*++
Abstract:
Schedule a job at time relative to current time
Parameters: ulTime : suggested scheduled time. pJob : Pointer to job to be scheduled
Returns:
ERROR_SUCCESS or error code.
--*/
{ BOOL bSuccess = TRUE; DWORD dwStatus = ERROR_SUCCESS;
if(pJob == NULL) { SetLastError(dwStatus = ERROR_INVALID_PARAMETER); goto cleanup; }
if(IsShuttingDown() == TRUE) { SetLastError(dwStatus = TLS_I_WORKMANAGER_SHUTDOWN); goto cleanup; }
DBGPrintf( DBG_INFORMATION, DBG_FACILITY_WORKMGR, DBGLEVEL_FUNCTION_TRACE, _TEXT("CWorkManager::ScheduleJob() : schedule job <%s> to queue at time %d\n"), pJob->GetJobDescription(), ulTime );
pJob->SetProcessingWorkManager(this);
if(ulTime == INFINITE && pJob->IsWorkPersistent() == FALSE) { //
// Only in-memory job can be executed at once.
//
dwStatus = RunJob(pJob, TRUE); } else { if(pJob->IsWorkPersistent() == TRUE) { if(m_pPersistentWorkStorage->AddJob(ulTime, pJob) == FALSE) { dwStatus = TLS_E_WORKMANAGER_PERSISTENJOB; } } else { //
// insert a workobject into job queue, reason not to
// use RegisterWaitForSingleObject() or threadpool's timer
// is that we don't need to track handle nor wait for
// DeleteTimerXXX to finish
//
dwStatus = AddJobIntoMemoryQueue( ulTime, // Memory queue is absolute time
pJob ); }
if(dwStatus == ERROR_SUCCESS) { if(SignalJobArrive() == FALSE) { dwStatus = GetLastError(); TLSASSERT(FALSE); } } }
cleanup:
return dwStatus; }
///////////////////////////////////////////////////////////////
//
// CWorkObject base class
//
CWorkObject::CWorkObject( IN BOOL bDestructorDelete /* = FALSE */ ) : m_dwLastRunStatus(ERROR_SUCCESS), m_refCount(0), m_pWkMgr(NULL), m_bCanBeFree(bDestructorDelete) { }
//----------------------------------------------------------
DWORD CWorkObject::Init( IN BOOL bDestructorDelete /* = FALSE */ ) /*++
Abstract:
Initialize a work object.
Parameter:
bDestructorDelete : TRUE if destructor should delete the memory, FALSE otherwise.
Returns:
ERROR_SUCCESS or error code.
Note:
if bDestructorDelete is FALSE, memory will not be free.
--*/ { m_dwLastRunStatus = ERROR_SUCCESS; m_refCount = 0; m_bCanBeFree = bDestructorDelete; return ERROR_SUCCESS; }
//----------------------------------------------------------
CLASS_PRIVATE long CWorkObject::GetReferenceCount() /*++
Abstract:
Return reference count of work object.
Parameter:
None.
Return:
Reference count.
--*/ { return m_refCount; }
//----------------------------------------------------------
CLASS_PRIVATE void CWorkObject::IncrementRefCount() /*++
Abstract:
Increment object's reference counter.
Parameter:
None.
Return:
None.
--*/ { InterlockedIncrement(&m_refCount); }
//----------------------------------------------------------
CLASS_PRIVATE void CWorkObject::DecrementRefCount() /*++
Abstract:
Decrement object's reference counter.
Parameter:
None.
Return:
None.
--*/ { InterlockedDecrement(&m_refCount); }
//----------------------------------------------------------
CLASS_PRIVATE void CWorkObject::ExecuteWorkObject() /*++
Abstract:
Execute a work object. Work manager invoke work object's ExecuteWorkObject so that base class can set its reference counter.
Parameter:
None.
Return:
None.
--*/ { if(IsValid() == TRUE) { IncrementRefCount(); m_dwLastRunStatus = Execute(); DecrementRefCount(); } else { m_dwLastRunStatus = ERROR_INVALID_DATA; TLSASSERT(FALSE); } }
//----------------------------------------------------------
CLASS_PRIVATE void CWorkObject::EndExecuteWorkObject() /*++
Abstract:
End a job, this does not terminate job currently in processing, it remove the job from work manager's in-processing list
Parameter:
None.
Return:
None.
--*/ { TLSASSERT(IsValid() == TRUE); m_pWkMgr->EndProcessingScheduledJob(this); }
|