You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1967 lines
48 KiB
1967 lines
48 KiB
//+--------------------------------------------------------------------------
|
|
//
|
|
// 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);
|
|
}
|
|
|